Version 3.0.0-81.0.dev

Merge e738858aae8b636425b0dca456a05fb6b3818b35 into dev
diff --git a/pkg/analyzer/analysis_options.yaml b/pkg/analyzer/analysis_options.yaml
index 07e706c..000ed6c 100644
--- a/pkg/analyzer/analysis_options.yaml
+++ b/pkg/analyzer/analysis_options.yaml
@@ -38,8 +38,7 @@
     - always_use_package_imports
     - avoid_dynamic_calls
     - avoid_unused_constructor_parameters
-    # Enable after linter 1.31 is released.
-    #- collection_methods_unrelated_type
+    - collection_methods_unrelated_type
     - enable_null_safety
     - implicit_call_tearoffs
     - library_annotations
diff --git a/pkg/dds_service_extensions/lib/dds_service_extensions.dart b/pkg/dds_service_extensions/lib/dds_service_extensions.dart
index 702cf58..e28fb7b 100644
--- a/pkg/dds_service_extensions/lib/dds_service_extensions.dart
+++ b/pkg/dds_service_extensions/lib/dds_service_extensions.dart
@@ -200,7 +200,6 @@
     required int? samplePeriod,
     required int? maxStackDepth,
     required int? sampleCount,
-    required int? timeSpan,
     required int? timeOriginMicros,
     required int? timeExtentMicros,
     required int? pid,
@@ -210,7 +209,6 @@
           samplePeriod: samplePeriod,
           maxStackDepth: maxStackDepth,
           sampleCount: sampleCount,
-          timeSpan: timeSpan,
           timeOriginMicros: timeOriginMicros,
           timeExtentMicros: timeExtentMicros,
           pid: pid,
@@ -225,7 +223,6 @@
           samplePeriod: json['samplePeriod'] ?? -1,
           maxStackDepth: json['maxStackDepth'] ?? -1,
           sampleCount: json['sampleCount'] ?? -1,
-          timeSpan: json['timeSpan'] ?? -1,
           timeOriginMicros: json['timeOriginMicros'] ?? -1,
           timeExtentMicros: json['timeExtentMicros'] ?? -1,
           pid: json['pid'] ?? -1,
diff --git a/pkg/vm_service/java/test/org/dartlang/vm/service/VmServiceTest.java b/pkg/vm_service/java/test/org/dartlang/vm/service/VmServiceTest.java
index aa191ed..5b31a47 100644
--- a/pkg/vm_service/java/test/org/dartlang/vm/service/VmServiceTest.java
+++ b/pkg/vm_service/java/test/org/dartlang/vm/service/VmServiceTest.java
@@ -587,17 +587,17 @@
   private static void vmPauseOnException(IsolateRef isolate, ExceptionPauseMode mode) {
     System.out.println("Request pause on exception: " + mode);
     final OpLatch latch = new OpLatch();
-    vmService.setExceptionPauseMode(isolate.getId(), mode, new SuccessConsumer() {
-      @Override
-      public void onError(RPCError error) {
-        showRPCError(error);
-      }
+    vmService.setIsolatePauseMode(isolate.getId(), mode, new SuccessConsumer() {
+        @Override
+        public void onError(RPCError error) {
+            showRPCError(error);
+        }
 
-      @Override
-      public void received(Success response) {
-        System.out.println("Successfully set pause on exception");
-        latch.opComplete();
-      }
+        @Override
+        public void received(Success response) {
+            System.out.println("Successfully set pause on exception");
+            latch.opComplete();
+        }
     });
     latch.waitAndAssertOpComplete();
   }
diff --git a/pkg/vm_service/java/version.properties b/pkg/vm_service/java/version.properties
index 8822aaf..db1ef8e5 100644
--- a/pkg/vm_service/java/version.properties
+++ b/pkg/vm_service/java/version.properties
@@ -1 +1 @@
-version=3.62
+version=4.0
diff --git a/pkg/vm_service/lib/src/vm_service.dart b/pkg/vm_service/lib/src/vm_service.dart
index 7b4745f..83972dd 100644
--- a/pkg/vm_service/lib/src/vm_service.dart
+++ b/pkg/vm_service/lib/src/vm_service.dart
@@ -26,7 +26,7 @@
         HeapSnapshotObjectNoData,
         HeapSnapshotObjectNullData;
 
-const String vmServiceVersion = '3.62.0';
+const String vmServiceVersion = '4.0.0';
 
 /// @optional
 const String optional = 'optional';
@@ -1101,9 +1101,6 @@
   /// The `shouldPauseOnExit` parameter specify whether the target isolate
   /// should pause on exit.
   ///
-  /// The `setExceptionPauseMode` RPC is used to control if an isolate pauses
-  /// when an exception is thrown.
-  ///
   /// mode | meaning
   /// ---- | -------
   /// None | Do not pause isolate on thrown exceptions
@@ -2717,6 +2714,9 @@
   static const String kFloat32x4List = 'Float32x4List';
   static const String kFloat64x2List = 'Float64x2List';
 
+  /// An instance of the Dart class Record.
+  static const String kRecord = 'Record';
+
   /// An instance of the Dart class StackTrace.
   static const String kStackTrace = 'StackTrace';
 
@@ -2748,6 +2748,9 @@
   /// An instance of the Dart class FunctionType.
   static const String kFunctionType = 'FunctionType';
 
+  /// An instance of the Dart class RecordType.
+  static const String kRecordType = 'RecordType';
+
   /// An instance of the Dart class BoundedType.
   static const String kBoundedType = 'BoundedType';
 
@@ -2907,18 +2910,29 @@
   static BoundField? parse(Map<String, dynamic>? json) =>
       json == null ? null : BoundField._fromJson(json);
 
+  /// Provided for fields of instances that are NOT of the following instance
+  /// kinds:
+  ///  - Record
+  ///
+  /// Note: this property is deprecated and will be replaced by `name`.
   FieldRef? decl;
 
+  /// [name] can be one of [String] or [int].
+  dynamic name;
+
   /// [value] can be one of [InstanceRef] or [Sentinel].
   dynamic value;
 
   BoundField({
     this.decl,
+    this.name,
     this.value,
   });
 
   BoundField._fromJson(Map<String, dynamic> json) {
     decl = createServiceObject(json['decl'], const ['FieldRef']) as FieldRef?;
+    name =
+        createServiceObject(json['name'], const ['String', 'int']) as dynamic;
     value =
         createServiceObject(json['value'], const ['InstanceRef', 'Sentinel'])
             as dynamic;
@@ -2928,12 +2942,14 @@
     final json = <String, dynamic>{};
     json.addAll({
       'decl': decl?.toJson(),
+      'name': name?.toJson(),
       'value': value?.toJson(),
     });
     return json;
   }
 
-  String toString() => '[BoundField decl: ${decl}, value: ${value}]';
+  String toString() =>
+      '[BoundField decl: ${decl}, name: ${name}, value: ${value}]';
 }
 
 /// A `BoundVariable` represents a local variable bound to a particular value in
@@ -3626,13 +3642,6 @@
   /// The number of samples returned.
   int? sampleCount;
 
-  /// The timespan the set of returned samples covers, in microseconds
-  /// (deprecated).
-  ///
-  /// Note: this property is deprecated and will always return -1. Use
-  /// `timeExtentMicros` instead.
-  int? timeSpan;
-
   /// The start of the period of time in which the returned samples were
   /// collected.
   int? timeOriginMicros;
@@ -3656,7 +3665,6 @@
     this.samplePeriod,
     this.maxStackDepth,
     this.sampleCount,
-    this.timeSpan,
     this.timeOriginMicros,
     this.timeExtentMicros,
     this.pid,
@@ -3668,7 +3676,6 @@
     samplePeriod = json['samplePeriod'] ?? -1;
     maxStackDepth = json['maxStackDepth'] ?? -1;
     sampleCount = json['sampleCount'] ?? -1;
-    timeSpan = json['timeSpan'] ?? -1;
     timeOriginMicros = json['timeOriginMicros'] ?? -1;
     timeExtentMicros = json['timeExtentMicros'] ?? -1;
     pid = json['pid'] ?? -1;
@@ -3692,7 +3699,6 @@
       'samplePeriod': samplePeriod ?? -1,
       'maxStackDepth': maxStackDepth ?? -1,
       'sampleCount': sampleCount ?? -1,
-      'timeSpan': timeSpan ?? -1,
       'timeOriginMicros': timeOriginMicros ?? -1,
       'timeExtentMicros': timeExtentMicros ?? -1,
       'pid': pid ?? -1,
@@ -3702,7 +3708,9 @@
     return json;
   }
 
-  String toString() => '[CpuSamples]';
+  String toString() => '[CpuSamples ' //
+      'samplePeriod: ${samplePeriod}, maxStackDepth: ${maxStackDepth}, ' //
+      'sampleCount: ${sampleCount}, timeOriginMicros: ${timeOriginMicros}, timeExtentMicros: ${timeExtentMicros}, pid: ${pid}, functions: ${functions}, samples: ${samples}]';
 }
 
 class CpuSamplesEvent {
@@ -3718,13 +3726,6 @@
   /// The number of samples returned.
   int? sampleCount;
 
-  /// The timespan the set of returned samples covers, in microseconds
-  /// (deprecated).
-  ///
-  /// Note: this property is deprecated and will always return -1. Use
-  /// `timeExtentMicros` instead.
-  int? timeSpan;
-
   /// The start of the period of time in which the returned samples were
   /// collected.
   int? timeOriginMicros;
@@ -3748,7 +3749,6 @@
     this.samplePeriod,
     this.maxStackDepth,
     this.sampleCount,
-    this.timeSpan,
     this.timeOriginMicros,
     this.timeExtentMicros,
     this.pid,
@@ -3760,7 +3760,6 @@
     samplePeriod = json['samplePeriod'] ?? -1;
     maxStackDepth = json['maxStackDepth'] ?? -1;
     sampleCount = json['sampleCount'] ?? -1;
-    timeSpan = json['timeSpan'] ?? -1;
     timeOriginMicros = json['timeOriginMicros'] ?? -1;
     timeExtentMicros = json['timeExtentMicros'] ?? -1;
     pid = json['pid'] ?? -1;
@@ -3778,7 +3777,6 @@
       'samplePeriod': samplePeriod ?? -1,
       'maxStackDepth': maxStackDepth ?? -1,
       'sampleCount': sampleCount ?? -1,
-      'timeSpan': timeSpan ?? -1,
       'timeOriginMicros': timeOriginMicros ?? -1,
       'timeExtentMicros': timeExtentMicros ?? -1,
       'pid': pid ?? -1,
@@ -3788,7 +3786,9 @@
     return json;
   }
 
-  String toString() => '[CpuSamplesEvent]';
+  String toString() => '[CpuSamplesEvent ' //
+      'samplePeriod: ${samplePeriod}, maxStackDepth: ${maxStackDepth}, ' //
+      'sampleCount: ${sampleCount}, timeOriginMicros: ${timeOriginMicros}, timeExtentMicros: ${timeExtentMicros}, pid: ${pid}, functions: ${functions}, samples: ${samples}]';
 }
 
 /// See [getCpuSamples] and [CpuSamples].
@@ -6023,15 +6023,24 @@
   /// The object holding the inbound reference.
   ObjRef? source;
 
-  /// If source is a List, parentListIndex is the index of the inbound
-  /// reference.
+  /// If source is a List, parentListIndex is the index of the inbound reference
+  /// (deprecated).
+  ///
+  /// Note: this property is deprecated and will be replaced by `parentField`.
   @optional
   int? parentListIndex;
 
-  /// If source is a field of an object, parentField is the field containing the
-  /// inbound reference.
+  /// If `source` is a `List`, `parentField` is the index of the inbound
+  /// reference. If `source` is a record, `parentField` is the field name of the
+  /// inbound reference. If `source` is an instance of any other kind,
+  /// `parentField` is the field containing the inbound reference.
+  ///
+  /// Note: In v5.0 of the spec, `@Field` will no longer be a part of this
+  /// property's type, i.e. the type will become `string|int`.
+  ///
+  /// [parentField] can be one of [FieldRef], [String] or [int].
   @optional
-  FieldRef? parentField;
+  dynamic parentField;
 
   InboundReference({
     this.source,
@@ -6042,8 +6051,8 @@
   InboundReference._fromJson(Map<String, dynamic> json) {
     source = createServiceObject(json['source'], const ['ObjRef']) as ObjRef?;
     parentListIndex = json['parentListIndex'];
-    parentField = createServiceObject(json['parentField'], const ['FieldRef'])
-        as FieldRef?;
+    parentField = createServiceObject(
+        json['parentField'], const ['FieldRef', 'String', 'int']) as dynamic;
   }
 
   Map<String, dynamic> toJson() {
@@ -7141,7 +7150,9 @@
   ObjRef? value;
 
   /// If `value` is a List, `parentListIndex` is the index where the previous
-  /// object on the retaining path is located.
+  /// object on the retaining path is located (deprecated).
+  ///
+  /// Note: this property is deprecated and will be replaced by `parentField`.
   @optional
   int? parentListIndex;
 
@@ -7152,8 +7163,10 @@
 
   /// If `value` is a non-List, non-Map object, `parentField` is the name of the
   /// field containing the previous object on the retaining path.
+  ///
+  /// [parentField] can be one of [String] or [int].
   @optional
-  String? parentField;
+  dynamic parentField;
 
   RetainingObject({
     this.value,
@@ -7167,7 +7180,9 @@
     parentListIndex = json['parentListIndex'];
     parentMapKey =
         createServiceObject(json['parentMapKey'], const ['ObjRef']) as ObjRef?;
-    parentField = json['parentField'];
+    parentField =
+        createServiceObject(json['parentField'], const ['String', 'int'])
+            as dynamic;
   }
 
   Map<String, dynamic> toJson() {
@@ -7177,7 +7192,7 @@
     });
     _setIfNotNull(json, 'parentListIndex', parentListIndex);
     _setIfNotNull(json, 'parentMapKey', parentMapKey?.toJson());
-    _setIfNotNull(json, 'parentField', parentField);
+    _setIfNotNull(json, 'parentField', parentField?.toJson());
     return json;
   }
 
diff --git a/pkg/vm_service/test/get_object_rpc_test.dart b/pkg/vm_service/test/get_object_rpc_test.dart
index 34f880a..0fe673a 100644
--- a/pkg/vm_service/test/get_object_rpc_test.dart
+++ b/pkg/vm_service/test/get_object_rpc_test.dart
@@ -7,6 +7,7 @@
 
 library get_object_rpc_test;
 
+import 'dart:collection';
 import 'dart:convert' show base64Decode;
 import 'dart:typed_data';
 
@@ -693,19 +694,23 @@
             as InstanceRef;
     final objectId = evalResult.id!;
     final result = await service.getObject(isolateId, objectId) as Instance;
-    expect(result.kind, '_Record');
+    expect(result.kind, InstanceKind.kRecord);
     expect(result.json!['_vmType'], 'Record');
     expect(result.id, startsWith('objects/'));
     expect(result.valueAsString, isNull);
     expect(result.classRef!.name, '_Record');
     expect(result.size, isPositive);
-    final fields = result.fields!;
-    expect(fields.length, 4);
-    // TODO(derekx): Include field names in this test once they are accessible
-    // through package:vm_service.
-    Set<String> fieldValues =
-        Set.from(fields.map((f) => f.value.valueAsString));
-    expect(fieldValues.containsAll(['1', '2', '3.0', '4.0']), true);
+    final fieldsMap = HashMap.fromEntries(
+        result.fields!.map((f) => MapEntry(f.name, f.value)));
+    expect(fieldsMap.keys.length, 4);
+    expect(fieldsMap.containsKey(0), true);
+    expect(fieldsMap[0].valueAsString, '1');
+    expect(fieldsMap.containsKey("x"), true);
+    expect(fieldsMap["x"].valueAsString, '2');
+    expect(fieldsMap.containsKey(1), true);
+    expect(fieldsMap[1].valueAsString, '3.0');
+    expect(fieldsMap.containsKey("y"), true);
+    expect(fieldsMap["y"].valueAsString, '4.0');
   },
 
   // library.
diff --git a/runtime/observatory/lib/src/elements/debugger.dart b/runtime/observatory/lib/src/elements/debugger.dart
index a4163a0..48fad75 100644
--- a/runtime/observatory/lib/src/elements/debugger.dart
+++ b/runtime/observatory/lib/src/elements/debugger.dart
@@ -637,7 +637,7 @@
   };
 
   static Future _setBreakOnException(debugger, name, value) async {
-    var result = await debugger.isolate.setExceptionPauseMode(value);
+    var result = await debugger.isolate.setIsolatePauseMode(value);
     if (result.isError) {
       debugger.console.print(result.toString());
     } else {
diff --git a/runtime/observatory/lib/src/service/object.dart b/runtime/observatory/lib/src/service/object.dart
index 81d1cea..d4b4505 100644
--- a/runtime/observatory/lib/src/service/object.dart
+++ b/runtime/observatory/lib/src/service/object.dart
@@ -1886,8 +1886,8 @@
     return invokeRpc('setName', {'name': newName});
   }
 
-  Future setExceptionPauseMode(String mode) {
-    return invokeRpc('setExceptionPauseMode', {'mode': mode});
+  Future setIsolatePauseMode(String mode) {
+    return invokeRpc('setIsolatePauseMode', {'exceptionPauseMode': mode});
   }
 
   Future<ServiceMap> getStack({int? limit}) {
diff --git a/runtime/observatory/tests/service/get_version_rpc_test.dart b/runtime/observatory/tests/service/get_version_rpc_test.dart
index 351000d..2d1ea37 100644
--- a/runtime/observatory/tests/service/get_version_rpc_test.dart
+++ b/runtime/observatory/tests/service/get_version_rpc_test.dart
@@ -11,8 +11,8 @@
   (VM vm) async {
     final result = await vm.invokeRpcNoUpgrade('getVersion', {});
     expect(result['type'], 'Version');
-    expect(result['major'], 3);
-    expect(result['minor'], 62);
+    expect(result['major'], 4);
+    expect(result['minor'], 0);
     expect(result['_privateMajor'], 0);
     expect(result['_privateMinor'], 0);
   },
diff --git a/runtime/observatory/tests/service/pause_on_exceptions_test.dart b/runtime/observatory/tests/service/pause_on_exceptions_test.dart
index 35bd59e..f73fdb8 100644
--- a/runtime/observatory/tests/service/pause_on_exceptions_test.dart
+++ b/runtime/observatory/tests/service/pause_on_exceptions_test.dart
@@ -52,8 +52,8 @@
         bool shouldBeCaught) async {
       print("Evaluating $expression with pause on $pauseMode exception");
 
-      expect((await isolate.setExceptionPauseMode(pauseMode)) is DartError,
-          isFalse);
+      expect(
+          (await isolate.setIsolatePauseMode(pauseMode)) is DartError, isFalse);
 
       var t;
       if (shouldPause) {
diff --git a/runtime/observatory_2/lib/src/elements/debugger.dart b/runtime/observatory_2/lib/src/elements/debugger.dart
index b63d349..9070138 100644
--- a/runtime/observatory_2/lib/src/elements/debugger.dart
+++ b/runtime/observatory_2/lib/src/elements/debugger.dart
@@ -632,7 +632,7 @@
   };
 
   static Future _setBreakOnException(debugger, name, value) async {
-    var result = await debugger.isolate.setExceptionPauseMode(value);
+    var result = await debugger.isolate.setIsolatePauseMode(value);
     if (result.isError) {
       debugger.console.print(result.toString());
     } else {
diff --git a/runtime/observatory_2/lib/src/service/object.dart b/runtime/observatory_2/lib/src/service/object.dart
index 63bf64f..9acde0e 100644
--- a/runtime/observatory_2/lib/src/service/object.dart
+++ b/runtime/observatory_2/lib/src/service/object.dart
@@ -1894,8 +1894,8 @@
     return invokeRpc('setName', {'name': newName});
   }
 
-  Future setExceptionPauseMode(String mode) {
-    return invokeRpc('setExceptionPauseMode', {'mode': mode});
+  Future setIsolatePauseMode(String mode) {
+    return invokeRpc('setIsolatePauseMode', {'exceptionPauseMode': mode});
   }
 
   Future<ServiceMap> getStack({int limit}) {
diff --git a/runtime/observatory_2/tests/service_2/get_version_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_version_rpc_test.dart
index 6f53fb7..5fe3ff7 100644
--- a/runtime/observatory_2/tests/service_2/get_version_rpc_test.dart
+++ b/runtime/observatory_2/tests/service_2/get_version_rpc_test.dart
@@ -11,8 +11,8 @@
   (VM vm) async {
     final result = await vm.invokeRpcNoUpgrade('getVersion', {});
     expect(result['type'], equals('Version'));
-    expect(result['major'], equals(3));
-    expect(result['minor'], equals(62));
+    expect(result['major'], equals(4));
+    expect(result['minor'], equals(0));
     expect(result['_privateMajor'], equals(0));
     expect(result['_privateMinor'], equals(0));
   },
diff --git a/runtime/observatory_2/tests/service_2/pause_on_exceptions_test.dart b/runtime/observatory_2/tests/service_2/pause_on_exceptions_test.dart
index fa506c9..1b4da98 100644
--- a/runtime/observatory_2/tests/service_2/pause_on_exceptions_test.dart
+++ b/runtime/observatory_2/tests/service_2/pause_on_exceptions_test.dart
@@ -52,8 +52,8 @@
         bool shouldBeCaught) async {
       print("Evaluating $expression with pause on $pauseMode exception");
 
-      expect((await isolate.setExceptionPauseMode(pauseMode)) is DartError,
-          isFalse);
+      expect(
+          (await isolate.setIsolatePauseMode(pauseMode)) is DartError, isFalse);
 
       var t;
       if (shouldPause) {
diff --git a/runtime/vm/object_service.cc b/runtime/vm/object_service.cc
index f260ecd..7edada7 100644
--- a/runtime/vm/object_service.cc
+++ b/runtime/vm/object_service.cc
@@ -1124,8 +1124,8 @@
           if (!field.is_static()) {
             field_value = GetField(field);
             JSONObject jsfield(&jsarr);
-            jsfield.AddProperty("type", "BoundField");
             jsfield.AddProperty("decl", field);
+            jsfield.AddProperty("name", field.UserVisibleNameCString());
             jsfield.AddProperty("value", field_value);
           }
         }
@@ -1219,7 +1219,7 @@
 void RecordType::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref);
-  jsobj.AddProperty("kind", "_RecordType");
+  jsobj.AddProperty("kind", "RecordType");
   if (ref) {
     return;
   }
@@ -1630,7 +1630,7 @@
 void Record::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref);
-  jsobj.AddProperty("kind", "_Record");
+  jsobj.AddProperty("kind", "Record");
   if (ref) {
     return;
   }
diff --git a/runtime/vm/profiler_service.cc b/runtime/vm/profiler_service.cc
index 52903dc..44105b1 100644
--- a/runtime/vm/profiler_service.cc
+++ b/runtime/vm/profiler_service.cc
@@ -1559,9 +1559,6 @@
   obj->AddProperty("maxStackDepth",
                    static_cast<intptr_t>(FLAG_max_profile_depth));
   obj->AddProperty("sampleCount", sample_count());
-  // TODO(bkonyi): remove timeSpan after next major revision.
-  ASSERT(SERVICE_PROTOCOL_MAJOR_VERSION == 3);
-  obj->AddProperty64("timeSpan", -1);
   obj->AddPropertyTimeMicros("timeOriginMicros", min_time());
   obj->AddPropertyTimeMicros("timeExtentMicros", GetTimeSpan());
   obj->AddProperty64("pid", pid);
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index 534a303..3270228 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -2321,6 +2321,7 @@
             (slot_offset.Value() - Array::element_offset(0)) /
             Array::kBytesPerElement;
         jselement.AddProperty("parentListIndex", element_index);
+        jselement.AddProperty("parentField", element_index);
       } else if (source.IsRecord()) {
         AddParentFieldToResponseBasedOnRecord(thread, &field_names, &name,
                                               jselement, Record::Cast(source),
@@ -2351,6 +2352,7 @@
               (slot_offset.Value() - Context::variable_offset(0)) /
               Context::kBytesPerElement;
           jselement.AddProperty("parentListIndex", element_index);
+          jselement.AddProperty("parentField", element_index);
         } else {
           jselement.AddProperty("_parentWordOffset", slot_offset.Value());
         }
@@ -2445,6 +2447,7 @@
             (slot_offset.Value() - Array::element_offset(0)) /
             Array::kBytesPerElement;
         jselement.AddProperty("parentListIndex", element_index);
+        jselement.AddProperty("parentField", element_index);
       } else if (element.IsRecord()) {
         AddParentFieldToResponseBasedOnRecord(thread, &field_names, &name,
                                               jselement, Record::Cast(element),
@@ -2491,6 +2494,7 @@
               (slot_offset.Value() - Context::variable_offset(0)) /
               Context::kBytesPerElement;
           jselement.AddProperty("parentListIndex", element_index);
+          jselement.AddProperty("parentField", element_index);
         } else {
           jselement.AddProperty("_parentWordOffset", slot_offset.Value());
         }
diff --git a/runtime/vm/service.h b/runtime/vm/service.h
index b596e14..d0aa669 100644
--- a/runtime/vm/service.h
+++ b/runtime/vm/service.h
@@ -16,8 +16,8 @@
 
 namespace dart {
 
-#define SERVICE_PROTOCOL_MAJOR_VERSION 3
-#define SERVICE_PROTOCOL_MINOR_VERSION 62
+#define SERVICE_PROTOCOL_MAJOR_VERSION 4
+#define SERVICE_PROTOCOL_MINOR_VERSION 0
 
 class Array;
 class EmbedderServiceHandler;
diff --git a/runtime/vm/service/service.md b/runtime/vm/service/service.md
index ded9453..864a15a 100644
--- a/runtime/vm/service/service.md
+++ b/runtime/vm/service/service.md
@@ -1,8 +1,8 @@
-# Dart VM Service Protocol 3.62
+# Dart VM Service Protocol 4.0
 
 > Please post feedback to the [observatory-discuss group][discuss-list]
 
-This document describes of _version 3.62_ of the Dart VM Service Protocol. This
+This document describes of _version 4.0_ of the Dart VM Service Protocol. This
 protocol is used to communicate with a running Dart Virtual Machine.
 
 To use the Service Protocol, start the VM with the *--observe* flag.
@@ -121,7 +121,7 @@
   - [NativeFunction](#nativefunction)
   - [Null](#null)
   - [Object](#object)
-  - [Parameter](#parameter)[
+  - [Parameter](#parameter)
   - [PortList](#portlist)
   - [ReloadReport](#reloadreport)
   - [Response](#response)
@@ -144,7 +144,7 @@
   - [TimelineFlags](#timelineflags)
   - [Timestamp](#timestamp)
   - [TypeArguments](#typearguments)
-  - [TypeParameters](#typeparameters)[
+  - [TypeParameters](#typeparameters)
   - [UnresolvedSourceLocation](#unresolvedsourcelocation)
   - [UriList](#urilist)
   - [Version](#version)
@@ -1384,6 +1384,7 @@
 values.
 
 See [Breakpoint](#breakpoint).
+
 ### setExceptionPauseMode
 
 ```
@@ -1417,9 +1418,6 @@
 
 The _shouldPauseOnExit_ parameter specify whether the target isolate should pause on exit.
 
-The _setExceptionPauseMode_ RPC is used to control if an isolate pauses when
-an exception is thrown.
-
 mode | meaning
 ---- | -------
 None | Do not pause isolate on thrown exceptions
@@ -1708,7 +1706,12 @@
 
 ```
 class BoundField {
+  // Provided for fields of instances that are NOT of the following instance kinds:
+  //   Record
+  //
+  // Note: this property is deprecated and will be replaced by `name`.
   @Field decl;
+  string|int name;
   @Instance|Sentinel value;
 }
 ```
@@ -1980,12 +1983,6 @@
   // The number of samples returned.
   int sampleCount;
 
-  // The timespan the set of returned samples covers, in microseconds (deprecated).
-  //
-  // Note: this property is deprecated and will always return -1. Use `timeExtentMicros`
-  // instead.
-  int timeSpan;
-
   // The start of the period of time in which the returned samples were
   // collected.
   int timeOriginMicros;
@@ -2022,12 +2019,6 @@
   // The number of samples returned.
   int sampleCount;
 
-  // The timespan the set of returned samples covers, in microseconds (deprecated).
-  //
-  // Note: this property is deprecated and will always return -1. Use `timeExtentMicros`
-  // instead.
-  int timeSpan;
-
   // The start of the period of time in which the returned samples were
   // collected.
   int timeOriginMicros;
@@ -3133,6 +3124,9 @@
   Float32x4List,
   Float64x2List,
 
+  // An instance of the Dart class Record.
+  Record,
+
   // An instance of the Dart class StackTrace.
   StackTrace,
 
@@ -3164,6 +3158,9 @@
   // An instance of the Dart class FunctionType.
   FunctionType,
 
+  // An instance of the Dart class RecordType.
+  RecordType,
+
   // An instance of the Dart class BoundedType.
   BoundedType,
 
@@ -3344,12 +3341,21 @@
   // The object holding the inbound reference.
   @Object source;
 
-  // If source is a List, parentListIndex is the index of the inbound reference.
+  // If source is a List, parentListIndex is the index of the inbound reference (deprecated).
+  //
+  // Note: this property is deprecated and will be replaced by `parentField`.
   int parentListIndex [optional];
 
-  // If source is a field of an object, parentField is the field containing the
-  // inbound reference.
-  @Field parentField [optional];
+  // If `source` is a `List`, `parentField` is the index of the inbound
+  // reference.
+  // If `source` is a record, `parentField` is the field name of the inbound
+  // reference.
+  // If `source` is an instance of any other kind, `parentField` is the field
+  // containing the inbound reference.
+  //
+  // Note: In v5.0 of the spec, `@Field` will no longer be a part of this
+  // property's type, i.e. the type will become `string|int`.
+  @Field|string|int parentField [optional];
 }
 ```
 
@@ -3761,7 +3767,9 @@
   @Object value;
 
   // If `value` is a List, `parentListIndex` is the index where the previous
-  // object on the retaining path is located.
+  // object on the retaining path is located (deprecated).
+  //
+  // Note: this property is deprecated and will be replaced by `parentField`.
   int parentListIndex [optional];
 
   // If `value` is a Map, `parentMapKey` is the key mapping to the previous
@@ -3770,7 +3778,7 @@
 
   // If `value` is a non-List, non-Map object, `parentField` is the name of the
   // field containing the previous object on the retaining path.
-  string parentField [optional];
+  string|int parentField [optional];
 }
 ```
 
@@ -4415,5 +4423,6 @@
 3.60 | Added `gcType` property to `Event`.
 3.61 | Added `isolateGroupId` property to `@Isolate` and `Isolate`.
 3.62 | Added `Set` to `InstanceKind`.
+4.0 | Added `Record` and `RecordType` `InstanceKind`s, added a deprecation notice to the `decl` property of `BoundField`, added `name` property to `BoundField`, added a deprecation notice to the `parentListIndex` property of `InboundReference`, changed the type of the `parentField` property of `InboundReference` from `@Field` to `@Field\|string\|int`, added a deprecation notice to the `parentListIndex` property of `RetainingObject`, changed the type of the `parentField` property of `RetainingObject` from `string` to `string\|int`, removed the deprecated `timeSpan` property from `CpuSamples`, and removed the deprecated `timeSpan` property from `CpuSamplesEvent`.
 
 [discuss-list]: https://groups.google.com/a/dartlang.org/forum/#!forum/observatory-discuss
diff --git a/tools/VERSION b/tools/VERSION
index aec924b..36a13c8 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 3
 MINOR 0
 PATCH 0
-PRERELEASE 80
+PRERELEASE 81
 PRERELEASE_PATCH 0