From bd594d453f429ac5347ea0275aa18b4fdae3825b Mon Sep 17 00:00:00 2001 From: Konstantin Shcheglov Date: Wed, 26 Oct 2016 11:48:49 -0700 Subject: [PATCH] Put fully resolved analysis results (just errors now) into the byte cache. R=brianwilkerson@google.com, paulberry@google.com BUG= Review URL: https://codereview.chromium.org/2455573003 . --- .../lib/src/dart/analysis/driver.dart | 89 +++++- pkg/analyzer/lib/src/summary/format.dart | 290 ++++++++++++++++++ pkg/analyzer/lib/src/summary/format.fbs | 40 +++ pkg/analyzer/lib/src/summary/idl.dart | 50 +++ 4 files changed, 466 insertions(+), 3 deletions(-) diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart index 85d2b396364..e009131b80d 100644 --- a/pkg/analyzer/lib/src/dart/analysis/driver.dart +++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart @@ -236,7 +236,8 @@ class AnalysisDriver { for (String path in _priorityFiles) { if (_filesToAnalyze.remove(path)) { _File file = _fileForPath(path); - AnalysisResult result = _computeAnalysisResult(file); + AnalysisResult result = + _computeAnalysisResult(file, withUnit: true); yield result; break; } @@ -252,7 +253,7 @@ class AnalysisDriver { if (_filesToAnalyze.isNotEmpty) { String path = _removeFirst(_filesToAnalyze); _File file = _fileForPath(path); - AnalysisResult result = _computeAnalysisResult(file); + AnalysisResult result = _computeAnalysisResult(file, withUnit: false); yield result; // Repeat the processing loop. _hasWork.notify(); @@ -366,10 +367,32 @@ class AnalysisDriver { /** * Compute the [AnalysisResult] for the [file]. + * + * The result will have the fully resolved unit only if [withUnit] is `true`. */ - AnalysisResult _computeAnalysisResult(_File file) { + AnalysisResult _computeAnalysisResult(_File file, {bool withUnit: false}) { + // If we don't need to the fully resolved unit, check for a cached result. + if (!withUnit) { + AnalysisResult result = _getCachedAnalysisResult(file); + if (result != null) { + return result; + } + } + + // We need the fully resolved unit, or the result is not cached. return _logger.run('Compute analysis result for $file', () { _LibraryContext libraryContext = _createLibraryContext(file); + + // We recomputed the dependency hash, and we might have a cached result. + if (!withUnit) { + AnalysisResult result = _getCachedAnalysisResult(file); + if (result != null) { + _logger.writeln('Return the cached analysis result.'); + return result; + } + } + + // Still no result, compute and store it. AnalysisContext analysisContext = _createAnalysisContext(libraryContext); try { analysisContext.setContents(file.source, file.content); @@ -377,6 +400,25 @@ class AnalysisDriver { CompilationUnit resolvedUnit = analysisContext.resolveCompilationUnit2(file.source, file.source); List errors = analysisContext.computeErrors(file.source); + + // Store the result into the cache. + { + List bytes = new AnalysisDriverResolvedUnitBuilder( + errors: errors + .map((error) => new AnalysisDriverUnitErrorBuilder( + offset: error.offset, + length: error.length, + uniqueName: error.errorCode.uniqueName, + message: error.message, + correction: error.correction)) + .toList()) + .toBuffer(); + String key = _getResolvedUnitKey(file); + _byteStore.put(key, bytes); + } + + // Return the full result. + _logger.writeln('Computed new analysis result.'); return new AnalysisResult(file.path, file.uri, file.content, file.contentHash, resolvedUnit, errors); } finally { @@ -526,6 +568,47 @@ class AnalysisDriver { return new _File.forResolution(this, source); } + /** + * If we know the dependency signature for the [file], try to load the + * analysis result from the cache. Return `null` if not found. + */ + AnalysisResult _getCachedAnalysisResult(_File file) { + String key = _getResolvedUnitKey(file); + if (key != null) { + List bytes = _byteStore.get(key); + if (bytes != null) { + var unit = new AnalysisDriverResolvedUnit.fromBuffer(bytes); + List errors = unit.errors + .map((error) => new AnalysisError.forValues( + file.source, + error.offset, + error.length, + ErrorCode.byUniqueName(error.uniqueName), + error.message, + error.correction)) + .toList(); + return new AnalysisResult( + file.path, file.uri, null, file.contentHash, null, errors); + } + } + return null; + } + + /** + * Return the key to store fully resolved results for the [file] into the + * cache. Return `null` if the dependency signature is not known yet. + */ + String _getResolvedUnitKey(_File file) { + String dependencyHash = _dependencySignatureMap[file.uri]; + if (dependencyHash != null) { + ApiSignature signature = new ApiSignature(); + signature.addString(dependencyHash); + signature.addString(file.contentHash); + return '${signature.toHex()}.resolved'; + } + return null; + } + /** * Verify the API signature for the file with the given [path], and decide * which linked libraries should be invalidated, and files reanalyzed. diff --git a/pkg/analyzer/lib/src/summary/format.dart b/pkg/analyzer/lib/src/summary/format.dart index d24aa70ebeb..85ac30ae6ae 100644 --- a/pkg/analyzer/lib/src/summary/format.dart +++ b/pkg/analyzer/lib/src/summary/format.dart @@ -129,6 +129,296 @@ class _UnlinkedParamKindReader extends fb.Reader { } } +class AnalysisDriverResolvedUnitBuilder extends Object with _AnalysisDriverResolvedUnitMixin implements idl.AnalysisDriverResolvedUnit { + List _errors; + + @override + List get errors => _errors ??= []; + + /** + * The full list of analysis errors, both syntactic and semantic. + */ + void set errors(List value) { + this._errors = value; + } + + AnalysisDriverResolvedUnitBuilder({List errors}) + : _errors = errors; + + /** + * Flush [informative] data recursively. + */ + void flushInformative() { + _errors?.forEach((b) => b.flushInformative()); + } + + /** + * Accumulate non-[informative] data into [signature]. + */ + void collectApiSignature(api_sig.ApiSignature signature) { + if (this._errors == null) { + signature.addInt(0); + } else { + signature.addInt(this._errors.length); + for (var x in this._errors) { + x?.collectApiSignature(signature); + } + } + } + + List toBuffer() { + fb.Builder fbBuilder = new fb.Builder(); + return fbBuilder.finish(finish(fbBuilder), "ADRU"); + } + + fb.Offset finish(fb.Builder fbBuilder) { + fb.Offset offset_errors; + if (!(_errors == null || _errors.isEmpty)) { + offset_errors = fbBuilder.writeList(_errors.map((b) => b.finish(fbBuilder)).toList()); + } + fbBuilder.startTable(); + if (offset_errors != null) { + fbBuilder.addOffset(0, offset_errors); + } + return fbBuilder.endTable(); + } +} + +idl.AnalysisDriverResolvedUnit readAnalysisDriverResolvedUnit(List buffer) { + fb.BufferContext rootRef = new fb.BufferContext.fromBytes(buffer); + return const _AnalysisDriverResolvedUnitReader().read(rootRef, 0); +} + +class _AnalysisDriverResolvedUnitReader extends fb.TableReader<_AnalysisDriverResolvedUnitImpl> { + const _AnalysisDriverResolvedUnitReader(); + + @override + _AnalysisDriverResolvedUnitImpl createObject(fb.BufferContext bc, int offset) => new _AnalysisDriverResolvedUnitImpl(bc, offset); +} + +class _AnalysisDriverResolvedUnitImpl extends Object with _AnalysisDriverResolvedUnitMixin implements idl.AnalysisDriverResolvedUnit { + final fb.BufferContext _bc; + final int _bcOffset; + + _AnalysisDriverResolvedUnitImpl(this._bc, this._bcOffset); + + List _errors; + + @override + List get errors { + _errors ??= const fb.ListReader(const _AnalysisDriverUnitErrorReader()).vTableGet(_bc, _bcOffset, 0, const []); + return _errors; + } +} + +abstract class _AnalysisDriverResolvedUnitMixin implements idl.AnalysisDriverResolvedUnit { + @override + Map toJson() { + Map _result = {}; + if (errors.isNotEmpty) _result["errors"] = errors.map((_value) => _value.toJson()).toList(); + return _result; + } + + @override + Map toMap() => { + "errors": errors, + }; + + @override + String toString() => convert.JSON.encode(toJson()); +} + +class AnalysisDriverUnitErrorBuilder extends Object with _AnalysisDriverUnitErrorMixin implements idl.AnalysisDriverUnitError { + String _correction; + int _length; + String _message; + int _offset; + String _uniqueName; + + @override + String get correction => _correction ??= ''; + + /** + * The optional correction hint for the error. + */ + void set correction(String value) { + this._correction = value; + } + + @override + int get length => _length ??= 0; + + /** + * The length of the error in the file. + */ + void set length(int value) { + assert(value == null || value >= 0); + this._length = value; + } + + @override + String get message => _message ??= ''; + + /** + * The message of the error. + */ + void set message(String value) { + this._message = value; + } + + @override + int get offset => _offset ??= 0; + + /** + * The offset from the beginning of the file. + */ + void set offset(int value) { + assert(value == null || value >= 0); + this._offset = value; + } + + @override + String get uniqueName => _uniqueName ??= ''; + + /** + * The unique name of the error code. + */ + void set uniqueName(String value) { + this._uniqueName = value; + } + + AnalysisDriverUnitErrorBuilder({String correction, int length, String message, int offset, String uniqueName}) + : _correction = correction, + _length = length, + _message = message, + _offset = offset, + _uniqueName = uniqueName; + + /** + * Flush [informative] data recursively. + */ + void flushInformative() { + } + + /** + * Accumulate non-[informative] data into [signature]. + */ + void collectApiSignature(api_sig.ApiSignature signature) { + signature.addInt(this._offset ?? 0); + signature.addInt(this._length ?? 0); + signature.addString(this._uniqueName ?? ''); + signature.addString(this._message ?? ''); + signature.addString(this._correction ?? ''); + } + + fb.Offset finish(fb.Builder fbBuilder) { + fb.Offset offset_correction; + fb.Offset offset_message; + fb.Offset offset_uniqueName; + if (_correction != null) { + offset_correction = fbBuilder.writeString(_correction); + } + if (_message != null) { + offset_message = fbBuilder.writeString(_message); + } + if (_uniqueName != null) { + offset_uniqueName = fbBuilder.writeString(_uniqueName); + } + fbBuilder.startTable(); + if (offset_correction != null) { + fbBuilder.addOffset(4, offset_correction); + } + if (_length != null && _length != 0) { + fbBuilder.addUint32(1, _length); + } + if (offset_message != null) { + fbBuilder.addOffset(3, offset_message); + } + if (_offset != null && _offset != 0) { + fbBuilder.addUint32(0, _offset); + } + if (offset_uniqueName != null) { + fbBuilder.addOffset(2, offset_uniqueName); + } + return fbBuilder.endTable(); + } +} + +class _AnalysisDriverUnitErrorReader extends fb.TableReader<_AnalysisDriverUnitErrorImpl> { + const _AnalysisDriverUnitErrorReader(); + + @override + _AnalysisDriverUnitErrorImpl createObject(fb.BufferContext bc, int offset) => new _AnalysisDriverUnitErrorImpl(bc, offset); +} + +class _AnalysisDriverUnitErrorImpl extends Object with _AnalysisDriverUnitErrorMixin implements idl.AnalysisDriverUnitError { + final fb.BufferContext _bc; + final int _bcOffset; + + _AnalysisDriverUnitErrorImpl(this._bc, this._bcOffset); + + String _correction; + int _length; + String _message; + int _offset; + String _uniqueName; + + @override + String get correction { + _correction ??= const fb.StringReader().vTableGet(_bc, _bcOffset, 4, ''); + return _correction; + } + + @override + int get length { + _length ??= const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 1, 0); + return _length; + } + + @override + String get message { + _message ??= const fb.StringReader().vTableGet(_bc, _bcOffset, 3, ''); + return _message; + } + + @override + int get offset { + _offset ??= const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 0, 0); + return _offset; + } + + @override + String get uniqueName { + _uniqueName ??= const fb.StringReader().vTableGet(_bc, _bcOffset, 2, ''); + return _uniqueName; + } +} + +abstract class _AnalysisDriverUnitErrorMixin implements idl.AnalysisDriverUnitError { + @override + Map toJson() { + Map _result = {}; + if (correction != '') _result["correction"] = correction; + if (length != 0) _result["length"] = length; + if (message != '') _result["message"] = message; + if (offset != 0) _result["offset"] = offset; + if (uniqueName != '') _result["uniqueName"] = uniqueName; + return _result; + } + + @override + Map toMap() => { + "correction": correction, + "length": length, + "message": message, + "offset": offset, + "uniqueName": uniqueName, + }; + + @override + String toString() => convert.JSON.encode(toJson()); +} + class CodeRangeBuilder extends Object with _CodeRangeMixin implements idl.CodeRange { int _length; int _offset; diff --git a/pkg/analyzer/lib/src/summary/format.fbs b/pkg/analyzer/lib/src/summary/format.fbs index 3da45129a85..c3eb5e2b5da 100644 --- a/pkg/analyzer/lib/src/summary/format.fbs +++ b/pkg/analyzer/lib/src/summary/format.fbs @@ -781,6 +781,46 @@ enum UnlinkedParamKind : byte { named } +/** + * Information about a resolved unit. + */ +table AnalysisDriverResolvedUnit { + /** + * The full list of analysis errors, both syntactic and semantic. + */ + errors:[AnalysisDriverUnitError] (id: 0); +} + +/** + * Information about an error in a resolved unit. + */ +table AnalysisDriverUnitError { + /** + * The optional correction hint for the error. + */ + correction:string (id: 4); + + /** + * The length of the error in the file. + */ + length:uint (id: 1); + + /** + * The message of the error. + */ + message:string (id: 3); + + /** + * The offset from the beginning of the file. + */ + offset:uint (id: 0); + + /** + * The unique name of the error code. + */ + uniqueName:string (id: 2); +} + /** * Information about an element code range. */ diff --git a/pkg/analyzer/lib/src/summary/idl.dart b/pkg/analyzer/lib/src/summary/idl.dart index 61f62a8bf09..c67dee6e45a 100644 --- a/pkg/analyzer/lib/src/summary/idl.dart +++ b/pkg/analyzer/lib/src/summary/idl.dart @@ -57,6 +57,56 @@ import 'format.dart' as generated; */ const informative = null; +/** + * Information about a resolved unit. + */ +@TopLevel('ADRU') +abstract class AnalysisDriverResolvedUnit extends base.SummaryClass { + factory AnalysisDriverResolvedUnit.fromBuffer(List buffer) => + generated.readAnalysisDriverResolvedUnit(buffer); + + /** + * The full list of analysis errors, both syntactic and semantic. + */ + @Id(0) + List get errors; +} + +/** + * Information about an error in a resolved unit. + */ +abstract class AnalysisDriverUnitError extends base.SummaryClass { + /** + * The optional correction hint for the error. + */ + @Id(4) + String get correction; + + /** + * The length of the error in the file. + */ + @Id(1) + int get length; + + /** + * The message of the error. + */ + @Id(3) + String get message; + + /** + * The offset from the beginning of the file. + */ + @Id(0) + int get offset; + + /** + * The unique name of the error code. + */ + @Id(2) + String get uniqueName; +} + /** * Information about an element code range. */