| 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 13 matching lines...) Expand all Loading... |
| 24 try { | 24 try { |
| 25 location = node?.location ?? context?.location; | 25 location = node?.location ?? context?.location; |
| 26 } catch (_) { | 26 } catch (_) { |
| 27 // TODO(ahe): Fix the compiler instead. | 27 // TODO(ahe): Fix the compiler instead. |
| 28 } | 28 } |
| 29 if (location != null) { | 29 if (location != null) { |
| 30 String file = location.file ?? ""; | 30 String file = location.file ?? ""; |
| 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 | 34 return "Verification error: $details\nContext: '$context'.\nNode: '$node'.
"; |
| 35 "Verification error: $details\nContext: '$context'.\nNode: '$node'."; | |
| 36 } | 35 } |
| 37 } | 36 } |
| 38 } | 37 } |
| 39 | 38 |
| 40 /// Checks that a kernel program is well-formed. | 39 /// Checks that a kernel program is well-formed. |
| 41 /// | 40 /// |
| 42 /// This does not include any kind of type checking. | 41 /// This does not include any kind of type checking. |
| 43 class VerifyingVisitor extends RecursiveVisitor { | 42 class VerifyingVisitor extends RecursiveVisitor { |
| 44 final Set<Class> classes = new Set<Class>(); | 43 final Set<Class> classes = new Set<Class>(); |
| 45 final Set<TypeParameter> typeParameters = new Set<TypeParameter>(); | 44 final Set<TypeParameter> typeParameters = new Set<TypeParameter>(); |
| (...skipping 17 matching lines...) Expand all Loading... |
| 63 defaultTreeNode(TreeNode node) { | 62 defaultTreeNode(TreeNode node) { |
| 64 visitChildren(node); | 63 visitChildren(node); |
| 65 } | 64 } |
| 66 | 65 |
| 67 problem(TreeNode node, String details) { | 66 problem(TreeNode node, String details) { |
| 68 throw new VerificationError(context, node, details); | 67 throw new VerificationError(context, node, details); |
| 69 } | 68 } |
| 70 | 69 |
| 71 TreeNode enterParent(TreeNode node) { | 70 TreeNode enterParent(TreeNode node) { |
| 72 if (!identical(node.parent, currentParent)) { | 71 if (!identical(node.parent, currentParent)) { |
| 73 problem(node, | 72 problem( |
| 73 node, |
| 74 "Incorrect parent pointer: expected '${node.parent.runtimeType}'," | 74 "Incorrect parent pointer: expected '${node.parent.runtimeType}'," |
| 75 " but found: '${currentParent.runtimeType}'."); | 75 " but found: '${currentParent.runtimeType}'."); |
| 76 } | 76 } |
| 77 var oldParent = currentParent; | 77 var oldParent = currentParent; |
| 78 currentParent = node; | 78 currentParent = node; |
| 79 return oldParent; | 79 return oldParent; |
| 80 } | 80 } |
| 81 | 81 |
| 82 void exitParent(TreeNode oldParent) { | 82 void exitParent(TreeNode oldParent) { |
| 83 currentParent = oldParent; | 83 currentParent = oldParent; |
| (...skipping 233 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 317 } | 317 } |
| 318 | 318 |
| 319 @override | 319 @override |
| 320 visitStaticInvocation(StaticInvocation node) { | 320 visitStaticInvocation(StaticInvocation node) { |
| 321 checkTargetedInvocation(node.target, node); | 321 checkTargetedInvocation(node.target, node); |
| 322 if (node.target.isInstanceMember) { | 322 if (node.target.isInstanceMember) { |
| 323 problem(node, | 323 problem(node, |
| 324 "StaticInvocation of '${node.target}' that's an instance member."); | 324 "StaticInvocation of '${node.target}' that's an instance member."); |
| 325 } | 325 } |
| 326 if (node.isConst && | 326 if (node.isConst && |
| 327 (!node.target.isConst || !node.target.isExternal || | 327 (!node.target.isConst || |
| 328 !node.target.isExternal || |
| 328 node.target.kind != ProcedureKind.Factory)) { | 329 node.target.kind != ProcedureKind.Factory)) { |
| 329 problem(node, "Constant StaticInvocation of '${node.target}' that isn't" | 330 problem( |
| 331 node, |
| 332 "Constant StaticInvocation of '${node.target}' that isn't" |
| 330 " a const external factory."); | 333 " a const external factory."); |
| 331 } | 334 } |
| 332 } | 335 } |
| 333 | 336 |
| 334 void checkTargetedInvocation(Member target, InvocationExpression node) { | 337 void checkTargetedInvocation(Member target, InvocationExpression node) { |
| 335 visitChildren(node); | 338 visitChildren(node); |
| 336 if (target == null) { | 339 if (target == null) { |
| 337 problem(node, "${node.runtimeType} without target."); | 340 problem(node, "${node.runtimeType} without target."); |
| 338 } | 341 } |
| 339 if (target.function == null) { | 342 if (target.function == null) { |
| 340 problem(node, "${node.runtimeType} without function."); | 343 problem(node, "${node.runtimeType} without function."); |
| 341 } | 344 } |
| 342 if (!areArgumentsCompatible(node.arguments, target.function)) { | 345 if (!areArgumentsCompatible(node.arguments, target.function)) { |
| 343 problem(node, | 346 problem(node, |
| 344 "${node.runtimeType} with incompatible arguments for '${target}'."); | 347 "${node.runtimeType} with incompatible arguments for '${target}'."); |
| 345 } | 348 } |
| 346 int expectedTypeParameters = target is Constructor | 349 int expectedTypeParameters = target is Constructor |
| 347 ? target.enclosingClass.typeParameters.length | 350 ? target.enclosingClass.typeParameters.length |
| 348 : target.function.typeParameters.length; | 351 : target.function.typeParameters.length; |
| 349 if (node.arguments.types.length != expectedTypeParameters) { | 352 if (node.arguments.types.length != expectedTypeParameters) { |
| 350 problem(node, "${node.runtimeType} with wrong number of type arguments" | 353 problem( |
| 354 node, |
| 355 "${node.runtimeType} with wrong number of type arguments" |
| 351 " for '${target}'."); | 356 " for '${target}'."); |
| 352 } | 357 } |
| 353 } | 358 } |
| 354 | 359 |
| 355 @override | 360 @override |
| 356 visitDirectPropertyGet(DirectPropertyGet node) { | 361 visitDirectPropertyGet(DirectPropertyGet node) { |
| 357 visitChildren(node); | 362 visitChildren(node); |
| 358 if (node.target == null) { | 363 if (node.target == null) { |
| 359 problem(node, "DirectPropertyGet without target."); | 364 problem(node, "DirectPropertyGet without target."); |
| 360 } | 365 } |
| 361 if (!node.target.hasGetter) { | 366 if (!node.target.hasGetter) { |
| 362 problem(node, "DirectPropertyGet of '${node.target}' without getter."); | 367 problem(node, "DirectPropertyGet of '${node.target}' without getter."); |
| 363 } | 368 } |
| 364 if (!node.target.isInstanceMember) { | 369 if (!node.target.isInstanceMember) { |
| 365 problem(node, "DirectPropertyGet of '${node.target}' that isn't an" | 370 problem( |
| 371 node, |
| 372 "DirectPropertyGet of '${node.target}' that isn't an" |
| 366 " instance member."); | 373 " instance member."); |
| 367 } | 374 } |
| 368 } | 375 } |
| 369 | 376 |
| 370 @override | 377 @override |
| 371 visitDirectPropertySet(DirectPropertySet node) { | 378 visitDirectPropertySet(DirectPropertySet node) { |
| 372 visitChildren(node); | 379 visitChildren(node); |
| 373 if (node.target == null) { | 380 if (node.target == null) { |
| 374 problem(node, "DirectPropertySet without target."); | 381 problem(node, "DirectPropertySet without target."); |
| 375 } | 382 } |
| (...skipping 13 matching lines...) Expand all Loading... |
| 389 } | 396 } |
| 390 } | 397 } |
| 391 | 398 |
| 392 @override | 399 @override |
| 393 visitConstructorInvocation(ConstructorInvocation node) { | 400 visitConstructorInvocation(ConstructorInvocation node) { |
| 394 checkTargetedInvocation(node.target, node); | 401 checkTargetedInvocation(node.target, node); |
| 395 if (node.target.enclosingClass.isAbstract) { | 402 if (node.target.enclosingClass.isAbstract) { |
| 396 problem(node, "ConstructorInvocation of abstract class."); | 403 problem(node, "ConstructorInvocation of abstract class."); |
| 397 } | 404 } |
| 398 if (node.isConst && !node.target.isConst) { | 405 if (node.isConst && !node.target.isConst) { |
| 399 problem(node, "Constant ConstructorInvocation fo '${node.target}' that" | 406 problem( |
| 407 node, |
| 408 "Constant ConstructorInvocation fo '${node.target}' that" |
| 400 " isn't const."); | 409 " isn't const."); |
| 401 } | 410 } |
| 402 } | 411 } |
| 403 | 412 |
| 404 bool areArgumentsCompatible(Arguments arguments, FunctionNode function) { | 413 bool areArgumentsCompatible(Arguments arguments, FunctionNode function) { |
| 405 if (arguments.positional.length < function.requiredParameterCount) { | 414 if (arguments.positional.length < function.requiredParameterCount) { |
| 406 return false; | 415 return false; |
| 407 } | 416 } |
| 408 if (arguments.positional.length > function.positionalParameters.length) { | 417 if (arguments.positional.length > function.positionalParameters.length) { |
| 409 return false; | 418 return false; |
| 410 } | 419 } |
| 411 namedLoop: | 420 namedLoop: |
| 412 for (int i = 0; i < arguments.named.length; ++i) { | 421 for (int i = 0; i < arguments.named.length; ++i) { |
| 413 var argument = arguments.named[i]; | 422 var argument = arguments.named[i]; |
| 414 String name = argument.name; | 423 String name = argument.name; |
| 415 for (int j = 0; j < function.namedParameters.length; ++j) { | 424 for (int j = 0; j < function.namedParameters.length; ++j) { |
| 416 if (function.namedParameters[j].name == name) continue namedLoop; | 425 if (function.namedParameters[j].name == name) continue namedLoop; |
| 417 } | 426 } |
| 418 return false; | 427 return false; |
| 419 } | 428 } |
| 420 return true; | 429 return true; |
| 421 } | 430 } |
| 422 | 431 |
| 423 @override | 432 @override |
| 424 defaultMemberReference(Member node) { | 433 defaultMemberReference(Member node) { |
| 425 if (node.transformerFlags & TransformerFlag.seenByVerifier == 0) { | 434 if (node.transformerFlags & TransformerFlag.seenByVerifier == 0) { |
| 426 problem(node, | 435 problem( |
| 427 "Dangling reference to '$node', parent is: '${node.parent}'."); | 436 node, "Dangling reference to '$node', parent is: '${node.parent}'."); |
| 428 } | 437 } |
| 429 } | 438 } |
| 430 | 439 |
| 431 @override | 440 @override |
| 432 visitClassReference(Class node) { | 441 visitClassReference(Class node) { |
| 433 if (!classes.contains(node)) { | 442 if (!classes.contains(node)) { |
| 434 problem(node, | 443 problem( |
| 435 "Dangling reference to '$node', parent is: '${node.parent}'."); | 444 node, "Dangling reference to '$node', parent is: '${node.parent}'."); |
| 436 } | 445 } |
| 437 } | 446 } |
| 438 | 447 |
| 439 @override | 448 @override |
| 440 visitTypeParameterType(TypeParameterType node) { | 449 visitTypeParameterType(TypeParameterType node) { |
| 441 var parameter = node.parameter; | 450 var parameter = node.parameter; |
| 442 if (!typeParameters.contains(parameter)) { | 451 if (!typeParameters.contains(parameter)) { |
| 443 problem(currentParent, "Type parameter '$parameter' referenced out of" | 452 problem( |
| 453 currentParent, |
| 454 "Type parameter '$parameter' referenced out of" |
| 444 " scope, parent is: '${parameter.parent}'."); | 455 " scope, parent is: '${parameter.parent}'."); |
| 445 } | 456 } |
| 446 if (parameter.parent is Class && !classTypeParametersAreInScope) { | 457 if (parameter.parent is Class && !classTypeParametersAreInScope) { |
| 447 problem(currentParent, "Type parameter '$parameter' referenced from" | 458 problem( |
| 459 currentParent, |
| 460 "Type parameter '$parameter' referenced from" |
| 448 " static context, parent is '${parameter.parent}'."); | 461 " static context, parent is '${parameter.parent}'."); |
| 449 } | 462 } |
| 450 } | 463 } |
| 451 | 464 |
| 452 @override | 465 @override |
| 453 visitInterfaceType(InterfaceType node) { | 466 visitInterfaceType(InterfaceType node) { |
| 454 node.visitChildren(this); | 467 node.visitChildren(this); |
| 455 if (node.typeArguments.length != node.classNode.typeParameters.length) { | 468 if (node.typeArguments.length != node.classNode.typeParameters.length) { |
| 456 problem(currentParent, "Type $node provides ${node.typeArguments.length}" | 469 problem( |
| 470 currentParent, |
| 471 "Type $node provides ${node.typeArguments.length}" |
| 457 " type arguments but the class declares" | 472 " type arguments but the class declares" |
| 458 " ${node.classNode.typeParameters.length} parameters."); | 473 " ${node.classNode.typeParameters.length} parameters."); |
| 459 } | 474 } |
| 460 } | 475 } |
| 461 } | 476 } |
| 462 | 477 |
| 463 class CheckParentPointers extends Visitor { | 478 class CheckParentPointers extends Visitor { |
| 464 static void check(TreeNode node) { | 479 static void check(TreeNode node) { |
| 465 node.accept(new CheckParentPointers(node.parent)); | 480 node.accept(new CheckParentPointers(node.parent)); |
| 466 } | 481 } |
| 467 | 482 |
| 468 TreeNode parent; | 483 TreeNode parent; |
| 469 | 484 |
| 470 CheckParentPointers([this.parent]); | 485 CheckParentPointers([this.parent]); |
| 471 | 486 |
| 472 defaultTreeNode(TreeNode node) { | 487 defaultTreeNode(TreeNode node) { |
| 473 if (node.parent != parent) { | 488 if (node.parent != parent) { |
| 474 throw new VerificationError(parent, node, | 489 throw new VerificationError( |
| 490 parent, |
| 491 node, |
| 475 "Parent pointer on '${node.runtimeType}' " | 492 "Parent pointer on '${node.runtimeType}' " |
| 476 "is '${node.parent.runtimeType}' " | 493 "is '${node.parent.runtimeType}' " |
| 477 "but should be '${parent.runtimeType}'."); | 494 "but should be '${parent.runtimeType}'."); |
| 478 } | 495 } |
| 479 var oldParent = parent; | 496 var oldParent = parent; |
| 480 parent = node; | 497 parent = node; |
| 481 node.visitChildren(this); | 498 node.visitChildren(this); |
| 482 parent = oldParent; | 499 parent = oldParent; |
| 483 } | 500 } |
| 484 } | 501 } |
| 485 | 502 |
| 486 void checkInitializers(Constructor constructor) { | 503 void checkInitializers(Constructor constructor) { |
| 487 // TODO(ahe): I'll add more here in other CLs. | 504 // TODO(ahe): I'll add more here in other CLs. |
| 488 } | 505 } |
| OLD | NEW |