[VM/Service] Add private _getImplementationFields procedure

TEST=CI

Change-Id: I4e4871ac4df74cb4daca7ef42a66489b2afbdc64
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/270260
Reviewed-by: Ben Konyi <bkonyi@google.com>
Commit-Queue: Derek Xu <derekx@google.com>
diff --git a/pkg/vm_service/test/get_implementation_fields_rpc_test.dart b/pkg/vm_service/test/get_implementation_fields_rpc_test.dart
new file mode 100644
index 0000000..e7b719f
--- /dev/null
+++ b/pkg/vm_service/test/get_implementation_fields_rpc_test.dart
@@ -0,0 +1,28 @@
+// Copyright (c) 2023, 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:test/test.dart';
+import 'package:vm_service/vm_service.dart';
+
+import 'common/test_helper.dart';
+
+Future<Response> getImplementationFields(
+    VmService service, String isolateId, String objectId) async {
+  return await service.callMethod("_getImplementationFields",
+      isolateId: isolateId, args: {"objectId": objectId});
+}
+
+var tests = <IsolateTest>[
+  // A null object.
+  (VmService service, IsolateRef isolateRef) async {
+    final isolateId = isolateRef.id!;
+    final objectId = 'objects/null';
+    final result = await getImplementationFields(service, isolateId, objectId);
+    expect(result.json!["type"]!, "ImplementationFields");
+    expect(result.json!["fields"]!, isEmpty);
+  },
+];
+
+main([args = const <String>[]]) async =>
+    runIsolateTests(args, tests, 'get_implementation_fields_rpc_test.dart');
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index e9b7956..6138ecb 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -217,7 +217,13 @@
   /* with an object id is printed. If ref is false the object is fully   */    \
   /* printed.                                                            */    \
   virtual void PrintJSONImpl(JSONStream* stream, bool ref) const;              \
-  virtual const char* JSONType() const { return "" #object; }
+  /* Prints JSON objects that describe the implementation-level fields of */   \
+  /* the current Object to |jsarr_fields|.                                */   \
+  virtual void PrintImplementationFieldsImpl(const JSONArray& jsarr_fields)    \
+      const;                                                                   \
+  virtual const char* JSONType() const {                                       \
+    return "" #object;                                                         \
+  }
 #else
 #define OBJECT_SERVICE_SUPPORT(object) protected: /* NOLINT */
 #endif                                            // !PRODUCT
@@ -350,6 +356,9 @@
 #ifndef PRODUCT
   void PrintJSON(JSONStream* stream, bool ref = true) const;
   virtual void PrintJSONImpl(JSONStream* stream, bool ref) const;
+  void PrintImplementationFields(JSONStream* stream) const;
+  virtual void PrintImplementationFieldsImpl(
+      const JSONArray& jsarr_fields) const;
   virtual const char* JSONType() const { return IsNull() ? "null" : "Object"; }
 #endif
 
diff --git a/runtime/vm/object_service.cc b/runtime/vm/object_service.cc
index 65fc7a0..14c7556 100644
--- a/runtime/vm/object_service.cc
+++ b/runtime/vm/object_service.cc
@@ -74,6 +74,20 @@
   }
 }
 
+void Object::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {
+  UNREACHABLE();
+}
+
+void Object::PrintImplementationFields(JSONStream* stream) const {
+  JSONObject jsobj(stream);
+  jsobj.AddProperty("type", "ImplementationFields");
+  JSONArray jsarr_fields(&jsobj, "fields");
+  if (!IsNull()) {
+    PrintImplementationFieldsImpl(jsarr_fields);
+  }
+}
+
 void Class::PrintJSONImpl(JSONStream* stream, bool ref) const {
   Isolate* isolate = Isolate::Current();
   JSONObject jsobj(stream);
@@ -180,6 +194,9 @@
   }
 }
 
+void Class::PrintImplementationFieldsImpl(const JSONArray& jsarr_fields) const {
+}
+
 void TypeParameters::PrintJSONImpl(JSONStream* stream, bool ref) const {
   // Consider making this type public if we decide to expose TypeParameters
   // through the protocol.
@@ -191,6 +208,9 @@
   jsobj.AddProperty("defaults", TypeArguments::Handle(defaults()));
 }
 
+void TypeParameters::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void TypeArguments::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   // The index in the canonical_type_arguments table cannot be used as part of
@@ -240,10 +260,16 @@
   }
 }
 
+void TypeArguments::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void PatchClass::PrintJSONImpl(JSONStream* stream, bool ref) const {
   Object::PrintJSONImpl(stream, ref);
 }
 
+void PatchClass::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void Function::AddFunctionServiceId(const JSONObject& jsobj) const {
   Class& cls = Class::Handle(Owner());
   // Special kinds of functions use indices in their respective lists.
@@ -388,10 +414,16 @@
   }
 }
 
+void Function::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void FfiTrampolineData::PrintJSONImpl(JSONStream* stream, bool ref) const {
   Object::PrintJSONImpl(stream, ref);
 }
 
+void FfiTrampolineData::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void Field::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   Class& cls = Class::Handle(Owner());
@@ -457,6 +489,9 @@
   }
 }
 
+void Field::PrintImplementationFieldsImpl(const JSONArray& jsarr_fields) const {
+}
+
 // See also Dart_ScriptGetTokenInfo.
 void Script::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
@@ -518,6 +553,9 @@
   }
 }
 
+void Script::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 static void PrintShowHideNamesToJSON(JSONObject* jsobj, const Namespace& ns) {
   Array& arr = Array::Handle();
   String& name = String::Handle();
@@ -666,18 +704,30 @@
   }
 }
 
+void Library::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void LibraryPrefix::PrintJSONImpl(JSONStream* stream, bool ref) const {
   Object::PrintJSONImpl(stream, ref);
 }
 
+void LibraryPrefix::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void Namespace::PrintJSONImpl(JSONStream* stream, bool ref) const {
   Object::PrintJSONImpl(stream, ref);
 }
 
+void Namespace::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void KernelProgramInfo::PrintJSONImpl(JSONStream* stream, bool ref) const {
   Object::PrintJSONImpl(stream, ref);
 }
 
+void KernelProgramInfo::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void Instructions::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   AddCommonObjectProperties(&jsobj, "Object", ref);
@@ -687,14 +737,23 @@
   }
 }
 
+void Instructions::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void InstructionsSection::PrintJSONImpl(JSONStream* stream, bool ref) const {
   Object::PrintJSONImpl(stream, ref);
 }
 
+void InstructionsSection::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void InstructionsTable::PrintJSONImpl(JSONStream* stream, bool ref) const {
   Object::PrintJSONImpl(stream, ref);
 }
 
+void InstructionsTable::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void WeakSerializationReference::PrintJSONImpl(JSONStream* stream,
                                                bool ref) const {
   JSONObject jsobj(stream);
@@ -705,6 +764,9 @@
   jsobj.AddProperty("target", obj);
 }
 
+void WeakSerializationReference::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void WeakArray::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   AddCommonObjectProperties(&jsobj, "Object", ref);
@@ -734,6 +796,9 @@
   }
 }
 
+void WeakArray::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void ObjectPool::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   AddCommonObjectProperties(&jsobj, "Object", ref);
@@ -773,6 +838,9 @@
   }
 }
 
+void ObjectPool::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void PcDescriptors::PrintToJSONObject(JSONObject* jsobj, bool ref) const {
   AddCommonObjectProperties(jsobj, "Object", ref);
   // TODO(johnmccutchan): Generate a stable id. PcDescriptors hang off a Code
@@ -799,14 +867,23 @@
   PrintToJSONObject(&jsobj, ref);
 }
 
+void PcDescriptors::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void CodeSourceMap::PrintJSONImpl(JSONStream* stream, bool ref) const {
   Object::PrintJSONImpl(stream, ref);
 }
 
+void CodeSourceMap::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void CompressedStackMaps::PrintJSONImpl(JSONStream* stream, bool ref) const {
   Object::PrintJSONImpl(stream, ref);
 }
 
+void CompressedStackMaps::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void LocalVarDescriptors::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   AddCommonObjectProperties(&jsobj, "Object", ref);
@@ -833,10 +910,16 @@
   }
 }
 
+void LocalVarDescriptors::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void ExceptionHandlers::PrintJSONImpl(JSONStream* stream, bool ref) const {
   Object::PrintJSONImpl(stream, ref);
 }
 
+void ExceptionHandlers::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void SingleTargetCache::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   AddCommonObjectProperties(&jsobj, "Object", ref);
@@ -849,6 +932,9 @@
   jsobj.AddProperty("_upperLimit", upper_limit());
 }
 
+void SingleTargetCache::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void UnlinkedCall::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   AddCommonObjectProperties(&jsobj, "Object", ref);
@@ -861,6 +947,9 @@
                     Array::Handle(arguments_descriptor()));
 }
 
+void UnlinkedCall::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void MonomorphicSmiableCall::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   AddCommonObjectProperties(&jsobj, "Object", ref);
@@ -871,10 +960,18 @@
   }
 }
 
+void MonomorphicSmiableCall::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void CallSiteData::PrintJSONImpl(JSONStream* stream, bool ref) const {
   UNREACHABLE();
 }
 
+void CallSiteData::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {
+  UNREACHABLE();
+}
+
 void ICData::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   AddCommonObjectProperties(&jsobj, "Object", ref);
@@ -916,6 +1013,9 @@
   }
 }
 
+void ICData::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void Code::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   AddCommonObjectProperties(&jsobj, "Code", ref);
@@ -978,6 +1078,8 @@
   PrintJSONInlineIntervals(&jsobj);
 }
 
+void Code::PrintImplementationFieldsImpl(const JSONArray& jsarr_fields) const {}
+
 void Context::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   // TODO(turnidge): Should the user level type for Context be Context
@@ -1005,10 +1107,16 @@
   }
 }
 
+void Context::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void ContextScope::PrintJSONImpl(JSONStream* stream, bool ref) const {
   Object::PrintJSONImpl(stream, ref);
 }
 
+void ContextScope::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void Sentinel::PrintJSONImpl(JSONStream* stream, bool ref) const {
   // Handle certain special sentinel values.
   if (ptr() == Object::sentinel().ptr()) {
@@ -1034,6 +1142,9 @@
   Object::PrintJSONImpl(stream, ref);
 }
 
+void Sentinel::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void MegamorphicCache::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   AddCommonObjectProperties(&jsobj, "Object", ref);
@@ -1048,6 +1159,9 @@
                     Object::Handle(arguments_descriptor()));
 }
 
+void MegamorphicCache::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void SubtypeTestCache::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   AddCommonObjectProperties(&jsobj, "Object", ref);
@@ -1058,6 +1172,9 @@
   jsobj.AddProperty("_cache", Array::Handle(cache()));
 }
 
+void SubtypeTestCache::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void LoadingUnit::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   AddCommonObjectProperties(&jsobj, "Object", ref);
@@ -1072,10 +1189,17 @@
   jsobj.AddProperty("_loadOutstanding", load_outstanding());
 }
 
+void LoadingUnit::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void Error::PrintJSONImpl(JSONStream* stream, bool ref) const {
   UNREACHABLE();
 }
 
+void Error::PrintImplementationFieldsImpl(const JSONArray& jsarr_fields) const {
+  UNREACHABLE();
+}
+
 void ApiError::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   AddCommonObjectProperties(&jsobj, "Error", ref);
@@ -1084,6 +1208,9 @@
   jsobj.AddProperty("message", ToErrorCString());
 }
 
+void ApiError::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void LanguageError::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   AddCommonObjectProperties(&jsobj, "Error", ref);
@@ -1092,6 +1219,9 @@
   jsobj.AddProperty("message", ToErrorCString());
 }
 
+void LanguageError::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void UnhandledException::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   AddCommonObjectProperties(&jsobj, "Error", ref);
@@ -1108,6 +1238,9 @@
   jsobj.AddProperty("stacktrace", instance);
 }
 
+void UnhandledException::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void UnwindError::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   AddCommonObjectProperties(&jsobj, "Error", ref);
@@ -1117,6 +1250,9 @@
   jsobj.AddProperty("_is_user_initiated", is_user_initiated());
 }
 
+void UnwindError::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void Instance::PrintSharedInstanceJSON(JSONObject* jsobj,
                                        bool ref,
                                        bool include_id) const {
@@ -1185,10 +1321,18 @@
   jsobj.AddProperty("kind", "PlainInstance");
 }
 
+void Instance::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void AbstractType::PrintJSONImpl(JSONStream* stream, bool ref) const {
   UNREACHABLE();
 }
 
+void AbstractType::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {
+  UNREACHABLE();
+}
+
 void Type::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref, /*include_id=*/false);
@@ -1213,6 +1357,8 @@
   }
 }
 
+void Type::PrintImplementationFieldsImpl(const JSONArray& jsarr_fields) const {}
+
 void FunctionType::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref);
@@ -1251,6 +1397,9 @@
   }
 }
 
+void FunctionType::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void RecordType::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref);
@@ -1283,6 +1432,9 @@
   }
 }
 
+void RecordType::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void TypeRef::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref);
@@ -1296,6 +1448,9 @@
   jsobj.AddProperty("targetType", AbstractType::Handle(type()));
 }
 
+void TypeRef::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void TypeParameter::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref);
@@ -1314,10 +1469,18 @@
   jsobj.AddProperty("bound", upper_bound);
 }
 
+void TypeParameter::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void Number::PrintJSONImpl(JSONStream* stream, bool ref) const {
   UNREACHABLE();
 }
 
+void Number::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {
+  UNREACHABLE();
+}
+
 void Integer::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref);
@@ -1325,6 +1488,9 @@
   jsobj.AddProperty("valueAsString", ToCString());
 }
 
+void Integer::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void Smi::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref, /*include_id=*/false);
@@ -1333,10 +1499,14 @@
   jsobj.AddPropertyF("valueAsString", "%" Pd "", Value());
 }
 
+void Smi::PrintImplementationFieldsImpl(const JSONArray& jsarr_fields) const {}
+
 void Mint::PrintJSONImpl(JSONStream* stream, bool ref) const {
   Integer::PrintJSONImpl(stream, ref);
 }
 
+void Mint::PrintImplementationFieldsImpl(const JSONArray& jsarr_fields) const {}
+
 void Double::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref);
@@ -1344,6 +1514,9 @@
   jsobj.AddProperty("valueAsString", ToCString());
 }
 
+void Double::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void String::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref);
@@ -1371,6 +1544,9 @@
   jsobj.AddPropertyStr("valueAsString", *this, offset, count);
 }
 
+void String::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void Bool::PrintJSONImpl(JSONStream* stream, bool ref) const {
   const char* str = ToCString();
   JSONObject jsobj(stream);
@@ -1380,6 +1556,8 @@
   jsobj.AddPropertyF("valueAsString", "%s", str);
 }
 
+void Bool::PrintImplementationFieldsImpl(const JSONArray& jsarr_fields) const {}
+
 void Array::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref);
@@ -1409,6 +1587,9 @@
   }
 }
 
+void Array::PrintImplementationFieldsImpl(const JSONArray& jsarr_fields) const {
+}
+
 void GrowableObjectArray::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref);
@@ -1438,6 +1619,9 @@
   }
 }
 
+void GrowableObjectArray::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void Map::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref);
@@ -1475,6 +1659,8 @@
   }
 }
 
+void Map::PrintImplementationFieldsImpl(const JSONArray& jsarr_fields) const {}
+
 void Set::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref);
@@ -1509,6 +1695,8 @@
   }
 }
 
+void Set::PrintImplementationFieldsImpl(const JSONArray& jsarr_fields) const {}
+
 void Float32x4::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref);
@@ -1516,6 +1704,9 @@
   jsobj.AddProperty("valueAsString", ToCString());
 }
 
+void Float32x4::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void Int32x4::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref);
@@ -1523,6 +1714,9 @@
   jsobj.AddProperty("valueAsString", ToCString());
 }
 
+void Int32x4::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void Float64x2::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref);
@@ -1530,10 +1724,18 @@
   jsobj.AddProperty("valueAsString", ToCString());
 }
 
+void Float64x2::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void TypedDataBase::PrintJSONImpl(JSONStream* stream, bool ref) const {
   UNREACHABLE();
 }
 
+void TypedDataBase::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {
+  UNREACHABLE();
+}
+
 void TypedData::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref);
@@ -1564,10 +1766,16 @@
   }
 }
 
+void TypedData::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void TypedDataView::PrintJSONImpl(JSONStream* stream, bool ref) const {
   Instance::PrintJSONImpl(stream, ref);
 }
 
+void TypedDataView::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void ExternalTypedData::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref);
@@ -1598,18 +1806,30 @@
   }
 }
 
+void ExternalTypedData::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void Pointer::PrintJSONImpl(JSONStream* stream, bool ref) const {
   Instance::PrintJSONImpl(stream, ref);
 }
 
+void Pointer::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void DynamicLibrary::PrintJSONImpl(JSONStream* stream, bool ref) const {
   Instance::PrintJSONImpl(stream, ref);
 }
 
+void DynamicLibrary::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void Capability::PrintJSONImpl(JSONStream* stream, bool ref) const {
   Instance::PrintJSONImpl(stream, ref);
 }
 
+void Capability::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void ReceivePort::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject obj(stream);
   Instance::PrintSharedInstanceJSON(&obj, ref);
@@ -1622,18 +1842,30 @@
   obj.AddProperty("allocationLocation", allocation_location_);
 }
 
+void ReceivePort::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void SendPort::PrintJSONImpl(JSONStream* stream, bool ref) const {
   Instance::PrintJSONImpl(stream, ref);
 }
 
+void SendPort::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void TransferableTypedData::PrintJSONImpl(JSONStream* stream, bool ref) const {
   Instance::PrintJSONImpl(stream, ref);
 }
 
+void TransferableTypedData::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void ClosureData::PrintJSONImpl(JSONStream* stream, bool ref) const {
   Object::PrintJSONImpl(stream, ref);
 }
 
+void ClosureData::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void Closure::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref);
@@ -1653,6 +1885,9 @@
   }
 }
 
+void Closure::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void Record::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref);
@@ -1685,6 +1920,9 @@
   }
 }
 
+void Record::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void StackTrace::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref);
@@ -1692,6 +1930,9 @@
   jsobj.AddProperty("valueAsString", ToCString());
 }
 
+void StackTrace::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void RegExp::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref);
@@ -1737,6 +1978,9 @@
   }
 }
 
+void RegExp::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void WeakProperty::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref);
@@ -1751,6 +1995,9 @@
   jsobj.AddProperty("propertyValue", value_handle);
 }
 
+void WeakProperty::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void WeakReference::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref);
@@ -1763,10 +2010,18 @@
   jsobj.AddProperty("target", target_handle);
 }
 
+void WeakReference::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void FinalizerBase::PrintJSONImpl(JSONStream* stream, bool ref) const {
   UNREACHABLE();
 }
 
+void FinalizerBase::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {
+  UNREACHABLE();
+}
+
 void Finalizer::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref);
@@ -1781,6 +2036,9 @@
   // Not exposing entries.
 }
 
+void Finalizer::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void NativeFinalizer::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref);
@@ -1795,10 +2053,18 @@
   // Not exposing entries.
 }
 
+void NativeFinalizer::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void FinalizerEntry::PrintJSONImpl(JSONStream* stream, bool ref) const {
   UNREACHABLE();
 }
 
+void FinalizerEntry::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {
+  UNREACHABLE();
+}
+
 void MirrorReference::PrintJSONImpl(JSONStream* stream, bool ref) const {
   JSONObject jsobj(stream);
   PrintSharedInstanceJSON(&jsobj, ref);
@@ -1812,18 +2078,30 @@
   jsobj.AddProperty("mirrorReferent", referent_handle);
 }
 
+void MirrorReference::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void UserTag::PrintJSONImpl(JSONStream* stream, bool ref) const {
   Instance::PrintJSONImpl(stream, ref);
 }
 
+void UserTag::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void FutureOr::PrintJSONImpl(JSONStream* stream, bool ref) const {
   Instance::PrintJSONImpl(stream, ref);
 }
 
+void FutureOr::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 void SuspendState::PrintJSONImpl(JSONStream* stream, bool ref) const {
   Instance::PrintJSONImpl(stream, ref);
 }
 
+void SuspendState::PrintImplementationFieldsImpl(
+    const JSONArray& jsarr_fields) const {}
+
 #endif
 
 }  // namespace dart
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index 5e6a10e..2d69e6a 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -177,6 +177,7 @@
 #define ISOLATE_GROUP_PARAMETER new IdParameter("isolateGroupId", true)
 #define NO_ISOLATE_PARAMETER new NoSuchParameter("isolateId")
 #define RUNNABLE_ISOLATE_PARAMETER new RunnableIsolateParameter("isolateId")
+#define OBJECT_PARAMETER new IdParameter("objectId", true)
 
 class EnumListParameter : public MethodParameter {
  public:
@@ -4987,6 +4988,42 @@
   jsobj.AddProperty("bart", "simpson");
 }
 
+// Returns |true| if a heap object with the specified ID was successfully found,
+// and |false| otherwise. If an object was found, it will be stored at address
+// |obj|.
+// This function should be used to handle shared logic between |GetObject| and
+// |GetImplementationFields|.
+static bool GetHeapObjectCommon(Thread* thread,
+                                JSONStream* js,
+                                const char* id,
+                                Object* obj,
+                                ObjectIdRing::LookupResult* lookup_result) {
+  *obj = LookupHeapObject(thread, id, lookup_result);
+  ASSERT(obj != nullptr);
+  ASSERT(lookup_result != nullptr);
+  if (obj->ptr() != Object::sentinel().ptr()) {
+#if !defined(DART_PRECOMPILED_RUNTIME)
+    // If obj is a script from dart:* and doesn't have source loaded, try and
+    // load the source before sending the response.
+    if (obj->IsScript()) {
+      const Script& script = Script::Cast(*obj);
+      script.LookupSourceAndLineStarts(thread->zone());
+      if (!script.HasSource() && script.IsPartOfDartColonLibrary() &&
+          Service::HasDartLibraryKernelForSources()) {
+        const uint8_t* kernel_buffer = Service::dart_library_kernel();
+        const intptr_t kernel_buffer_len =
+            Service::dart_library_kernel_length();
+        script.LoadSourceFromKernel(kernel_buffer, kernel_buffer_len);
+      }
+    }
+#endif  // !defined(DART_PRECOMPILED_RUNTIME)
+    // We found a heap object for this id.
+    return true;
+  }
+
+  return false;
+}
+
 static const MethodParameter* const get_object_params[] = {
     RUNNABLE_ISOLATE_PARAMETER,
     new UIntParameter("offset", false),
@@ -5018,25 +5055,9 @@
   }
 
   // Handle heap objects.
+  Object& obj = Object::Handle();
   ObjectIdRing::LookupResult lookup_result;
-  Object& obj = Object::Handle(LookupHeapObject(thread, id, &lookup_result));
-  if (obj.ptr() != Object::sentinel().ptr()) {
-#if !defined(DART_PRECOMPILED_RUNTIME)
-    // If obj is a script from dart:* and doesn't have source loaded, try and
-    // load the source before sending the response.
-    if (obj.IsScript()) {
-      const Script& script = Script::Cast(obj);
-      script.LookupSourceAndLineStarts(thread->zone());
-      if (!script.HasSource() && script.IsPartOfDartColonLibrary() &&
-          Service::HasDartLibraryKernelForSources()) {
-        const uint8_t* kernel_buffer = Service::dart_library_kernel();
-        const intptr_t kernel_buffer_len =
-            Service::dart_library_kernel_length();
-        script.LoadSourceFromKernel(kernel_buffer, kernel_buffer_len);
-      }
-    }
-#endif  // !defined(DART_PRECOMPILED_RUNTIME)
-    // We found a heap object for this id.  Return it.
+  if (GetHeapObjectCommon(thread, js, id, &obj, &lookup_result)) {
     obj.PrintJSON(js, false);
     return;
   } else if (lookup_result == ObjectIdRing::kCollected) {
@@ -5060,6 +5081,44 @@
   PrintInvalidParamError(js, "objectId");
 }
 
+static const MethodParameter* const get_implementation_fields_params[] = {
+    RUNNABLE_ISOLATE_PARAMETER,
+    OBJECT_PARAMETER,
+    nullptr,
+};
+
+static void GetImplementationFields(Thread* thread, JSONStream* js) {
+  const char* id = js->LookupParam("objectId");
+
+  // Handle heap objects.
+  Object& obj = Object::Handle();
+  ObjectIdRing::LookupResult lookup_result;
+  if (GetHeapObjectCommon(thread, js, id, &obj, &lookup_result)) {
+    obj.PrintImplementationFields(js);
+    return;
+  } else if (lookup_result == ObjectIdRing::kCollected) {
+    PrintSentinel(js, kCollectedSentinel);
+    return;
+  } else if (lookup_result == ObjectIdRing::kExpired) {
+    PrintSentinel(js, kExpiredSentinel);
+    return;
+  }
+
+  // Handle non-heap objects.
+  Breakpoint* bpt = LookupBreakpoint(thread->isolate(), id, &lookup_result);
+  if (bpt != nullptr) {
+    const JSONObject jsobj(js);
+    jsobj.AddProperty("type", "ImplementationFields");
+    JSONArray jsarr_fields(&jsobj, "fields");
+    return;
+  } else if (lookup_result == ObjectIdRing::kCollected) {
+    PrintSentinel(js, kCollectedSentinel);
+    return;
+  }
+
+  PrintInvalidParamError(js, "objectId");
+}
+
 static const MethodParameter* const get_object_store_params[] = {
     RUNNABLE_ISOLATE_PARAMETER,
     NULL,
@@ -5815,6 +5874,8 @@
     get_flag_list_params },
   { "_getHeapMap", GetHeapMap,
     get_heap_map_params },
+  { "_getImplementationFields", GetImplementationFields,
+    get_implementation_fields_params },
   { "getInboundReferences", GetInboundReferences,
     get_inbound_references_params },
   { "getInstances", GetInstances,