Fix for adding new elements into library cycles.

R=brianwilkerson@google.com
BUG=

Review URL: https://codereview.chromium.org/2212093002 .
This commit is contained in:
Konstantin Shcheglov 2016-08-04 14:42:40 -07:00
parent 99baa3de32
commit bc8b32145b
3 changed files with 151 additions and 89 deletions

View file

@ -5872,17 +5872,6 @@ class LibraryElementImpl extends ElementImpl implements LibraryElement {
* cached library cycles in the element model which may have been invalidated.
*/
void invalidateLibraryCycles() {
if (_libraryCycle == null) {
// We have already invalidated this node, or we have never computed
// library cycle information for it. In the former case, we're done. In
// the latter case, this node cannot be reachable from any node for which
// we have computed library cycle information. Therefore, any edges added
// or deleted in the update causing this invalidation can only be edges to
// nodes which either have no library cycle information (and hence do not
// need invalidation), or which do not reach this node by any path.
// In either case, no further invalidation is needed.
return;
}
// If we have pre-computed library cycle information, then we must
// invalidate the information both on this element, and on certain
// other elements. Edges originating at this node may have been

View file

@ -487,6 +487,43 @@ class B {}
expect(context.getErrors(a).errors, hasLength(0));
}
void test_applyChanges_addNewImport_invalidateLibraryCycle() {
context.analysisOptions =
new AnalysisOptionsImpl.from(context.analysisOptions)
..strongMode = true;
Source embedder = addSource(
'/a.dart',
r'''
library a;
import 'b.dart';
//import 'c.dart';
''');
addSource(
'/b.dart',
r'''
library b;
import 'a.dart';
''');
addSource(
'/c.dart',
r'''
library c;
import 'b.dart';
''');
_performPendingAnalysisTasks();
// Add a new import into a.dart, this should invalidate its library cycle.
// If it doesn't, we will get a task cycle exception.
context.setContents(
embedder,
r'''
library a;
import 'b.dart';
import 'c.dart';
''');
_performPendingAnalysisTasks();
expect(context.getCacheEntry(embedder).exception, isNull);
}
void test_cacheConsistencyValidator_computed_deleted() {
CacheConsistencyValidator validator = context.cacheConsistencyValidator;
var stat = PerformanceStatistics.cacheConsistencyValidationStatistics;

View file

@ -1556,113 +1556,143 @@ class ComputeLibraryCycleTaskTest extends _AbstractDartTaskTest {
void test_library_cycle_incremental() {
enableStrongMode();
Source lib1Source = newSource(
'/my_lib1.dart',
Source a = newSource(
'/a.dart',
'''
library my_lib1;
library a;
''');
Source lib2Source = newSource(
'/my_lib2.dart',
Source b = newSource(
'/b.dart',
'''
library my_lib2;
import 'my_lib1.dart';
library b;
import 'a.dart';
''');
Source lib3Source = newSource(
'/my_lib3.dart',
Source c = newSource(
'/c.dart',
'''
library my_lib3;
import 'my_lib2.dart';
library c;
import 'b.dart';
''');
computeResult(lib1Source, LIBRARY_CYCLE);
expect(outputs[LIBRARY_CYCLE], hasLength(1));
computeResult(lib2Source, LIBRARY_CYCLE);
expect(outputs[LIBRARY_CYCLE], hasLength(1));
computeResult(lib3Source, LIBRARY_CYCLE);
expect(outputs[LIBRARY_CYCLE], hasLength(1));
_assertLibraryCycle(a, [a]);
_assertLibraryCycle(b, [b]);
_assertLibraryCycle(c, [c]);
// create a cycle
// Create a cycle.
context.setContents(
lib1Source,
a,
'''
library my_lib1;
import 'my_lib3.dart';
library a;
import 'c.dart';
''');
_expectInvalid(lib1Source);
_expectInvalid(lib2Source);
_expectInvalid(lib3Source);
_expectInvalid(a);
_expectInvalid(b);
_expectInvalid(c);
computeResult(lib1Source, LIBRARY_CYCLE);
expect(outputs[LIBRARY_CYCLE], hasLength(3));
computeResult(lib2Source, LIBRARY_CYCLE);
expect(outputs[LIBRARY_CYCLE], hasLength(3));
computeResult(lib3Source, LIBRARY_CYCLE);
expect(outputs[LIBRARY_CYCLE], hasLength(3));
_assertLibraryCycle(a, [a, b, c]);
_assertLibraryCycle(b, [a, b, c]);
_assertLibraryCycle(c, [a, b, c]);
// break the cycle again
// Break the cycle again.
context.setContents(
lib1Source,
a,
'''
library my_lib1;
library a;
''');
_expectInvalid(lib1Source);
_expectInvalid(lib2Source);
_expectInvalid(lib3Source);
_expectInvalid(a);
_expectInvalid(b);
_expectInvalid(c);
computeResult(lib1Source, LIBRARY_CYCLE);
expect(outputs[LIBRARY_CYCLE], hasLength(1));
computeResult(lib2Source, LIBRARY_CYCLE);
expect(outputs[LIBRARY_CYCLE], hasLength(1));
computeResult(lib3Source, LIBRARY_CYCLE);
expect(outputs[LIBRARY_CYCLE], hasLength(1));
_assertLibraryCycle(a, [a]);
_assertLibraryCycle(b, [b]);
_assertLibraryCycle(c, [c]);
}
void test_library_cycle_incremental_partial() {
enableStrongMode();
Source lib1Source = newSource(
'/my_lib1.dart',
'''
library my_lib1;
Source a = newSource(
'/a.dart',
r'''
library a;
''');
Source lib2Source = newSource(
'/my_lib2.dart',
'''
library my_lib2;
import 'my_lib1.dart';
Source b = newSource(
'/b.dart',
r'''
library b;
import 'a.dart';
''');
Source lib3Source = newSource(
'/my_lib3.dart',
'''
library my_lib3;
import 'my_lib2.dart';
Source c = newSource(
'/c.dart',
r'''
library c;
import 'b.dart';
''');
computeResult(lib1Source, LIBRARY_CYCLE);
expect(outputs[LIBRARY_CYCLE], hasLength(1));
computeResult(lib2Source, LIBRARY_CYCLE);
expect(outputs[LIBRARY_CYCLE], hasLength(1));
// lib3 is not reachable, so we have not yet computed its library
// cycles
_assertLibraryCycle(a, [a]);
_assertLibraryCycle(b, [b]);
// 'c' is not reachable, so we have not yet computed its library cycles.
// complete the cycle, via lib3
// Complete the cycle, via 'c'.
context.setContents(
lib1Source,
'''
library my_lib1;
import 'my_lib3.dart';
a,
r'''
library a;
import 'c.dart';
''');
_expectInvalid(lib1Source);
_expectInvalid(lib2Source);
_expectInvalid(lib3Source);
_expectInvalid(a);
_expectInvalid(b);
_expectInvalid(c);
// Ensure that invalidation correctly invalidated everything reachable
// through lib3
computeResult(lib1Source, LIBRARY_CYCLE);
expect(outputs[LIBRARY_CYCLE], hasLength(3));
computeResult(lib2Source, LIBRARY_CYCLE);
expect(outputs[LIBRARY_CYCLE], hasLength(3));
computeResult(lib3Source, LIBRARY_CYCLE);
expect(outputs[LIBRARY_CYCLE], hasLength(3));
// Ensure that everything reachable through 'c' was invalidated,
// and recomputed to include all three sources.
_assertLibraryCycle(a, [a, b, c]);
_assertLibraryCycle(b, [a, b, c]);
_assertLibraryCycle(c, [a, b, c]);
}
void test_library_cycle_incremental_partial2() {
enableStrongMode();
Source a = newSource(
'/a.dart',
r'''
library a;
import 'b.dart';
''');
Source b = newSource(
'/b.dart',
r'''
library b;
import 'a.dart';
''');
Source c = newSource(
'/c.dart',
r'''
library c;
import 'b.dart';
''');
_assertLibraryCycle(a, [a, b]);
_assertLibraryCycle(b, [a, b]);
_assertLibraryCycle(c, [c]);
// Include 'c' into the cycle.
context.setContents(
a,
r'''
library a;
import 'b.dart';
import 'c.dart';
''');
_expectInvalid(a);
_expectInvalid(b);
_expectInvalid(c);
// Start processing with 'b', so that when we resolve 'b' directives,
// and invalidate library cycles, the 'a' directives are not resolved yet,
// so we don't know that the cycle must include 'c'.
_assertLibraryCycle(b, [a, b, c]);
_assertLibraryCycle(a, [a, b, c]);
_assertLibraryCycle(c, [a, b, c]);
}
void test_library_cycle_linear() {
@ -1974,6 +2004,12 @@ import 'dart:core';
expect(dep5, hasLength(5)); // dart:core, a.dart, aa.dart, ab.dart, b.dart
}
void _assertLibraryCycle(Source source, List<Source> expected) {
computeResult(source, LIBRARY_CYCLE);
List<LibraryElement> cycle = outputs[LIBRARY_CYCLE] as List<LibraryElement>;
expect(cycle.map((e) => e.source), unorderedEquals(expected));
}
void _expectInvalid(Source librarySource) {
CacheEntry entry = context.getCacheEntry(librarySource);
expect(entry.getState(LIBRARY_CYCLE), CacheState.INVALID);