Version 2.12.0-115.0.dev

Merge commit 'c7f364562cabef42a42869646b3246a1aaf58d2f' into 'dev'
diff --git a/pkg/dartdev/test/fix_driver_test.dart b/pkg/dartdev/test/fix_driver_test.dart
new file mode 100644
index 0000000..35ec72a
--- /dev/null
+++ b/pkg/dartdev/test/fix_driver_test.dart
@@ -0,0 +1,39 @@
+// Copyright (c) 2020, 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 'package:test/test.dart';
+
+import '../tool/fix_driver.dart';
+import 'utils.dart';
+
+void main() {
+  group('Driver', _driver);
+}
+
+Future<FixOutput> runFix(List<String> args) async {
+  var runner = FixRunner(logger: CapturingLogger());
+  var result = await runner.runFix(args);
+  return FixOutput(result);
+}
+
+void _driver() {
+  TestProject p;
+  tearDown(() => p?.dispose());
+
+  test('no fixes', () async {
+    p = project(mainSrc: 'int get foo => 1;\n');
+    var result = await runFix(['--apply', p.dirPath]);
+    expect(result.stdout, contains('Nothing to fix!'));
+    expect(result.returnCode, 0);
+  });
+}
+
+class FixOutput {
+  final FixResult<CapturingLogger> result;
+  FixOutput(this.result);
+
+  int get returnCode => result.returnCode;
+  String get stderr => result.logger.output.stderr.toString();
+  String get stdout => result.logger.output.stdout.toString();
+}
diff --git a/pkg/dartdev/test/test_all.dart b/pkg/dartdev/test/test_all.dart
index 2576ac9..edd644b 100644
--- a/pkg/dartdev/test/test_all.dart
+++ b/pkg/dartdev/test/test_all.dart
@@ -18,6 +18,7 @@
 import 'commands/test_test.dart' as test;
 import 'core_test.dart' as core;
 import 'experiments_test.dart' as experiments;
+import 'fix_driver_test.dart' as fix_driver;
 import 'no_such_file_test.dart' as no_such_file;
 import 'sdk_test.dart' as sdk;
 import 'smoke/implicit_smoke_test.dart' as implicit_smoke;
@@ -32,6 +33,7 @@
     create.main();
     experiments.main();
     fix.main();
+    fix_driver.main();
     flag.main();
     format.main();
     help.main();
diff --git a/pkg/dartdev/tool/fix_driver.dart b/pkg/dartdev/tool/fix_driver.dart
new file mode 100644
index 0000000..17f246a
--- /dev/null
+++ b/pkg/dartdev/tool/fix_driver.dart
@@ -0,0 +1,140 @@
+// Copyright (c) 2020, 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 'package:args/args.dart';
+import 'package:args/command_runner.dart';
+import 'package:cli_util/cli_logging.dart';
+import 'package:dartdev/src/commands/fix.dart';
+import 'package:dartdev/src/core.dart';
+import 'package:dartdev/src/utils.dart';
+import 'package:meta/meta.dart';
+
+Future<void> main(List<String> args) async {
+  var runner = FixRunner(logger: Logger.standard());
+  var result = await runner.runFix(args);
+  return result.returnCode;
+}
+
+class CapturedProgress extends Progress {
+  final LoggerOutput output;
+
+  bool canceled = false;
+  bool finished = false;
+
+  CapturedProgress(this.output, String message) : super(message) {
+    output.progress.writeln(message);
+  }
+
+  @override
+  void cancel() {
+    canceled = true;
+  }
+
+  @override
+  void finish({String message, bool showTiming = false}) {
+    // todo (pq): consider capturing / tracking finish display updates.
+    finished = true;
+  }
+}
+
+class CapturingLogger implements Logger {
+  final LoggerOutput output = LoggerOutput();
+
+  @override
+  final Ansi ansi = Ansi(Ansi.terminalSupportsAnsi);
+
+  @override
+  bool isVerbose;
+
+  CapturingLogger({this.isVerbose = false});
+
+  @override
+  void flush() {
+    // deprecated.
+  }
+
+  @override
+  Progress progress(String message) => CapturedProgress(output, message);
+
+  @override
+  void stderr(String message) {
+    output.stderr.writeln(message);
+  }
+
+  @override
+  void stdout(String message) {
+    output.stdout.writeln(message);
+  }
+
+  @override
+  void trace(String message) {
+    output.trace.writeln(message);
+  }
+
+  @override
+  void write(String message) {
+    output.stdout.write(message);
+  }
+
+  @override
+  void writeCharCode(int charCode) {
+    output.stdout.writeCharCode(charCode);
+  }
+}
+
+class FixResult<T extends Logger> {
+  /// The value returned by [FixCommand.run].
+  final int returnCode;
+
+  /// The logger used in driving fixes.
+  final T logger;
+
+  FixResult(this.logger, this.returnCode);
+}
+
+class FixRunner<T extends Logger> extends CommandRunner<int> {
+  final _supportedOptions = ['dry-run', 'apply'];
+
+  T logger;
+
+  @override
+  final ArgParser argParser = ArgParser(
+    usageLineLength: dartdevUsageLineLength,
+    allowTrailingOptions: false,
+  );
+
+  FixRunner({@required this.logger})
+      : super('fix_runner',
+            'A command-line utility for testing the `dart fix` command.') {
+    addCommand(FixCommand());
+    _supportedOptions.forEach(argParser.addOption);
+  }
+
+  @override
+  Future<int> runCommand(ArgResults topLevelResults) async {
+    var result = await super.runCommand(topLevelResults);
+    return result;
+  }
+
+  Future<FixResult<T>> runFix(List<String> args) async {
+    log = logger;
+    var argResults = argParser.parse(['fix', ...?args]);
+    var result = await runCommand(argResults);
+    return FixResult(logger, result);
+  }
+}
+
+class LoggerOutput {
+  /// Messages reported to progress.
+  final StringBuffer progress = StringBuffer();
+
+  /// Messages reported to stdout.
+  final StringBuffer stdout = StringBuffer();
+
+  /// Messages reported to stderr.
+  final StringBuffer stderr = StringBuffer();
+
+  /// Messages reported to trace.
+  final StringBuffer trace = StringBuffer();
+}
diff --git a/runtime/vm/compiler/backend/slot.cc b/runtime/vm/compiler/backend/slot.cc
index 1c348aa..f0a4f2c 100644
--- a/runtime/vm/compiler/backend/slot.cc
+++ b/runtime/vm/compiler/backend/slot.cc
@@ -110,10 +110,18 @@
   return kIllegalCid;
 }
 
+AcqRelAtomic<Slot*> Slot::native_fields_(nullptr);
+
+enum NativeSlotsEnumeration {
+#define DECLARE_KIND(CN, __, FN, ___, ____) k##CN##_##FN,
+  NATIVE_SLOTS_LIST(DECLARE_KIND)
+#undef DECLARE_KIND
+      kNativeSlotsCount
+};
+
 const Slot& Slot::GetNativeSlot(Kind kind) {
-  // There is a fixed statically known number of native slots so we cache
-  // them statically.
-  static const Slot fields[] = {
+  if (native_fields_.load() == nullptr) {
+    Slot* new_value = new Slot[kNativeSlotsCount]{
 #define NULLABLE_FIELD_FINAL                                                   \
   (IsNullableBit::encode(true) | IsImmutableBit::encode(true))
 #define NULLABLE_FIELD_VAR (IsNullableBit::encode(true))
@@ -123,7 +131,7 @@
        k##cid##Cid, compiler::target::ClassName::FieldName##_offset(),         \
        #ClassName "." #FieldName, nullptr, kTagged),
 
-      NULLABLE_BOXED_NATIVE_SLOTS_LIST(DEFINE_NULLABLE_BOXED_NATIVE_FIELD)
+        NULLABLE_BOXED_NATIVE_SLOTS_LIST(DEFINE_NULLABLE_BOXED_NATIVE_FIELD)
 
 #undef DEFINE_NULLABLE_BOXED_NATIVE_FIELD
 #undef NULLABLE_FIELD_FINAL
@@ -137,8 +145,8 @@
        k##cid##Cid, compiler::target::ClassName::FieldName##_offset(),         \
        #ClassName "." #FieldName, nullptr, kTagged),
 
-          NONNULLABLE_BOXED_NATIVE_SLOTS_LIST(
-              DEFINE_NONNULLABLE_BOXED_NATIVE_FIELD)
+            NONNULLABLE_BOXED_NATIVE_SLOTS_LIST(
+                DEFINE_NONNULLABLE_BOXED_NATIVE_FIELD)
 
 #undef DEFINE_NONNULLABLE_BOXED_NATIVE_FIELD
 #define DEFINE_UNBOXED_NATIVE_FIELD(ClassName, UnderlyingType, FieldName,      \
@@ -148,15 +156,20 @@
        compiler::target::ClassName::FieldName##_offset(),                      \
        #ClassName "." #FieldName, nullptr, kUnboxed##representation),
 
-              UNBOXED_NATIVE_SLOTS_LIST(DEFINE_UNBOXED_NATIVE_FIELD)
+                UNBOXED_NATIVE_SLOTS_LIST(DEFINE_UNBOXED_NATIVE_FIELD)
 
 #undef DEFINE_UNBOXED_NATIVE_FIELD
 #undef NONNULLABLE_FIELD_VAR
 #undef NONNULLABLE_FIELD_FINAL
-  };
+    };
+    Slot* old_value = nullptr;
+    if (!native_fields_.compare_exchange_strong(old_value, new_value)) {
+      delete[] new_value;
+    }
+  }
 
-  ASSERT(static_cast<uint8_t>(kind) < ARRAY_SIZE(fields));
-  return fields[static_cast<uint8_t>(kind)];
+  ASSERT(static_cast<uint8_t>(kind) < kNativeSlotsCount);
+  return native_fields_.load()[static_cast<uint8_t>(kind)];
 }
 
 // Note: should only be called with cids of array-like classes.
diff --git a/runtime/vm/compiler/backend/slot.h b/runtime/vm/compiler/backend/slot.h
index 8c24078..ba85613 100644
--- a/runtime/vm/compiler/backend/slot.h
+++ b/runtime/vm/compiler/backend/slot.h
@@ -295,6 +295,9 @@
     return static_cast<const T*>(data_);
   }
 
+  // There is a fixed statically known number of native slots so we cache
+  // them statically.
+  static AcqRelAtomic<Slot*> native_fields_;
   static const Slot& GetNativeSlot(Kind kind);
 
   const Kind kind_;
diff --git a/tools/VERSION b/tools/VERSION
index 41ab758..3e3f95d 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 114
+PRERELEASE 115
 PRERELEASE_PATCH 0
\ No newline at end of file