diff --git a/sdk/lib/_internal/compiler/implementation/library_loader.dart b/sdk/lib/_internal/compiler/implementation/library_loader.dart index b7ceefc04d9..1217a113625 100644 --- a/sdk/lib/_internal/compiler/implementation/library_loader.dart +++ b/sdk/lib/_internal/compiler/implementation/library_loader.dart @@ -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; }); } } diff --git a/sdk/lib/_internal/compiler/implementation/scanner/listener.dart b/sdk/lib/_internal/compiler/implementation/scanner/listener.dart index 234352bb34e..468473e83ce 100644 --- a/sdk/lib/_internal/compiler/implementation/scanner/listener.dart +++ b/sdk/lib/_internal/compiler/implementation/scanner/listener.dart @@ -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); } diff --git a/site/try/poi/diff.dart b/site/try/poi/diff.dart new file mode 100644 index 00000000000..a2e17eb4201 --- /dev/null +++ b/site/try/poi/diff.dart @@ -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 computeDifference( + ScopeContainerElement before, + ScopeContainerElement after) { + Map beforeMap = {}; + before.forEachLocalMember((Element element) { + beforeMap[element.name] = element; + }); + List modifications = []; + List potentiallyChanged = []; + 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; +} diff --git a/tests/try/poi/diff_test.dart b/tests/try/poi/diff_test.dart new file mode 100644 index 00000000000..ae384cf80a4 --- /dev/null +++ b/tests/try/poi/diff_test.dart @@ -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> testDifference( + String beforeSource, + String afterSource) { + Uri scriptUri = customUri('main.dart'); + MockCompiler compiler = compilerFor(beforeSource, scriptUri); + + Future 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 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 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)); +}