blob: ea14d1d292ab10b74688d49f2dc442f8f4d4f747 [file] [log] [blame]
// Copyright (c) 2017, 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.
import 'dart:convert';
import 'package:front_end/src/fasta/builder/qualified_name.dart';
import 'package:front_end/src/fasta/kernel/metadata_collector.dart';
import 'package:front_end/src/incremental/kernel_driver.dart';
import 'package:kernel/kernel.dart' as kernel;
/// Additional information that Analyzer needs for nodes.
class AnalyzerMetadata {
/// The offset of the beginning of the node code.
int codeOffset = -1;
/// The length of the node code.
int codeLength = 0;
/// If the node is a named constructor, the offset of the name.
/// Otherwise `-1`.
int constructorNameOffset = -1;
/// Optional documentation comment, may be `null`.
String documentationComment;
/// If the node is an import library dependency, the offset of the prefix.
/// Otherwise `-1`.
int importPrefixOffset = -1;
/// Return the [AnalyzerMetadata] for the [node], or `null` absent.
static AnalyzerMetadata forNode(kernel.TreeNode node) {
var repository =
node.enclosingComponent.metadata[AnalyzerMetadataRepository.TAG];
if (repository != null) {
return repository.mapping[node];
}
return null;
}
}
/// Analyzer specific implementation of [MetadataCollector].
class AnalyzerMetadataCollector implements MetadataCollector {
@override
final AnalyzerMetadataRepository repository =
new AnalyzerMetadataRepository();
@override
void setCodeStartEnd(kernel.TreeNode node, int start, int end) {
var metadata = repository._forWriting(node);
metadata.codeOffset = start;
metadata.codeLength = end - start;
}
@override
void setConstructorNameOffset(kernel.Member node, Object name) {
if (name is QualifiedName) {
var metadata = repository._forWriting(node);
metadata.constructorNameOffset = name.charOffset;
}
}
@override
void setDocumentationComment(kernel.NamedNode node, String comment) {
var metadata = repository._forWriting(node);
metadata.documentationComment = comment;
}
@override
void setImportPrefixOffset(kernel.LibraryDependency node, int offset) {
var metadata = repository._forWriting(node);
metadata.importPrefixOffset = offset;
}
}
/// Factory for creating Analyzer specific sink and repository.
class AnalyzerMetadataFactory implements MetadataFactory {
@override
int get version => 1;
@override
MetadataCollector newCollector() {
return new AnalyzerMetadataCollector();
}
@override
kernel.MetadataRepository newRepositoryForReading() {
return new AnalyzerMetadataRepository();
}
}
/// Index of metadata.
class AnalyzerMetadataIndex {
final Map<kernel.Library, List<kernel.TreeNode>> libraryNodes = {};
AnalyzerMetadataRepository repository;
/// The [library] was invalidated, flush its metadata.
void invalidate(kernel.Library library) {
var nodes = libraryNodes.remove(library);
nodes?.forEach(repository.mapping.remove);
}
/// A [newComponent] has been compiled, with new, and only new, metadata.
/// Merge the existing [repository] into the new one, and replace it.
void replaceComponent(kernel.Component newComponent) {
AnalyzerMetadataRepository newRepository =
newComponent.metadata[AnalyzerMetadataRepository.TAG];
if (newRepository != null) {
_indexNewMetadata(newRepository);
if (repository != null) {
newRepository.mapping.addAll(repository.mapping);
}
repository = newRepository;
} else {
newComponent.metadata[AnalyzerMetadataRepository.TAG] = repository;
}
}
void _indexNewMetadata(AnalyzerMetadataRepository newRepository) {
for (var node in newRepository.mapping.keys) {
var library = _enclosingLibrary(node);
assert(library != null);
var nodes = libraryNodes[library];
if (nodes == null) {
nodes = <kernel.TreeNode>[];
libraryNodes[library] = nodes;
}
nodes.add(node);
}
}
static kernel.Library _enclosingLibrary(kernel.TreeNode node) {
for (; node != null; node = node.parent) {
if (node is kernel.Library) {
return node;
}
}
return null;
}
}
/// Analyzer specific implementation of [kernel.MetadataRepository].
class AnalyzerMetadataRepository
implements kernel.MetadataRepository<AnalyzerMetadata> {
static const TAG = 'kernel.metadata.analyzer';
@override
final String tag = TAG;
@override
final Map<kernel.TreeNode, AnalyzerMetadata> mapping =
<kernel.TreeNode, AnalyzerMetadata>{};
@override
AnalyzerMetadata readFromBinary(
kernel.Node node, kernel.BinarySource source) {
return new AnalyzerMetadata()
..codeOffset = _readOffset(source)
..codeLength = _readLength(source)
..constructorNameOffset = _readOffset(source)
..documentationComment = _readOptionalString(source)
..importPrefixOffset = _readOffset(source);
}
@override
void writeToBinary(
AnalyzerMetadata metadata, kernel.Node node, kernel.BinarySink sink) {
_writeOffset(sink, metadata.codeOffset);
_writeLength(sink, metadata.codeLength);
_writeOffset(sink, metadata.constructorNameOffset);
_writeOptionalString(sink, metadata.documentationComment);
_writeOffset(sink, metadata.importPrefixOffset);
}
/// Return the existing or new [AnalyzerMetadata] instance for the [node].
AnalyzerMetadata _forWriting(kernel.TreeNode node) {
return mapping[node] ??= new AnalyzerMetadata();
}
int _readLength(kernel.BinarySource source) {
return source.readUint32();
}
int _readOffset(kernel.BinarySource source) {
return source.readUint32() - 1;
}
String _readOptionalString(kernel.BinarySource source) {
int flag = source.readByte();
if (flag == 1) {
List<int> bytes = source.readByteList();
return utf8.decode(bytes);
} else {
return null;
}
}
/// The [length] value must be `>= 0`.
void _writeLength(kernel.BinarySink sink, int length) {
assert(length >= 0);
sink.writeUInt32(length);
}
/// The [offset] value must be `>= -1`.
void _writeOffset(kernel.BinarySink sink, int offset) {
assert(offset >= -1);
sink.writeUInt32(1 + offset);
}
void _writeOptionalString(kernel.BinarySink sink, String str) {
if (str != null) {
sink.writeByte(1);
List<int> bytes = utf8.encode(str);
sink.writeByteList(bytes);
} else {
sink.writeByte(0);
}
}
}