OLD | NEW |
| (Empty) |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 library fletchc.incremental.diff; | |
6 | |
7 import 'package:compiler/src/elements/elements.dart' show | |
8 AbstractFieldElement, | |
9 ClassElement, | |
10 CompilationUnitElement, | |
11 Element, | |
12 ElementCategory, | |
13 FunctionElement, | |
14 LibraryElement, | |
15 ScopeContainerElement; | |
16 | |
17 import 'package:compiler/src/elements/modelx.dart' as modelx; | |
18 | |
19 import 'package:compiler/src/elements/modelx.dart' show | |
20 DeclarationSite; | |
21 | |
22 import 'package:compiler/src/tokens/token_constants.dart' show | |
23 EOF_TOKEN, | |
24 IDENTIFIER_TOKEN, | |
25 KEYWORD_TOKEN; | |
26 | |
27 import 'package:compiler/src/tokens/token.dart' show | |
28 ErrorToken, | |
29 Token; | |
30 | |
31 import 'package:compiler/src/parser/partial_elements.dart' show | |
32 PartialClassElement, | |
33 PartialElement; | |
34 | |
35 import 'fletchc_incremental.dart' show | |
36 IncrementalCompilationFailed; | |
37 | |
38 class Difference { | |
39 final DeclarationSite before; | |
40 final DeclarationSite after; | |
41 | |
42 /// Records the position of first difference between [before] and [after]. If | |
43 /// either [before] or [after] are null, [token] is null. | |
44 Token token; | |
45 | |
46 Difference(this.before, this.after) { | |
47 if (before == after) { | |
48 throw '[before] and [after] are the same.'; | |
49 } | |
50 } | |
51 | |
52 String toString() { | |
53 if (before == null) return 'Added($after)'; | |
54 if (after == null) return 'Removed($before)'; | |
55 return 'Modified($after -> $before)'; | |
56 } | |
57 } | |
58 | |
59 void checkCanComputeDifference(modelx.ElementX element) { | |
60 if (element.isMixinApplication) { | |
61 // TODO(ahe): issue 91 | |
62 throw new IncrementalCompilationFailed( | |
63 "Mixin applications not supported: $element"); | |
64 } | |
65 if (element.isClass) { | |
66 modelx.ClassElementX cls = element; | |
67 if (cls.isEnumClass) { | |
68 throw new IncrementalCompilationFailed("Enums not supported: $element"); | |
69 } | |
70 } | |
71 if (element.declarationSite == null && !element.isSynthesized) { | |
72 throw new IncrementalCompilationFailed( | |
73 "Unable to compute diff for $element"); | |
74 } | |
75 } | |
76 | |
77 List<Difference> computeDifference( | |
78 ScopeContainerElement before, | |
79 ScopeContainerElement after) { | |
80 Map<String, DeclarationSite> beforeMap = <String, DeclarationSite>{}; | |
81 before.forEachLocalMember((modelx.ElementX element) { | |
82 checkCanComputeDifference(element); | |
83 DeclarationSite site = element.declarationSite; | |
84 assert(site != null || element.isSynthesized); | |
85 if (!element.isSynthesized) { | |
86 beforeMap[element.name] = site; | |
87 } | |
88 }); | |
89 List<Difference> modifications = <Difference>[]; | |
90 List<Difference> potentiallyChanged = <Difference>[]; | |
91 after.forEachLocalMember((modelx.ElementX element) { | |
92 checkCanComputeDifference(element); | |
93 DeclarationSite existing = beforeMap.remove(element.name); | |
94 if (existing == null) { | |
95 if (!element.isSynthesized) { | |
96 modifications.add(new Difference(null, element.declarationSite)); | |
97 } | |
98 } else { | |
99 potentiallyChanged.add(new Difference(existing, element.declarationSite)); | |
100 } | |
101 }); | |
102 | |
103 modifications.addAll( | |
104 beforeMap.values.map( | |
105 (DeclarationSite site) => new Difference(site, null))); | |
106 | |
107 modifications.addAll( | |
108 potentiallyChanged.where(areDifferentElements)); | |
109 | |
110 return modifications; | |
111 } | |
112 | |
113 bool areDifferentElements(Difference diff) { | |
114 DeclarationSite before = diff.before; | |
115 DeclarationSite after = diff.after; | |
116 if (before is PartialElement && after is PartialElement) { | |
117 Token beforeToken = before.beginToken; | |
118 Token afterToken = after.beginToken; | |
119 Token stop = before.endToken; | |
120 int beforeKind = beforeToken.kind; | |
121 int afterKind = afterToken.kind; | |
122 while (beforeKind != EOF_TOKEN && afterKind != EOF_TOKEN) { | |
123 | |
124 if (beforeKind != afterKind) { | |
125 diff.token = afterToken; | |
126 return true; | |
127 } | |
128 | |
129 if (beforeToken is! ErrorToken && afterToken is! ErrorToken) { | |
130 if (beforeToken.value != afterToken.value) { | |
131 diff.token = afterToken; | |
132 return true; | |
133 } | |
134 } | |
135 | |
136 if (beforeToken == stop) { | |
137 diff.token = afterToken; | |
138 // We didn't find a difference, and normally that would mean that the | |
139 // element hasn't changed. However, for elements with members, the | |
140 // situation is more tricky. For example, consider a class that never | |
141 // has any changes to its header (everything before the first `{`). The | |
142 // tokens of this class aren't patched up if one of its members | |
143 // change. So we can't actually look at the tokens of the class to see | |
144 // if one of its members changed. Instead, we must say that the class | |
145 // changed, and then look at its members one by one. | |
146 return before is ScopeContainerElement; | |
147 } | |
148 | |
149 beforeToken = beforeToken.next; | |
150 afterToken = afterToken.next; | |
151 beforeKind = beforeToken.kind; | |
152 afterKind = afterToken.kind; | |
153 } | |
154 return beforeKind != afterKind; | |
155 } | |
156 print("$before isn't a PartialElement"); | |
157 return true; | |
158 } | |
OLD | NEW |