Version 2.14.0-54.0.dev

Merge commit '3c83032c60ee6b7b705effe397bf5820d076aa5b' into 'dev'
diff --git a/pkg/kernel/bin/size_breakdown.dart b/pkg/kernel/bin/size_breakdown.dart
index 79fbefa..dc4cd9b 100755
--- a/pkg/kernel/bin/size_breakdown.dart
+++ b/pkg/kernel/bin/size_breakdown.dart
@@ -52,9 +52,9 @@
     return result;
   }
 
-  void readStringTable(List<String> table) {
+  void readStringTable() {
     stringTableSize -= byteOffset;
-    super.readStringTable(table);
+    super.readStringTable();
     stringTableSize += byteOffset;
   }
 
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index 9f0dc34..24f0361 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -329,9 +329,9 @@
   List<String>? problemsAsJson;
 
   @override
-  final List<Expression> annotations;
+  List<Expression> annotations;
 
-  final List<LibraryDependency> dependencies;
+  List<LibraryDependency> dependencies;
 
   /// References to nodes exported by `export` declarations that:
   /// - aren't ambiguous, or
@@ -339,13 +339,13 @@
   final List<Reference> additionalExports = <Reference>[];
 
   @informative
-  final List<LibraryPart> parts;
+  List<LibraryPart> parts;
 
-  final List<Typedef> typedefs;
-  final List<Class> classes;
-  final List<Extension> extensions;
-  final List<Procedure> procedures;
-  final List<Field> fields;
+  List<Typedef> _typedefs;
+  List<Class> _classes;
+  List<Extension> _extensions;
+  List<Procedure> _procedures;
+  List<Field> _fields;
 
   Library(this.importUri,
       {this.name,
@@ -364,19 +364,64 @@
         this.annotations = annotations ?? <Expression>[],
         this.dependencies = dependencies ?? <LibraryDependency>[],
         this.parts = parts ?? <LibraryPart>[],
-        this.typedefs = typedefs ?? <Typedef>[],
-        this.classes = classes ?? <Class>[],
-        this.extensions = extensions ?? <Extension>[],
-        this.procedures = procedures ?? <Procedure>[],
-        this.fields = fields ?? <Field>[],
+        this._typedefs = typedefs ?? <Typedef>[],
+        this._classes = classes ?? <Class>[],
+        this._extensions = extensions ?? <Extension>[],
+        this._procedures = procedures ?? <Procedure>[],
+        this._fields = fields ?? <Field>[],
         super(reference) {
     setParents(this.dependencies, this);
     setParents(this.parts, this);
-    setParents(this.typedefs, this);
-    setParents(this.classes, this);
-    setParents(this.extensions, this);
-    setParents(this.procedures, this);
-    setParents(this.fields, this);
+    setParents(this._typedefs, this);
+    setParents(this._classes, this);
+    setParents(this._extensions, this);
+    setParents(this._procedures, this);
+    setParents(this._fields, this);
+  }
+
+  List<Typedef> get typedefs => _typedefs;
+
+  /// Internal. Should *ONLY* be used from within kernel.
+  ///
+  /// Used for adding typedefs when reading the dill file.
+  void set typedefsInternal(List<Typedef> typedefs) {
+    _typedefs = typedefs;
+  }
+
+  List<Class> get classes => _classes;
+
+  /// Internal. Should *ONLY* be used from within kernel.
+  ///
+  /// Used for adding classes when reading the dill file.
+  void set classesInternal(List<Class> classes) {
+    _classes = classes;
+  }
+
+  List<Extension> get extensions => _extensions;
+
+  /// Internal. Should *ONLY* be used from within kernel.
+  ///
+  /// Used for adding extensions when reading the dill file.
+  void set extensionsInternal(List<Extension> extensions) {
+    _extensions = extensions;
+  }
+
+  List<Procedure> get procedures => _procedures;
+
+  /// Internal. Should *ONLY* be used from within kernel.
+  ///
+  /// Used for adding procedures when reading the dill file.
+  void set proceduresInternal(List<Procedure> procedures) {
+    _procedures = procedures;
+  }
+
+  List<Field> get fields => _fields;
+
+  /// Internal. Should *ONLY* be used from within kernel.
+  ///
+  /// Used for adding fields when reading the dill file.
+  void set fieldsInternal(List<Field> fields) {
+    _fields = fields;
   }
 
   Nullability get nullable {
@@ -1050,7 +1095,7 @@
   Supertype? mixedInType;
 
   /// The types from the `implements` clause.
-  final List<Supertype> implementedTypes;
+  List<Supertype> implementedTypes;
 
   /// Internal. Should *ONLY* be used from within kernel.
   ///
@@ -1070,10 +1115,7 @@
     }
   }
 
-  /// Internal. Should *ONLY* be used from within kernel.
-  ///
-  /// Used for adding fields when reading the dill file.
-  final List<Field> fieldsInternal;
+  List<Field> _fieldsInternal;
   DirtifyingList<Field>? _fieldsView;
 
   /// Fields declared in the class.
@@ -1082,28 +1124,39 @@
   List<Field> get fields {
     ensureLoaded();
     // If already dirty the caller just might as well add stuff directly too.
-    if (dirty) return fieldsInternal;
-    return _fieldsView ??= new DirtifyingList(this, fieldsInternal);
+    if (dirty) return _fieldsInternal;
+    return _fieldsView ??= new DirtifyingList(this, _fieldsInternal);
   }
 
   /// Internal. Should *ONLY* be used from within kernel.
   ///
-  /// Used for adding constructors when reading the dill file.
-  final List<Constructor> constructorsInternal;
+  /// Used for adding fields when reading the dill file.
+  void set fieldsInternal(List<Field> fields) {
+    _fieldsInternal = fields;
+    _fieldsView = null;
+  }
+
+  List<Constructor> _constructorsInternal;
   DirtifyingList<Constructor>? _constructorsView;
 
   /// Constructors declared in the class.
   List<Constructor> get constructors {
     ensureLoaded();
     // If already dirty the caller just might as well add stuff directly too.
-    if (dirty) return constructorsInternal;
-    return _constructorsView ??= new DirtifyingList(this, constructorsInternal);
+    if (dirty) return _constructorsInternal;
+    return _constructorsView ??=
+        new DirtifyingList(this, _constructorsInternal);
   }
 
   /// Internal. Should *ONLY* be used from within kernel.
   ///
-  /// Used for adding procedures when reading the dill file.
-  final List<Procedure> proceduresInternal;
+  /// Used for adding constructors when reading the dill file.
+  void set constructorsInternal(List<Constructor> constructors) {
+    _constructorsInternal = constructors;
+    _constructorsView = null;
+  }
+
+  List<Procedure> _proceduresInternal;
   DirtifyingList<Procedure>? _proceduresView;
 
   /// Procedures declared in the class.
@@ -1112,16 +1165,19 @@
   List<Procedure> get procedures {
     ensureLoaded();
     // If already dirty the caller just might as well add stuff directly too.
-    if (dirty) return proceduresInternal;
-    return _proceduresView ??= new DirtifyingList(this, proceduresInternal);
+    if (dirty) return _proceduresInternal;
+    return _proceduresView ??= new DirtifyingList(this, _proceduresInternal);
   }
 
   /// Internal. Should *ONLY* be used from within kernel.
   ///
-  /// Used for adding redirecting factory constructor when reading the dill
-  /// file.
-  final List<RedirectingFactoryConstructor>
-      redirectingFactoryConstructorsInternal;
+  /// Used for adding procedures when reading the dill file.
+  void set proceduresInternal(List<Procedure> procedures) {
+    _proceduresInternal = procedures;
+    _proceduresView = null;
+  }
+
+  List<RedirectingFactoryConstructor> _redirectingFactoryConstructorsInternal;
   DirtifyingList<RedirectingFactoryConstructor>?
       _redirectingFactoryConstructorsView;
 
@@ -1131,9 +1187,19 @@
   List<RedirectingFactoryConstructor> get redirectingFactoryConstructors {
     ensureLoaded();
     // If already dirty the caller just might as well add stuff directly too.
-    if (dirty) return redirectingFactoryConstructorsInternal;
+    if (dirty) return _redirectingFactoryConstructorsInternal;
     return _redirectingFactoryConstructorsView ??=
-        new DirtifyingList(this, redirectingFactoryConstructorsInternal);
+        new DirtifyingList(this, _redirectingFactoryConstructorsInternal);
+  }
+
+  /// Internal. Should *ONLY* be used from within kernel.
+  ///
+  /// Used for adding redirecting factory constructor when reading the dill
+  /// file.
+  void set redirectingFactoryConstructorsInternal(
+      List<RedirectingFactoryConstructor> redirectingFactoryConstructors) {
+    _redirectingFactoryConstructorsInternal = redirectingFactoryConstructors;
+    _redirectingFactoryConstructorsView = null;
   }
 
   Class(
@@ -1156,17 +1222,17 @@
         assert(fileUri != null),
         this.typeParameters = typeParameters ?? <TypeParameter>[],
         this.implementedTypes = implementedTypes ?? <Supertype>[],
-        this.fieldsInternal = fields ?? <Field>[],
-        this.constructorsInternal = constructors ?? <Constructor>[],
-        this.proceduresInternal = procedures ?? <Procedure>[],
-        this.redirectingFactoryConstructorsInternal =
+        this._fieldsInternal = fields ?? <Field>[],
+        this._constructorsInternal = constructors ?? <Constructor>[],
+        this._proceduresInternal = procedures ?? <Procedure>[],
+        this._redirectingFactoryConstructorsInternal =
             redirectingFactoryConstructors ?? <RedirectingFactoryConstructor>[],
         super(reference) {
     setParents(this.typeParameters, this);
-    setParents(this.constructorsInternal, this);
-    setParents(this.proceduresInternal, this);
-    setParents(this.fieldsInternal, this);
-    setParents(this.redirectingFactoryConstructorsInternal, this);
+    setParents(this._constructorsInternal, this);
+    setParents(this._proceduresInternal, this);
+    setParents(this._fieldsInternal, this);
+    setParents(this._redirectingFactoryConstructorsInternal, this);
     this.isAbstract = isAbstract;
     this.isAnonymousMixin = isAnonymousMixin;
   }
@@ -1292,21 +1358,21 @@
   void addConstructor(Constructor constructor) {
     dirty = true;
     constructor.parent = this;
-    constructorsInternal.add(constructor);
+    _constructorsInternal.add(constructor);
   }
 
   /// Adds a procedure to this class.
   void addProcedure(Procedure procedure) {
     dirty = true;
     procedure.parent = this;
-    proceduresInternal.add(procedure);
+    _proceduresInternal.add(procedure);
   }
 
   /// Adds a field to this class.
   void addField(Field field) {
     dirty = true;
     field.parent = this;
-    fieldsInternal.add(field);
+    _fieldsInternal.add(field);
   }
 
   /// Adds a field to this class.
@@ -1314,7 +1380,7 @@
       RedirectingFactoryConstructor redirectingFactoryConstructor) {
     dirty = true;
     redirectingFactoryConstructor.parent = this;
-    redirectingFactoryConstructorsInternal.add(redirectingFactoryConstructor);
+    _redirectingFactoryConstructorsInternal.add(redirectingFactoryConstructor);
   }
 
   @override
@@ -1451,7 +1517,7 @@
   ///
   /// The members are converted into top-level members and only accessible
   /// by reference through [ExtensionMemberDescriptor].
-  final List<ExtensionMemberDescriptor> members;
+  List<ExtensionMemberDescriptor> members;
 
   @override
   List<Expression> annotations = const <Expression>[];
@@ -13566,6 +13632,77 @@
 final List<TypeParameter> emptyListOfTypeParameter =
     List.filled(0, dummyTypeParameter, growable: false);
 
+/// Almost const <Constant>[], but not const in an attempt to avoid
+/// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828.
+final List<Constant> emptyListOfConstant =
+    List.filled(0, dummyConstant, growable: false);
+
+/// Almost const <String>[], but not const in an attempt to avoid
+/// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828.
+final List<String> emptyListOfString = List.filled(0, '', growable: false);
+
+/// Almost const <Typedef>[], but not const in an attempt to avoid
+/// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828.
+final List<Typedef> emptyListOfTypedef =
+    List.filled(0, dummyTypedef, growable: false);
+
+/// Almost const <Extension>[], but not const in an attempt to avoid
+/// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828.
+final List<Extension> emptyListOfExtension =
+    List.filled(0, dummyExtension, growable: false);
+
+/// Almost const <Field>[], but not const in an attempt to avoid
+/// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828.
+final List<Field> emptyListOfField =
+    List.filled(0, dummyField, growable: false);
+
+/// Almost const <LibraryPart>[], but not const in an attempt to avoid
+/// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828.
+final List<LibraryPart> emptyListOfLibraryPart =
+    List.filled(0, dummyLibraryPart, growable: false);
+
+/// Almost const <LibraryDependency>[], but not const in an attempt to avoid
+/// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828.
+final List<LibraryDependency> emptyListOfLibraryDependency =
+    List.filled(0, dummyLibraryDependency, growable: false);
+
+/// Almost const <Procedure>[], but not const in an attempt to avoid
+/// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828.
+final List<Procedure> emptyListOfProcedure =
+    List.filled(0, dummyProcedure, growable: false);
+
+/// Almost const <MapLiteralEntry>[], but not const in an attempt to avoid
+/// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828.
+final List<MapLiteralEntry> emptyListOfMapLiteralEntry =
+    List.filled(0, dummyMapLiteralEntry, growable: false);
+
+/// Almost const <Class>[], but not const in an attempt to avoid
+/// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828.
+final List<Class> emptyListOfClass =
+    List.filled(0, dummyClass, growable: false);
+
+/// Almost const <ExtensionMemberDescriptor>[], but not const in an attempt to
+/// avoid polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828.
+final List<ExtensionMemberDescriptor> emptyListOfExtensionMemberDescriptor =
+    List.filled(0, dummyExtensionMemberDescriptor, growable: false);
+
+/// Almost const <Constructor>[], but not const in an attempt to avoid
+/// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828.
+final List<Constructor> emptyListOfConstructor =
+    List.filled(0, dummyConstructor, growable: false);
+
+/// Almost const <RedirectingFactoryConstructor>[], but not const in an attempt
+/// to avoid polymorphism. See
+/// https://dart-review.googlesource.com/c/sdk/+/185828.
+final List<RedirectingFactoryConstructor>
+    emptyListOfRedirectingFactoryConstructor =
+    List.filled(0, dummyRedirectingFactoryConstructor, growable: false);
+
+/// Almost const <Initializer>[], but not const in an attempt to avoid
+/// polymorphism. See https://dart-review.googlesource.com/c/sdk/+/185828.
+final List<Initializer> emptyListOfInitializer =
+    List.filled(0, dummyInitializer, growable: false);
+
 /// Non-nullable [DartType] dummy value.
 ///
 /// This is used as the removal sentinel in [RemovingTransformer] and can be
@@ -13594,6 +13731,9 @@
 /// Non-nullable [Name] dummy value.
 final Name dummyName = new _PublicName('');
 
+/// Non-nullable [Reference] dummy value.
+final Reference dummyReference = new Reference();
+
 /// Non-nullable [Library] dummy value.
 ///
 /// This is used as the removal sentinel in [RemovingTransformer] and can be
@@ -13645,6 +13785,17 @@
 /// constructor.
 final Extension dummyExtension = new Extension(name: '', fileUri: dummyUri);
 
+/// Non-nullable [ExtensionMemberDescriptor] dummy value.
+///
+/// This is used as the removal sentinel in [RemovingTransformer] and can be
+/// used for instance as a dummy initial value for the `List.filled`
+/// constructor.
+final ExtensionMemberDescriptor dummyExtensionMemberDescriptor =
+    new ExtensionMemberDescriptor(
+        name: dummyName,
+        kind: ExtensionMemberKind.Getter,
+        member: dummyReference);
+
 /// Non-nullable [Member] dummy value.
 ///
 /// This can be used for instance as a dummy initial value for the
@@ -13772,6 +13923,13 @@
 /// constructor.
 final Catch dummyCatch = new Catch(null, dummyStatement);
 
+/// Non-nullable [Constant] dummy value.
+///
+/// This is used as the removal sentinel in [RemovingTransformer] and can be
+/// used for instance as a dummy initial value for the `List.filled`
+/// constructor.
+final Constant dummyConstant = new NullConstant();
+
 /// Sentinel value used to signal that a node cannot be removed through the
 /// [RemovingTransformer].
 const Null cannotRemoveSentinel = null;
diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart
index 2f49538..6c8a87d 100644
--- a/pkg/kernel/lib/binary/ast_from_binary.dart
+++ b/pkg/kernel/lib/binary/ast_from_binary.dart
@@ -120,8 +120,8 @@
   final String? filename;
   final List<int> _bytes;
   int _byteOffset = 0;
-  final List<String> _stringTable = <String>[];
-  final List<Uri?> _sourceUriTable = <Uri>[];
+  List<String> _stringTable = const [];
+  List<Uri> _sourceUriTable = const [];
   Map<int, Constant> _constantTable = <int, Constant>{};
   late List<CanonicalName> _linkTable;
   int _transformerFlags = 0;
@@ -313,18 +313,18 @@
     return node;
   }
 
-  void readStringTable(List<String> table) {
+  void readStringTable() {
     // Read the table of end offsets.
     int length = readUInt30();
     List<int> endOffsets =
         new List<int>.generate(length, (_) => readUInt30(), growable: false);
     // Read the WTF-8 encoded strings.
-    table.length = length;
     int startOffset = 0;
-    for (int i = 0; i < length; ++i) {
-      table[i] = readStringEntry(endOffsets[i] - startOffset);
-      startOffset = endOffsets[i];
-    }
+    _stringTable = new List<String>.generate(length, (int index) {
+      String result = readStringEntry(endOffsets[index] - startOffset);
+      startOffset = endOffsets[index];
+      return result;
+    }, growable: false);
   }
 
   void readConstantTable() {
@@ -465,12 +465,17 @@
 
   List<Constant> _readConstantReferenceList() {
     final int length = readUInt30();
+    if (!useGrowableLists && length == 0) {
+      // When lists don't have to be growable anyway, we might as well use an
+      // almost constant one for the empty list.
+      return emptyListOfConstant;
+    }
     return new List<Constant>.generate(length, (_) => readConstantReference(),
         growable: useGrowableLists);
   }
 
   Uri readUriReference() {
-    return _sourceUriTable[readUInt30()]!;
+    return _sourceUriTable[readUInt30()];
   }
 
   String readStringReference() {
@@ -479,6 +484,11 @@
 
   List<String> readStringReferenceList() {
     int length = readUInt30();
+    if (!useGrowableLists && length == 0) {
+      // When lists don't have to be growable anyway, we might as well use an
+      // almost constant one for the empty list.
+      return emptyListOfString;
+    }
     return new List<String>.generate(length, (_) => readStringReference(),
         growable: useGrowableLists);
   }
@@ -501,45 +511,16 @@
 
   List<Expression> readAnnotationList([TreeNode? parent]) {
     int length = readUInt30();
-    if (length == 0) return const <Expression>[];
+    if (!useGrowableLists && length == 0) {
+      // When lists don't have to be growable anyway, we might as well use an
+      // almost constant one for the empty list.
+      return emptyListOfExpression;
+    }
     return new List<Expression>.generate(
         length, (_) => readExpression()..parent = parent,
         growable: useGrowableLists);
   }
 
-  void _fillTreeNodeList(
-      List<TreeNode> list, TreeNode buildObject(int index), TreeNode parent) {
-    int length = readUInt30();
-    list.length = length;
-    for (int i = 0; i < length; ++i) {
-      TreeNode object = buildObject(i);
-      list[i] = object..parent = parent;
-    }
-  }
-
-  void _fillNonTreeNodeList(List<Node> list, Node buildObject()) {
-    int length = readUInt30();
-    list.length = length;
-    for (int i = 0; i < length; ++i) {
-      Node object = buildObject();
-      list[i] = object;
-    }
-  }
-
-  /// Reads a list of named nodes, reusing any existing objects already in the
-  /// linking tree. The nodes are merged into [list], and if reading the library
-  /// implementation, the order is corrected.
-  ///
-  /// [readObject] should read the object definition and its canonical name.
-  /// If an existing object is bound to the canonical name, the existing object
-  /// must be reused and returned.
-  void _mergeNamedNodeList(
-      List<NamedNode> list, NamedNode readObject(int index), TreeNode parent) {
-    // When reading the library implementation, overwrite the whole list
-    // with the new one.
-    _fillTreeNodeList(list, readObject, parent);
-  }
-
   void readLinkTable(CanonicalName linkRoot) {
     int length = readUInt30();
     _linkTable = new List<CanonicalName>.filled(
@@ -806,7 +787,7 @@
         mergeCompilationModeOrThrow(compilationMode, index.compiledMode);
 
     _byteOffset = index.binaryOffsetForStringTable;
-    readStringTable(_stringTable);
+    readStringTable();
 
     _byteOffset = index.binaryOffsetForCanonicalNames;
     readLinkTable(component.root);
@@ -873,7 +854,7 @@
     int length = readUint32();
 
     // Read data.
-    _sourceUriTable.length = length;
+    _sourceUriTable = new List<Uri>.filled(length, dummyUri, growable: false);
     Map<Uri, Source> uriToSource = <Uri, Source>{};
     for (int i = 0; i < length; ++i) {
       String uriString = readString();
@@ -1073,32 +1054,22 @@
     // There is a field for the procedure count.
     _byteOffset = endOffset - (1) * 4;
     int procedureCount = readUint32();
-    List<int> procedureOffsets = new List<int>.filled(
-        procedureCount + 1,
-        // Use `-1` as a dummy default value.
-        -1,
-        growable: false);
 
     // There is a field for the procedure count, that number + 1 (for the end)
     // offsets, and then the class count (i.e. procedure count + 3 fields).
     _byteOffset = endOffset - (procedureCount + 3) * 4;
     int classCount = readUint32();
-    for (int i = 0; i < procedureCount + 1; i++) {
-      procedureOffsets[i] = _componentStartOffset + readUint32();
-    }
-    List<int> classOffsets = new List<int>.filled(
-        classCount + 1,
-        // Use `-1` as a dummy default value.
-        -1,
+    List<int> procedureOffsets = new List<int>.generate(
+        procedureCount + 1, (int index) => _componentStartOffset + readUint32(),
         growable: false);
 
     // There is a field for the procedure count, that number + 1 (for the end)
     // offsets, then the class count and that number + 1 (for the end) offsets.
     // (i.e. procedure count + class count + 4 fields).
     _byteOffset = endOffset - (procedureCount + classCount + 4) * 4;
-    for (int i = 0; i < classCount + 1; i++) {
-      classOffsets[i] = _componentStartOffset + readUint32();
-    }
+    List<int> classOffsets = new List<int>.generate(
+        classCount + 1, (int index) => _componentStartOffset + readUint32(),
+        growable: false);
     _byteOffset = savedByteOffset;
 
     int flags = readByte();
@@ -1145,44 +1116,104 @@
       return true;
     }());
 
-    _fillTreeNodeList(
-        library.annotations, (index) => readExpression(), library);
+    library.annotations = readAnnotationList(library);
     _readLibraryDependencies(library);
     _readAdditionalExports(library);
     _readLibraryParts(library);
-    _mergeNamedNodeList(library.typedefs, (index) => readTypedef(), library);
-
-    _mergeNamedNodeList(library.classes, (index) {
-      _byteOffset = classOffsets[index];
-      return readClass(classOffsets[index + 1]);
-    }, library);
-    _byteOffset = classOffsets.last;
-
-    _mergeNamedNodeList(library.extensions, (index) {
-      return readExtension();
-    }, library);
-
-    _mergeNamedNodeList(library.fields, (index) => readField(), library);
-    _mergeNamedNodeList(library.procedures, (index) {
-      _byteOffset = procedureOffsets[index];
-      return readProcedure(procedureOffsets[index + 1]);
-    }, library);
-    _byteOffset = procedureOffsets.last;
+    _readTypedefList(library);
+    _readClassList(library, classOffsets);
+    _readExtensionList(library);
+    library.fieldsInternal = _readFieldList(library);
+    library.proceduresInternal = _readProcedureList(library, procedureOffsets);
 
     assert(((_) => true)(debugPath.removeLast()));
     _currentLibrary = null;
     return library;
   }
 
-  void _readLibraryDependencies(Library library) {
+  void _readTypedefList(Library library) {
     int length = readUInt30();
-    library.dependencies.length = length;
-    for (int i = 0; i < length; ++i) {
-      library.dependencies[i] = readLibraryDependency(library);
+    if (!useGrowableLists && length == 0) {
+      // When lists don't have to be growable anyway, we might as well use an
+      // almost constant one for the empty list.
+      library.typedefsInternal = emptyListOfTypedef;
+    } else {
+      library.typedefsInternal = new List<Typedef>.generate(
+          length, (int index) => readTypedef()..parent = library,
+          growable: useGrowableLists);
     }
   }
 
-  LibraryDependency readLibraryDependency(Library library) {
+  void _readClassList(Library library, List<int> classOffsets) {
+    int length = readUInt30();
+    if (!useGrowableLists && length == 0) {
+      // When lists don't have to be growable anyway, we might as well use an
+      // almost constant one for the empty list.
+      library.classesInternal = emptyListOfClass;
+    } else {
+      library.classesInternal = new List<Class>.generate(length, (int index) {
+        _byteOffset = classOffsets[index];
+        return readClass(classOffsets[index + 1])..parent = library;
+      }, growable: useGrowableLists);
+      _byteOffset = classOffsets.last;
+    }
+  }
+
+  void _readExtensionList(Library library) {
+    int length = readUInt30();
+    if (!useGrowableLists && length == 0) {
+      // When lists don't have to be growable anyway, we might as well use an
+      // almost constant one for the empty list.
+      library.extensionsInternal = emptyListOfExtension;
+    } else {
+      library.extensionsInternal = new List<Extension>.generate(
+          length, (int index) => readExtension()..parent = library,
+          growable: useGrowableLists);
+    }
+  }
+
+  List<Field> _readFieldList(TreeNode parent) {
+    int length = readUInt30();
+    if (!useGrowableLists && length == 0) {
+      // When lists don't have to be growable anyway, we might as well use an
+      // almost constant one for the empty list.
+      return emptyListOfField;
+    }
+    return new List<Field>.generate(
+        length, (int index) => readField()..parent = parent,
+        growable: useGrowableLists);
+  }
+
+  List<Procedure> _readProcedureList(
+      TreeNode parent, List<int> procedureOffsets) {
+    int length = readUInt30();
+    if (!useGrowableLists && length == 0) {
+      // When lists don't have to be growable anyway, we might as well use an
+      // almost constant one for the empty list.
+      return emptyListOfProcedure;
+    }
+    List<Procedure> list = new List<Procedure>.generate(length, (int index) {
+      _byteOffset = procedureOffsets[index];
+      return readProcedure(procedureOffsets[index + 1])..parent = parent;
+    }, growable: useGrowableLists);
+    _byteOffset = procedureOffsets.last;
+    return list;
+  }
+
+  void _readLibraryDependencies(Library library) {
+    int length = readUInt30();
+    if (!useGrowableLists && length == 0) {
+      // When lists don't have to be growable anyway, we might as well use an
+      // almost constant one for the empty list.
+      library.dependencies = emptyListOfLibraryDependency;
+    } else {
+      library.dependencies = new List<LibraryDependency>.generate(
+          length, (int index) => readLibraryDependency()..parent = library,
+          growable: useGrowableLists);
+    }
+  }
+
+  LibraryDependency readLibraryDependency() {
     int fileOffset = readOffset();
     int flags = readByte();
     List<Expression> annotations = readExpressionList();
@@ -1191,8 +1222,7 @@
     List<Combinator> names = readCombinatorList();
     return new LibraryDependency.byReference(
         flags, annotations, targetLibrary, prefixName, names)
-      ..fileOffset = fileOffset
-      ..parent = library;
+      ..fileOffset = fileOffset;
   }
 
   void _readAdditionalExports(Library library) {
@@ -1226,16 +1256,21 @@
 
   void _readLibraryParts(Library library) {
     int length = readUInt30();
-    library.parts.length = length;
-    for (int i = 0; i < length; ++i) {
-      library.parts[i] = readLibraryPart(library);
+    if (!useGrowableLists && length == 0) {
+      // When lists don't have to be growable anyway, we might as well use an
+      // almost constant one for the empty list.
+      library.parts = emptyListOfLibraryPart;
+    } else {
+      library.parts = new List<LibraryPart>.generate(
+          length, (int index) => readLibraryPart()..parent = library,
+          growable: useGrowableLists);
     }
   }
 
-  LibraryPart readLibraryPart(Library library) {
+  LibraryPart readLibraryPart() {
     List<Expression> annotations = readExpressionList();
     String partUri = readStringReference();
-    return new LibraryPart(annotations, partUri)..parent = library;
+    return new LibraryPart(annotations, partUri);
   }
 
   Typedef readTypedef() {
@@ -1319,7 +1354,7 @@
     readAndPushTypeParameterList(node.typeParameters, node);
     Supertype? supertype = readSupertypeOption();
     Supertype? mixedInType = readSupertypeOption();
-    _fillNonTreeNodeList(node.implementedTypes, readSupertype);
+    node.implementedTypes = readSupertypeList();
     if (_disableLazyClassReading) {
       readClassPartialContent(node, procedureOffsets);
     } else {
@@ -1377,36 +1412,70 @@
     node.fileUri = fileUri;
     node.onType = onType;
 
-    int length = readUInt30();
-    node.members.length = length;
-    for (int i = 0; i < length; i++) {
-      Name name = readName();
-      int kind = readByte();
-      int flags = readByte();
-      CanonicalName canonicalName = readNonNullCanonicalNameReference();
-      node.members[i] = new ExtensionMemberDescriptor(
-          name: name,
-          kind: ExtensionMemberKind.values[kind],
-          member: canonicalName.reference)
-        ..flags = flags;
-    }
+    node.members = _readExtensionMemberDescriptorList();
+
     return node;
   }
 
+  List<ExtensionMemberDescriptor> _readExtensionMemberDescriptorList() {
+    int length = readUInt30();
+    if (!useGrowableLists && length == 0) {
+      // When lists don't have to be growable anyway, we might as well use a
+      // constant one for the empty list.
+      return emptyListOfExtensionMemberDescriptor;
+    }
+    return new List<ExtensionMemberDescriptor>.generate(
+        length, (_) => _readExtensionMemberDescriptor(),
+        growable: useGrowableLists);
+  }
+
+  ExtensionMemberDescriptor _readExtensionMemberDescriptor() {
+    Name name = readName();
+    int kind = readByte();
+    int flags = readByte();
+    CanonicalName canonicalName = readNonNullCanonicalNameReference();
+    return new ExtensionMemberDescriptor(
+        name: name,
+        kind: ExtensionMemberKind.values[kind],
+        member: canonicalName.reference)
+      ..flags = flags;
+  }
+
   /// Reads the partial content of a class, namely fields, procedures,
   /// constructors and redirecting factory constructors.
   void readClassPartialContent(Class node, List<int> procedureOffsets) {
-    _mergeNamedNodeList(node.fieldsInternal, (index) => readField(), node);
-    _mergeNamedNodeList(
-        node.constructorsInternal, (index) => readConstructor(), node);
+    node.fieldsInternal = _readFieldList(node);
+    _readConstructorList(node);
+    node.proceduresInternal = _readProcedureList(node, procedureOffsets);
+    _readRedirectingFactoryConstructorList(node);
+  }
 
-    _mergeNamedNodeList(node.proceduresInternal, (index) {
-      _byteOffset = procedureOffsets[index];
-      return readProcedure(procedureOffsets[index + 1]);
-    }, node);
-    _byteOffset = procedureOffsets.last;
-    _mergeNamedNodeList(node.redirectingFactoryConstructorsInternal,
-        (index) => readRedirectingFactoryConstructor(), node);
+  void _readConstructorList(Class node) {
+    int length = readUInt30();
+    if (!useGrowableLists && length == 0) {
+      // When lists don't have to be growable anyway, we might as well use a
+      // constant one for the empty list.
+      node.constructorsInternal = emptyListOfConstructor;
+    } else {
+      node.constructorsInternal = new List<Constructor>.generate(
+          length, (int index) => readConstructor()..parent = node,
+          growable: useGrowableLists);
+    }
+  }
+
+  void _readRedirectingFactoryConstructorList(Class node) {
+    int length = readUInt30();
+    if (!useGrowableLists && length == 0) {
+      // When lists don't have to be growable anyway, we might as well use a
+      // constant one for the empty list.
+      node.redirectingFactoryConstructorsInternal =
+          emptyListOfRedirectingFactoryConstructor;
+    } else {
+      node.redirectingFactoryConstructorsInternal =
+          new List<RedirectingFactoryConstructor>.generate(length,
+              (int index) => readRedirectingFactoryConstructor()..parent = node,
+              growable: useGrowableLists);
+    }
   }
 
   /// Set the lazyBuilder on the class so it can be lazy loaded in the future.
@@ -1513,7 +1582,7 @@
     }
     pushVariableDeclarations(function.positionalParameters);
     pushVariableDeclarations(function.namedParameters);
-    _fillTreeNodeList(node.initializers, (index) => readInitializer(), node);
+    _readInitializers(node);
     variableStack.length = 0;
     int transformerFlags = getAndResetTransformerFlags();
     assert(((_) => true)(debugPath.removeLast()));
@@ -1641,6 +1710,19 @@
     return node;
   }
 
+  void _readInitializers(Constructor constructor) {
+    int length = readUInt30();
+    if (!useGrowableLists && length == 0) {
+      // When lists don't have to be growable anyway, we might as well use a
+      // constant one for the empty list.
+      constructor.initializers = emptyListOfInitializer;
+    } else {
+      constructor.initializers = new List<Initializer>.generate(
+          length, (int index) => readInitializer()..parent = constructor,
+          growable: useGrowableLists);
+    }
+  }
+
   Initializer readInitializer() {
     int tag = readByte();
     bool isSynthetic = readByte() == 1;
@@ -2442,7 +2524,7 @@
     int offset = readOffset();
     DartType keyType = readDartType();
     DartType valueType = readDartType();
-    return new MapLiteral(readMapEntryList(),
+    return new MapLiteral(readMapLiteralEntryList(),
         keyType: keyType, valueType: valueType, isConst: false)
       ..fileOffset = offset;
   }
@@ -2451,7 +2533,7 @@
     int offset = readOffset();
     DartType keyType = readDartType();
     DartType valueType = readDartType();
-    return new MapLiteral(readMapEntryList(),
+    return new MapLiteral(readMapLiteralEntryList(),
         keyType: keyType, valueType: valueType, isConst: true)
       ..fileOffset = offset;
   }
@@ -2496,8 +2578,13 @@
     return new ConstantExpression(constant, type)..fileOffset = offset;
   }
 
-  List<MapLiteralEntry> readMapEntryList() {
+  List<MapLiteralEntry> readMapLiteralEntryList() {
     int length = readUInt30();
+    if (!useGrowableLists && length == 0) {
+      // When lists don't have to be growable anyway, we might as well use an
+      // almost constant one for the empty list.
+      return emptyListOfMapLiteralEntry;
+    }
     return new List<MapLiteralEntry>.generate(length, (_) => readMapEntry(),
         growable: useGrowableLists);
   }
@@ -2732,11 +2819,9 @@
 
   void _readSwitchCaseInto(SwitchCase caseNode) {
     int length = readUInt30();
-    caseNode.expressions.length = length;
-    caseNode.expressionOffsets.length = length;
     for (int i = 0; i < length; ++i) {
-      caseNode.expressionOffsets[i] = readOffset();
-      caseNode.expressions[i] = readExpression()..parent = caseNode;
+      caseNode.expressionOffsets.add(readOffset());
+      caseNode.expressions.add(readExpression()..parent = caseNode);
     }
     caseNode.isDefault = readByte() == 1;
     caseNode.body = readStatement()..parent = caseNode;
@@ -2977,9 +3062,8 @@
           length, (_) => new TypeParameter(null, null)..parent = parent,
           growable: useGrowableLists);
     } else if (list.length != length) {
-      list.length = length;
       for (int i = 0; i < length; ++i) {
-        list[i] = new TypeParameter(null, null)..parent = parent;
+        list.add(new TypeParameter(null, null)..parent = parent);
       }
     }
     typeParameterStack.addAll(list);
@@ -3304,16 +3388,16 @@
   }
 
   @override
-  LibraryDependency readLibraryDependency(Library library) {
+  LibraryDependency readLibraryDependency() {
     final int nodeOffset = _byteOffset;
-    final LibraryDependency result = super.readLibraryDependency(library);
+    final LibraryDependency result = super.readLibraryDependency();
     return _associateMetadata(result, nodeOffset);
   }
 
   @override
-  LibraryPart readLibraryPart(Library library) {
+  LibraryPart readLibraryPart() {
     final int nodeOffset = _byteOffset;
-    final LibraryPart result = super.readLibraryPart(library);
+    final LibraryPart result = super.readLibraryPart();
     return _associateMetadata(result, nodeOffset);
   }
 
diff --git a/pkg/kernel/lib/src/tool/command_line_util.dart b/pkg/kernel/lib/src/tool/command_line_util.dart
index 858e1b1..f0e3f56 100644
--- a/pkg/kernel/lib/src/tool/command_line_util.dart
+++ b/pkg/kernel/lib/src/tool/command_line_util.dart
@@ -37,9 +37,9 @@
   static Component tryLoadDill(String file) {
     try {
       return loadComponentFromBinary(file);
-    } catch (e) {
-      print("$file can't be loaded.");
-      print(e);
+    } catch (e, s) {
+      print("$file can't be loaded:");
+      print('$e\n$s');
       exit(1);
     }
   }
diff --git a/pkg/kernel/lib/text/ast_to_text.dart b/pkg/kernel/lib/text/ast_to_text.dart
index dc1c1d6..08e48b1 100644
--- a/pkg/kernel/lib/text/ast_to_text.dart
+++ b/pkg/kernel/lib/text/ast_to_text.dart
@@ -427,9 +427,9 @@
       endLine(":");
       endLine("//");
       for (String s in problemsAsJson) {
-        Map<String, Object> decoded = json.decode(s);
-        List<Object> plainTextFormatted =
-            decoded["plainTextFormatted"] as List<Object>;
+        Map<String, dynamic> decoded = json.decode(s);
+        List<dynamic> plainTextFormatted =
+            decoded["plainTextFormatted"] as List<dynamic>;
         List<String> lines = plainTextFormatted.join("\n").split("\n");
         for (int i = 0; i < lines.length; i++) {
           write("//");
diff --git a/tools/VERSION b/tools/VERSION
index 8d92026..2bb6277 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 14
 PATCH 0
-PRERELEASE 53
+PRERELEASE 54
 PRERELEASE_PATCH 0
\ No newline at end of file