[ VM / Service ] Add abstract flag to Function

Bug: https://github.com/dart-lang/coverage/issues/398
Change-Id: I1f6e16483e28fb7a77aebae079fd62abe9d16099
TEST=Added to get_object_rpc_test.dart
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/249421
Reviewed-by: Ben Konyi <bkonyi@google.com>
Commit-Queue: Liam Appelbe <liama@google.com>
diff --git a/pkg/vm_service/CHANGELOG.md b/pkg/vm_service/CHANGELOG.md
index 5de2a68..3e16bee 100644
--- a/pkg/vm_service/CHANGELOG.md
+++ b/pkg/vm_service/CHANGELOG.md
@@ -1,5 +1,9 @@
 # Changelog
 
+## 9.1.0
+- Update to version `3.59` of the spec.
+- Add `abstract` flag to `FuncRef`.
+
 ## 9.0.0
 - Update to version `3.58` of the spec.
 - Added optional `local` parameter to `lookupResolvedPackageUris` RPC.
diff --git a/pkg/vm_service/java/version.properties b/pkg/vm_service/java/version.properties
index 8aee343..cf92a35 100644
--- a/pkg/vm_service/java/version.properties
+++ b/pkg/vm_service/java/version.properties
@@ -1 +1 @@
-version=3.58
+version=3.59
diff --git a/pkg/vm_service/lib/src/vm_service.dart b/pkg/vm_service/lib/src/vm_service.dart
index de2a2ea..5503f9e 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.58.0';
+const String vmServiceVersion = '3.59.0';
 
 /// @optional
 const String optional = 'optional';
@@ -4675,6 +4675,9 @@
   /// Is this function implicitly defined (e.g., implicit getter/setter)?
   bool? implicit;
 
+  /// Is this function an abstract method?
+  bool? isAbstract;
+
   /// The location of this function in the source code.
   ///
   /// Note: this may not agree with the location of `owner` if this is a
@@ -4689,6 +4692,7 @@
     required this.isStatic,
     required this.isConst,
     required this.implicit,
+    required this.isAbstract,
     required String id,
     this.location,
   }) : super(
@@ -4702,6 +4706,7 @@
     isStatic = json['static'] ?? false;
     isConst = json['const'] ?? false;
     implicit = json['implicit'] ?? false;
+    isAbstract = json['abstract'] ?? false;
     location = createServiceObject(json['location'], const ['SourceLocation'])
         as SourceLocation?;
   }
@@ -4719,6 +4724,7 @@
       'static': isStatic,
       'const': isConst,
       'implicit': implicit,
+      'abstract': isAbstract,
     });
     _setIfNotNull(json, 'location', location?.toJson());
     return json;
@@ -4730,7 +4736,7 @@
 
   String toString() => '[FuncRef ' //
       'id: ${id}, name: ${name}, owner: ${owner}, isStatic: ${isStatic}, ' //
-      'isConst: ${isConst}, implicit: ${implicit}]';
+      'isConst: ${isConst}, implicit: ${implicit}, isAbstract: ${isAbstract}]';
 }
 
 /// A `Func` represents a Dart language function.
@@ -4759,6 +4765,9 @@
   /// Is this function implicitly defined (e.g., implicit getter/setter)?
   bool? implicit;
 
+  /// Is this function an abstract method?
+  bool? isAbstract;
+
   /// The location of this function in the source code.
   ///
   /// Note: this may not agree with the location of `owner` if this is a
@@ -4780,6 +4789,7 @@
     required this.isStatic,
     required this.isConst,
     required this.implicit,
+    required this.isAbstract,
     required this.signature,
     required String id,
     this.location,
@@ -4795,6 +4805,7 @@
     isStatic = json['static'] ?? false;
     isConst = json['const'] ?? false;
     implicit = json['implicit'] ?? false;
+    isAbstract = json['abstract'] ?? false;
     location = createServiceObject(json['location'], const ['SourceLocation'])
         as SourceLocation?;
     signature = createServiceObject(json['signature'], const ['InstanceRef'])
@@ -4815,6 +4826,7 @@
       'static': isStatic,
       'const': isConst,
       'implicit': implicit,
+      'abstract': isAbstract,
       'signature': signature?.toJson(),
     });
     _setIfNotNull(json, 'location', location?.toJson());
@@ -4828,7 +4840,7 @@
 
   String toString() => '[Func ' //
       'id: ${id}, name: ${name}, owner: ${owner}, isStatic: ${isStatic}, ' //
-      'isConst: ${isConst}, implicit: ${implicit}, signature: ${signature}]';
+      'isConst: ${isConst}, implicit: ${implicit}, isAbstract: ${isAbstract}, signature: ${signature}]';
 }
 
 /// `InstanceRef` is a reference to an `Instance`.
@@ -6322,7 +6334,9 @@
     return json;
   }
 
-  String toString() => '[LogRecord]';
+  String toString() => '[LogRecord ' //
+      'message: ${message}, time: ${time}, level: ${level}, sequenceNumber: ${sequenceNumber}, ' //
+      'loggerName: ${loggerName}, zone: ${zone}, error: ${error}, stackTrace: ${stackTrace}]';
 }
 
 class MapAssociation {
diff --git a/pkg/vm_service/pubspec.yaml b/pkg/vm_service/pubspec.yaml
index 68572038..0f11496 100644
--- a/pkg/vm_service/pubspec.yaml
+++ b/pkg/vm_service/pubspec.yaml
@@ -1,5 +1,5 @@
 name: vm_service
-version: 9.0.0
+version: 9.1.0
 description: >-
   A library to communicate with a service implementing the Dart VM
   service protocol.
diff --git a/pkg/vm_service/tool/dart/generate_dart.dart b/pkg/vm_service/tool/dart/generate_dart.dart
index e15328c..cea96cd 100644
--- a/pkg/vm_service/tool/dart/generate_dart.dart
+++ b/pkg/vm_service/tool/dart/generate_dart.dart
@@ -1611,7 +1611,7 @@
     // toString()
     Iterable<TypeField> toStringFields =
         getAllFields().where((f) => !f.optional);
-    if (toStringFields.length <= 7) {
+    if (toStringFields.length <= 8) {
       String properties = toStringFields
           .map(
               (TypeField f) => "${f.generatableName}: \${${f.generatableName}}")
diff --git a/runtime/observatory/tests/service/get_object_rpc_test.dart b/runtime/observatory/tests/service/get_object_rpc_test.dart
index a1f0582..774c855 100644
--- a/runtime/observatory/tests/service/get_object_rpc_test.dart
+++ b/runtime/observatory/tests/service/get_object_rpc_test.dart
@@ -11,12 +11,17 @@
 import 'service_test_common.dart';
 import 'test_helper.dart';
 
-class _DummyClass {
+abstract class _DummyAbstractBaseClass {
+  void dummyFunction(int a, [bool b = false]);
+}
+
+class _DummyClass extends _DummyAbstractBaseClass {
   static var dummyVar = 11;
   final List<String> dummyList = new List<String>.filled(20, '');
   static var dummyVarWithInit = foo();
   late String dummyLateVarWithInit = 'bar';
   late String dummyLateVar;
+  @override
   void dummyFunction(int a, [bool b = false]) {}
   void dummyGenericFunction<K, V>(K a, {required V param}) {}
   static List foo() => List<String>.filled(20, '');
@@ -889,6 +894,7 @@
     expect(result['static'], equals(false));
     expect(result['const'], equals(false));
     expect(result['implicit'], equals(false));
+    expect(result['abstract'], equals(false));
     expect(result['signature']['typeParameters'], isNull);
     expect(result['signature']['returnType'], isNotNull);
     expect(result['signature']['parameters'].length, 3);
@@ -923,6 +929,7 @@
     expect(result['static'], equals(false));
     expect(result['const'], equals(false));
     expect(result['implicit'], equals(false));
+    expect(result['abstract'], equals(false));
     expect(result['signature']['typeParameters'].length, 2);
     expect(result['signature']['returnType'], isNotNull);
     expect(result['signature']['parameters'].length, 3);
@@ -943,6 +950,60 @@
     expect(result['_deoptimizations'], isZero);
   },
 
+  // abstract function.
+  (Isolate isolate) async {
+    // Call eval to get a class id.
+    var evalResult = await invoke(isolate, 'getDummyClass');
+    var result = await isolate.invokeRpcNoUpgrade('getObject', {
+      'objectId': evalResult['class']['id'],
+    });
+    expect(result['type'], equals('Class'));
+    expect(result['id'], startsWith('classes/'));
+    expect(result['name'], equals('_DummyClass'));
+    expect(result['abstract'], equals(false));
+
+    // Get the super class.
+    var superClass = await isolate.invokeRpcNoUpgrade('getObject', {
+      'objectId': result['super']['id'],
+    });
+    expect(superClass['type'], equals('Class'));
+    expect(superClass['id'], startsWith('classes/'));
+    expect(superClass['name'], equals('_DummyAbstractBaseClass'));
+    expect(superClass['abstract'], equals(true));
+
+    // Find the abstract dummyFunction on the super class.
+    var funcId = superClass['functions']
+        .firstWhere((f) => f['name'] == 'dummyFunction')['id'];
+    var funcResult = await isolate.invokeRpcNoUpgrade('getObject', {
+      'objectId': funcId,
+    });
+
+    expect(funcResult['type'], equals('Function'));
+    expect(funcResult['id'], equals(funcId));
+    expect(funcResult['name'], equals('dummyFunction'));
+    expect(funcResult['_kind'], equals('RegularFunction'));
+    expect(funcResult['static'], equals(false));
+    expect(funcResult['const'], equals(false));
+    expect(funcResult['implicit'], equals(false));
+    expect(funcResult['abstract'], equals(true));
+    expect(funcResult['signature']['typeParameters'], isNull);
+    expect(funcResult['signature']['returnType'], isNotNull);
+    expect(funcResult['signature']['parameters'].length, 3);
+    expect(funcResult['signature']['parameters'][1]['parameterType']['name'],
+        equals('int'));
+    expect(funcResult['signature']['parameters'][1]['fixed'], isTrue);
+    expect(funcResult['signature']['parameters'][2]['parameterType']['name'],
+        equals('bool'));
+    expect(funcResult['signature']['parameters'][2]['fixed'], isFalse);
+    expect(funcResult['location']['type'], equals('SourceLocation'));
+    expect(funcResult['code']['type'], equals('@Code'));
+    expect(funcResult['_optimizable'], equals(true));
+    expect(funcResult['_inlinable'], equals(true));
+    expect(funcResult['_usageCounter'], isZero);
+    expect(funcResult['_optimizedCallSiteCount'], isZero);
+    expect(funcResult['_deoptimizations'], isZero);
+  },
+
   // invalid function.
   (Isolate isolate) async {
     // Call eval to get a class id.
@@ -1002,6 +1063,7 @@
     expect(result['static'], equals(true));
     expect(result['const'], equals(false));
     expect(result['implicit'], equals(false));
+    expect(result['abstract'], equals(false));
     expect(result['signature']['typeParameters'], isNull);
     expect(result['signature']['returnType'], isNotNull);
     expect(result['signature']['parameters'].length, 0);
@@ -1030,6 +1092,7 @@
     expect(result['static'], equals(false));
     expect(result['const'], equals(false));
     expect(result['implicit'], equals(false));
+    expect(result['abstract'], equals(false));
     expect(result['signature']['typeParameters'], isNull);
     expect(result['signature']['returnType'], isNotNull);
     expect(result['signature']['parameters'].length, 1);
diff --git a/runtime/observatory/tests/service/get_version_rpc_test.dart b/runtime/observatory/tests/service/get_version_rpc_test.dart
index 2e89731..bf51c64 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'], 57);
+    expect(result['minor'], 59);
     expect(result['_privateMajor'], 0);
     expect(result['_privateMinor'], 0);
   },
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 9c45a9e..20c65ff 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
@@ -11,10 +11,15 @@
 import 'service_test_common.dart';
 import 'test_helper.dart';
 
-class _DummyClass {
+abstract class _DummyAbstractBaseClass {
+  void dummyFunction(int a, [bool b = false]);
+}
+
+class _DummyClass extends _DummyAbstractBaseClass {
   static var dummyVar = 11;
   final List<String> dummyList = new List<String>.filled(20, null);
   static var dummyVarWithInit = foo();
+  @override
   void dummyFunction(int a, [bool b = false]) {}
   void dummyGenericFunction<K, V>(K a, {V param}) {}
   static List foo() => List<String>.filled(20, '');
@@ -886,6 +891,7 @@
     expect(result['static'], equals(false));
     expect(result['const'], equals(false));
     expect(result['implicit'], equals(false));
+    expect(result['abstract'], equals(false));
     expect(result['signature']['typeParameters'], isNull);
     expect(result['signature']['returnType'], isNotNull);
     expect(result['signature']['parameters'].length, 3);
@@ -920,6 +926,7 @@
     expect(result['static'], equals(false));
     expect(result['const'], equals(false));
     expect(result['implicit'], equals(false));
+    expect(result['abstract'], equals(false));
     expect(result['signature']['typeParameters'].length, 2);
     expect(result['signature']['returnType'], isNotNull);
     expect(result['signature']['parameters'].length, 3);
@@ -940,6 +947,60 @@
     expect(result['_deoptimizations'], isZero);
   },
 
+  // abstract function.
+  (Isolate isolate) async {
+    // Call eval to get a class id.
+    var evalResult = await invoke(isolate, 'getDummyClass');
+    var result = await isolate.invokeRpcNoUpgrade('getObject', {
+      'objectId': evalResult['class']['id'],
+    });
+    expect(result['type'], equals('Class'));
+    expect(result['id'], startsWith('classes/'));
+    expect(result['name'], equals('_DummyClass'));
+    expect(result['abstract'], equals(false));
+
+    // Get the super class.
+    var superClass = await isolate.invokeRpcNoUpgrade('getObject', {
+      'objectId': result['super']['id'],
+    });
+    expect(superClass['type'], equals('Class'));
+    expect(superClass['id'], startsWith('classes/'));
+    expect(superClass['name'], equals('_DummyAbstractBaseClass'));
+    expect(superClass['abstract'], equals(true));
+
+    // Find the abstract dummyFunction on the super class.
+    var funcId = superClass['functions']
+        .firstWhere((f) => f['name'] == 'dummyFunction')['id'];
+    var funcResult = await isolate.invokeRpcNoUpgrade('getObject', {
+      'objectId': funcId,
+    });
+
+    expect(funcResult['type'], equals('Function'));
+    expect(funcResult['id'], equals(funcId));
+    expect(funcResult['name'], equals('dummyFunction'));
+    expect(funcResult['_kind'], equals('RegularFunction'));
+    expect(funcResult['static'], equals(false));
+    expect(funcResult['const'], equals(false));
+    expect(funcResult['implicit'], equals(false));
+    expect(funcResult['abstract'], equals(true));
+    expect(funcResult['signature']['typeParameters'], isNull);
+    expect(funcResult['signature']['returnType'], isNotNull);
+    expect(funcResult['signature']['parameters'].length, 3);
+    expect(funcResult['signature']['parameters'][1]['parameterType']['name'],
+        equals('int'));
+    expect(funcResult['signature']['parameters'][1]['fixed'], isTrue);
+    expect(funcResult['signature']['parameters'][2]['parameterType']['name'],
+        equals('bool'));
+    expect(funcResult['signature']['parameters'][2]['fixed'], isFalse);
+    expect(funcResult['location']['type'], equals('SourceLocation'));
+    expect(funcResult['code']['type'], equals('@Code'));
+    expect(funcResult['_optimizable'], equals(true));
+    expect(funcResult['_inlinable'], equals(true));
+    expect(funcResult['_usageCounter'], isZero);
+    expect(funcResult['_optimizedCallSiteCount'], isZero);
+    expect(funcResult['_deoptimizations'], isZero);
+  },
+
   // invalid function.
   (Isolate isolate) async {
     // Call eval to get a class id.
@@ -1032,6 +1093,7 @@
     expect(result['static'], equals(true));
     expect(result['const'], equals(false));
     expect(result['implicit'], equals(false));
+    expect(result['abstract'], equals(false));
     expect(result['signature']['typeParameters'], isNull);
     expect(result['signature']['returnType'], isNotNull);
     expect(result['signature']['parameters'].length, 0);
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 451c93e..4bb45af 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(57));
+    expect(result['minor'], equals(59));
     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 fd957bc..3ecf23c 100644
--- a/runtime/vm/object_service.cc
+++ b/runtime/vm/object_service.cc
@@ -341,6 +341,7 @@
   jsobj.AddProperty("static", is_static());
   jsobj.AddProperty("const", is_const());
   jsobj.AddProperty("implicit", IsImplicitGetterOrSetter());
+  jsobj.AddProperty("abstract", is_abstract());
   jsobj.AddProperty("_intrinsic", is_intrinsic());
   jsobj.AddProperty("_native", is_native());
 
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index b168abc..2df9119 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -5890,7 +5890,7 @@
         "\"library\":{\"type\":\"@Library\",\"fixedId\":true,\"id\":\"\","
         "\"name\":\"dart.core\",\"uri\":\"dart:core\"}},"
         "\"_kind\":\"RegularFunction\",\"static\":false,\"const\":false,"
-        "\"implicit\":false,"
+        "\"implicit\":false,\"abstract\":false,"
         "\"_intrinsic\":false,\"_native\":false,"
         "\"location\":{\"type\":\"SourceLocation\","
         "\"script\":{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
diff --git a/runtime/vm/service.h b/runtime/vm/service.h
index b8e3124..38a0bf6 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 57
+#define SERVICE_PROTOCOL_MINOR_VERSION 59
 
 class Array;
 class EmbedderServiceHandler;
diff --git a/runtime/vm/service/service.md b/runtime/vm/service/service.md
index 8c07540..69c4c63 100644
--- a/runtime/vm/service/service.md
+++ b/runtime/vm/service/service.md
@@ -1,4 +1,4 @@
-# Dart VM Service Protocol 3.58
+# Dart VM Service Protocol 3.59
 
 > Please post feedback to the [observatory-discuss group][discuss-list]
 
@@ -2596,6 +2596,9 @@
   // Is this function implicitly defined (e.g., implicit getter/setter)?
   bool implicit;
 
+  // Is this function an abstract method?
+  bool abstract;
+
   // The location of this function in the source code.
   //
   // Note: this may not agree with the location of `owner` if this is a function
@@ -2628,6 +2631,9 @@
   // Is this function implicitly defined (e.g., implicit getter/setter)?
   bool implicit;
 
+  // Is this function an abstract method?
+  bool abstract;
+
   // The location of this function in the source code.
   //
   // Note: this may not agree with the location of `owner` if this is a function
@@ -4372,5 +4378,6 @@
 3.56 | Added optional `line` and `column` properties to `SourceLocation`. Added a new `SourceReportKind`, `BranchCoverage`, which reports branch level coverage information.
 3.57 | Added optional `libraryFilters` parameter to `getSourceReport` RPC.
 3.58 | Added optional `local` parameter to `lookupResolvedPackageUris` RPC.
+3.59 | Added `abstract` property to `@Function` and `Function`.
 
 [discuss-list]: https://groups.google.com/a/dartlang.org/forum/#!forum/observatory-discuss
diff --git a/runtime/vm/source_report_test.cc b/runtime/vm/source_report_test.cc
index 34a00bf..22ef9de 100644
--- a/runtime/vm/source_report_test.cc
+++ b/runtime/vm/source_report_test.cc
@@ -603,7 +603,7 @@
       "\"name\":\"helper0\",\"owner\":{\"type\":\"@Library\",\"fixedId\":true,"
       "\"id\":\"\",\"name\":\"\",\"uri\":\"file:\\/\\/\\/test-lib\"},"
       "\"_kind\":\"RegularFunction\",\"static\":true,\"const\":false,"
-      "\"implicit\":false,"
+      "\"implicit\":false,\"abstract\":false,"
       "\"_intrinsic\":false,\"_native\":false,\"location\":{\"type\":"
       "\"SourceLocation\",\"script\":{\"type\":\"@Script\",\"fixedId\":true,"
       "\"id\":\"\",\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"},"
@@ -687,8 +687,8 @@
       "\"library\":{\"type\":\"@Library\",\"fixedId\":true,"
       "\"id\":\"\",\"name\":\"\",\"uri\":\"file:\\/\\/\\/test-lib\"}"
       "},\"_kind\":\"RegularFunction\","
-      "\"static\":false,\"const\":false,\"implicit\":false,\"_intrinsic\":"
-      "false,"
+      "\"static\":false,\"const\":false,\"implicit\":false,\"abstract\":"
+      "false,\"_intrinsic\":false,"
       "\"_native\":false,"
       "\"location\":{\"type\":\"SourceLocation\","
       "\"script\":{\"type\":\"@Script\",\"fixedId\":true,"
@@ -723,8 +723,8 @@
       "\"library\":{\"type\":\"@Library\",\"fixedId\":true,"
       "\"id\":\"\",\"name\":\"\",\"uri\":\"file:\\/\\/\\/test-lib\"}"
       "},\"_kind\":\"RegularFunction\","
-      "\"static\":false,\"const\":false,\"implicit\":false,\"_intrinsic\":"
-      "false,"
+      "\"static\":false,\"const\":false,\"implicit\":false,\"abstract\":"
+      "false,\"_intrinsic\":false,"
       "\"_native\":false,"
       "\"location\":{\"type\":\"SourceLocation\","
       "\"script\":{\"type\":\"@Script\",\"fixedId\":true,"
@@ -782,7 +782,7 @@
       "\"name\":\"helper0\",\"owner\":{\"type\":\"@Library\",\"fixedId\":true,"
       "\"id\":\"\",\"name\":\"\",\"uri\":\"file:\\/\\/\\/test-lib\"},\"_"
       "kind\":\"RegularFunction\",\"static\":true,\"const\":false,\"implicit\":"
-      "false,\"_"
+      "false,\"abstract\":false,\"_"
       "intrinsic\":false,\"_native\":false,\"location\":{\"type\":"
       "\"SourceLocation\",\"script\":{\"type\":\"@Script\",\"fixedId\":true,"
       "\"id\":\"\",\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"},"