[vm, service] Report InstanceKind for Sets.

(The elements were already being populated.)

TEST=ci
Change-Id: I02cfa2f311e7871836f1eddd8ed131c282235d58
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/269383
Reviewed-by: Ben Konyi <bkonyi@google.com>
Reviewed-by: Derek Xu <derekx@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
diff --git a/pkg/vm_service/lib/src/vm_service.dart b/pkg/vm_service/lib/src/vm_service.dart
index 9f30e1c..9331689 100644
--- a/pkg/vm_service/lib/src/vm_service.dart
+++ b/pkg/vm_service/lib/src/vm_service.dart
@@ -2691,6 +2691,10 @@
   /// be PlainInstance.
   static const String kMap = 'Map';
 
+  /// An instance of the built-in VM Set implementation. User-defined Sets will
+  /// be PlainInstance.
+  static const String kSet = 'Set';
+
   /// Vector instance kinds.
   static const String kFloat32x4 = 'Float32x4';
   static const String kFloat64x2 = 'Float64x2';
diff --git a/pkg/vm_service/test/get_object_rpc_test.dart b/pkg/vm_service/test/get_object_rpc_test.dart
index d84c844..c722d98 100644
--- a/pkg/vm_service/test/get_object_rpc_test.dart
+++ b/pkg/vm_service/test/get_object_rpc_test.dart
@@ -55,6 +55,9 @@
 getMap() => {"x": 3, "y": 4, "z": 5};
 
 @pragma("vm:entry-point")
+getSet() => {6, 7, 8};
+
+@pragma("vm:entry-point")
 getUint8List() => uint8List;
 
 @pragma("vm:entry-point")
@@ -441,6 +444,38 @@
     expect(result.associations, isEmpty);
   },
 
+  // A built-in Set.
+  (VmService service, IsolateRef isolateRef) async {
+    final isolateId = isolateRef.id!;
+    final isolate = await service.getIsolate(isolateId);
+    // Call eval to get a Dart set.
+    final evalResult = await service
+        .invoke(isolateId, isolate.rootLib!.id!, 'getSet', []) as InstanceRef;
+    final objectId = evalResult.id!;
+    final result = await service.getObject(isolateId, objectId) as Instance;
+    expect(result.kind, InstanceKind.kSet);
+    expect(result.json!['_vmType'], equals('LinkedHashSet'));
+    expect(result.id, startsWith('objects/'));
+    expect(result.valueAsString, isNull);
+    expect(result.classRef!.name, equals('_InternalLinkedHashSet'));
+    expect(result.size, isPositive);
+    expect(result.fields, isEmpty);
+    expect(result.length, equals(3));
+    expect(result.offset, isNull);
+    expect(result.count, isNull);
+    final elements = result.elements!;
+    expect(elements.length, equals(3));
+    expect(elements[0] is InstanceRef, true);
+    expect(elements[0].kind, InstanceKind.kInt);
+    expect(elements[0].valueAsString, equals('6'));
+    expect(elements[1] is InstanceRef, true);
+    expect(elements[1].kind, InstanceKind.kInt);
+    expect(elements[1].valueAsString, equals('7'));
+    expect(elements[2] is InstanceRef, true);
+    expect(elements[2].kind, InstanceKind.kInt);
+    expect(elements[2].valueAsString, equals('8'));
+  },
+
   // Uint8List.
   (VmService service, IsolateRef isolateRef) async {
     final isolateId = isolateRef.id!;
diff --git a/runtime/observatory/lib/src/elements/instance_ref.dart b/runtime/observatory/lib/src/elements/instance_ref.dart
index 01ba033..9cc74a6 100644
--- a/runtime/observatory/lib/src/elements/instance_ref.dart
+++ b/runtime/observatory/lib/src/elements/instance_ref.dart
@@ -171,6 +171,7 @@
         ];
       case M.InstanceKind.list:
       case M.InstanceKind.map:
+      case M.InstanceKind.set:
       case M.InstanceKind.uint8ClampedList:
       case M.InstanceKind.uint8List:
       case M.InstanceKind.uint16List:
@@ -219,6 +220,7 @@
         return true;
       case M.InstanceKind.list:
       case M.InstanceKind.map:
+      case M.InstanceKind.set:
       case M.InstanceKind.uint8ClampedList:
       case M.InstanceKind.uint8List:
       case M.InstanceKind.uint16List:
@@ -297,6 +299,14 @@
               ])
             .toList()
           ..addAll(_createShowMoreButton());
+      case M.InstanceKind.set:
+        return _loadedInstance!.elements!
+            .map<Element>((element) => new DivElement()
+              ..children = <Element>[
+                anyRef(_isolate, element, _objects, queue: _r.queue)
+              ])
+            .toList()
+          ..addAll(_createShowMoreButton());
       case M.InstanceKind.uint8ClampedList:
       case M.InstanceKind.uint8List:
       case M.InstanceKind.uint16List:
diff --git a/runtime/observatory/lib/src/models/objects/instance.dart b/runtime/observatory/lib/src/models/objects/instance.dart
index 1b2d58e..5c0a344 100644
--- a/runtime/observatory/lib/src/models/objects/instance.dart
+++ b/runtime/observatory/lib/src/models/objects/instance.dart
@@ -31,6 +31,10 @@
   /// Maps will be PlainInstance.
   map,
 
+  /// An instance of the built-in VM Set implementation. User-defined
+  /// Sets will be PlainInstance.
+  set,
+
   /// Vector instance kinds.
   float32x4,
 
diff --git a/runtime/observatory/lib/src/service/object.dart b/runtime/observatory/lib/src/service/object.dart
index 5815ca6..b0c17f1 100644
--- a/runtime/observatory/lib/src/service/object.dart
+++ b/runtime/observatory/lib/src/service/object.dart
@@ -152,6 +152,7 @@
   bool get isInt => false;
   bool get isList => false;
   bool get isMap => false;
+  bool get isSet => false;
   bool get isTypedData => false;
   bool get isRegExp => false;
   bool get isMirrorReference => false;
@@ -2737,6 +2738,8 @@
       return M.InstanceKind.list;
     case 'Map':
       return M.InstanceKind.map;
+    case 'Set':
+      return M.InstanceKind.set;
     case 'Float32x4':
       return M.InstanceKind.float32x4;
     case 'Float64x2':
@@ -2890,6 +2893,7 @@
   bool get isInt => kind == M.InstanceKind.int;
   bool get isList => kind == M.InstanceKind.list;
   bool get isMap => kind == M.InstanceKind.map;
+  bool get isSet => kind == M.InstanceKind.set;
   bool get isTypedData => M.isTypedData(kind);
   bool get isSimdValue => M.isSimdValue(kind);
   bool get isRegExp => kind == M.InstanceKind.regExp;
diff --git a/runtime/observatory/tests/service/get_object_rpc_test.dart b/runtime/observatory/tests/service/get_object_rpc_test.dart
index 774c855..6749c5a 100644
--- a/runtime/observatory/tests/service/get_object_rpc_test.dart
+++ b/runtime/observatory/tests/service/get_object_rpc_test.dart
@@ -49,6 +49,9 @@
 getMap() => {"x": 3, "y": 4, "z": 5};
 
 @pragma("vm:entry-point")
+getSet() => {6, 7, 8};
+
+@pragma("vm:entry-point")
 getUint8List() => uint8List;
 
 @pragma("vm:entry-point")
@@ -462,6 +465,38 @@
     expect(result['associations'], isEmpty);
   },
 
+  // A built-in Set.
+  (Isolate isolate) async {
+    // Call eval to get a Dart set.
+    var evalResult = await invoke(isolate, 'getSet');
+    var params = {
+      'objectId': evalResult['id'],
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Instance'));
+    expect(result['kind'], equals('Set'));
+    expect(result['_vmType'], equals('LinkedHashSet'));
+    expect(result['id'], startsWith('objects/'));
+    expect(result['valueAsString'], isNull);
+    expect(result['class']['type'], equals('@Class'));
+    expect(result['class']['name'], equals('_InternalLinkedHashSet'));
+    expect(result['size'], isPositive);
+    expect(result['fields'], isEmpty);
+    expect(result['length'], equals(3));
+    expect(result['offset'], isNull);
+    expect(result['count'], isNull);
+    expect(result['elements'].length, equals(3));
+    expect(result['elements'][0]['type'], equals('@Instance'));
+    expect(result['elements'][0]['kind'], equals('Int'));
+    expect(result['elements'][0]['valueAsString'], equals('6'));
+    expect(result['elements'][1]['type'], equals('@Instance'));
+    expect(result['elements'][1]['kind'], equals('Int'));
+    expect(result['elements'][1]['valueAsString'], equals('7'));
+    expect(result['elements'][2]['type'], equals('@Instance'));
+    expect(result['elements'][2]['kind'], equals('Int'));
+    expect(result['elements'][2]['valueAsString'], equals('8'));
+  },
+
   // Uint8List.
   (Isolate isolate) async {
     // Call eval to get a Dart list.
diff --git a/runtime/observatory/tests/service/get_version_rpc_test.dart b/runtime/observatory/tests/service/get_version_rpc_test.dart
index 41fe377..351000d 100644
--- a/runtime/observatory/tests/service/get_version_rpc_test.dart
+++ b/runtime/observatory/tests/service/get_version_rpc_test.dart
@@ -12,7 +12,7 @@
     final result = await vm.invokeRpcNoUpgrade('getVersion', {});
     expect(result['type'], 'Version');
     expect(result['major'], 3);
-    expect(result['minor'], 61);
+    expect(result['minor'], 62);
     expect(result['_privateMajor'], 0);
     expect(result['_privateMinor'], 0);
   },
diff --git a/runtime/observatory_2/lib/src/elements/instance_ref.dart b/runtime/observatory_2/lib/src/elements/instance_ref.dart
index e98148e..d63a023 100644
--- a/runtime/observatory_2/lib/src/elements/instance_ref.dart
+++ b/runtime/observatory_2/lib/src/elements/instance_ref.dart
@@ -169,6 +169,7 @@
         ];
       case M.InstanceKind.list:
       case M.InstanceKind.map:
+      case M.InstanceKind.set:
       case M.InstanceKind.uint8ClampedList:
       case M.InstanceKind.uint8List:
       case M.InstanceKind.uint16List:
@@ -217,6 +218,7 @@
         return true;
       case M.InstanceKind.list:
       case M.InstanceKind.map:
+      case M.InstanceKind.set:
       case M.InstanceKind.uint8ClampedList:
       case M.InstanceKind.uint8List:
       case M.InstanceKind.uint16List:
@@ -295,6 +297,14 @@
               ])
             .toList()
           ..addAll(_createShowMoreButton());
+      case M.InstanceKind.set:
+        return _loadedInstance.elements
+            .map<Element>((element) => new DivElement()
+              ..children = <Element>[
+                anyRef(_isolate, element, _objects, queue: _r.queue)
+              ])
+            .toList()
+          ..addAll(_createShowMoreButton());
       case M.InstanceKind.uint8ClampedList:
       case M.InstanceKind.uint8List:
       case M.InstanceKind.uint16List:
diff --git a/runtime/observatory_2/lib/src/models/objects/instance.dart b/runtime/observatory_2/lib/src/models/objects/instance.dart
index ff914bc..3053bcb2 100644
--- a/runtime/observatory_2/lib/src/models/objects/instance.dart
+++ b/runtime/observatory_2/lib/src/models/objects/instance.dart
@@ -31,6 +31,10 @@
   /// Maps will be PlainInstance.
   map,
 
+  /// An instance of the built-in VM Set implementation. User-defined
+  /// Sets will be PlainInstance.
+  set,
+
   /// Vector instance kinds.
   float32x4,
 
diff --git a/runtime/observatory_2/lib/src/service/object.dart b/runtime/observatory_2/lib/src/service/object.dart
index bfe5b05..2cd8e66 100644
--- a/runtime/observatory_2/lib/src/service/object.dart
+++ b/runtime/observatory_2/lib/src/service/object.dart
@@ -152,6 +152,7 @@
   bool get isInt => false;
   bool get isList => false;
   bool get isMap => false;
+  bool get isSet => false;
   bool get isTypedData => false;
   bool get isRegExp => false;
   bool get isMirrorReference => false;
@@ -2746,6 +2747,8 @@
       return M.InstanceKind.list;
     case 'Map':
       return M.InstanceKind.map;
+    case 'Set':
+      return M.InstanceKind.set;
     case 'Float32x4':
       return M.InstanceKind.float32x4;
     case 'Float64x2':
@@ -2899,6 +2902,7 @@
   bool get isInt => kind == M.InstanceKind.int;
   bool get isList => kind == M.InstanceKind.list;
   bool get isMap => kind == M.InstanceKind.map;
+  bool get isSet => kind == M.InstanceKind.set;
   bool get isTypedData {
     return M.isTypedData(kind);
   }
diff --git a/runtime/observatory_2/tests/service_2/get_object_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_object_rpc_test.dart
index 20c65ff..d2094b5 100644
--- a/runtime/observatory_2/tests/service_2/get_object_rpc_test.dart
+++ b/runtime/observatory_2/tests/service_2/get_object_rpc_test.dart
@@ -47,6 +47,9 @@
 getMap() => {"x": 3, "y": 4, "z": 5};
 
 @pragma("vm:entry-point")
+getSet() => {6, 7, 8};
+
+@pragma("vm:entry-point")
 getUint8List() => uint8List;
 
 @pragma("vm:entry-point")
@@ -460,6 +463,38 @@
     expect(result['associations'], isEmpty);
   },
 
+  // A built-in Set.
+  (Isolate isolate) async {
+    // Call eval to get a Dart set.
+    var evalResult = await invoke(isolate, 'getSet');
+    var params = {
+      'objectId': evalResult['id'],
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Instance'));
+    expect(result['kind'], equals('Set'));
+    expect(result['_vmType'], equals('LinkedHashSet'));
+    expect(result['id'], startsWith('objects/'));
+    expect(result['valueAsString'], isNull);
+    expect(result['class']['type'], equals('@Class'));
+    expect(result['class']['name'], equals('_InternalLinkedHashSet'));
+    expect(result['size'], isPositive);
+    expect(result['fields'], isEmpty);
+    expect(result['length'], equals(3));
+    expect(result['offset'], isNull);
+    expect(result['count'], isNull);
+    expect(result['elements'].length, equals(3));
+    expect(result['elements'][0]['type'], equals('@Instance'));
+    expect(result['elements'][0]['kind'], equals('Int'));
+    expect(result['elements'][0]['valueAsString'], equals('6'));
+    expect(result['elements'][1]['type'], equals('@Instance'));
+    expect(result['elements'][1]['kind'], equals('Int'));
+    expect(result['elements'][1]['valueAsString'], equals('7'));
+    expect(result['elements'][2]['type'], equals('@Instance'));
+    expect(result['elements'][2]['kind'], equals('Int'));
+    expect(result['elements'][2]['valueAsString'], equals('8'));
+  },
+
   // Uint8List.
   (Isolate isolate) async {
     // Call eval to get a Dart list.
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 7704093..6f53fb7 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
@@ -12,7 +12,7 @@
     final result = await vm.invokeRpcNoUpgrade('getVersion', {});
     expect(result['type'], equals('Version'));
     expect(result['major'], equals(3));
-    expect(result['minor'], equals(61));
+    expect(result['minor'], equals(62));
     expect(result['_privateMajor'], equals(0));
     expect(result['_privateMinor'], equals(0));
   },
diff --git a/runtime/vm/object_service.cc b/runtime/vm/object_service.cc
index 79b826e..6f4a272 100644
--- a/runtime/vm/object_service.cc
+++ b/runtime/vm/object_service.cc
@@ -1455,7 +1455,7 @@
 void LinkedHashSet::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref);
-  jsobj.AddProperty("kind", "PlainInstance");
+  jsobj.AddProperty("kind", "Set");
   jsobj.AddProperty("length", Length());
   if (ref) {
     return;
diff --git a/runtime/vm/service.h b/runtime/vm/service.h
index 0ffb69e..b596e14 100644
--- a/runtime/vm/service.h
+++ b/runtime/vm/service.h
@@ -17,7 +17,7 @@
 namespace dart {
 
 #define SERVICE_PROTOCOL_MAJOR_VERSION 3
-#define SERVICE_PROTOCOL_MINOR_VERSION 61
+#define SERVICE_PROTOCOL_MINOR_VERSION 62
 
 class Array;
 class EmbedderServiceHandler;
diff --git a/runtime/vm/service/service.md b/runtime/vm/service/service.md
index 8525ff0..be92fa4 100644
--- a/runtime/vm/service/service.md
+++ b/runtime/vm/service/service.md
@@ -962,7 +962,7 @@
 collected, then an [Object](#object) will be returned.
 
 The _offset_ and _count_ parameters are used to request subranges of
-Instance objects with the kinds: String, List, Map, Uint8ClampedList,
+Instance objects with the kinds: String, List, Map, Set, Uint8ClampedList,
 Uint8List, Uint16List, Uint32List, Uint64List, Int8List, Int16List,
 Int32List, Int64List, Flooat32List, Float64List, Inst32x3List,
 Float32x4List, and Float64x2List.  These parameters are otherwise
@@ -2698,6 +2698,7 @@
   //   String
   //   List
   //   Map
+  //   Set
   //   Uint8ClampedList
   //   Uint8List
   //   Uint16List
@@ -2828,6 +2829,7 @@
   //   String
   //   List
   //   Map
+  //   Set
   //   Uint8ClampedList
   //   Uint8List
   //   Uint16List
@@ -2874,6 +2876,7 @@
   //   String
   //   List
   //   Map
+  //   Set
   //   Uint8ClampedList
   //   Uint8List
   //   Uint16List
@@ -2929,10 +2932,11 @@
   // The fields of this Instance.
   BoundField[] fields [optional];
 
-  // The elements of a List instance.
+  // The elements of a List or Set instance.
   //
   // Provided for instance kinds:
   //   List
+  //   Set
   (@Instance|Sentinel)[] elements [optional];
 
   // The elements of a Map instance.
@@ -3096,6 +3100,10 @@
   // Maps will be PlainInstance.
   Map,
 
+  // An instance of the built-in VM Set implementation. User-defined
+  // Sets will be PlainInstance.
+  Set,
+
   // Vector instance kinds.
   Float32x4,
   Float64x2,
@@ -4396,5 +4404,6 @@
 3.59 | Added `abstract` property to `@Function` and `Function`.
 3.60 | Added `gcType` property to `Event`.
 3.61 | Added `isolateGroupId` property to `@Isolate` and `Isolate`.
+3.62 | Added `Set` to `InstanceKind`.
 
 [discuss-list]: https://groups.google.com/a/dartlang.org/forum/#!forum/observatory-discuss