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:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
Reference in New Issue
Block a user