blob: e77929abb8675071998c9f58fdde43550700563a [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:collection';
import 'package:kernel/kernel.dart';
/// Information about libraries and nodes that use references.
///
/// This allows us quickly replace one library with another, that has the
/// same API, but provided by different nodes, so different references.
class ReferenceIndex {
/// The nodes in a library pointing to each reference.
///
/// A key is an indexed library.
/// Its value is a map.
/// A key is a target reference used in the library.
/// Its value is the list of nodes in the library that use the reference.
final Map<Library, Map<Reference, List<Node>>> _libraryReferences = {};
/// The nodes in the program pointing to each reference.
///
/// A key in the map is a used reference.
/// Its value is a set of nodes that use this reference.
final Map<Reference, Set<Node>> _referenceToNodes = {};
/// Index any libraries of the [program] that are not indexed yet.
void indexNewLibraries(Program program) {
for (var library in program.libraries) {
if (!_libraryReferences.containsKey(library)) {
_indexLibrary(library);
}
}
}
/// Remove information about references used in given [library].
void removeLibrary(Library library) {
var referencedFromNodes = _libraryReferences.remove(library);
if (referencedFromNodes != null) {
referencedFromNodes.forEach((reference, nodes) {
_referenceToNodes[reference]?.removeAll(nodes);
});
}
}
/// Remove the [oldLibrary] from the index, index the [newLibrary]. Replace
/// references to [oldLibrary] node with references to the corresponding
/// nodes in the [newLibrary].
///
/// Canonical name trees of the [oldLibrary] and [newLibrary] are expected to
/// be isomorphic.
void replaceLibrary(Library oldLibrary, Library newLibrary) {
removeLibrary(oldLibrary);
_indexLibrary(newLibrary);
/// Visit in parallel two isomorphic name trees, and replace references.
void visitNames(CanonicalName oldName, CanonicalName newName) {
var oldReference = oldName.reference;
var newReference = newName.reference;
if (oldReference != null && newReference != null) {
var nodes = _referenceToNodes.remove(oldReference);
if (nodes != null) {
_referenceToNodes[newReference] = nodes;
var visitor = new _ReplaceVisitor(oldReference, newReference);
for (var node in nodes) {
node.accept(visitor);
}
}
}
// Replace references to children.
var oldChildren = oldName.children.iterator;
var newChildren = newName.children.iterator;
while (oldChildren.moveNext() && newChildren.moveNext()) {
visitNames(oldChildren.current, newChildren.current);
}
}
visitNames(oldLibrary.canonicalName, newLibrary.canonicalName);
}
/// Index the given [library], which is not yet indexed.
void _indexLibrary(Library library) {
var referenceToNodes = <Reference, List<Node>>{};
_libraryReferences[library] = referenceToNodes;
var visitor = new _IndexVisitor(this, library, referenceToNodes);
library.accept(visitor);
}
}
/// Visitor visits a library and records nodes and references they use.
class _IndexVisitor extends RecursiveVisitor {
final ReferenceIndex index;
final Library libraryBeingIndexed;
final Map<Reference, List<Node>> referenceToNodes;
_IndexVisitor(this.index, this.libraryBeingIndexed, this.referenceToNodes);
/// Add the given [node] that uses the [reference].
void addNode(Node node, Reference reference) {
if (reference == null) return;
(index._referenceToNodes[reference] ??= new HashSet<Node>()).add(node);
}
@override
void visitConstructorInvocation(ConstructorInvocation node) {
addNode(node, node.targetReference);
}
@override
void visitDirectMethodInvocation(DirectMethodInvocation node) {
addNode(node, node.targetReference);
}
@override
void visitDirectPropertyGet(DirectPropertyGet node) {
addNode(node, node.targetReference);
}
@override
void visitDirectPropertySet(DirectPropertySet node) {
addNode(node, node.targetReference);
}
@override
void visitInterfaceType(InterfaceType node) {
addNode(node, node.className);
super.visitInterfaceType(node);
}
@override
void visitLibrary(Library node) {
for (var i = 0; i < node.additionalExports.length; i++) {
addNode(node, node.additionalExports[i]);
}
super.visitLibrary(node);
}
@override
void visitLibraryDependency(LibraryDependency node) {
addNode(node, node.importedLibraryReference);
}
@override
void visitMethodInvocation(MethodInvocation node) {
addNode(node, node.interfaceTargetReference);
}
@override
void visitPropertyGet(PropertyGet node) {
addNode(node, node.interfaceTargetReference);
}
@override
void visitPropertySet(PropertySet node) {
addNode(node, node.interfaceTargetReference);
}
@override
void visitRedirectingInitializer(RedirectingInitializer node) {
addNode(node, node.targetReference);
}
@override
void visitStaticGet(StaticGet node) {
addNode(node, node.targetReference);
}
@override
void visitStaticInvocation(StaticInvocation node) {
addNode(node, node.targetReference);
}
@override
void visitStaticSet(StaticSet node) {
addNode(node, node.targetReference);
}
@override
void visitSuperInitializer(SuperInitializer node) {
addNode(node, node.targetReference);
}
@override
void visitSuperMethodInvocation(SuperMethodInvocation node) {
addNode(node, node.interfaceTargetReference);
}
@override
void visitSuperPropertyGet(SuperPropertyGet node) {
addNode(node, node.interfaceTargetReference);
}
@override
void visitSuperPropertySet(SuperPropertySet node) {
addNode(node, node.interfaceTargetReference);
}
@override
void visitSupertype(Supertype node) {
addNode(node, node.className);
}
}
/// [Visitor] that replaces the [oldReference] with the [newReference] in
/// a single [Node].
class _ReplaceVisitor extends Visitor {
final Reference oldReference;
final Reference newReference;
_ReplaceVisitor(this.oldReference, this.newReference);
@override
void visitConstructorInvocation(ConstructorInvocation node) {
node.targetReference = newReference;
}
@override
void visitDirectMethodInvocation(DirectMethodInvocation node) {
node.targetReference = newReference;
}
@override
void visitDirectPropertyGet(DirectPropertyGet node) {
node.targetReference = newReference;
}
@override
void visitDirectPropertySet(DirectPropertySet node) {
node.targetReference = newReference;
}
@override
void visitInterfaceType(InterfaceType node) {
node.className = newReference;
}
@override
void visitLibrary(Library node) {
for (var i = 0; i < node.additionalExports.length; i++) {
if (node.additionalExports[i] == oldReference) {
node.additionalExports[i] = newReference;
return;
}
}
}
@override
void visitLibraryDependency(LibraryDependency node) {
node.importedLibraryReference = newReference;
}
@override
void visitMethodInvocation(MethodInvocation node) {
node.interfaceTargetReference = newReference;
}
@override
void visitPropertyGet(PropertyGet node) {
node.interfaceTargetReference = newReference;
}
@override
void visitPropertySet(PropertySet node) {
node.interfaceTargetReference = newReference;
}
@override
void visitRedirectingInitializer(RedirectingInitializer node) {
node.targetReference = newReference;
}
@override
void visitStaticGet(StaticGet node) {
node.targetReference = newReference;
}
@override
void visitStaticInvocation(StaticInvocation node) {
node.targetReference = newReference;
}
@override
void visitStaticSet(StaticSet node) {
node.targetReference = newReference;
}
@override
void visitSuperInitializer(SuperInitializer node) {
node.targetReference = newReference;
}
@override
void visitSuperMethodInvocation(SuperMethodInvocation node) {
node.interfaceTargetReference = newReference;
}
@override
void visitSuperPropertyGet(SuperPropertyGet node) {
node.interfaceTargetReference = newReference;
}
@override
void visitSuperPropertySet(SuperPropertySet node) {
node.interfaceTargetReference = newReference;
}
@override
void visitSupertype(Supertype node) {
node.className = newReference;
}
}