mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 21:30:17 +00:00
Issue 26629. Detect cache inconsistency in GetContentTask.
When it is detected, invalidate the outputs and throw an exception to let Driver know that it should to recover. R=brianwilkerson@google.com, paulberry@google.com BUG= https://github.com/dart-lang/sdk/issues/26629 Review URL: https://codereview.chromium.org/2050573003 .
This commit is contained in:
parent
d7ec8ca711
commit
979543e3a8
|
@ -682,6 +682,7 @@ class AnalysisContextImpl implements InternalAnalysisContext {
|
|||
CacheState state = entry.getState(descriptor);
|
||||
if (state == CacheState.FLUSHED || state == CacheState.INVALID) {
|
||||
driver.computeResult(target, descriptor);
|
||||
entry = getCacheEntry(target);
|
||||
}
|
||||
state = entry.getState(descriptor);
|
||||
if (state == CacheState.ERROR) {
|
||||
|
@ -729,7 +730,8 @@ class AnalysisContextImpl implements InternalAnalysisContext {
|
|||
* to stand in for a real one if one does not exist
|
||||
* facilitating creation a type provider without dart:async.
|
||||
*/
|
||||
LibraryElement createMockAsyncLib(LibraryElement coreLibrary, Source asyncSource) {
|
||||
LibraryElement createMockAsyncLib(
|
||||
LibraryElement coreLibrary, Source asyncSource) {
|
||||
InterfaceType objType = coreLibrary.getType('Object').type;
|
||||
|
||||
ClassElement _classElement(String typeName, [List<String> parameterNames]) {
|
||||
|
@ -1057,13 +1059,13 @@ class AnalysisContextImpl implements InternalAnalysisContext {
|
|||
bool changed = newContents != originalContents;
|
||||
if (newContents != null) {
|
||||
if (changed) {
|
||||
entry.modificationTime = _contentCache.getModificationStamp(source);
|
||||
if (!analysisOptions.incremental ||
|
||||
!_tryPoorMansIncrementalResolution(source, newContents)) {
|
||||
// Don't compare with old contents because the cache has already been
|
||||
// updated, and we know at this point that it changed.
|
||||
_sourceChanged(source, compareWithOld: false);
|
||||
}
|
||||
entry.modificationTime = _contentCache.getModificationStamp(source);
|
||||
entry.setValue(CONTENT, newContents, TargetedResult.EMPTY_LIST);
|
||||
} else {
|
||||
entry.modificationTime = _contentCache.getModificationStamp(source);
|
||||
|
|
|
@ -867,6 +867,27 @@ final ResultDescriptor<CompilationUnit> RESOLVED_UNIT9 =
|
|||
new ResultDescriptor<CompilationUnit>('RESOLVED_UNIT9', null,
|
||||
cachingPolicy: AST_CACHING_POLICY);
|
||||
|
||||
/**
|
||||
* List of all `RESOLVED_UNITx` results.
|
||||
*/
|
||||
final List<ResultDescriptor<CompilationUnit>> RESOLVED_UNIT_RESULTS =
|
||||
<ResultDescriptor<CompilationUnit>>[
|
||||
RESOLVED_UNIT1,
|
||||
RESOLVED_UNIT2,
|
||||
RESOLVED_UNIT3,
|
||||
RESOLVED_UNIT4,
|
||||
RESOLVED_UNIT5,
|
||||
RESOLVED_UNIT6,
|
||||
RESOLVED_UNIT7,
|
||||
RESOLVED_UNIT8,
|
||||
RESOLVED_UNIT9,
|
||||
RESOLVED_UNIT10,
|
||||
RESOLVED_UNIT11,
|
||||
RESOLVED_UNIT12,
|
||||
RESOLVED_UNIT13,
|
||||
RESOLVED_UNIT
|
||||
];
|
||||
|
||||
/**
|
||||
* The errors produced while scanning a compilation unit.
|
||||
*
|
||||
|
@ -5578,7 +5599,7 @@ class ScanDartTask extends SourceBasedAnalysisTask {
|
|||
static Map<String, TaskInput> buildInputs(AnalysisTarget target) {
|
||||
if (target is Source) {
|
||||
return <String, TaskInput>{
|
||||
CONTENT_INPUT_NAME: CONTENT.of(target),
|
||||
CONTENT_INPUT_NAME: CONTENT.of(target, flushOnAccess: true),
|
||||
MODIFICATION_TIME_INPUT: MODIFICATION_TIME.of(target)
|
||||
};
|
||||
} else if (target is DartScript) {
|
||||
|
|
|
@ -101,15 +101,18 @@ class AnalysisDriver {
|
|||
try {
|
||||
isTaskRunning = true;
|
||||
AnalysisTask task;
|
||||
WorkOrder workOrder = createWorkOrderForResult(target, result);
|
||||
if (workOrder != null) {
|
||||
while (workOrder.moveNext()) {
|
||||
// AnalysisTask previousTask = task;
|
||||
// String message = workOrder.current.toString();
|
||||
task = performWorkItem(workOrder.current);
|
||||
// if (task == null) {
|
||||
// throw new AnalysisException(message, previousTask.caughtException);
|
||||
// }
|
||||
while (true) {
|
||||
try {
|
||||
WorkOrder workOrder = createWorkOrderForResult(target, result);
|
||||
if (workOrder != null) {
|
||||
while (workOrder.moveNext()) {
|
||||
task = performWorkItem(workOrder.current);
|
||||
}
|
||||
}
|
||||
break;
|
||||
} on ModificationTimeMismatchError {
|
||||
// Cache inconsistency was detected and fixed by invalidating
|
||||
// corresponding results in cache. Computation must be restarted.
|
||||
}
|
||||
}
|
||||
return task;
|
||||
|
@ -248,7 +251,12 @@ class AnalysisDriver {
|
|||
if (currentWorkOrder == null) {
|
||||
currentWorkOrder = createNextWorkOrder();
|
||||
} else if (currentWorkOrder.moveNext()) {
|
||||
performWorkItem(currentWorkOrder.current);
|
||||
try {
|
||||
performWorkItem(currentWorkOrder.current);
|
||||
} on ModificationTimeMismatchError {
|
||||
reset();
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
currentWorkOrder = createNextWorkOrder();
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
library analyzer.src.task.general;
|
||||
|
||||
import 'package:analyzer/src/context/cache.dart';
|
||||
import 'package:analyzer/src/generated/engine.dart';
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
import 'package:analyzer/task/general.dart';
|
||||
|
@ -33,13 +34,36 @@ class GetContentTask extends SourceBasedAnalysisTask {
|
|||
@override
|
||||
internalPerform() {
|
||||
Source source = getRequiredSource();
|
||||
String content;
|
||||
int modificationTime;
|
||||
try {
|
||||
TimestampedData<String> data = context.getContents(source);
|
||||
outputs[CONTENT] = data.data;
|
||||
outputs[MODIFICATION_TIME] = data.modificationTime;
|
||||
content = data.data;
|
||||
modificationTime = data.modificationTime;
|
||||
} catch (exception) {
|
||||
outputs[CONTENT] = '';
|
||||
outputs[MODIFICATION_TIME] = -1;
|
||||
content = '';
|
||||
modificationTime = -1;
|
||||
}
|
||||
_validateModificationTime(source, modificationTime);
|
||||
outputs[CONTENT] = content;
|
||||
outputs[MODIFICATION_TIME] = modificationTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that the [target] cache entry has the same modification time
|
||||
* as the given. Otherwise throw a new [ModificationTimeMismatchError] and
|
||||
* instruct to invalidate targets content.
|
||||
*/
|
||||
void _validateModificationTime(Source source, int modificationTime) {
|
||||
AnalysisContext context = this.context;
|
||||
if (context is InternalAnalysisContext) {
|
||||
CacheEntry entry = context.getCacheEntry(target);
|
||||
if (entry != null && entry.modificationTime != modificationTime) {
|
||||
entry.modificationTime = modificationTime;
|
||||
entry.setState(CONTENT, CacheState.INVALID);
|
||||
entry.setState(MODIFICATION_TIME, CacheState.INVALID);
|
||||
throw new ModificationTimeMismatchError(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -335,6 +335,8 @@ abstract class AnalysisTask {
|
|||
// }
|
||||
} on AnalysisException {
|
||||
rethrow;
|
||||
} on ModificationTimeMismatchError {
|
||||
rethrow;
|
||||
} catch (exception, stackTrace) {
|
||||
throw new AnalysisException(
|
||||
'Unexpected exception while performing $description',
|
||||
|
@ -422,6 +424,18 @@ abstract class MapTaskInput<K, V> implements TaskInput<Map<K, V>> {
|
|||
BinaryFunction<K, dynamic /*element of V*/, dynamic/*=E*/ > mapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instances of this class are thrown when a task detects that the modification
|
||||
* time of a cache entry is not the same as the actual modification time. This
|
||||
* means that any analysis results based on the content of the target cannot be
|
||||
* used anymore and must be invalidated.
|
||||
*/
|
||||
class ModificationTimeMismatchError {
|
||||
final Source source;
|
||||
|
||||
ModificationTimeMismatchError(this.source);
|
||||
}
|
||||
|
||||
/**
|
||||
* A policy object that can compute sizes of results and provide the maximum
|
||||
* active and idle sizes that can be kept in the cache.
|
||||
|
|
|
@ -856,6 +856,57 @@ part of lib;
|
|||
isTrue);
|
||||
}
|
||||
|
||||
void test_flushResolvedUnit_updateFile_dontNotify() {
|
||||
String oldCode = '';
|
||||
String newCode = r'''
|
||||
import 'dart:async';
|
||||
''';
|
||||
String path = '/test.dart';
|
||||
Source source = resourceProvider.newFile(path, oldCode).createSource();
|
||||
context.applyChanges(new ChangeSet()..addedSource(source));
|
||||
context.resolveCompilationUnit2(source, source);
|
||||
// Flush all results units.
|
||||
context.analysisCache.flush((target, result) {
|
||||
if (target.source == source) {
|
||||
return RESOLVED_UNIT_RESULTS.contains(result);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
// Update the file, but don't notify the context.
|
||||
resourceProvider.updateFile(path, newCode);
|
||||
// Driver must detect that the file was changed and recover.
|
||||
CompilationUnit unit = context.resolveCompilationUnit2(source, source);
|
||||
expect(unit, isNotNull);
|
||||
}
|
||||
|
||||
void test_flushResolvedUnit_updateFile_dontNotify2() {
|
||||
String oldCode = r'''
|
||||
main() {}
|
||||
''';
|
||||
String newCode = r'''
|
||||
import 'dart:async';
|
||||
main() {}
|
||||
''';
|
||||
String path = '/test.dart';
|
||||
Source source = resourceProvider.newFile(path, oldCode).createSource();
|
||||
context.applyChanges(new ChangeSet()..addedSource(source));
|
||||
context.resolveCompilationUnit2(source, source);
|
||||
// Flush all results units.
|
||||
context.analysisCache.flush((target, result) {
|
||||
if (target.source == source) {
|
||||
if (target.source == source) {
|
||||
return RESOLVED_UNIT_RESULTS.contains(result);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
// Update the file, but don't notify the context.
|
||||
resourceProvider.updateFile(path, newCode);
|
||||
// Driver must detect that the file was changed and recover.
|
||||
CompilationUnit unit = context.resolveCompilationUnit2(source, source);
|
||||
expect(unit, isNotNull);
|
||||
}
|
||||
|
||||
void test_getAnalysisOptions() {
|
||||
expect(context.analysisOptions, isNotNull);
|
||||
}
|
||||
|
@ -1956,7 +2007,7 @@ library expectedToFindSemicolon
|
|||
if (AnalysisEngine.instance.limitInvalidationInTaskModel) {
|
||||
expect(source.readCount, 7);
|
||||
} else {
|
||||
expect(source.readCount, 5);
|
||||
expect(source.readCount, 4);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2171,6 +2222,17 @@ class ClassTwo {
|
|||
["dart.core", "dart.async", "dart.math", "libA", "libB"]);
|
||||
}
|
||||
|
||||
// void test_resolveCompilationUnit_sourceChangeDuringResolution() {
|
||||
// _context = new _AnalysisContext_sourceChangeDuringResolution();
|
||||
// AnalysisContextFactory.initContextWithCore(_context);
|
||||
// _sourceFactory = _context.sourceFactory;
|
||||
// Source source = _addSource("/lib.dart", "library lib;");
|
||||
// CompilationUnit compilationUnit =
|
||||
// _context.resolveCompilationUnit2(source, source);
|
||||
// expect(compilationUnit, isNotNull);
|
||||
// expect(_context.getLineInfo(source), isNotNull);
|
||||
// }
|
||||
|
||||
void test_resolveCompilationUnit_library() {
|
||||
Source source = addSource("/lib.dart", "library lib;");
|
||||
LibraryElement library = context.computeLibraryElement(source);
|
||||
|
@ -2187,17 +2249,6 @@ class ClassTwo {
|
|||
expect(compilationUnit, isNotNull);
|
||||
}
|
||||
|
||||
// void test_resolveCompilationUnit_sourceChangeDuringResolution() {
|
||||
// _context = new _AnalysisContext_sourceChangeDuringResolution();
|
||||
// AnalysisContextFactory.initContextWithCore(_context);
|
||||
// _sourceFactory = _context.sourceFactory;
|
||||
// Source source = _addSource("/lib.dart", "library lib;");
|
||||
// CompilationUnit compilationUnit =
|
||||
// _context.resolveCompilationUnit2(source, source);
|
||||
// expect(compilationUnit, isNotNull);
|
||||
// expect(_context.getLineInfo(source), isNotNull);
|
||||
// }
|
||||
|
||||
void test_setAnalysisOptions() {
|
||||
AnalysisOptionsImpl options = new AnalysisOptionsImpl();
|
||||
options.cacheSize = 42;
|
||||
|
|
Loading…
Reference in a new issue