Version 2.10.0-130.0.dev

Merge commit 'acbd51c08d3023a4ffaf3a1a9276edf61c9bbfcf' into 'dev'
diff --git a/DEPS b/DEPS
index dd033f0..973a186 100644
--- a/DEPS
+++ b/DEPS
@@ -97,7 +97,7 @@
   "dart_style_tag": "1.3.7",  # Please see the note above before updating.
 
   "chromedriver_tag": "83.0.4103.39",
-  "dartdoc_rev" : "ab98003fc368f484fcc4c055770adea47bc83fbd",
+  "dartdoc_rev" : "2bef0f260594b822f55c8c8f777d9c4c1ea8f76c",
   "ffi_rev": "454ab0f9ea6bd06942a983238d8a6818b1357edb",
   "fixnum_rev": "16d3890c6dc82ca629659da1934e412292508bba",
   "glob_rev": "e9f4e6b7ae8abe5071461cf8f47191bb19cf7ef6",
diff --git a/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart
index 989ea2d..e544c4f 100644
--- a/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart
@@ -144,45 +144,29 @@
   void _lookupInterfaceType(InterfaceType type) {
     var isSuper = _receiver is SuperExpression;
 
-    if (_name == '[]') {
-      _getterRequested = type.lookUpMethod2(
-        '[]',
-        _definingLibrary,
-        concrete: isSuper,
-        inherited: isSuper,
-      );
-      _needsGetterError = _getterRequested == null;
+    var getterName = Name(_definingLibrary.source.uri, _name);
+    _getterRequested =
+        _resolver.inheritance.getMember(type, getterName, forSuper: isSuper);
+    _needsGetterError = _getterRequested == null;
 
-      _setterRequested = type.lookUpMethod2(
-        '[]=',
-        _definingLibrary,
-        concrete: isSuper,
-        inherited: isSuper,
-      );
-      _needsSetterError = _setterRequested == null;
-    } else {
+    if (_getterRequested == null) {
       var classElement = type.element as AbstractClassElementImpl;
+      _getterRecovery ??=
+          classElement.lookupStaticGetter(_name, _definingLibrary) ??
+              classElement.lookupStaticMethod(_name, _definingLibrary);
+      _needsGetterError = _getterRecovery == null;
+    }
 
-      var getterName = Name(_definingLibrary.source.uri, _name);
-      _getterRequested =
-          _resolver.inheritance.getMember(type, getterName, forSuper: isSuper);
-      _needsGetterError = _getterRequested == null;
-      if (_getterRequested == null) {
-        _getterRecovery ??=
-            classElement.lookupStaticGetter(_name, _definingLibrary) ??
-                classElement.lookupStaticMethod(_name, _definingLibrary);
-        _needsGetterError = _getterRecovery == null;
-      }
+    var setterName = Name(_definingLibrary.source.uri, '$_name=');
+    _setterRequested =
+        _resolver.inheritance.getMember(type, setterName, forSuper: isSuper);
+    _needsSetterError = _setterRequested == null;
 
-      var setterName = Name(_definingLibrary.source.uri, '$_name=');
-      _setterRequested =
-          _resolver.inheritance.getMember(type, setterName, forSuper: isSuper);
-      _needsSetterError = _setterRequested == null;
-      if (_setterRequested == null) {
-        _setterRecovery ??=
-            classElement.lookupStaticSetter(_name, _definingLibrary);
-        _needsSetterError = _setterRecovery == null;
-      }
+    if (_setterRequested == null) {
+      var classElement = type.element as AbstractClassElementImpl;
+      _setterRecovery ??=
+          classElement.lookupStaticSetter(_name, _definingLibrary);
+      _needsSetterError = _setterRecovery == null;
     }
   }
 
diff --git a/pkg/dds/CHANGELOG.md b/pkg/dds/CHANGELOG.md
index 5ea6b54..8050d63 100644
--- a/pkg/dds/CHANGELOG.md
+++ b/pkg/dds/CHANGELOG.md
@@ -1,3 +1,8 @@
+# 1.3.4
+
+- Fixed issue where `isolateId`s were expected to take the form `isolates/123`
+  although this is not required by the VM service specification.
+
 # 1.3.3
 
 - Fixed issue where `DartDevelopmentService.sseUri` did not return a URI with a
diff --git a/pkg/dds/lib/src/isolate_manager.dart b/pkg/dds/lib/src/isolate_manager.dart
index 57711bf..50ace65 100644
--- a/pkg/dds/lib/src/isolate_manager.dart
+++ b/pkg/dds/lib/src/isolate_manager.dart
@@ -31,7 +31,7 @@
 }
 
 class _RunningIsolate {
-  _RunningIsolate(this.isolateManager, this.portId, this.name);
+  _RunningIsolate(this.isolateManager, this.id, this.name);
 
   // State setters.
   void pausedOnExit() => _state = _IsolateState.pauseExit;
@@ -52,7 +52,7 @@
     if (shouldResume()) {
       clearResumeApprovals();
       await isolateManager.dds._vmServiceClient.sendRequest('resume', {
-        'isolateId': 'isolates/$portId',
+        'isolateId': id,
       });
     }
   }
@@ -104,7 +104,7 @@
 
   final _IsolateManager isolateManager;
   final String name;
-  final String portId;
+  final String id;
   final Set<String> _resumeApprovalsByName = {};
   _IsolateState _state;
 }
@@ -124,7 +124,7 @@
     }
 
     final isolateData = event['isolate'];
-    final id = isolateData['number'].asString;
+    final id = isolateData['id'].asString;
     final name = isolateData['name'].asString;
     _updateIsolateState(id, name, eventKind);
   }
@@ -165,32 +165,32 @@
     // Check the pause event for each isolate to determine whether or not the
     // isolate is already paused.
     for (final isolateRef in isolateRefs) {
+      final id = isolateRef['id'];
       final isolate = await dds._vmServiceClient.sendRequest('getIsolate', {
-        'isolateId': isolateRef['id'],
+        'isolateId': id,
       });
-      final portId = isolate['number'];
       final name = isolate['name'];
       if (isolate.containsKey('pauseEvent')) {
-        isolates[portId] = _RunningIsolate(this, portId, name);
+        isolates[id] = _RunningIsolate(this, id, name);
         final eventKind = isolate['pauseEvent']['kind'];
-        _updateIsolateState(portId, name, eventKind);
+        _updateIsolateState(id, name, eventKind);
       } else {
         // If the isolate doesn't have a pauseEvent, assume it's running.
-        isolateStarted(portId, name);
+        isolateStarted(id, name);
       }
     }
   }
 
   /// Initializes state for a newly started isolate.
-  void isolateStarted(String portId, String name) {
-    final isolate = _RunningIsolate(this, portId, name);
+  void isolateStarted(String id, String name) {
+    final isolate = _RunningIsolate(this, id, name);
     isolate.running();
-    isolates[portId] = isolate;
+    isolates[id] = isolate;
   }
 
   /// Cleans up state for an isolate that has exited.
-  void isolateExited(String portId) {
-    isolates.remove(portId);
+  void isolateExited(String id) {
+    isolates.remove(id);
   }
 
   /// Handles `resume` RPC requests. If the client requires that approval be
@@ -206,8 +206,7 @@
     json_rpc.Parameters parameters,
   ) async {
     final isolateId = parameters['isolateId'].asString;
-    final portId = _isolateIdToPortId(isolateId);
-    final isolate = isolates[portId];
+    final isolate = isolates[isolateId];
     if (isolate == null) {
       return _RPCResponses.collectedSentinel;
     }
@@ -233,9 +232,6 @@
     return resumeResult;
   }
 
-  static String _isolateIdToPortId(String isolateId) =>
-      isolateId.substring('isolates/'.length);
-
   final _DartDevelopmentService dds;
   final Map<String, _RunningIsolate> isolates = {};
 }
diff --git a/pkg/dds/pubspec.yaml b/pkg/dds/pubspec.yaml
index 16e8c0b..6fa115b 100644
--- a/pkg/dds/pubspec.yaml
+++ b/pkg/dds/pubspec.yaml
@@ -3,7 +3,7 @@
   A library used to spawn the Dart Developer Service, used to communicate with
   a Dart VM Service instance.
 
-version: 1.3.3
+version: 1.3.4
 
 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/dds
 
diff --git a/pkg/front_end/lib/widget_cache.dart b/pkg/front_end/lib/widget_cache.dart
new file mode 100644
index 0000000..9ccfa38
--- /dev/null
+++ b/pkg/front_end/lib/widget_cache.dart
@@ -0,0 +1,191 @@
+// 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:kernel/class_hierarchy.dart';
+import 'package:kernel/kernel.dart';
+
+/// Provides support for "single widget reloads" in Flutter, by determining if
+/// a partial component contains single change to the class body of a
+/// StatelessWidget, StatefulWidget, or State subtype.
+class WidgetCache {
+  /// Create a [WidgetCache] from a [Component] containing the flutter
+  /// framework.
+  WidgetCache(Component fullComponent) {
+    Library frameworkLibrary;
+    for (Library library in fullComponent.libraries) {
+      if (library?.importUri?.path == 'flutter/src/widgets/framework.dart') {
+        frameworkLibrary = library;
+        break;
+      }
+    }
+    if (frameworkLibrary == null) {
+      return;
+    }
+    for (Class classDeclaration in frameworkLibrary.classes) {
+      if (classDeclaration.name == _statelessWidgetClassName) {
+        _statelessWidget = classDeclaration;
+      } else if (classDeclaration.name == _statefulWidgetClassName) {
+        _statefulWidget = classDeclaration;
+      } else if (classDeclaration.name == _stateClassName) {
+        _state = classDeclaration;
+      }
+    }
+    _frameworkTypesLocated =
+        _statefulWidget != null && _state != null && _statelessWidget != null;
+  }
+
+  static const String _stateClassName = 'State';
+  static const String _statefulWidgetClassName = 'StatefulWidget';
+  static const String _statelessWidgetClassName = 'StatelessWidget';
+
+  Class _statelessWidget;
+  Class _state;
+  Class _statefulWidget;
+  bool _frameworkTypesLocated = false;
+
+  /// Mark [uri] as invalidated.
+  void invalidate(Uri uri) {
+    _invalidatedLibraries.add(uri);
+  }
+
+  /// Reset the invalidated libraries.
+  void reset() {
+    _invalidatedLibraries.clear();
+  }
+
+  final List<Uri> _invalidatedLibraries = <Uri>[];
+
+  /// Determine if any changes to [partialComponent] were located entirely
+  /// within the class body of a single `StatefulWidget`, `StatelessWidget` or
+  /// `State` subtype.
+  ///
+  /// Returns the class name if located, otherwise `null`.
+  String checkWidgetCache(
+    Component lastGoodComponent,
+    Component partialComponent,
+    ClassHierarchy classHierarchy,
+  ) {
+    if (!_frameworkTypesLocated ||
+        lastGoodComponent == null ||
+        _invalidatedLibraries.length != 1) {
+      return null;
+    }
+    Uri importUri = _invalidatedLibraries[0];
+    Library library;
+    for (Library candidateLibrary in partialComponent.libraries) {
+      if (candidateLibrary.importUri == importUri) {
+        library = candidateLibrary;
+        break;
+      }
+    }
+    if (library == null) {
+      return null;
+    }
+    List<int> oldSource = lastGoodComponent.uriToSource[library.fileUri].source;
+    List<int> newSource = partialComponent.uriToSource[library.fileUri].source;
+    // Library was added and does not exist in the old component.
+    if (oldSource == null) {
+      return null;
+    }
+    int newStartIndex = 0;
+    int newEndIndex = newSource.length - 1;
+    int oldStartIndex = 0;
+    int oldEndIndex = oldSource.length - 1;
+
+    while (newStartIndex < newEndIndex && oldStartIndex < oldEndIndex) {
+      if (newSource[newStartIndex] != oldSource[oldStartIndex]) {
+        break;
+      }
+      newStartIndex += 1;
+      oldStartIndex += 1;
+    }
+    while (newEndIndex > newStartIndex && oldEndIndex > oldStartIndex) {
+      if (newSource[newEndIndex] != oldSource[oldEndIndex]) {
+        break;
+      }
+      newEndIndex -= 1;
+      oldEndIndex -= 1;
+    }
+
+    Class newClass =
+        _locateContainingClass(library, newStartIndex, newEndIndex);
+    if (newClass == null) {
+      return null;
+    }
+
+    Library oldLibrary =
+        lastGoodComponent.libraries.firstWhere((Library library) {
+      return library.importUri == importUri;
+    });
+
+    Class oldClass =
+        _locateContainingClass(oldLibrary, oldStartIndex, oldEndIndex);
+
+    if (oldClass == null || oldClass.name != newClass.name) {
+      return null;
+    }
+
+    if (classHierarchy.isSubclassOf(newClass, _statelessWidget) ||
+        classHierarchy.isSubclassOf(newClass, _statefulWidget)) {
+      if (_hasSubClasses(newClass, partialComponent, classHierarchy)) {
+        return null;
+      }
+      return newClass.name;
+    }
+
+    // For changes to State classes, locate the name of the corresponding
+    // StatefulWidget that is provided as a type parameter. If the bounds are
+    // StatefulWidget itself, fail as that indicates the type was not
+    // specified.
+    Supertype stateSuperType =
+        classHierarchy.getClassAsInstanceOf(newClass, _state);
+    if (stateSuperType != null) {
+      if (stateSuperType.typeArguments.length != 1) {
+        return null;
+      }
+      DartType widgetType = stateSuperType.typeArguments[0];
+      if (widgetType is InterfaceType) {
+        Class statefulWidgetType = widgetType.classNode;
+        if (statefulWidgetType.name == _statefulWidgetClassName) {
+          return null;
+        }
+        if (_hasSubClasses(
+            statefulWidgetType, partialComponent, classHierarchy)) {
+          return null;
+        }
+        return statefulWidgetType.name;
+      }
+    }
+
+    return null;
+  }
+
+  /// Checks whether the class [node] has any subclasses.
+  bool _hasSubClasses(
+      Class node, Component component, ClassHierarchy classHierarchy) {
+    for (Library library in component.libraries) {
+      for (Class otherClass in library.classes) {
+        if (identical(otherClass, node)) {
+          continue;
+        }
+        if (classHierarchy.isSubclassOf(otherClass, node)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  // Locate the that fully contains the edit range, or null.
+  Class _locateContainingClass(
+      Library library, int startOffset, int endOffset) {
+    for (Class classDeclaration in library.classes) {
+      if (classDeclaration.startFileOffset <= startOffset &&
+          classDeclaration.fileEndOffset >= endOffset) {
+        return classDeclaration;
+      }
+    }
+    return null;
+  }
+}
diff --git a/pkg/front_end/test/spell_checking_list_common.txt b/pkg/front_end/test/spell_checking_list_common.txt
index 0073a80..0ca4f6d 100644
--- a/pkg/front_end/test/spell_checking_list_common.txt
+++ b/pkg/front_end/test/spell_checking_list_common.txt
@@ -2790,6 +2790,7 @@
 startup
 state
 stateful
+stateless
 statement
 statement's
 statements
diff --git a/runtime/observatory/tests/service/get_retaining_path_rpc_test.dart b/runtime/observatory/tests/service/get_retaining_path_rpc_test.dart
index 73ac5bd..d03bffb 100644
--- a/runtime/observatory/tests/service/get_retaining_path_rpc_test.dart
+++ b/runtime/observatory/tests/service/get_retaining_path_rpc_test.dart
@@ -21,9 +21,13 @@
 var target3 = new _TestClass();
 var target4 = new _TestClass();
 var target5 = new _TestClass();
+@pragma("vm:entry-point") // Prevent obfuscation
 var globalObject = new _TestClass();
+@pragma("vm:entry-point") // Prevent obfuscation
 var globalList = new List(100);
+@pragma("vm:entry-point") // Prevent obfuscation
 var globalMap1 = new Map();
+@pragma("vm:entry-point") // Prevent obfuscation
 var globalMap2 = new Map();
 
 void warmup() {
@@ -93,9 +97,9 @@
       'limit': 100,
     };
     var result = await isolate.invokeRpcNoUpgrade('getRetainingPath', params);
-    expect(result['gcRootType'], 'static fields table');
-    expect(result['elements'].length, equals(1));
-    expect(result['elements'][0]['value']['type'], equals('@Instance'));
+    expect(result['gcRootType'], 'user global');
+    expect(result['elements'].length, equals(2));
+    expect(result['elements'][1]['value']['name'], equals('globalObject'));
   },
 
   // missing limit.
@@ -125,10 +129,10 @@
     };
     var result = await isolate.invokeRpcNoUpgrade('getRetainingPath', params);
     expect(result['type'], equals('RetainingPath'));
-    expect(result['gcRootType'], 'static fields table');
-    expect(result['elements'].length, equals(2));
-    expect(result['elements'][0]['value']['type'], equals('@Instance'));
+    expect(result['gcRootType'], 'user global');
+    expect(result['elements'].length, equals(3));
     expect(result['elements'][1]['parentField'], equals('x'));
+    expect(result['elements'][2]['value']['name'], equals('globalObject'));
   },
 
   (Isolate isolate) async {
@@ -139,10 +143,10 @@
     };
     var result = await isolate.invokeRpcNoUpgrade('getRetainingPath', params);
     expect(result['type'], equals('RetainingPath'));
-    expect(result['gcRootType'], 'static fields table');
-    expect(result['elements'].length, equals(2));
+    expect(result['gcRootType'], 'user global');
+    expect(result['elements'].length, equals(3));
     expect(result['elements'][1]['parentField'], equals('y'));
-    expect(result['elements'][1]['value']['type'], equals('@Instance'));
+    expect(result['elements'][2]['value']['name'], equals('globalObject'));
   },
 
   (Isolate isolate) async {
@@ -153,10 +157,10 @@
     };
     var result = await isolate.invokeRpcNoUpgrade('getRetainingPath', params);
     expect(result['type'], equals('RetainingPath'));
-    expect(result['gcRootType'], 'static fields table');
-    expect(result['elements'].length, equals(2));
+    expect(result['gcRootType'], 'user global');
+    expect(result['elements'].length, equals(3));
     expect(result['elements'][1]['parentListIndex'], equals(12));
-    expect(result['elements'][1]['value']['type'], equals('@Instance'));
+    expect(result['elements'][2]['value']['name'], equals('globalList'));
   },
 
   (Isolate isolate) async {
@@ -167,11 +171,11 @@
     };
     var result = await isolate.invokeRpcNoUpgrade('getRetainingPath', params);
     expect(result['type'], equals('RetainingPath'));
-    expect(result['gcRootType'], 'static fields table');
-    expect(result['elements'].length, equals(2));
+    expect(result['gcRootType'], 'user global');
+    expect(result['elements'].length, equals(3));
     expect(
         result['elements'][1]['parentMapKey']['valueAsString'], equals('key'));
-    expect(result['elements'][1]['value']['type'], equals('@Instance'));
+    expect(result['elements'][2]['value']['name'], equals('globalMap1'));
   },
 
   (Isolate isolate) async {
@@ -182,10 +186,10 @@
     };
     var result = await isolate.invokeRpcNoUpgrade('getRetainingPath', params);
     expect(result['type'], equals('RetainingPath'));
-    expect(result['elements'].length, equals(2));
+    expect(result['elements'].length, equals(3));
     expect(result['elements'][1]['parentMapKey']['class']['name'],
         equals('_TestClass'));
-    expect(result['elements'][1]['value']['type'], equals('@Instance'));
+    expect(result['elements'][2]['value']['name'], equals('globalMap2'));
   },
 
   // object store
diff --git a/runtime/observatory/tests/service/get_user_level_retaining_path_rpc_test.dart b/runtime/observatory/tests/service/get_user_level_retaining_path_rpc_test.dart
index 9f69aeb..535c882 100644
--- a/runtime/observatory/tests/service/get_user_level_retaining_path_rpc_test.dart
+++ b/runtime/observatory/tests/service/get_user_level_retaining_path_rpc_test.dart
@@ -53,9 +53,10 @@
     };
     var result = await isolate.invokeRpcNoUpgrade('getRetainingPath', params);
     expect(result['type'], equals('RetainingPath'));
-    expect(result['elements'].length, equals(1));
+    expect(result['elements'].length, equals(2));
     expect(
         result['elements'][0]['value']['class']['name'], equals('_TestConst'));
+    expect(result['elements'][1]['value']['name'], equals('x'));
   },
 
   // Expect a simple path through variable fn instead of long path filled
@@ -68,8 +69,9 @@
     };
     var result = await isolate.invokeRpcNoUpgrade('getRetainingPath', params);
     expect(result['type'], equals('RetainingPath'));
-    expect(result['elements'].length, equals(1));
+    expect(result['elements'].length, equals(2));
     expect(result['elements'][0]['value']['class']['name'], equals('_Closure'));
+    expect(result['elements'][1]['value']['name'], equals('fn'));
   }
 ];
 
diff --git a/runtime/observatory/tests/service/inbound_references_test.dart b/runtime/observatory/tests/service/inbound_references_test.dart
index 267f1b1..5c10ae1 100644
--- a/runtime/observatory/tests/service/inbound_references_test.dart
+++ b/runtime/observatory/tests/service/inbound_references_test.dart
@@ -48,6 +48,8 @@
         r['source'].clazz.name == 'Node');
     hasReferenceSuchThat(
         (r) => r['parentListIndex'] == 1 && r['source'].isList);
+    hasReferenceSuchThat(
+        (r) => r['source'] is Field && r['source'].name == 'e');
   }
 ];
 
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index a973ce7..c31e1a2c 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -4847,10 +4847,8 @@
       OneByteStringPtr str = objects_[i];
       AutoTraceObject(str);
       const intptr_t length = Smi::Value(str->ptr()->length_);
-      s->WriteUnsigned(length);
-      s->Write<bool>(str->ptr()->IsCanonical());
-      intptr_t hash = String::GetCachedHash(str);
-      s->Write<int32_t>(hash);
+      ASSERT(length <= compiler::target::kSmiMax);
+      s->WriteUnsigned((length << 1) | (str->ptr()->IsCanonical() ? 1 : 0));
       s->WriteBytes(str->ptr()->data(), length);
     }
   }
@@ -4880,16 +4878,20 @@
   void ReadFill(Deserializer* d) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       OneByteStringPtr str = static_cast<OneByteStringPtr>(d->Ref(id));
-      const intptr_t length = d->ReadUnsigned();
-      bool is_canonical = d->Read<bool>();
+      const intptr_t combined = d->ReadUnsigned();
+      const intptr_t length = combined >> 1;
+      const bool is_canonical = (combined & 1) != 0;
       Deserializer::InitializeHeader(str, kOneByteStringCid,
                                      OneByteString::InstanceSize(length),
                                      is_canonical);
       str->ptr()->length_ = Smi::New(length);
-      String::SetCachedHash(str, d->Read<int32_t>());
+      uint32_t hash = 0;
       for (intptr_t j = 0; j < length; j++) {
-        str->ptr()->data()[j] = d->Read<uint8_t>();
+        uint8_t code_point = d->Read<uint8_t>();
+        str->ptr()->data()[j] = code_point;
+        hash = CombineHashes(hash, code_point);
       }
+      String::SetCachedHash(str, FinalizeHash(hash, String::kHashBits));
     }
   }
 };
@@ -4924,10 +4926,8 @@
       TwoByteStringPtr str = objects_[i];
       AutoTraceObject(str);
       const intptr_t length = Smi::Value(str->ptr()->length_);
-      s->WriteUnsigned(length);
-      s->Write<bool>(str->ptr()->IsCanonical());
-      intptr_t hash = String::GetCachedHash(str);
-      s->Write<int32_t>(hash);
+      ASSERT(length <= (compiler::target::kSmiMax / 2));
+      s->WriteUnsigned((length << 1) | (str->ptr()->IsCanonical() ? 1 : 0));
       s->WriteBytes(reinterpret_cast<uint8_t*>(str->ptr()->data()), length * 2);
     }
   }
@@ -4957,15 +4957,16 @@
   void ReadFill(Deserializer* d) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       TwoByteStringPtr str = static_cast<TwoByteStringPtr>(d->Ref(id));
-      const intptr_t length = d->ReadUnsigned();
-      bool is_canonical = d->Read<bool>();
+      const intptr_t combined = d->ReadUnsigned();
+      const intptr_t length = combined >> 1;
+      const bool is_canonical = (combined & 1) != 0;
       Deserializer::InitializeHeader(str, kTwoByteStringCid,
                                      TwoByteString::InstanceSize(length),
                                      is_canonical);
       str->ptr()->length_ = Smi::New(length);
-      String::SetCachedHash(str, d->Read<int32_t>());
       uint8_t* cdata = reinterpret_cast<uint8_t*>(str->ptr()->data());
       d->ReadBytes(cdata, length * 2);
+      String::SetCachedHash(str, String::Hash(str));
     }
   }
 };
@@ -5622,10 +5623,6 @@
       return "CodeSourceMap";
     case kCompressedStackMapsCid:
       return "CompressedStackMaps";
-    case kOneByteStringCid:
-      return "OneByteString";
-    case kTwoByteStringCid:
-      return "TwoByteString";
     default:
       return nullptr;
   }
@@ -6352,8 +6349,6 @@
       case kPcDescriptorsCid:
       case kCodeSourceMapCid:
       case kCompressedStackMapsCid:
-      case kOneByteStringCid:
-      case kTwoByteStringCid:
         return new (Z) RODataDeserializationCluster();
     }
   }
diff --git a/runtime/vm/compiler/asm_intrinsifier.h b/runtime/vm/compiler/asm_intrinsifier.h
index 01fd20d..58a97e0 100644
--- a/runtime/vm/compiler/asm_intrinsifier.h
+++ b/runtime/vm/compiler/asm_intrinsifier.h
@@ -24,6 +24,7 @@
 
 namespace compiler {
 class Assembler;
+class Intrinsifier;
 class Label;
 
 class AsmIntrinsifier : public AllStatic {
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 50cba71..f94e466 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -2736,7 +2736,9 @@
   }
 
   // Visit objects in the field table.
-  field_table()->VisitObjectPointers(visitor);
+  if (!visitor->trace_values_through_fields()) {
+    field_table()->VisitObjectPointers(visitor);
+  }
 
   visitor->clear_gc_root_type();
   // Visit the objects directly referenced from the isolate structure.
diff --git a/runtime/vm/object_graph.cc b/runtime/vm/object_graph.cc
index e127267..2cd2590 100644
--- a/runtime/vm/object_graph.cc
+++ b/runtime/vm/object_graph.cc
@@ -48,6 +48,8 @@
     object_ids_ = nullptr;
   }
 
+  virtual bool trace_values_through_fields() const { return true; }
+
   // Marks and pushes. Used to initialize this stack with roots.
   // We can use ObjectIdTable normally used by serializers because it
   // won't be in use while handling a service request (ObjectGraph's only use).
@@ -472,6 +474,8 @@
     ASSERT(Thread::Current()->no_safepoint_scope_depth() != 0);
   }
 
+  virtual bool trace_values_through_fields() const { return true; }
+
   intptr_t length() const { return length_; }
 
   virtual void VisitObject(ObjectPtr raw_obj) {
@@ -765,6 +769,8 @@
         HandleVisitor(Thread::Current()),
         writer_(writer) {}
 
+  virtual bool trace_values_through_fields() const { return true; }
+
   void VisitObject(ObjectPtr obj) {
     if (obj->IsPseudoObject()) return;
 
@@ -819,6 +825,8 @@
         isolate_(thread()->isolate()),
         writer_(writer) {}
 
+  virtual bool trace_values_through_fields() const { return true; }
+
   void VisitObject(ObjectPtr obj) {
     if (obj->IsPseudoObject()) return;
 
diff --git a/runtime/vm/raw_object.cc b/runtime/vm/raw_object.cc
index 9797fc2..3dfd910 100644
--- a/runtime/vm/raw_object.cc
+++ b/runtime/vm/raw_object.cc
@@ -516,7 +516,6 @@
 REGULAR_VISITOR(SignatureData)
 REGULAR_VISITOR(RedirectionData)
 REGULAR_VISITOR(FfiTrampolineData)
-REGULAR_VISITOR(Field)
 REGULAR_VISITOR(Script)
 REGULAR_VISITOR(Library)
 REGULAR_VISITOR(LibraryPrefix)
@@ -592,6 +591,26 @@
 REGULAR_VISITOR(WeakSerializationReference)
 #endif
 
+intptr_t FieldLayout::VisitFieldPointers(FieldPtr raw_obj,
+                                         ObjectPointerVisitor* visitor) {
+  ASSERT(raw_obj->IsHeapObject());
+  ASSERT_UNCOMPRESSED(Field);
+  visitor->VisitPointers(raw_obj->ptr()->from(), raw_obj->ptr()->to());
+
+  if (visitor->trace_values_through_fields()) {
+    if (Field::StaticBit::decode(raw_obj->ptr()->kind_bits_)) {
+      visitor->isolate_group()->ForEachIsolate(
+          [&](Isolate* isolate) {
+            intptr_t index =
+                Smi::Value(raw_obj->ptr()->host_offset_or_field_id_);
+            visitor->VisitPointer(&isolate->field_table()->table()[index]);
+          },
+          /*at_safepoint=*/true);
+    }
+  }
+  return Field::InstanceSize();
+}
+
 bool CodeLayout::ContainsPC(const ObjectPtr raw_obj, uword pc) {
   if (!raw_obj->IsCode()) return false;
   auto const raw_code = static_cast<const CodePtr>(raw_obj);
diff --git a/runtime/vm/visitor.h b/runtime/vm/visitor.h
index 93baf0d..eb9d343 100644
--- a/runtime/vm/visitor.h
+++ b/runtime/vm/visitor.h
@@ -52,6 +52,11 @@
 
   virtual bool visit_weak_persistent_handles() const { return false; }
 
+  // When visiting objects to build retaining paths, trace field values
+  // through fields.
+  // Otherwise trace field values through isolate's field_table.
+  virtual bool trace_values_through_fields() const { return false; }
+
   const SharedClassTable* shared_class_table() const {
     return shared_class_table_;
   }
diff --git a/tools/VERSION b/tools/VERSION
index 14a7a99..38b781d 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 10
 PATCH 0
-PRERELEASE 129
+PRERELEASE 130
 PRERELEASE_PATCH 0
\ No newline at end of file