Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 engine.incremental_resolver; | 5 library engine.incremental_resolver; |
| 6 | 6 |
| 7 import 'dart:collection'; | 7 import 'dart:collection'; |
| 8 import 'dart:io'; | |
|
Brian Wilkerson
2014/12/03 15:05:17
Won't this pull in a dependency on dart:io that so
| |
| 8 import 'dart:math' as math; | 9 import 'dart:math' as math; |
| 9 | 10 |
| 10 import 'package:analyzer/src/generated/error_verifier.dart'; | |
| 11 import 'package:analyzer/src/generated/utilities_dart.dart'; | |
| 12 | |
| 13 import 'ast.dart'; | 11 import 'ast.dart'; |
| 14 import 'element.dart'; | 12 import 'element.dart'; |
| 15 import 'engine.dart'; | 13 import 'engine.dart'; |
| 16 import 'error.dart'; | 14 import 'error.dart'; |
| 15 import 'error_verifier.dart'; | |
| 17 import 'java_engine.dart'; | 16 import 'java_engine.dart'; |
| 18 import 'parser.dart'; | 17 import 'parser.dart'; |
| 19 import 'resolver.dart'; | 18 import 'resolver.dart'; |
| 20 import 'scanner.dart'; | 19 import 'scanner.dart'; |
| 21 import 'source.dart'; | 20 import 'source.dart'; |
| 21 import 'utilities_dart.dart'; | |
| 22 | |
| 23 | |
| 24 _Logger _logger = new _NullLogger(); | |
|
Brian Wilkerson
2014/12/03 15:05:17
The logger should be moved to a separate library (
scheglov
2014/12/03 15:58:09
Done.
| |
| 25 String _loggerSpec = null; | |
| 26 | |
| 27 | |
| 28 void _initLogger(String spec) { | |
| 29 // check if the same logging specification | |
| 30 if (spec == _loggerSpec) { | |
| 31 return; | |
| 32 } | |
| 33 _loggerSpec = spec; | |
| 34 // check for null | |
| 35 _logger = new _NullLogger(); | |
| 36 if (spec == null) { | |
| 37 return; | |
| 38 } | |
| 39 // create logger | |
| 40 if (spec == 'console') { | |
| 41 _logger = new _StringSinkLogger(console.log); | |
| 42 } | |
| 43 if (spec.startsWith('file:')) { | |
| 44 String fileName = spec.substring('file:'.length); | |
| 45 File file = new File(fileName); | |
| 46 IOSink sink = file.openWrite(); | |
| 47 _logger = new _StringSinkLogger(sink); | |
| 48 } | |
| 49 } | |
| 22 | 50 |
| 23 | 51 |
| 24 /** | 52 /** |
| 25 * Instances of the class [DeclarationMatcher] determine whether the element | 53 * Instances of the class [DeclarationMatcher] determine whether the element |
| 26 * model defined by a given AST structure matches an existing element model. | 54 * model defined by a given AST structure matches an existing element model. |
| 27 */ | 55 */ |
| 28 class DeclarationMatcher extends RecursiveAstVisitor { | 56 class DeclarationMatcher extends RecursiveAstVisitor { |
| 29 /** | 57 /** |
| 30 * The libary containing the AST nodes being visited. | 58 * The libary containing the AST nodes being visited. |
| 31 */ | 59 */ |
| (...skipping 873 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 905 int _updateEndOld; | 933 int _updateEndOld; |
| 906 int _updateEndNew; | 934 int _updateEndNew; |
| 907 | 935 |
| 908 List<AnalysisError> _newScanErrors = <AnalysisError>[]; | 936 List<AnalysisError> _newScanErrors = <AnalysisError>[]; |
| 909 List<AnalysisError> _newParseErrors = <AnalysisError>[]; | 937 List<AnalysisError> _newParseErrors = <AnalysisError>[]; |
| 910 List<AnalysisError> _newResolveErrors = <AnalysisError>[]; | 938 List<AnalysisError> _newResolveErrors = <AnalysisError>[]; |
| 911 List<AnalysisError> _newVerifyErrors = <AnalysisError>[]; | 939 List<AnalysisError> _newVerifyErrors = <AnalysisError>[]; |
| 912 List<AnalysisError> _newHints = <AnalysisError>[]; | 940 List<AnalysisError> _newHints = <AnalysisError>[]; |
| 913 | 941 |
| 914 PoorMansIncrementalResolver(this._typeProvider, this._unitSource, | 942 PoorMansIncrementalResolver(this._typeProvider, this._unitSource, |
| 915 this._librarySource, this._entry); | 943 this._librarySource, this._entry, String logSpec) { |
| 944 _initLogger(logSpec); | |
| 945 } | |
| 916 | 946 |
| 917 /** | 947 /** |
| 918 * Attempts to update [oldUnit] to the state corresponding to [newCode]. | 948 * Attempts to update [oldUnit] to the state corresponding to [newCode]. |
| 919 * Returns `true` if success, or `false` otherwise. | 949 * Returns `true` if success, or `false` otherwise. |
| 920 * The [oldUnit] might be damaged. | 950 * The [oldUnit] might be damaged. |
| 921 */ | 951 */ |
| 922 bool resolve(CompilationUnit oldUnit, String newCode) { | 952 bool resolve(CompilationUnit oldUnit, String newCode) { |
| 953 _logger.enter('resolve $_unitSource'); | |
| 954 _logger.log(oldUnit != null ? 'has oldUnit' : 'oldUnit is null'); | |
| 923 try { | 955 try { |
| 924 CompilationUnit newUnit = _parseUnit(newCode); | 956 CompilationUnit newUnit = _parseUnit(newCode); |
| 925 _TokenPair firstPair = | 957 _TokenPair firstPair = |
| 926 _findFirstDifferentToken(oldUnit.beginToken, newUnit.beginToken); | 958 _findFirstDifferentToken(oldUnit.beginToken, newUnit.beginToken); |
| 927 _TokenPair lastPair = | 959 _TokenPair lastPair = |
| 928 _findLastDifferentToken(oldUnit.endToken, newUnit.endToken); | 960 _findLastDifferentToken(oldUnit.endToken, newUnit.endToken); |
| 929 if (firstPair != null && lastPair != null) { | 961 if (firstPair != null && lastPair != null) { |
| 930 int firstOffsetOld = firstPair.oldToken.offset; | 962 int firstOffsetOld = firstPair.oldToken.offset; |
| 931 int firstOffsetNew = firstPair.newToken.offset; | 963 int firstOffsetNew = firstPair.newToken.offset; |
| 932 int lastOffsetOld = lastPair.oldToken.end; | 964 int lastOffsetOld = lastPair.oldToken.end; |
| 933 int lastOffsetNew = lastPair.newToken.end; | 965 int lastOffsetNew = lastPair.newToken.end; |
| 934 int beginOffsetOld = math.min(firstOffsetOld, lastOffsetOld); | 966 int beginOffsetOld = math.min(firstOffsetOld, lastOffsetOld); |
| 935 int endOffsetOld = math.max(firstOffsetOld, lastOffsetOld); | 967 int endOffsetOld = math.max(firstOffsetOld, lastOffsetOld); |
| 936 int beginOffsetNew = math.min(firstOffsetNew, lastOffsetNew); | 968 int beginOffsetNew = math.min(firstOffsetNew, lastOffsetNew); |
| 937 int endOffsetNew = math.max(firstOffsetNew, lastOffsetNew); | 969 int endOffsetNew = math.max(firstOffsetNew, lastOffsetNew); |
| 938 // check for a whitespace only change | 970 // check for a whitespace only change |
| 939 if (identical(lastPair.oldToken, firstPair.oldToken) && | 971 if (identical(lastPair.oldToken, firstPair.oldToken) && |
| 940 identical(lastPair.newToken, firstPair.newToken)) { | 972 identical(lastPair.newToken, firstPair.newToken)) { |
| 941 _updateOffset = beginOffsetOld - 1; | 973 _updateOffset = beginOffsetOld - 1; |
| 942 _updateEndOld = endOffsetOld; | 974 _updateEndOld = endOffsetOld; |
| 943 _updateDelta = newUnit.length - oldUnit.length; | 975 _updateDelta = newUnit.length - oldUnit.length; |
| 944 if (firstPair.atComment && lastPair.atComment) { | 976 if (firstPair.atComment && lastPair.atComment) { |
| 977 _logger.log('Comment change.'); | |
| 945 _resolveComment(oldUnit, newUnit, firstPair); | 978 _resolveComment(oldUnit, newUnit, firstPair); |
| 946 } else { | 979 } else { |
| 980 _logger.log('Whitespace change.'); | |
| 947 _shiftTokens(firstPair.oldToken); | 981 _shiftTokens(firstPair.oldToken); |
| 948 IncrementalResolver._updateElementNameOffsets( | 982 IncrementalResolver._updateElementNameOffsets( |
| 949 oldUnit.element, | 983 oldUnit.element, |
| 950 _updateOffset, | 984 _updateOffset, |
| 951 _updateDelta); | 985 _updateDelta); |
| 952 _updateEntry(); | 986 _updateEntry(); |
| 953 } | 987 } |
| 988 _logger.log('Success.'); | |
| 954 return true; | 989 return true; |
| 955 } | 990 } |
| 956 // Find nodes covering the "old" and "new" token ranges. | 991 // Find nodes covering the "old" and "new" token ranges. |
| 957 AstNode oldNode = | 992 AstNode oldNode = |
| 958 _findNodeCovering(oldUnit, beginOffsetOld, endOffsetOld); | 993 _findNodeCovering(oldUnit, beginOffsetOld, endOffsetOld); |
| 959 AstNode newNode = | 994 AstNode newNode = |
| 960 _findNodeCovering(newUnit, beginOffsetNew, endOffsetNew); | 995 _findNodeCovering(newUnit, beginOffsetNew, endOffsetNew); |
| 961 // print('oldNode: $oldNode'); | 996 _logger.log('oldNode: $oldNode'); |
| 962 // print('newNode: $newNode'); | 997 _logger.log('newNode: $newNode'); |
| 963 // Try to find the smallest common node, a FunctionBody currently. | 998 // Try to find the smallest common node, a FunctionBody currently. |
| 964 { | 999 { |
| 965 List<AstNode> oldParents = _getParents(oldNode); | 1000 List<AstNode> oldParents = _getParents(oldNode); |
| 966 List<AstNode> newParents = _getParents(newNode); | 1001 List<AstNode> newParents = _getParents(newNode); |
| 967 int length = math.min(oldParents.length, newParents.length); | 1002 int length = math.min(oldParents.length, newParents.length); |
| 968 bool found = false; | 1003 bool found = false; |
| 969 for (int i = 0; i < length; i++) { | 1004 for (int i = 0; i < length; i++) { |
| 970 AstNode oldParent = oldParents[i]; | 1005 AstNode oldParent = oldParents[i]; |
| 971 AstNode newParent = newParents[i]; | 1006 AstNode newParent = newParents[i]; |
| 972 if (oldParent is FunctionDeclaration && | 1007 if (oldParent is FunctionDeclaration && |
| 973 newParent is FunctionDeclaration || | 1008 newParent is FunctionDeclaration || |
| 974 oldParent is MethodDeclaration && newParent is MethodDeclaration || | 1009 oldParent is MethodDeclaration && newParent is MethodDeclaration || |
| 975 oldParent is ConstructorDeclaration && newParent is ConstructorD eclaration) { | 1010 oldParent is ConstructorDeclaration && newParent is ConstructorD eclaration) { |
| 976 oldNode = oldParent; | 1011 oldNode = oldParent; |
| 977 newNode = newParent; | 1012 newNode = newParent; |
| 978 found = true; | 1013 found = true; |
| 979 } | 1014 } |
| 980 if (oldParent is FunctionBody && newParent is FunctionBody) { | 1015 if (oldParent is FunctionBody && newParent is FunctionBody) { |
| 981 oldNode = oldParent; | 1016 oldNode = oldParent; |
| 982 newNode = newParent; | 1017 newNode = newParent; |
| 983 found = true; | 1018 found = true; |
| 984 break; | 1019 break; |
| 985 } | 1020 } |
| 986 } | 1021 } |
| 987 if (!found) { | 1022 if (!found) { |
| 1023 _logger.log('Failure: no enclosing function body or executable.'); | |
| 988 return false; | 1024 return false; |
| 989 } | 1025 } |
| 990 } | 1026 } |
| 991 // print('oldNode: $oldNode'); | 1027 _logger.log('oldNode: $oldNode'); |
| 992 // print('newNode: $newNode'); | 1028 _logger.log('newNode: $newNode'); |
| 993 // prepare update range | 1029 // prepare update range |
| 994 _updateOffset = oldNode.offset; | 1030 _updateOffset = oldNode.offset; |
| 995 _updateEndOld = oldNode.end; | 1031 _updateEndOld = oldNode.end; |
| 996 _updateEndNew = newNode.end; | 1032 _updateEndNew = newNode.end; |
| 997 _updateDelta = _updateEndNew - _updateEndOld; | 1033 _updateDelta = _updateEndNew - _updateEndOld; |
| 998 // replace node | 1034 // replace node |
| 999 NodeReplacer.replace(oldNode, newNode); | 1035 NodeReplacer.replace(oldNode, newNode); |
| 1000 // update token references | 1036 // update token references |
| 1001 { | 1037 { |
| 1002 Token oldBeginToken = _getBeginTokenNotComment(oldNode); | 1038 Token oldBeginToken = _getBeginTokenNotComment(oldNode); |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 1015 _typeProvider, | 1051 _typeProvider, |
| 1016 oldUnitElement, | 1052 oldUnitElement, |
| 1017 _updateOffset, | 1053 _updateOffset, |
| 1018 oldNode.length, | 1054 oldNode.length, |
| 1019 newNode.length); | 1055 newNode.length); |
| 1020 incrementalResolver.resolve(newNode); | 1056 incrementalResolver.resolve(newNode); |
| 1021 _newResolveErrors = incrementalResolver._resolveErrors; | 1057 _newResolveErrors = incrementalResolver._resolveErrors; |
| 1022 _newVerifyErrors = incrementalResolver._verifyErrors; | 1058 _newVerifyErrors = incrementalResolver._verifyErrors; |
| 1023 _newHints = incrementalResolver._hints; | 1059 _newHints = incrementalResolver._hints; |
| 1024 _updateEntry(); | 1060 _updateEntry(); |
| 1025 // print('Successfully incrementally resolved.'); | 1061 _logger.log('Success.'); |
| 1026 return true; | 1062 return true; |
| 1027 } | 1063 } |
| 1028 } catch (e) { | 1064 } catch (e, st) { |
| 1029 // TODO(scheglov) find a way to log these exceptions | 1065 _logger.log(e); |
| 1030 // print(e); | 1066 _logger.log(st); |
| 1031 // print(st); | 1067 _logger.log('Failure: exception.'); |
| 1068 } finally { | |
| 1069 _logger.exit(); | |
| 1032 } | 1070 } |
| 1033 return false; | 1071 return false; |
| 1034 } | 1072 } |
| 1035 | 1073 |
| 1036 CompilationUnit _parseUnit(String code) { | 1074 CompilationUnit _parseUnit(String code) { |
| 1037 Token token = _scan(code); | 1075 Token token = _scan(code); |
| 1038 RecordingErrorListener errorListener = new RecordingErrorListener(); | 1076 RecordingErrorListener errorListener = new RecordingErrorListener(); |
| 1039 Parser parser = new Parser(_unitSource, errorListener); | 1077 Parser parser = new Parser(_unitSource, errorListener); |
| 1040 CompilationUnit unit = parser.parseCompilationUnit(token); | 1078 CompilationUnit unit = parser.parseCompilationUnit(token); |
| 1041 _newParseErrors = errorListener.errors; | 1079 _newParseErrors = errorListener.errors; |
| 1042 return unit; | 1080 return unit; |
| 1043 } | 1081 } |
| 1044 | 1082 |
| 1045 void _resolveComment(CompilationUnit oldUnit, CompilationUnit newUnit, | 1083 void _resolveComment(CompilationUnit oldUnit, CompilationUnit newUnit, |
| 1046 _TokenPair firstPair) { | 1084 _TokenPair firstPair) { |
| 1047 Token oldToken = firstPair.oldToken; | 1085 Token oldToken = firstPair.oldToken; |
| 1048 int offset = oldToken.precedingComments.offset; | 1086 CommentToken precedingComments = oldToken.precedingComments; |
| 1087 int offset = precedingComments.offset; | |
| 1088 _logger.log('offset: $offset'); | |
| 1049 Comment oldComment = _findNodeCovering(oldUnit, offset, offset); | 1089 Comment oldComment = _findNodeCovering(oldUnit, offset, offset); |
| 1050 Comment newComment = _findNodeCovering(newUnit, offset, offset); | 1090 Comment newComment = _findNodeCovering(newUnit, offset, offset); |
| 1091 _logger.log('oldComment.beginToken: ${oldComment.beginToken}'); | |
| 1092 _logger.log('newComment.beginToken: ${newComment.beginToken}'); | |
| 1051 _updateOffset = oldToken.offset - 1; | 1093 _updateOffset = oldToken.offset - 1; |
| 1052 // update token references | 1094 // update token references |
| 1053 _shiftTokens(firstPair.oldToken); | 1095 _shiftTokens(firstPair.oldToken); |
| 1054 _setPrecedingComments(oldToken, newComment.tokens.first); | 1096 _setPrecedingComments(oldToken, newComment.tokens.first); |
| 1055 // replace node | 1097 // replace node |
| 1056 NodeReplacer.replace(oldComment, newComment); | 1098 NodeReplacer.replace(oldComment, newComment); |
| 1057 // update elements | 1099 // update elements |
| 1058 IncrementalResolver._updateElementNameOffsets( | 1100 IncrementalResolver._updateElementNameOffsets( |
| 1059 oldUnit.element, | 1101 oldUnit.element, |
| 1060 _updateOffset, | 1102 _updateOffset, |
| (...skipping 479 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1540 super.visitFunctionExpression(node); | 1582 super.visitFunctionExpression(node); |
| 1541 } | 1583 } |
| 1542 | 1584 |
| 1543 @override | 1585 @override |
| 1544 visitSimpleIdentifier(SimpleIdentifier node) { | 1586 visitSimpleIdentifier(SimpleIdentifier node) { |
| 1545 _elements[node] = node.staticElement; | 1587 _elements[node] = node.staticElement; |
| 1546 } | 1588 } |
| 1547 } | 1589 } |
| 1548 | 1590 |
| 1549 | 1591 |
| 1592 /** | |
| 1593 * A simple hierarchical logger. | |
| 1594 */ | |
| 1595 abstract class _Logger { | |
| 1596 /** | |
| 1597 * Mark an enter to a new section with the given [name]. | |
| 1598 */ | |
| 1599 void enter(String name); | |
| 1600 | |
| 1601 /** | |
| 1602 * Mark an exit from the current sections, logs the duration. | |
| 1603 */ | |
| 1604 void exit(); | |
| 1605 | |
| 1606 /** | |
| 1607 * Logs the given [message]. | |
| 1608 */ | |
| 1609 void log(Object obj); | |
| 1610 } | |
| 1611 | |
| 1612 | |
| 1613 class _LoggerSection { | |
| 1614 final DateTime start = new DateTime.now(); | |
| 1615 final String indent; | |
| 1616 final String name; | |
| 1617 _LoggerSection(this.indent, this.name); | |
| 1618 } | |
| 1619 | |
| 1620 | |
| 1621 /** | |
| 1622 * A [_Logger] that does nothing. | |
| 1623 */ | |
| 1624 class _NullLogger implements _Logger { | |
| 1625 @override | |
| 1626 void enter(String name) { | |
| 1627 } | |
| 1628 | |
| 1629 @override | |
| 1630 void exit() { | |
| 1631 } | |
| 1632 | |
| 1633 @override | |
| 1634 void log(Object obj) { | |
| 1635 } | |
| 1636 } | |
| 1637 | |
| 1638 | |
| 1639 /** | |
| 1640 * A [_Logger] that writes to a [StringSink]. | |
| 1641 */ | |
| 1642 class _StringSinkLogger implements _Logger { | |
| 1643 static const int MAX_LINE_LENGTH = 512; | |
| 1644 final StringSink sink; | |
| 1645 final List<_LoggerSection> sectionStack = <_LoggerSection>[]; | |
| 1646 _LoggerSection section = new _LoggerSection('', 'ROOT'); | |
| 1647 | |
| 1648 _StringSinkLogger(this.sink); | |
| 1649 | |
| 1650 @override | |
| 1651 void enter(String name) { | |
| 1652 log('+++ $name'); | |
| 1653 sectionStack.add(section); | |
| 1654 section = new _LoggerSection(section.indent + '\t', name); | |
| 1655 } | |
| 1656 | |
| 1657 @override | |
| 1658 void exit() { | |
| 1659 DateTime now = new DateTime.now(); | |
| 1660 Duration duration = now.difference(section.start); | |
| 1661 String message = '--- ${section.name} in ${duration.inMilliseconds} ms'; | |
| 1662 section = sectionStack.removeLast(); | |
| 1663 log(message); | |
| 1664 } | |
| 1665 | |
| 1666 @override | |
| 1667 void log(Object obj) { | |
| 1668 DateTime now = new DateTime.now(); | |
| 1669 String indent = section.indent; | |
| 1670 String objStr = _getObjectString(obj); | |
| 1671 String line = '[$now] $indent$objStr'; | |
| 1672 sink.writeln(line); | |
| 1673 } | |
| 1674 | |
| 1675 String _getObjectString(Object obj) { | |
| 1676 if (obj == null) { | |
| 1677 return 'null'; | |
| 1678 } | |
| 1679 String str = obj.toString(); | |
| 1680 if (str.length < MAX_LINE_LENGTH) { | |
| 1681 return str; | |
| 1682 } | |
| 1683 return str.split('\n').map((String line) { | |
| 1684 if (line.length > MAX_LINE_LENGTH) { | |
| 1685 line = line.substring(0, MAX_LINE_LENGTH) + '...'; | |
| 1686 } | |
| 1687 return line; | |
| 1688 }).join('\n'); | |
| 1689 } | |
| 1690 } | |
| 1691 | |
| 1692 | |
| 1550 class _TokenPair { | 1693 class _TokenPair { |
| 1551 final Token oldToken; | 1694 final Token oldToken; |
| 1552 final Token newToken; | 1695 final Token newToken; |
| 1553 final bool atComment; | 1696 final bool atComment; |
| 1554 _TokenPair(this.oldToken, this.newToken, [this.atComment = false]); | 1697 _TokenPair(this.oldToken, this.newToken, [this.atComment = false]); |
| 1555 } | 1698 } |
| OLD | NEW |