a9f684e624
In https://dartbug.com/55357 a crash is reported happening after a hot restart (not reload!) with a compilation error. This causes a "reject" call, which means we create a new incremental compiler, initializing it from the component of the previous good world. World 1: Initial state. World 2: The library is new, but (most) references are reused because of advanced invaidation. References of unnamed extensions are not reused. Instead the canonical names are unbound (so they can successfully be bound to the new reference). We now have: * ref1 (from world1) pointing to no canonical name. * ref2 (from world2) pointing to the canonical name. * The canonical name pointing to ref2. This world is rejected. World 3: We start from the world 1 state, but because world 2 *did* happen and that only most references (i.e. not references for unnamed extensions) were reused, when about to unbind the unnamed extenesion we are looking at ref1 and have nothing to unbind. Compiling creates ref3 (from world 3). Upon attempt to serialize we try to bind the canonical name to "ref3", but it is already bound to "ref2" and we crash. The main problem here is that not only `Reference`s are reused, but because `Reference`s point to `CanonicalName`s these are reused too. This CL clears the canonical name in the references in the libraries that are reused, meaning that a whole new canonical name tree for those libraries will be created. This is more in line with what the non-advanced-invalidation does (by it not reusing references and naturally getting a whole new canonical name tree). In my opinion it even makes more sense --- and the fact that the canonical names were reused was probably more me not thinking about it, than it was a deliberate design decision. Fixes https://github.com/dart-lang/sdk/issues/55357 Change-Id: I90bd579984f4aefad78243e8366ac0ab91a905bf Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/363563 Commit-Queue: Jens Johansen <jensj@google.com> Reviewed-by: Johnni Winther <johnniwinther@google.com>
242 lines
8.0 KiB
Dart
242 lines
8.0 KiB
Dart
// Copyright (c) 2019, 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 "ast.dart"
|
|
show
|
|
Class,
|
|
Constructor,
|
|
Extension,
|
|
ExtensionTypeDeclaration,
|
|
Field,
|
|
Library,
|
|
Name,
|
|
Procedure,
|
|
ProcedureKind,
|
|
Reference,
|
|
Typedef;
|
|
|
|
class ReferenceFromIndex {
|
|
Map<Library, IndexedLibrary> _indexedLibraries =
|
|
new Map<Library, IndexedLibrary>.identity();
|
|
|
|
/// Add an entry mapping from *new* [library] to an index of the old library.
|
|
void addIndexedLibrary(Library library, IndexedLibrary indexedLibrary) {
|
|
assert(!_indexedLibraries.containsKey(library));
|
|
_indexedLibraries[library] = indexedLibrary;
|
|
}
|
|
|
|
/// Lookup the new library and get an index of the old library.
|
|
IndexedLibrary? lookupLibrary(Library library) => _indexedLibraries[library];
|
|
}
|
|
|
|
abstract class IndexedContainer {
|
|
Library get library;
|
|
|
|
/// Reference to this container node.
|
|
Reference get reference;
|
|
|
|
Reference? lookupConstructorReference(Name name);
|
|
Reference? lookupFieldReference(Name name);
|
|
Reference? lookupGetterReference(Name name);
|
|
Reference? lookupSetterReference(Name name);
|
|
}
|
|
|
|
mixin _IndexedProceduresMixin {
|
|
final Map<Name, Reference> _getterReferences = new Map<Name, Reference>();
|
|
final Map<Name, Reference> _setterReferences = new Map<Name, Reference>();
|
|
|
|
void _addProcedures(List<Procedure> procedures) {
|
|
for (int i = 0; i < procedures.length; i++) {
|
|
_addProcedure(procedures[i]);
|
|
}
|
|
}
|
|
|
|
void _addProcedure(Procedure procedure) {
|
|
procedure.reference.canonicalName = null;
|
|
Name name = procedure.name;
|
|
if (procedure.isSetter) {
|
|
assert(_setterReferences[name] == null);
|
|
_setterReferences[name] = procedure.reference;
|
|
} else {
|
|
assert(_getterReferences[name] == null);
|
|
assert(procedure.kind == ProcedureKind.Method ||
|
|
procedure.kind == ProcedureKind.Getter ||
|
|
procedure.kind == ProcedureKind.Operator);
|
|
_getterReferences[name] = procedure.reference;
|
|
}
|
|
}
|
|
}
|
|
|
|
abstract class IndexedContainerImpl
|
|
with _IndexedProceduresMixin
|
|
implements IndexedContainer {
|
|
final Map<Name, Reference> _fieldReferences = new Map<Name, Reference>();
|
|
|
|
@override
|
|
Reference? lookupFieldReference(Name name) => _fieldReferences[name];
|
|
@override
|
|
Reference? lookupGetterReference(Name name) => _getterReferences[name];
|
|
@override
|
|
Reference? lookupSetterReference(Name name) => _setterReferences[name];
|
|
|
|
void _addFields(List<Field> fields) {
|
|
for (int i = 0; i < fields.length; i++) {
|
|
Field field = fields[i];
|
|
field.fieldReference.canonicalName = null;
|
|
field.getterReference.canonicalName = null;
|
|
field.setterReference?.canonicalName = null;
|
|
Name name = field.name;
|
|
assert(_fieldReferences[name] == null);
|
|
_fieldReferences[name] = field.fieldReference;
|
|
assert(_getterReferences[name] == null);
|
|
_getterReferences[name] = field.getterReference;
|
|
if (field.hasSetter) {
|
|
assert(_setterReferences[name] == null);
|
|
_setterReferences[name] = field.setterReference!;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class IndexedLibrary extends IndexedContainerImpl {
|
|
final Map<String, Typedef> _typedefs = {};
|
|
final Map<String, IndexedClass> _indexedClasses = {};
|
|
final Map<String, IndexedExtensionTypeDeclaration>
|
|
_indexedExtensionTypeDeclarations = {};
|
|
final Map<String, Extension> _extensions = {};
|
|
@override
|
|
final Library library;
|
|
|
|
/// Index [library], and clear all canonical names in references in this
|
|
/// library and its containing classes, procedures etc.
|
|
/// TODO(jensj): Should this class be renamed to make it more immediately
|
|
/// clear that it also clears canonical names? And should the class be moved
|
|
/// as it is more tightly bound with the incremental compiler?
|
|
IndexedLibrary(this.library) {
|
|
library.reference.canonicalName = null;
|
|
for (int i = 0; i < library.typedefs.length; i++) {
|
|
Typedef typedef = library.typedefs[i];
|
|
typedef.reference.canonicalName = null;
|
|
assert(_typedefs[typedef.name] == null);
|
|
_typedefs[typedef.name] = typedef;
|
|
}
|
|
for (int i = 0; i < library.classes.length; i++) {
|
|
Class c = library.classes[i];
|
|
c.reference.canonicalName = null;
|
|
assert(_indexedClasses[c.name] == null);
|
|
_indexedClasses[c.name] = new IndexedClass._(c, library);
|
|
}
|
|
List<Extension> unnamedExtensions = [];
|
|
for (int i = 0; i < library.extensions.length; i++) {
|
|
Extension extension = library.extensions[i];
|
|
extension.reference.canonicalName = null;
|
|
if (extension.isUnnamedExtension) {
|
|
unnamedExtensions.add(extension);
|
|
} else {
|
|
assert(_extensions[extension.name] == null);
|
|
_extensions[extension.name] = extension;
|
|
}
|
|
}
|
|
for (int i = 0; i < library.extensionTypeDeclarations.length; i++) {
|
|
ExtensionTypeDeclaration extensionTypeDeclaration =
|
|
library.extensionTypeDeclarations[i];
|
|
extensionTypeDeclaration.reference.canonicalName = null;
|
|
assert(_indexedExtensionTypeDeclarations[extensionTypeDeclaration.name] ==
|
|
null);
|
|
_indexedExtensionTypeDeclarations[extensionTypeDeclaration.name] =
|
|
new IndexedExtensionTypeDeclaration(this, extensionTypeDeclaration);
|
|
}
|
|
_addProcedures(library.procedures);
|
|
_addFields(library.fields);
|
|
}
|
|
|
|
@override
|
|
Reference get reference => library.reference;
|
|
|
|
Typedef? lookupTypedef(String name) => _typedefs[name];
|
|
IndexedClass? lookupIndexedClass(String name) => _indexedClasses[name];
|
|
|
|
Extension? lookupExtension(String name) => _extensions[name];
|
|
|
|
IndexedExtensionTypeDeclaration? lookupIndexedExtensionTypeDeclaration(
|
|
String name) =>
|
|
_indexedExtensionTypeDeclarations[name];
|
|
|
|
@override
|
|
Reference? lookupConstructorReference(Name name) {
|
|
throw new UnsupportedError("$runtimeType.lookupConstructorReference");
|
|
}
|
|
}
|
|
|
|
class IndexedClass extends IndexedContainerImpl {
|
|
final Class cls;
|
|
final Map<Name, Reference> _constructors = {};
|
|
@override
|
|
final Library library;
|
|
|
|
IndexedClass._(this.cls, this.library) {
|
|
for (int i = 0; i < cls.constructors.length; i++) {
|
|
Constructor constructor = cls.constructors[i];
|
|
constructor.reference.canonicalName = null;
|
|
_constructors[constructor.name] = constructor.reference;
|
|
}
|
|
for (int i = 0; i < cls.procedures.length; i++) {
|
|
Procedure procedure = cls.procedures[i];
|
|
if (procedure.isFactory) {
|
|
procedure.reference.canonicalName = null;
|
|
_constructors[procedure.name] = procedure.reference;
|
|
} else {
|
|
_addProcedure(procedure);
|
|
}
|
|
}
|
|
_addFields(cls.fields);
|
|
}
|
|
|
|
@override
|
|
Reference get reference => cls.reference;
|
|
|
|
@override
|
|
Reference? lookupConstructorReference(Name name) => _constructors[name];
|
|
}
|
|
|
|
class IndexedExtensionTypeDeclaration
|
|
with _IndexedProceduresMixin
|
|
implements IndexedContainer {
|
|
final IndexedLibrary _indexedLibrary;
|
|
final ExtensionTypeDeclaration extensionTypeDeclaration;
|
|
|
|
IndexedExtensionTypeDeclaration(
|
|
this._indexedLibrary, this.extensionTypeDeclaration) {
|
|
_addProcedures(extensionTypeDeclaration.procedures);
|
|
}
|
|
|
|
@override
|
|
Library get library => _indexedLibrary.library;
|
|
|
|
@override
|
|
Reference get reference => extensionTypeDeclaration.reference;
|
|
|
|
@override
|
|
Reference? lookupConstructorReference(Name name) =>
|
|
// Constructors are stored as methods in the library.
|
|
_indexedLibrary.lookupGetterReference(name);
|
|
|
|
@override
|
|
Reference? lookupFieldReference(Name name) =>
|
|
// Static fields are stored in the library.
|
|
_indexedLibrary.lookupFieldReference(name);
|
|
|
|
@override
|
|
Reference? lookupGetterReference(Name name) {
|
|
return _getterReferences[name] ??
|
|
_indexedLibrary.lookupGetterReference(name);
|
|
}
|
|
|
|
@override
|
|
Reference? lookupSetterReference(Name name) {
|
|
return _setterReferences[name] ??
|
|
_indexedLibrary.lookupSetterReference(name);
|
|
}
|
|
}
|