Version 2.12.0-64.0.dev

Merge commit '149d7ff4151d83d53eb1fe7e1b5c8cf4cbcc3924' into 'dev'
diff --git a/DEPS b/DEPS
index 8e475fa..40aa610 100644
--- a/DEPS
+++ b/DEPS
@@ -98,10 +98,10 @@
   #     and land the review.
   #
   # For more details, see https://github.com/dart-lang/sdk/issues/30164
-"dart_style_tag": "1.3.9",  # Please see the note above before updating.
+"dart_style_tag": "1.3.10",  # Please see the note above before updating.
 
   "chromedriver_tag": "83.0.4103.39",
-  "dartdoc_rev" : "6935dcd8f2d78cf1797e6365b4b71505e6579659",
+  "dartdoc_rev" : "d79877d0764ce23ffea7055049f8da5dffce0308",
   "ffi_rev": "a5d4232cd38562c75a3ed847baa340e399538028",
   "fixnum_rev": "16d3890c6dc82ca629659da1934e412292508bba",
   "glob_rev": "e9f4e6b7ae8abe5071461cf8f47191bb19cf7ef6",
diff --git a/pkg/dartdev/lib/src/commands/fix.dart b/pkg/dartdev/lib/src/commands/fix.dart
index 6cb007f..f93be4c 100644
--- a/pkg/dartdev/lib/src/commands/fix.dart
+++ b/pkg/dartdev/lib/src/commands/fix.dart
@@ -25,6 +25,11 @@
         abbr: 'n',
         defaultsTo: false,
         help: 'Show which files would be modified but make no changes.');
+    argParser.addFlag('compare-to-golden',
+        defaultsTo: false,
+        help:
+            'Compare the result of applying fixes to a golden file for testing.',
+        hide: true);
   }
 
   @override
@@ -33,13 +38,15 @@
         'provisional and subject to change or removal in future releases.\n');
 
     var dryRun = argResults['dry-run'];
-    if (argResults.rest.length - (dryRun ? 1 : 0) > 1) {
+    var testMode = argResults['compare-to-golden'];
+    var arguments = argResults.rest;
+    var argumentCount = arguments.length;
+    if (argumentCount > 1) {
       usageException('Only one file or directory is expected.');
     }
 
-    var dir = argResults.rest.isEmpty
-        ? io.Directory.current
-        : io.Directory(argResults.rest.single);
+    var dir =
+        argumentCount == 0 ? io.Directory.current : io.Directory(arguments[0]);
     if (!dir.existsSync()) {
       usageException("Directory doesn't exist: ${dir.path}");
     }
@@ -73,7 +80,11 @@
 
     progress.finish(showTiming: true);
 
-    if (edits.isEmpty) {
+    if (testMode) {
+      if (_compareFixes(edits)) {
+        return 1;
+      }
+    } else if (edits.isEmpty) {
       log.stdout('Nothing to fix!');
     } else {
       var details = fixes.details;
@@ -119,6 +130,34 @@
     }
   }
 
+  /// Return `true` if any of the fixes fail to create the same content as is
+  /// found in the golden file.
+  bool _compareFixes(List<SourceFileEdit> edits) {
+    var passCount = 0;
+    var failCount = 0;
+    for (var edit in edits) {
+      var filePath = edit.file;
+      var baseName = path.basename(filePath);
+      var expectFileName = baseName + '.expect';
+      var expectFilePath = path.join(path.dirname(filePath), expectFileName);
+      try {
+        var originalCode = io.File(filePath).readAsStringSync();
+        var expectedCode = io.File(expectFilePath).readAsStringSync();
+        var actualCode = SourceEdit.applySequence(originalCode, edit.edits);
+        if (actualCode != expectedCode) {
+          failCount++;
+          _reportFailure(filePath, actualCode, expectedCode);
+        } else {
+          passCount++;
+        }
+      } on io.FileSystemException {
+        // Ignored for now.
+      }
+    }
+    log.stdout('Passed: $passCount, Failed: $failCount');
+    return failCount > 0;
+  }
+
   String _pluralFix(int count) => count == 1 ? 'fix' : 'fixes';
 
   void _printDetails(List<BulkFix> details, io.Directory workingDir) {
@@ -138,5 +177,16 @@
     }
   }
 
+  /// Report that the [actualCode] produced by applying fixes to the content of
+  /// [filePath] did not match the [expectedCode].
+  void _reportFailure(String filePath, String actualCode, String expectedCode) {
+    log.stdout('Failed when applying fixes to $filePath');
+    log.stdout('Expected:');
+    log.stdout(expectedCode);
+    log.stdout('');
+    log.stdout('Actual:');
+    log.stdout(actualCode);
+  }
+
   static String _format(int value) => _numberFormat.format(value);
 }
diff --git a/pkg/dartdev/test/commands/fix_test.dart b/pkg/dartdev/test/commands/fix_test.dart
index 3303da3..b0a1a70 100644
--- a/pkg/dartdev/test/commands/fix_test.dart
+++ b/pkg/dartdev/test/commands/fix_test.dart
@@ -145,4 +145,73 @@
     expect(result.stderr, isEmpty);
     expect(result.stdout, contains('Nothing to fix!'));
   });
+
+  group('compare-to-golden', () {
+    test('different', () {
+      p = project(
+        mainSrc: '''
+class A { 
+  String a() => "";
+}
+
+class B extends A {
+  String a() => "";
+}
+''',
+        analysisOptions: '''
+linter:
+  rules:
+    - annotate_overrides  
+    - prefer_single_quotes
+''',
+      );
+      p.file('lib/main.dart.expect', '''
+class A { 
+  String a() => '';
+}
+
+class B extends A {
+  String a() => '';
+}
+''');
+      var result =
+          p.runSync('fix', ['--compare-to-golden', '.'], workingDir: p.dirPath);
+      expect(result.exitCode, 1);
+      expect(result.stderr, isEmpty);
+    });
+
+    test('same', () {
+      p = project(
+        mainSrc: '''
+class A { 
+  String a() => "";
+}
+
+class B extends A {
+  String a() => "";
+}
+''',
+        analysisOptions: '''
+linter:
+  rules:
+    - annotate_overrides  
+    - prefer_single_quotes
+''',
+      );
+      p.file('lib/main.dart.expect', '''
+class A { 
+  String a() => '';
+}
+
+class B extends A {
+  @override
+  String a() => '';
+}
+''');
+      var result =
+          p.runSync('fix', ['--compare-to-golden', '.'], workingDir: p.dirPath);
+      expect(result.exitCode, 0);
+      expect(result.stderr, isEmpty);
+    });
+  });
 }
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index 906d5f6..3caa919 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -1165,8 +1165,8 @@
     if (!Isolate::Current()->use_field_guards()) {
       for (intptr_t i = start_index_; i < stop_index_; i++) {
         field ^= refs.At(i);
-        field.set_guarded_cid(kDynamicCid);
-        field.set_is_nullable(true);
+        field.set_guarded_cid_unsafe(kDynamicCid);
+        field.set_is_nullable_unsafe(true);
         field.set_guarded_list_length(Field::kNoFixedLength);
         field.set_guarded_list_length_in_object_offset(
             Field::kUnknownLengthOffset);
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.cc b/runtime/vm/compiler/backend/flow_graph_compiler.cc
index 20428a1..b8b18c7 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.cc
@@ -174,11 +174,13 @@
   // The `field.is_non_nullable_integer()` is set in the kernel loader and can
   // only be set if we consume a AOT kernel (annotated with inferred types).
   ASSERT(!field.is_non_nullable_integer() || FLAG_precompiled_mode);
+  // Unboxed fields in JIT lightweight isolates mode are not supported yet.
   const bool valid_class =
-      (SupportsUnboxedDoubles() && (field.guarded_cid() == kDoubleCid)) ||
-      (SupportsUnboxedSimd128() && (field.guarded_cid() == kFloat32x4Cid)) ||
-      (SupportsUnboxedSimd128() && (field.guarded_cid() == kFloat64x2Cid)) ||
-      field.is_non_nullable_integer();
+      (FLAG_precompiled_mode || !FLAG_enable_isolate_groups) &&
+      ((SupportsUnboxedDoubles() && (field.guarded_cid() == kDoubleCid)) ||
+       (SupportsUnboxedSimd128() && (field.guarded_cid() == kFloat32x4Cid)) ||
+       (SupportsUnboxedSimd128() && (field.guarded_cid() == kFloat64x2Cid)) ||
+       field.is_non_nullable_integer());
   return field.is_unboxing_candidate() && !field.is_nullable() && valid_class;
 }
 
@@ -189,7 +191,8 @@
     // proven to be correct.
     return IsUnboxedField(field);
   }
-  return field.is_unboxing_candidate() &&
+  // Unboxed fields in JIT lightweight isolates mode are not supported yet.
+  return !FLAG_enable_isolate_groups && field.is_unboxing_candidate() &&
          (FlowGraphCompiler::IsUnboxedField(field) ||
           (field.guarded_cid() == kIllegalCid));
 }
diff --git a/runtime/vm/compiler/backend/slot_test.cc b/runtime/vm/compiler/backend/slot_test.cc
index a506698..8c4b620 100644
--- a/runtime/vm/compiler/backend/slot_test.cc
+++ b/runtime/vm/compiler/backend/slot_test.cc
@@ -59,8 +59,8 @@
                  TokenPosition::kMinSource));
 
   // Set non-trivial guarded state on the field.
-  field.set_guarded_cid(kSmiCid);
-  field.set_is_nullable(false);
+  field.set_guarded_cid_unsafe(kSmiCid);
+  field.set_is_nullable_unsafe(false);
 
   // Enter compiler state.
   CompilerState compiler_state(thread, /*is_aot=*/false);
@@ -87,8 +87,8 @@
   // Change the guarded state of the field to "unknown" - emulating concurrent
   // modification of the guarded state in mutator) and create a new clone of
   // the field.
-  field.set_guarded_cid(kDynamicCid);
-  field.set_is_nullable(true);
+  field.set_guarded_cid_unsafe(kDynamicCid);
+  field.set_is_nullable_unsafe(true);
   const Field& field_clone_3 = Field::ZoneHandle(field.CloneFromOriginal());
 
   // Slot::Get must return the same slot and add the field from which it
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 2e907af..aff7bc5 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -10128,8 +10128,8 @@
       FLAG_precompiled_mode ||
       (isolate->use_field_guards() && !isolate->HasAttemptedReload());
 #endif  // !defined(PRODUCT)
-  result.set_guarded_cid(use_guarded_cid ? kIllegalCid : kDynamicCid);
-  result.set_is_nullable(use_guarded_cid ? false : true);
+  result.set_guarded_cid_unsafe(use_guarded_cid ? kIllegalCid : kDynamicCid);
+  result.set_is_nullable_unsafe(use_guarded_cid ? false : true);
   result.set_guarded_list_length_in_object_offset(Field::kUnknownLengthOffset);
   // Presently, we only attempt to remember the list length for final fields.
   if (is_final && use_guarded_cid) {
@@ -10961,6 +10961,8 @@
   // We should never try to record a sentinel.
   ASSERT(value.raw() != Object::sentinel().raw());
 
+  Thread* const thread = Thread::Current();
+  SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
   if ((guarded_cid() == kDynamicCid) ||
       (is_nullable() && value.raw() == Object::null())) {
     // Nothing to do: the field is not guarded or we are storing null into
@@ -10986,8 +10988,6 @@
       THR_Print("    => %s\n", GuardedPropertiesAsCString());
     }
 
-    Thread* const thread = Thread::Current();
-    SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
     DeoptimizeDependentCode();
   }
 }
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 6538625..a950136 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -4162,6 +4162,11 @@
   }
 
   void set_guarded_cid(intptr_t cid) const {
+    DEBUG_ASSERT(
+        IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
+    set_guarded_cid_unsafe(cid);
+  }
+  void set_guarded_cid_unsafe(intptr_t cid) const {
 #if defined(DEBUG)
     Thread* thread = Thread::Current();
     ASSERT(!IsOriginal() || is_static() || thread->IsMutatorThread() ||
@@ -4246,6 +4251,11 @@
     return raw_ptr()->is_nullable_ == kNullCid;
   }
   void set_is_nullable(bool val) const {
+    DEBUG_ASSERT(
+        IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
+    set_is_nullable_unsafe(val);
+  }
+  void set_is_nullable_unsafe(bool val) const {
     ASSERT(Thread::Current()->IsMutatorThread());
     StoreNonPointer(&raw_ptr()->is_nullable_, val ? kNullCid : kIllegalCid);
   }
diff --git a/tools/VERSION b/tools/VERSION
index 3f6c997..2069731 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 63
+PRERELEASE 64
 PRERELEASE_PATCH 0
\ No newline at end of file