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 |