Implement diff algorithm for libraries.

R=johnniwinther@google.com

Review URL: https://codereview.chromium.org//494623002

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@39551 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
ahe@google.com
2014-08-26 13:01:36 +00:00
parent 71d3f89fe3
commit eadbb803ef
4 changed files with 291 additions and 22 deletions
@@ -498,23 +498,33 @@ class _LibraryLoaderTask extends CompilerTask implements LibraryLoaderTask {
return new Future.value(library);
}
return compiler.withCurrentElement(importingLibrary, () {
return compiler.readScript(node, readableUri)
.then((Script script) {
if (script == null) return null;
LibraryElement element = new LibraryElementX(script, resolvedUri);
compiler.withCurrentElement(element, () {
handler.registerNewLibrary(element);
native.maybeEnableNative(compiler, element);
libraryCanonicalUriMap[resolvedUri] = element;
compiler.scanner.scanLibrary(element);
});
return processLibraryTags(handler, element).then((_) {
compiler.withCurrentElement(element, () {
handler.registerLibraryExports(element);
});
return element;
});
return compiler.readScript(node, readableUri).then((Script script) {
if (script == null) return null;
LibraryElement element =
createLibrarySync(handler, script, resolvedUri);
return processLibraryTags(handler, element).then((_) {
compiler.withCurrentElement(element, () {
handler.registerLibraryExports(element);
});
return element;
});
});
});
}
LibraryElement createLibrarySync(
LibraryDependencyHandler handler,
Script script,
Uri resolvedUri) {
LibraryElement element = new LibraryElementX(script, resolvedUri);
return compiler.withCurrentElement(element, () {
if (handler != null) {
handler.registerNewLibrary(element);
libraryCanonicalUriMap[resolvedUri] = element;
}
native.maybeEnableNative(compiler, element);
compiler.scanner.scanLibrary(element);
return element;
});
}
}
@@ -2169,7 +2169,7 @@ class NodeListener extends ElementListener {
}
}
abstract class PartialElement implements Element {
abstract class PartialElement {
Token get beginToken;
Token get endToken;
@@ -2254,16 +2254,17 @@ class PartialConstructorElement extends ConstructorElementX
}
}
class PartialFieldList extends VariableList {
class PartialFieldList extends VariableList with PartialElement {
final Token beginToken;
final Token endToken;
final bool hasParseError;
PartialFieldList(this.beginToken,
this.endToken,
Modifiers modifiers,
this.hasParseError)
: super(modifiers);
bool hasParseError)
: super(modifiers) {
super.hasParseError = hasParseError;
}
VariableDefinitions parseNode(Element element, DiagnosticListener listener) {
if (definitions != null) return definitions;
@@ -2365,7 +2366,8 @@ Node parse(DiagnosticListener diagnosticListener,
doParse(new Parser(listener));
} on ParserError catch (e) {
if (element is PartialElement) {
element.hasParseError = true;
PartialElement partial = element as PartialElement;
partial.hasParseError = true;
}
return new ErrorNode(element.position, e.reason);
}
+134
View File
@@ -0,0 +1,134 @@
// 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 'dart:async' show
Completer,
Future,
Stream;
import 'dart:convert' show
LineSplitter,
UTF8;
import 'package:compiler/compiler.dart' as api;
import 'package:compiler/implementation/dart2jslib.dart' show
Compiler,
Enqueuer,
QueueFilter,
Script,
WorkItem;
import 'package:compiler/implementation/elements/visitor.dart' show
ElementVisitor;
import 'package:compiler/implementation/elements/elements.dart' show
AbstractFieldElement,
ClassElement,
CompilationUnitElement,
Element,
ElementCategory,
FunctionElement,
LibraryElement,
ScopeContainerElement;
import 'package:compiler/implementation/elements/modelx.dart' as modelx;
import 'package:compiler/implementation/dart_types.dart' show
DartType;
import 'package:compiler/implementation/scanner/scannerlib.dart' show
EOF_TOKEN,
ErrorToken,
IDENTIFIER_TOKEN,
KEYWORD_TOKEN,
PartialClassElement,
PartialElement,
Token;
import 'package:compiler/implementation/source_file.dart' show
StringSourceFile;
class Difference {
final Element before;
final Element after;
Token token;
Difference(this.before, this.after);
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, Element> beforeMap = <String, Element>{};
before.forEachLocalMember((Element element) {
beforeMap[element.name] = element;
});
List<Difference> modifications = <Difference>[];
List<Difference> potentiallyChanged = <Difference>[];
after.forEachLocalMember((Element element) {
Element existing = beforeMap.remove(element.name);
if (existing == null) {
modifications.add(new Difference(null, element));
} else {
potentiallyChanged.add(new Difference(existing, element));
}
});
modifications.addAll(
beforeMap.values.map((Element element) => new Difference(element, null)));
modifications.addAll(
potentiallyChanged.where(areDifferentElements));
return modifications;
}
bool areDifferentElements(Difference diff) {
Element beforeElement = diff.before;
Element afterElement = diff.after;
var before = (beforeElement is modelx.VariableElementX)
? beforeElement.variables : beforeElement;
var after = (afterElement is modelx.VariableElementX)
? afterElement.variables : afterElement;
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;
}
+123
View File
@@ -0,0 +1,123 @@
// 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.
/// Test of element diff.
library trydart.diff_test;
import 'dart:async' show
Future;
import 'package:expect/expect.dart' show
Expect;
import 'package:async_helper/async_helper.dart' show
asyncTest;
import 'package:compiler/implementation/dart2jslib.dart' show
Compiler,
Script;
import 'package:compiler/implementation/source_file.dart' show
StringSourceFile;
import 'package:compiler/implementation/elements/elements.dart' show
Element,
LibraryElement;
import 'package:try/poi/diff.dart' show
Difference,
computeDifference;
import '../../compiler/dart2js/compiler_helper.dart' show
MockCompiler,
compilerFor;
final TEST_DATA = [
{
'beforeSource': 'main() {}',
'afterSource': 'main() { var x; }',
'expectations': [['main', 'main']],
},
{
'beforeSource': 'main() {}',
'afterSource': 'main() { /* ignored */ }',
'expectations': [],
},
{
'beforeSource': 'main() {}',
'afterSource': 'main() { }',
'expectations': [],
},
{
'beforeSource': 'var i; main() {}',
'afterSource': 'main() { } var i;',
'expectations': [],
},
{
'beforeSource': 'main() {}',
'afterSource': '',
'expectations': [['main', null]],
},
{
'beforeSource': '',
'afterSource': 'main() {}',
'expectations': [[null, 'main']],
},
];
const String SCHEME = 'org.trydart.diff-test';
Uri customUri(String path) => Uri.parse('$SCHEME://$path');
Future<List<Difference>> testDifference(
String beforeSource,
String afterSource) {
Uri scriptUri = customUri('main.dart');
MockCompiler compiler = compilerFor(beforeSource, scriptUri);
Future<LibraryElement> future = compiler.libraryLoader.loadLibrary(scriptUri);
return future.then((LibraryElement library) {
Script sourceScript = new Script(
scriptUri, scriptUri, new StringSourceFile('$scriptUri', afterSource));
var dartPrivacyIsBroken = compiler.libraryLoader;
LibraryElement newLibrary = dartPrivacyIsBroken.createLibrarySync(
null, sourceScript, scriptUri);
return computeDifference(library, newLibrary);
});
}
Future testData(Map data) {
String beforeSource = data['beforeSource'];
String afterSource = data['afterSource'];
List expectations = data['expectations'];
validate(List<Difference> differences) {
return checkExpectations(expectations, differences);
}
return testDifference(beforeSource, afterSource).then(validate);
}
String elementNameOrNull(Element element) {
return element == null ? null : element.name;
}
checkExpectations(List expectations, List<Difference> differences) {
Iterator iterator = expectations.iterator;
for (Difference difference in differences) {
Expect.isTrue(iterator.moveNext());
List expectation = iterator.current;
String expectedBeforeName = expectation[0];
String expectedAfterName = expectation[1];
Expect.stringEquals(
expectedBeforeName, elementNameOrNull(difference.before));
Expect.stringEquals(expectedAfterName, elementNameOrNull(difference.after));
print(difference);
}
Expect.isFalse(iterator.moveNext());
}
void main() {
asyncTest(() => Future.forEach(TEST_DATA, testData));
}