Version 2.12.0-161.0.dev

Merge commit 'b606a183cbc7ec3b28c75df69a1e052347f01d8f' into 'dev'
diff --git a/pkg/analyzer/lib/src/workspace/bazel.dart b/pkg/analyzer/lib/src/workspace/bazel.dart
index d32be7a..cb5b7cc 100644
--- a/pkg/analyzer/lib/src/workspace/bazel.dart
+++ b/pkg/analyzer/lib/src/workspace/bazel.dart
@@ -359,6 +359,10 @@
   /// The absolute path to the `bazel-genfiles` folder.
   final String genfiles;
 
+  /// The cache of packages. The key is the directory path, the value is
+  /// the corresponding package.
+  final Map<String, BazelWorkspacePackage> _directoryToPackage = {};
+
   final _bazelCandidateFiles =
       StreamController<BazelFileNotification>.broadcast();
 
@@ -441,7 +445,14 @@
   @override
   WorkspacePackage findPackageFor(String filePath) {
     path.Context context = provider.pathContext;
-    Folder folder = provider.getFolder(context.dirname(filePath));
+    var directoryPath = context.dirname(filePath);
+
+    var cachedPackage = _directoryToPackage[directoryPath];
+    if (cachedPackage != null) {
+      return cachedPackage;
+    }
+
+    Folder folder = provider.getFolder(directoryPath);
     if (!context.isWithin(root, folder.path)) {
       return null;
     }
@@ -471,11 +482,13 @@
       BazelWorkspacePackage packageRootedHere() {
         List<String> uriParts = (packageUriResolver as BazelPackageUriResolver)
             ._restoreUriParts(root, '${folder.path}/lib/__fake__.dart');
-        if (uriParts == null || uriParts.isEmpty) {
-          return BazelWorkspacePackage(null, folder.path, this);
-        } else {
-          return BazelWorkspacePackage(uriParts[0], folder.path, this);
+        String packageName;
+        if (uriParts != null && uriParts.isNotEmpty) {
+          packageName = uriParts[0];
         }
+        var package = BazelWorkspacePackage(packageName, folder.path, this);
+        _directoryToPackage[directoryPath] = package;
+        return package;
       }
 
       // In some distributed build environments, BUILD files are not preserved.
diff --git a/pkg/compiler/lib/src/elements/entities.dart b/pkg/compiler/lib/src/elements/entities.dart
index ccd058d..f136327 100644
--- a/pkg/compiler/lib/src/elements/entities.dart
+++ b/pkg/compiler/lib/src/elements/entities.dart
@@ -282,21 +282,62 @@
   /// The number of type parameters.
   final int typeParameters;
 
-  const ParameterStructure(
+  static const ParameterStructure getter =
+      ParameterStructure._(0, 0, [], {}, 0);
+
+  static const ParameterStructure setter =
+      ParameterStructure._(1, 1, [], {}, 0);
+
+  static const ParameterStructure zeroArguments =
+      ParameterStructure._(0, 0, [], {}, 0);
+
+  static const List<ParameterStructure> _simple = [
+    ParameterStructure._(0, 0, [], {}, 0),
+    ParameterStructure._(1, 1, [], {}, 0),
+    ParameterStructure._(2, 2, [], {}, 0),
+    ParameterStructure._(3, 3, [], {}, 0),
+    ParameterStructure._(4, 4, [], {}, 0),
+    ParameterStructure._(5, 5, [], {}, 0),
+  ];
+
+  const ParameterStructure._(
       this.requiredPositionalParameters,
       this.positionalParameters,
       this.namedParameters,
       this.requiredNamedParameters,
       this.typeParameters);
 
-  const ParameterStructure.getter()
-      : this(0, 0, const <String>[], const <String>{}, 0);
+  factory ParameterStructure(
+      int requiredPositionalParameters,
+      int positionalParameters,
+      List<String> namedParameters,
+      Set<String> requiredNamedParameters,
+      int typeParameters) {
+    // This simple canonicalization reduces the number of ParameterStructure
+    // objects by over 90%.
+    if (requiredPositionalParameters == positionalParameters &&
+        namedParameters.isEmpty &&
+        requiredNamedParameters.isEmpty &&
+        typeParameters == 0 &&
+        positionalParameters < _simple.length) {
+      return _simple[positionalParameters];
+    }
 
-  const ParameterStructure.setter()
-      : this(1, 1, const <String>[], const <String>{}, 0);
+    // Force sharing of empty collections.
+    if (namedParameters.isEmpty) namedParameters = const [];
+    if (requiredNamedParameters.isEmpty) requiredNamedParameters = const {};
+
+    return ParameterStructure._(
+      requiredPositionalParameters,
+      positionalParameters,
+      namedParameters,
+      requiredNamedParameters,
+      typeParameters,
+    );
+  }
 
   factory ParameterStructure.fromType(FunctionType type) {
-    return new ParameterStructure(
+    return ParameterStructure(
         type.parameterTypes.length,
         type.parameterTypes.length + type.optionalParameterTypes.length,
         type.namedParameters,
@@ -314,7 +355,7 @@
         source.readStrings(emptyAsNull: true)?.toSet() ?? const <String>{};
     int typeParameters = source.readInt();
     source.end(tag);
-    return new ParameterStructure(
+    return ParameterStructure(
         requiredPositionalParameters,
         positionalParameters,
         namedParameters,
@@ -344,7 +385,7 @@
   /// Returns the [CallStructure] corresponding to a call site passing all
   /// parameters both required and optional.
   CallStructure get callStructure {
-    return new CallStructure(totalParameters, namedParameters, typeParameters);
+    return CallStructure(totalParameters, namedParameters, typeParameters);
   }
 
   @override
@@ -382,7 +423,7 @@
 
   /// Short textual representation use for testing.
   String get shortText {
-    StringBuffer sb = new StringBuffer();
+    StringBuffer sb = StringBuffer();
     if (typeParameters != 0) {
       sb.write('<');
       sb.write(typeParameters);
@@ -401,7 +442,7 @@
 
   @override
   String toString() {
-    StringBuffer sb = new StringBuffer();
+    StringBuffer sb = StringBuffer();
     sb.write('ParameterStructure(');
     sb.write('requiredPositionalParameters=$requiredPositionalParameters,');
     sb.write('positionalParameters=$positionalParameters,');
diff --git a/pkg/compiler/lib/src/js_model/elements.dart b/pkg/compiler/lib/src/js_model/elements.dart
index 05a965f..38337b4 100644
--- a/pkg/compiler/lib/src/js_model/elements.dart
+++ b/pkg/compiler/lib/src/js_model/elements.dart
@@ -506,7 +506,7 @@
   JGetter(JLibrary library, JClass enclosingClass, Name name,
       AsyncMarker asyncMarker,
       {bool isStatic, bool isExternal, this.isAbstract})
-      : super(library, enclosingClass, name, const ParameterStructure.getter(),
+      : super(library, enclosingClass, name, ParameterStructure.getter,
             asyncMarker,
             isStatic: isStatic, isExternal: isExternal);
 
@@ -571,7 +571,7 @@
 
   JSetter(JLibrary library, JClass enclosingClass, Name name,
       {bool isStatic, bool isExternal, this.isAbstract})
-      : super(library, enclosingClass, name, const ParameterStructure.setter(),
+      : super(library, enclosingClass, name, ParameterStructure.setter,
             AsyncMarker.SYNC,
             isStatic: isStatic, isExternal: isExternal);
 
@@ -733,15 +733,9 @@
   static const String tag = 'signature-method';
 
   JSignatureMethod(ClassEntity enclosingClass)
-      : super(
-            enclosingClass.library,
-            enclosingClass,
-            Names.signature,
-            const ParameterStructure(0, 0, const [], const {}, 0),
-            AsyncMarker.SYNC,
-            isStatic: false,
-            isExternal: false,
-            isAbstract: false);
+      : super(enclosingClass.library, enclosingClass, Names.signature,
+            ParameterStructure.zeroArguments, AsyncMarker.SYNC,
+            isStatic: false, isExternal: false, isAbstract: false);
 
   factory JSignatureMethod.readFromDataSource(DataSource source) {
     source.begin(tag);
diff --git a/pkg/compiler/lib/src/kernel/element_map_impl.dart b/pkg/compiler/lib/src/kernel/element_map_impl.dart
index e1afb79..8246f0b 100644
--- a/pkg/compiler/lib/src/kernel/element_map_impl.dart
+++ b/pkg/compiler/lib/src/kernel/element_map_impl.dart
@@ -853,7 +853,7 @@
         requiredNamedParameters.add(variable.name);
       }
     }
-    return new ParameterStructure(
+    return ParameterStructure(
         requiredPositionalParameters,
         positionalParameters,
         namedParameters,
diff --git a/pkg/compiler/lib/src/kernel/kelements.dart b/pkg/compiler/lib/src/kernel/kelements.dart
index 8356cb5..886ccbf 100644
--- a/pkg/compiler/lib/src/kernel/kelements.dart
+++ b/pkg/compiler/lib/src/kernel/kelements.dart
@@ -202,7 +202,7 @@
   KGetter(KLibrary library, KClass enclosingClass, Name name,
       AsyncMarker asyncMarker,
       {bool isStatic, bool isExternal, this.isAbstract})
-      : super(library, enclosingClass, name, const ParameterStructure.getter(),
+      : super(library, enclosingClass, name, ParameterStructure.getter,
             asyncMarker,
             isStatic: isStatic, isExternal: isExternal);
 
@@ -219,7 +219,7 @@
 
   KSetter(KLibrary library, KClass enclosingClass, Name name,
       {bool isStatic, bool isExternal, this.isAbstract})
-      : super(library, enclosingClass, name, const ParameterStructure.setter(),
+      : super(library, enclosingClass, name, ParameterStructure.setter,
             AsyncMarker.SYNC,
             isStatic: isStatic, isExternal: isExternal);
 
diff --git a/pkg/compiler/test/serialization/serialization_test_helper.dart b/pkg/compiler/test/serialization/serialization_test_helper.dart
index a3d44ea..b429960 100644
--- a/pkg/compiler/test/serialization/serialization_test_helper.dart
+++ b/pkg/compiler/test/serialization/serialization_test_helper.dart
@@ -6,6 +6,7 @@
 
 import 'package:compiler/compiler_new.dart';
 import 'package:compiler/src/compiler.dart';
+import 'package:compiler/src/js_model/js_world.dart';
 import 'package:compiler/src/inferrer/types.dart';
 import 'package:compiler/src/serialization/strategies.dart';
 import 'package:expect/expect.dart';
@@ -20,55 +21,33 @@
   '"toJsonDuration":'
 ];
 
-runTest(
-    {Uri entryPoint,
-    Map<String, String> memorySourceFiles: const <String, String>{},
-    Uri packageConfig,
-    Uri librariesSpecificationUri,
-    List<String> options,
-    SerializationStrategy strategy: const BytesInMemorySerializationStrategy(),
-    bool useDataKinds: false}) async {
-  OutputCollector collector1 = new OutputCollector();
-  CompilationResult result1 = await runCompiler(
-      entryPoint: entryPoint,
-      memorySourceFiles: memorySourceFiles,
-      packageConfig: packageConfig,
-      librariesSpecificationUri: librariesSpecificationUri,
-      options: options,
-      outputProvider: collector1,
-      beforeRun: (Compiler compiler) {
-        compiler.kernelLoader.forceSerialization = true;
-      });
-  Expect.isTrue(result1.isSuccess);
+void finishCompileAndCompare(
+    Map<OutputType, Map<String, String>> expectedOutput,
+    OutputCollector actualOutputCollector,
+    Compiler compiler,
+    SerializationStrategy strategy,
+    {bool stoppedAfterClosedWorld = false,
+    bool stoppedAfterTypeInference = false}) {
+  if (stoppedAfterClosedWorld) {
+    JsClosedWorld closedWorld = compiler.backendClosedWorldForTesting;
+    JsClosedWorld newClosedWorld =
+        cloneClosedWorld(compiler, closedWorld, strategy);
+    compiler.performGlobalTypeInference(newClosedWorld);
+  }
 
-  OutputCollector collector2 = new OutputCollector();
-  CompilationResult result = await runCompiler(
-      entryPoint: entryPoint,
-      memorySourceFiles: memorySourceFiles,
-      packageConfig: packageConfig,
-      librariesSpecificationUri: librariesSpecificationUri,
-      options: options,
-      outputProvider: collector2,
-      beforeRun: (Compiler compiler) {
-        compiler.kernelLoader.forceSerialization = true;
-        compiler.stopAfterTypeInference = true;
-      });
-  Expect.isTrue(result.isSuccess);
-  Compiler compiler = result.compiler;
-  GlobalTypeInferenceResults globalInferenceResults =
-      compiler.globalInference.resultsForTesting;
-  GlobalTypeInferenceResults newGlobalInferenceResults =
-      cloneInferenceResults(compiler, globalInferenceResults, strategy);
+  if (stoppedAfterClosedWorld || stoppedAfterTypeInference) {
+    GlobalTypeInferenceResults globalInferenceResults =
+        compiler.globalInference.resultsForTesting;
+    GlobalTypeInferenceResults newGlobalInferenceResults =
+        cloneInferenceResults(compiler, globalInferenceResults, strategy);
+    compiler.generateJavaScriptCode(newGlobalInferenceResults);
+  }
+  var actualOutput = actualOutputCollector.clear();
+  Expect.setEquals(
+      expectedOutput.keys, actualOutput.keys, "Output type mismatch.");
 
-  Map<OutputType, Map<String, String>> output = collector1.clear();
-
-  compiler.generateJavaScriptCode(newGlobalInferenceResults);
-  Map<OutputType, Map<String, String>> newOutput = collector2.clear();
-
-  Expect.setEquals(output.keys, newOutput.keys, "Output type mismatch.");
-
-  output.forEach((OutputType outputType, Map<String, String> fileMap) {
-    Map<String, String> newFileMap = newOutput[outputType];
+  void check(OutputType outputType, Map<String, String> fileMap) {
+    Map<String, String> newFileMap = actualOutput[outputType];
     Expect.setEquals(fileMap.keys, newFileMap.keys,
         "File mismatch for output type $outputType.");
     fileMap.forEach((String fileName, String code) {
@@ -96,14 +75,117 @@
           "Output mismatch at line $failureLine in "
           "file '${fileName}' of type ${outputType}.");
     });
-  });
+  }
+
+  expectedOutput.forEach(check);
+}
+
+runTest(
+    {Uri entryPoint,
+    Map<String, String> memorySourceFiles: const <String, String>{},
+    Uri packageConfig,
+    Uri librariesSpecificationUri,
+    List<String> options,
+    SerializationStrategy strategy: const BytesInMemorySerializationStrategy(),
+    bool useDataKinds: false}) async {
+  OutputCollector collector = new OutputCollector();
+  CompilationResult result = await runCompiler(
+      entryPoint: entryPoint,
+      memorySourceFiles: memorySourceFiles,
+      packageConfig: packageConfig,
+      librariesSpecificationUri: librariesSpecificationUri,
+      options: options,
+      outputProvider: collector,
+      beforeRun: (Compiler compiler) {
+        compiler.kernelLoader.forceSerialization = true;
+      });
+  Expect.isTrue(result.isSuccess);
+  Map<OutputType, Map<String, String>> expectedOutput = collector.clear();
+
+  OutputCollector collector2 = new OutputCollector();
+  CompilationResult result2 = await runCompiler(
+      entryPoint: entryPoint,
+      memorySourceFiles: memorySourceFiles,
+      packageConfig: packageConfig,
+      librariesSpecificationUri: librariesSpecificationUri,
+      options: options,
+      outputProvider: collector2,
+      beforeRun: (Compiler compiler) {
+        compiler.kernelLoader.forceSerialization = true;
+        compiler.stopAfterClosedWorld = true;
+      });
+  Expect.isTrue(result2.isSuccess);
+
+  OutputCollector collector3 = new OutputCollector();
+  CompilationResult result3 = await runCompiler(
+      entryPoint: entryPoint,
+      memorySourceFiles: memorySourceFiles,
+      packageConfig: packageConfig,
+      librariesSpecificationUri: librariesSpecificationUri,
+      options: options,
+      outputProvider: collector3,
+      beforeRun: (Compiler compiler) {
+        compiler.kernelLoader.forceSerialization = true;
+        compiler.stopAfterTypeInference = true;
+      });
+  Expect.isTrue(result3.isSuccess);
+
+  finishCompileAndCompare(
+      expectedOutput, collector2, result2.compiler, strategy,
+      stoppedAfterClosedWorld: true);
+  finishCompileAndCompare(
+      expectedOutput, collector3, result3.compiler, strategy,
+      stoppedAfterTypeInference: true);
+}
+
+void checkData(List<int> data, List<int> newData) {
+  Expect.equals(
+      data.length, newData.length, "Reserialization data length mismatch.");
+  for (int i = 0; i < data.length; i++) {
+    if (data[i] != newData[i]) {
+      print('Reserialization data mismatch at offset $i:');
+      for (int j = i - 50; j < i + 50; j++) {
+        if (0 <= j && j <= data.length) {
+          String text;
+          if (data[j] == newData[j]) {
+            text = '${data[j]}';
+          } else {
+            text = '${data[j]} <> ${newData[j]}';
+          }
+          print('${j == i ? '> ' : '  '}$j: $text');
+        }
+      }
+      break;
+    }
+  }
+  Expect.listEquals(data, newData);
+}
+
+JsClosedWorld cloneClosedWorld(Compiler compiler, JsClosedWorld closedWorld,
+    SerializationStrategy strategy) {
+  ir.Component component = closedWorld.elementMap.programEnv.mainComponent;
+  List<int> irData = strategy.serializeComponent(component);
+  List<int> closedWorldData = strategy.serializeClosedWorld(closedWorld);
+  print('data size: ${closedWorldData.length}');
+
+  ir.Component newComponent = strategy.deserializeComponent(irData);
+  JsClosedWorld newClosedWorld = strategy.deserializeClosedWorld(
+      compiler.options,
+      compiler.reporter,
+      compiler.environment,
+      compiler.abstractValueStrategy,
+      newComponent,
+      closedWorldData);
+  List<int> newClosedWorldData = strategy.serializeClosedWorld(newClosedWorld);
+  checkData(closedWorldData, newClosedWorldData);
+  return newClosedWorld;
 }
 
 GlobalTypeInferenceResults cloneInferenceResults(Compiler compiler,
     GlobalTypeInferenceResults results, SerializationStrategy strategy) {
   List<int> irData = strategy.unpackAndSerializeComponent(results);
 
-  List worldData = strategy.serializeGlobalTypeInferenceResults(results);
+  List<int> worldData = strategy.serializeGlobalTypeInferenceResults(results);
   print('data size: ${worldData.length}');
 
   ir.Component newComponent = strategy.deserializeComponent(irData);
@@ -115,27 +197,8 @@
           compiler.abstractValueStrategy,
           newComponent,
           worldData);
-  List newWorldData = strategy.serializeGlobalTypeInferenceResults(newResults);
-  Expect.equals(worldData.length, newWorldData.length,
-      "Reserialization data length mismatch.");
-  for (int i = 0; i < worldData.length; i++) {
-    if (worldData[i] != newWorldData[i]) {
-      print('Reserialization data mismatch at offset $i:');
-      for (int j = i - 50; j < i + 50; j++) {
-        if (0 <= j && j <= worldData.length) {
-          String text;
-          if (worldData[j] == newWorldData[j]) {
-            text = '${worldData[j]}';
-          } else {
-            text = '${worldData[j]} <> ${newWorldData[j]}';
-          }
-          print('${j == i ? '> ' : '  '}$j: $text');
-        }
-      }
-      break;
-    }
-  }
-  Expect.listEquals(worldData, newWorldData);
-
+  List<int> newWorldData =
+      strategy.serializeGlobalTypeInferenceResults(newResults);
+  checkData(worldData, newWorldData);
   return newResults;
 }
diff --git a/tools/VERSION b/tools/VERSION
index 6bda5c1..dab9525 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 160
+PRERELEASE 161
 PRERELEASE_PATCH 0
\ No newline at end of file