blob: b0569d1e399c37219f779a365dfdd7bc8b042d90 [file] [log] [blame]
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library trydart.poi.diff;
import 'package:compiler/src/elements/elements.dart' show
AbstractFieldElement,
ClassElement,
CompilationUnitElement,
Element,
ElementCategory,
FunctionElement,
LibraryElement,
ScopeContainerElement;
import 'package:compiler/src/elements/modelx.dart' as modelx;
import 'package:compiler/src/elements/modelx.dart' show
DeclarationSite;
import 'package:compiler/src/parser/partial_elements.dart' show
PartialClassElement,
PartialElement;
import 'package:compiler/src/tokens/token.dart' show
ErrorToken,
Token;
import 'package:compiler/src/tokens/token_constants.dart' show
EOF_TOKEN,
IDENTIFIER_TOKEN,
KEYWORD_TOKEN;
class Difference {
final DeclarationSite before;
final DeclarationSite after;
/// Records the position of first difference between [before] and [after]. If
/// either [before] or [after] are null, [token] is null.
Token token;
Difference(this.before, this.after) {
if (before == after) {
throw '[before] and [after] are the same.';
}
}
String toString() {
if (before == null) return 'Added($after)';
if (after == null) return 'Removed($before)';
return 'Modified($after -> $before)';
}
}
List<Difference> computeDifference(
ScopeContainerElement before,
ScopeContainerElement after) {
Map<String, DeclarationSite> beforeMap = <String, DeclarationSite>{};
before.forEachLocalMember((modelx.ElementX element) {
DeclarationSite site = element.declarationSite;
assert(site != null || element.isSynthesized);
if (!element.isSynthesized) {
beforeMap[element.name] = site;
}
});
List<Difference> modifications = <Difference>[];
List<Difference> potentiallyChanged = <Difference>[];
after.forEachLocalMember((modelx.ElementX element) {
DeclarationSite existing = beforeMap.remove(element.name);
if (existing == null) {
modifications.add(new Difference(null, element.declarationSite));
} else {
potentiallyChanged.add(new Difference(existing, element.declarationSite));
}
});
modifications.addAll(
beforeMap.values.map(
(DeclarationSite site) => new Difference(site, null)));
modifications.addAll(
potentiallyChanged.where(areDifferentElements));
return modifications;
}
bool areDifferentElements(Difference diff) {
DeclarationSite before = diff.before;
DeclarationSite after = diff.after;
if (before is PartialElement && after is PartialElement) {
Token beforeToken = before.beginToken;
Token afterToken = after.beginToken;
Token stop = before.endToken;
int beforeKind = beforeToken.kind;
int afterKind = afterToken.kind;
while (beforeKind != EOF_TOKEN && afterKind != EOF_TOKEN) {
if (beforeKind != afterKind) {
diff.token = afterToken;
return true;
}
if (beforeToken is! ErrorToken && afterToken is! ErrorToken) {
if (beforeToken.value != afterToken.value) {
diff.token = afterToken;
return true;
}
}
if (beforeToken == stop) return false;
beforeToken = beforeToken.next;
afterToken = afterToken.next;
beforeKind = beforeToken.kind;
afterKind = afterToken.kind;
}
return beforeKind != afterKind;
}
print("$before isn't a PartialElement");
return true;
}