| 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 |