text: // Copyright (c) 2016, 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.
  
  #include "vm/canonical_tables.h"
  #include "vm/closure_functions_cache.h"
  #include "vm/compiler/assembler/disassembler.h"
  #include "vm/debugger.h"
  #include "vm/object.h"
  #include "vm/object_graph.h"
  #include "vm/object_store.h"
  #include "vm/resolver.h"
  #include "vm/stub_code.h"
  #include "vm/symbols.h"
  
  namespace dart {
  
  #ifndef PRODUCT
  
  static void AddNameProperties(JSONObject* jsobj,
                                const char* name,
                                const char* vm_name) {
    jsobj->AddProperty("name", name);
    if (strcmp(name, vm_name) != 0) {
      jsobj->AddProperty("_vmName", vm_name);
    }
  }
  
  void Object::AddCommonObjectProperties(JSONObject* jsobj,
                                         const char* protocol_type,
                                         bool ref) const {
    const char* vm_type = JSONType();
    bool same_type = (strcmp(protocol_type, vm_type) == 0);
    if (ref) {
      jsobj->AddPropertyF("type", "@%s", protocol_type);
    } else {
      jsobj->AddProperty("type", protocol_type);
    }
    if (!same_type) {
      jsobj->AddProperty("_vmType", vm_type);
    }
    if (!ref || IsInstance() || IsNull()) {
      // TODO(turnidge): Provide the type arguments here too?
      const Class& cls = Class::Handle(this->clazz());
      jsobj->AddProperty("class", cls);
    }
    if (!ref) {
      if (ptr()->IsHeapObject()) {
        jsobj->AddProperty("size", ptr()->untag()->HeapSize());
      } else {
        jsobj->AddProperty("size", (intptr_t)0);
      }
    }
  }
  
  void Object::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    AddCommonObjectProperties(&jsobj, "Object", ref);
    jsobj.AddServiceId(*this);
    if (ref) {
      return;
    }
  }
  
  void Object::PrintJSON(JSONStream* stream, bool ref) const {
    if (IsNull()) {
      JSONObject jsobj(stream);
      AddCommonObjectProperties(&jsobj, "Instance", ref);
      jsobj.AddProperty("kind", "Null");
      jsobj.AddFixedServiceId("objects/null");
      jsobj.AddProperty("valueAsString", "null");
    } else {
      PrintJSONImpl(stream, ref);
    }
  }
  
  void Class::PrintJSONImpl(JSONStream* stream, bool ref) const {
    Isolate* isolate = Isolate::Current();
    JSONObject jsobj(stream);
    if ((ptr() == Class::null()) || (id() == kFreeListElement)) {
      // TODO(turnidge): This is weird and needs to be changed.
      jsobj.AddProperty("type", "null");
      return;
    }
    AddCommonObjectProperties(&jsobj, "Class", ref);
    jsobj.AddFixedServiceId("classes/%" Pd "", id());
    const String& scrubbed_name = String::Handle(ScrubbedName());
    const String& vm_name = String::Handle(Name());
    AddNameProperties(&jsobj, scrubbed_name.ToCString(), vm_name.ToCString());
    const Script& script = Script::Handle(this->script());
    if (!script.IsNull()) {
      jsobj.AddLocation(script, token_pos(), end_token_pos());
    }
  
    jsobj.AddProperty("library", Object::Handle(library()));
  
    const intptr_t num_type_params = NumTypeParameters();
    if (num_type_params > 0) {
      JSONArray jsarr(&jsobj, "typeParameters");
      TypeParameter& type_param = TypeParameter::Handle();
      for (intptr_t i = 0; i < num_type_params; ++i) {
        type_param = TypeParameterAt(i);
        jsarr.AddValue(type_param);
      }
    }
    if (ref) {
      return;
    }
  
    const Error& err = Error::Handle(EnsureIsFinalized(Thread::Current()));
    if (!err.IsNull()) {
      jsobj.AddProperty("error", err);
    }
    jsobj.AddProperty("abstract", is_abstract());
    jsobj.AddProperty("const", is_const());
    jsobj.AddProperty("_finalized", is_finalized());
    jsobj.AddProperty("_implemented", is_implemented());
    jsobj.AddProperty("_patch", false);
    jsobj.AddProperty("traceAllocations", TraceAllocation(isolate->group()));
  
    const Class& superClass = Class::Handle(SuperClass());
    if (!superClass.IsNull()) {
      jsobj.AddProperty("super", superClass);
    }
    const AbstractType& superType = AbstractType::Handle(super_type());
    if (!superType.IsNull()) {
      jsobj.AddProperty("superType", superType);
    }
    const Array& interface_array = Array::Handle(interfaces());
    if (is_transformed_mixin_application()) {
      Type& mix = Type::Handle();
      mix ^= interface_array.At(interface_array.Length() - 1);
      jsobj.AddProperty("mixin", mix);
    }
    {
      JSONArray interfaces_array(&jsobj, "interfaces");
      Type& interface_type = Type::Handle();
      if (!interface_array.IsNull()) {
        for (intptr_t i = 0; i < interface_array.Length(); ++i) {
          interface_type ^= interface_array.At(i);
          interfaces_array.AddValue(interface_type);
        }
      }
    }
    {
      JSONArray fields_array(&jsobj, "fields");
      const Array& field_array = Array::Handle(fields());
      Field& field = Field::Handle();
      if (!field_array.IsNull()) {
        for (intptr_t i = 0; i < field_array.Length(); ++i) {
          field ^= field_array.At(i);
          fields_array.AddValue(field);
        }
      }
    }
    {
      JSONArray functions_array(&jsobj, "functions");
      const Array& function_array = Array::Handle(current_functions());
      Function& function = Function::Handle();
      if (!function_array.IsNull()) {
        for (intptr_t i = 0; i < function_array.Length(); i++) {
          function ^= function_array.At(i);
          functions_array.AddValue(function);
        }
      }
    }
    {
      JSONArray subclasses_array(&jsobj, "subclasses");
      const GrowableObjectArray& subclasses =
          GrowableObjectArray::Handle(direct_subclasses_unsafe());
      if (!subclasses.IsNull()) {
        Class& subclass = Class::Handle();
        for (intptr_t i = 0; i < subclasses.Length(); ++i) {
          // TODO(turnidge): Use the Type directly once regis has added
          // types to the vmservice.
          subclass ^= subclasses.At(i);
          subclasses_array.AddValue(subclass);
        }
      }
    }
  }
  
  void TypeParameters::PrintJSONImpl(JSONStream* stream, bool ref) const {
    // Consider making this type public if we decide to expose TypeParameters
    // through the protocol.
    JSONObject jsobj(stream);
    jsobj.AddProperty("kind", "_TypeParameters");
    jsobj.AddProperty("flags", Array::Handle(flags()));
    jsobj.AddProperty("names", Array::Handle(names()));
    jsobj.AddProperty("bounds", TypeArguments::Handle(bounds()));
    jsobj.AddProperty("defaults", TypeArguments::Handle(defaults()));
  }
  
  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
    // the object id (as in typearguments/id), because the indices are not
    // preserved when the table grows and the entries get rehashed. Use the ring.
    Thread* thread = Thread::Current();
    Zone* zone = thread->zone();
    auto object_store = thread->isolate_group()->object_store();
    CanonicalTypeArgumentsSet typeargs_table(
        zone, object_store->canonical_type_arguments());
    const Array& table =
        Array::Handle(HashTables::ToArray(typeargs_table, false));
    typeargs_table.Release();
    ASSERT(table.Length() > 0);
    AddCommonObjectProperties(&jsobj, "TypeArguments", ref);
    jsobj.AddServiceId(*this);
    const String& user_name = String::Handle(UserVisibleName());
    const String& vm_name = String::Handle(Name());
    AddNameProperties(&jsobj, user_name.ToCString(), vm_name.ToCString());
    if (ref) {
      return;
    }
    {
      JSONArray jsarr(&jsobj, "types");
      AbstractType& type_arg = AbstractType::Handle();
      for (intptr_t i = 0; i < Length(); i++) {
        type_arg = TypeAt(i);
        jsarr.AddValue(type_arg);
      }
    }
    if (!IsInstantiated()) {
      JSONArray jsarr(&jsobj, "_instantiations");
      Array& prior_instantiations = Array::Handle(instantiations());
      ASSERT(prior_instantiations.Length() > 0);  // Always at least a sentinel.
      TypeArguments& type_args = TypeArguments::Handle();
      intptr_t i = 0;
      while (prior_instantiations.At(i) !=
             Smi::New(TypeArguments::kNoInstantiator)) {
        JSONObject instantiation(&jsarr);
        type_args ^= prior_instantiations.At(
            i + TypeArguments::Instantiation::kInstantiatorTypeArgsIndex);
        instantiation.AddProperty("instantiatorTypeArguments", type_args, true);
        type_args ^= prior_instantiations.At(
            i + TypeArguments::Instantiation::kFunctionTypeArgsIndex);
        instantiation.AddProperty("functionTypeArguments", type_args, true);
        type_args ^= prior_instantiations.At(
            i + TypeArguments::Instantiation::kInstantiatedTypeArgsIndex);
        instantiation.AddProperty("instantiated", type_args, true);
        i += TypeArguments::Instantiation::kSizeInWords;
      }
    }
  }
  
  void PatchClass::PrintJSONImpl(JSONStream* stream, bool ref) const {
    Object::PrintJSONImpl(stream, ref);
  }
  
  void Function::AddFunctionServiceId(const JSONObject& jsobj) const {
    Class& cls = Class::Handle(Owner());
    // Special kinds of functions use indices in their respective lists.
    intptr_t id = -1;
    const char* selector = NULL;
    // Regular functions known to their owner use their name (percent-encoded).
    String& name = String::Handle(this->name());
  
    if (IsNonImplicitClosureFunction()) {
      id = ClosureFunctionsCache::FindClosureIndex(*this);
      selector = "closures";
    } else if (IsImplicitClosureFunction()) {
      id = cls.FindImplicitClosureFunctionIndex(*this);
      selector = "implicit_closures";
    } else if (IsNoSuchMethodDispatcher() || IsInvokeFieldDispatcher()) {
      id = cls.FindInvocationDispatcherFunctionIndex(*this);
      selector = "dispatchers";
    } else if (IsFieldInitializer()) {
      name ^= Field::NameFromInit(name);
      const char* encoded_field_name = String::EncodeIRI(name);
      if (cls.IsTopLevel()) {
        const auto& library = Library::Handle(cls.library());
        const auto& private_key = String::Handle(library.private_key());
        jsobj.AddFixedServiceId("libraries/%s/field_inits/%s",
                                private_key.ToCString(), encoded_field_name);
      } else {
        jsobj.AddFixedServiceId("classes/%" Pd "/field_inits/%s", cls.id(),
                                encoded_field_name);
      }
      return;
    }
    if (id != -1) {
      ASSERT(selector != NULL);
      if (cls.IsTopLevel()) {
        const auto& library = Library::Handle(cls.library());
        const auto& private_key = String::Handle(library.private_key());
        jsobj.AddFixedServiceId("libraries/%s/%s/%" Pd "",
                                private_key.ToCString(), selector, id);
      } else {
        jsobj.AddFixedServiceId("classes/%" Pd "/%s/%" Pd "", cls.id(), selector,
                                id);
      }
      return;
    }
    Thread* thread = Thread::Current();
    if (Resolver::ResolveFunction(thread->zone(), cls, name) == ptr()) {
      const char* encoded_name = String::EncodeIRI(name);
      if (cls.IsTopLevel()) {
        const auto& library = Library::Handle(cls.library());
        const auto& private_key = String::Handle(library.private_key());
        jsobj.AddFixedServiceId("libraries/%s/functions/%s",
                                private_key.ToCString(), encoded_name);
      } else {
        jsobj.AddFixedServiceId("classes/%" Pd "/functions/%s", cls.id(),
                                encoded_name);
      }
      return;
    }
    // Oddball functions (not known to their owner) fall back to use the object
    // id ring. Current known examples are signature functions of closures
    // and stubs like 'megamorphic_call_miss'.
    jsobj.AddServiceId(*this);
  }
  
  void Function::PrintJSONImpl(JSONStream* stream, bool ref) const {
    Class& cls = Class::Handle(Owner());
    ASSERT(!cls.IsNull());
    Error& err = Error::Handle();
    err = cls.EnsureIsFinalized(Thread::Current());
    ASSERT(err.IsNull());
    JSONObject jsobj(stream);
    AddCommonObjectProperties(&jsobj, "Function", ref);
    AddFunctionServiceId(jsobj);
    const char* user_name = UserVisibleNameCString();
    const String& vm_name = String::Handle(name());
    AddNameProperties(&jsobj, user_name, vm_name.ToCString());
    const Function& parent = Function::Handle(parent_function());
    if (!parent.IsNull()) {
      jsobj.AddProperty("owner", parent);
    } else if (!cls.IsNull()) {
      if (cls.IsTopLevel()) {
        const Library& library = Library::Handle(cls.library());
        jsobj.AddProperty("owner", library);
      } else {
        jsobj.AddProperty("owner", cls);
      }
    }
  
    const char* kind_string = Function::KindToCString(kind());
    jsobj.AddProperty("_kind", kind_string);
    jsobj.AddProperty("static", is_static());
    jsobj.AddProperty("const", is_const());
    jsobj.AddProperty("implicit", IsImplicitGetterOrSetter());
    jsobj.AddProperty("_intrinsic", is_intrinsic());
    jsobj.AddProperty("_native", is_native());
  
    const Script& script = Script::Handle(this->script());
    if (!script.IsNull()) {
      jsobj.AddLocation(script, token_pos(), end_token_pos());
    }
  
    if (ref) {
      return;
    }
    const FunctionType& sig = FunctionType::Handle(signature());
    jsobj.AddProperty("signature", sig);
  
    Code& code = Code::Handle(CurrentCode());
    if (!code.IsNull()) {
      jsobj.AddProperty("code", code);
    }
    Array& ics = Array::Handle(ic_data_array());
    if (!ics.IsNull()) {
      jsobj.AddProperty("_icDataArray", ics);
    }
    jsobj.AddProperty("_optimizable", is_optimizable());
    jsobj.AddProperty("_inlinable", is_inlinable());
    jsobj.AddProperty("_recognized", IsRecognized());
    code = unoptimized_code();
    if (!code.IsNull()) {
      jsobj.AddProperty("_unoptimizedCode", code);
    }
    jsobj.AddProperty("_usageCounter", usage_counter());
    jsobj.AddProperty("_optimizedCallSiteCount", optimized_call_site_count());
    jsobj.AddProperty("_deoptimizations",
                      static_cast<intptr_t>(deoptimization_counter()));
    if ((kind() == UntaggedFunction::kImplicitGetter) ||
        (kind() == UntaggedFunction::kImplicitSetter) ||
        (kind() == UntaggedFunction::kImplicitStaticGetter) ||
        (kind() == UntaggedFunction::kFieldInitializer)) {
      const Field& field = Field::Handle(accessor_field());
      if (!field.IsNull()) {
        jsobj.AddProperty("_field", field);
      }
    }
  }
  
  void FfiTrampolineData::PrintJSONImpl(JSONStream* stream, bool ref) const {
    Object::PrintJSONImpl(stream, ref);
  }
  
  void Field::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    Class& cls = Class::Handle(Owner());
    String& field_name = String::Handle(name());
    const char* encoded_field_name = String::EncodeIRI(field_name);
    AddCommonObjectProperties(&jsobj, "Field", ref);
    if (cls.IsTopLevel()) {
      const auto& library = Library::Handle(cls.library());
      const auto& private_key = String::Handle(library.private_key());
      jsobj.AddFixedServiceId("libraries/%s/fields/%s", private_key.ToCString(),
                              encoded_field_name);
    } else {
      jsobj.AddFixedServiceId("classes/%" Pd "/fields/%s", cls.id(),
                              encoded_field_name);
    }
  
    const char* user_name = UserVisibleNameCString();
    const String& vm_name = String::Handle(name());
    AddNameProperties(&jsobj, user_name, vm_name.ToCString());
    if (cls.IsTopLevel()) {
      const Library& library = Library::Handle(cls.library());
      jsobj.AddProperty("owner", library);
    } else {
      jsobj.AddProperty("owner", cls);
    }
  
    AbstractType& declared_type = AbstractType::Handle(type());
    jsobj.AddProperty("declaredType", declared_type);
    jsobj.AddProperty("static", is_static());
    jsobj.AddProperty("final", is_final());
    jsobj.AddProperty("const", is_const());
  
    const class Script& script = Script::Handle(Script());
    if (!script.IsNull()) {
      jsobj.AddLocation(script, token_pos(), end_token_pos());
    }
  
    if (ref) {
      return;
    }
    if (is_static()) {
      const Object& valueObj = Object::Handle(StaticValue());
      jsobj.AddProperty("staticValue", valueObj);
    }
  
    jsobj.AddProperty("_guardNullable", is_nullable());
    if (guarded_cid() == kIllegalCid) {
      jsobj.AddProperty("_guardClass", "unknown");
    } else if (guarded_cid() == kDynamicCid) {
      jsobj.AddProperty("_guardClass", "dynamic");
    } else {
      ClassTable* table = IsolateGroup::Current()->class_table();
      ASSERT(table->IsValidIndex(guarded_cid()));
      cls = table->At(guarded_cid());
      jsobj.AddProperty("_guardClass", cls);
    }
    if (guarded_list_length() == kUnknownFixedLength) {
      jsobj.AddProperty("_guardLength", "unknown");
    } else if (guarded_list_length() == kNoFixedLength) {
      jsobj.AddProperty("_guardLength", "variable");
    } else {
      jsobj.AddPropertyF("_guardLength", "%" Pd, guarded_list_length());
    }
  }
  
  // See also Dart_ScriptGetTokenInfo.
  void Script::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    AddCommonObjectProperties(&jsobj, "Script", ref);
    const String& uri = String::Handle(url());
    ASSERT(!uri.IsNull());
    const char* encoded_uri = String::EncodeIRI(uri);
    const Library& lib = Library::Handle(FindLibrary());
    if (lib.IsNull()) {
      jsobj.AddServiceId(*this);
    } else {
      const String& lib_id = String::Handle(lib.private_key());
      jsobj.AddFixedServiceId("libraries/%s/scripts/%s/%" Px64 "",
                              lib_id.ToCString(), encoded_uri, load_timestamp());
    }
    jsobj.AddPropertyStr("uri", uri);
    jsobj.AddProperty("_kind", "kernel");
    if (ref) {
      return;
    }
    jsobj.AddPropertyTimeMillis("_loadTime", load_timestamp());
    if (!lib.IsNull()) {
      jsobj.AddProperty("library", lib);
    }
    const String& source = String::Handle(Source());
    jsobj.AddProperty("lineOffset", line_offset());
    jsobj.AddProperty("columnOffset", col_offset());
    if (!source.IsNull()) {
      jsobj.AddPropertyStr("source", source);
    }
  
    // Print the line number table
    const GrowableObjectArray& lineNumberArray =
        GrowableObjectArray::Handle(GenerateLineNumberArray());
    if (!lineNumberArray.IsNull() && (lineNumberArray.Length() > 0)) {
      JSONArray tokenPosTable(&jsobj, "tokenPosTable");
  
      Object& value = Object::Handle();
      intptr_t pos = 0;
  
      // Skip leading null.
      ASSERT(lineNumberArray.Length() > 0);
      value = lineNumberArray.At(pos);
      ASSERT(value.IsNull());
      pos++;
  
      while (pos < lineNumberArray.Length()) {
        JSONArray lineInfo(&tokenPosTable);
        while (pos < lineNumberArray.Length()) {
          value = lineNumberArray.At(pos);
          pos++;
          if (value.IsNull()) {
            break;
          }
          const Smi& smi = Smi::Cast(value);
          lineInfo.AddValue(smi.Value());
        }
      }
    }
  }
  
  static void PrintShowHideNamesToJSON(JSONObject* jsobj, const Namespace& ns) {
    Array& arr = Array::Handle();
    String& name = String::Handle();
    arr ^= ns.show_names();
    if (!arr.IsNull()) {
      JSONArray jsarr(jsobj, "shows");
      for (intptr_t i = 0; i < arr.Length(); ++i) {
        name ^= arr.At(i);
        jsarr.AddValue(name.ToCString());
      }
    }
    arr ^= ns.hide_names();
    if (!arr.IsNull()) {
      JSONArray jsarr(jsobj, "hides");
      for (intptr_t i = 0; i < arr.Length(); ++i) {
        name ^= arr.At(i);
        jsarr.AddValue(name.ToCString());
      }
    }
  }
  
  void Library::PrintJSONImpl(JSONStream* stream, bool ref) const {
    const String& id = String::Handle(private_key());
    JSONObject jsobj(stream);
    AddCommonObjectProperties(&jsobj, "Library", ref);
    jsobj.AddFixedServiceId("libraries/%s", id.ToCString());
    const String& vm_name = String::Handle(name());
    const char* scrubbed_name = String::ScrubName(vm_name);
    AddNameProperties(&jsobj, scrubbed_name, vm_name.ToCString());
    const String& library_url = String::Handle(url());
    jsobj.AddPropertyStr("uri", library_url);
    if (ref) {
      return;
    }
    jsobj.AddProperty("debuggable", IsDebuggable());
    {
      JSONArray jsarr(&jsobj, "classes");
      ClassDictionaryIterator class_iter(*this);
      Class& klass = Class::Handle();
      while (class_iter.HasNext()) {
        klass = class_iter.GetNextClass();
        jsarr.AddValue(klass);
      }
    }
    {
      JSONArray jsarr(&jsobj, "dependencies");
  
      Namespace& ns = Namespace::Handle();
      Library& target = Library::Handle();
  
      // Unprefixed imports.
      Array& imports = Array::Handle(this->imports());
      for (intptr_t i = 0; i < imports.Length(); i++) {
        ns ^= imports.At(i);
        if (ns.IsNull()) continue;
  
        JSONObject jsdep(&jsarr);
        jsdep.AddProperty("isDeferred", false);
        jsdep.AddProperty("isExport", false);
        jsdep.AddProperty("isImport", true);
        target = ns.target();
        jsdep.AddProperty("target", target);
        PrintShowHideNamesToJSON(&jsdep, ns);
      }
  
      // Exports.
      const Array& exports = Array::Handle(this->exports());
      for (intptr_t i = 0; i < exports.Length(); i++) {
        ns ^= exports.At(i);
        if (ns.IsNull()) continue;
  
        JSONObject jsdep(&jsarr);
        jsdep.AddProperty("isDeferred", false);
        jsdep.AddProperty("isExport", true);
        jsdep.AddProperty("isImport", false);
        target = ns.target();
        jsdep.AddProperty("target", target);
        PrintShowHideNamesToJSON(&jsdep, ns);
      }
  
      // Prefixed imports.
      DictionaryIterator entries(*this);
      Object& entry = Object::Handle();
      LibraryPrefix& prefix = LibraryPrefix::Handle();
      String& prefix_name = String::Handle();
      while (entries.HasNext()) {
        entry = entries.GetNext();
        if (entry.IsLibraryPrefix()) {
          prefix ^= entry.ptr();
          imports = prefix.imports();
          if (!imports.IsNull()) {
            for (intptr_t i = 0; i < imports.Length(); i++) {
              ns ^= imports.At(i);
              if (ns.IsNull()) continue;
  
              JSONObject jsdep(&jsarr);
              jsdep.AddProperty("isDeferred", prefix.is_deferred_load());
              jsdep.AddProperty("isExport", false);
              jsdep.AddProperty("isImport", true);
              prefix_name = prefix.name();
              ASSERT(!prefix_name.IsNull());
              jsdep.AddProperty("prefix", prefix_name.ToCString());
              target = ns.target();
              jsdep.AddProperty("target", target);
              PrintShowHideNamesToJSON(&jsdep, ns);
            }
          }
        }
      }
    }
    {
      JSONArray jsarr(&jsobj, "variables");
      DictionaryIterator entries(*this);
      Object& entry = Object::Handle();
      while (entries.HasNext()) {
        entry = entries.GetNext();
        if (entry.IsField()) {
          jsarr.AddValue(entry);
        }
      }
    }
    {
      JSONArray jsarr(&jsobj, "functions");
      DictionaryIterator entries(*this);
      Object& entry = Object::Handle();
      while (entries.HasNext()) {
        entry = entries.GetNext();
        if (entry.IsFunction()) {
          const Function& func = Function::Cast(entry);
          if (func.kind() == UntaggedFunction::kRegularFunction ||
              func.kind() == UntaggedFunction::kGetterFunction ||
              func.kind() == UntaggedFunction::kSetterFunction) {
            jsarr.AddValue(func);
          }
        }
      }
    }
    {
      JSONArray jsarr(&jsobj, "scripts");
      Array& scripts = Array::Handle(LoadedScripts());
      Script& script = Script::Handle();
      for (intptr_t i = 0; i < scripts.Length(); i++) {
        script ^= scripts.At(i);
        jsarr.AddValue(script);
      }
    }
  }
  
  void LibraryPrefix::PrintJSONImpl(JSONStream* stream, bool ref) const {
    Object::PrintJSONImpl(stream, ref);
  }
  
  void Namespace::PrintJSONImpl(JSONStream* stream, bool ref) const {
    Object::PrintJSONImpl(stream, ref);
  }
  
  void KernelProgramInfo::PrintJSONImpl(JSONStream* stream, bool ref) const {
    Object::PrintJSONImpl(stream, ref);
  }
  
  void Instructions::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    AddCommonObjectProperties(&jsobj, "Object", ref);
    jsobj.AddServiceId(*this);
    if (ref) {
      return;
    }
  }
  
  void InstructionsSection::PrintJSONImpl(JSONStream* stream, bool ref) const {
    Object::PrintJSONImpl(stream, ref);
  }
  
  void InstructionsTable::PrintJSONImpl(JSONStream* stream, bool ref) const {
    Object::PrintJSONImpl(stream, ref);
  }
  
  void WeakSerializationReference::PrintJSONImpl(JSONStream* stream,
                                                 bool ref) const {
    JSONObject jsobj(stream);
    AddCommonObjectProperties(&jsobj, "Object", ref);
    jsobj.AddServiceId(*this);
    if (ref) return;
    auto& obj = Object::Handle(target());
    jsobj.AddProperty("target", obj);
  }
  
  void ObjectPool::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    AddCommonObjectProperties(&jsobj, "Object", ref);
    jsobj.AddServiceId(*this);
    jsobj.AddProperty("length", Length());
    if (ref) {
      return;
    }
  
    {
      JSONArray jsarr(&jsobj, "_entries");
      uword imm;
      Object& obj = Object::Handle();
      for (intptr_t i = 0; i < Length(); i++) {
        JSONObject jsentry(stream);
        jsentry.AddProperty("offset", OffsetFromIndex(i));
        switch (TypeAt(i)) {
          case ObjectPool::EntryType::kTaggedObject:
            obj = ObjectAt(i);
            jsentry.AddProperty("kind", "Object");
            jsentry.AddProperty("value", obj);
            break;
          case ObjectPool::EntryType::kImmediate:
            imm = RawValueAt(i);
            jsentry.AddProperty("kind", "Immediate");
            jsentry.AddProperty64("value", imm);
            break;
          case ObjectPool::EntryType::kNativeFunction:
            imm = RawValueAt(i);
            jsentry.AddProperty("kind", "NativeFunction");
            jsentry.AddProperty64("value", imm);
            break;
          default:
            UNREACHABLE();
        }
      }
    }
  }
  
  void PcDescriptors::PrintToJSONObject(JSONObject* jsobj, bool ref) const {
    AddCommonObjectProperties(jsobj, "Object", ref);
    // TODO(johnmccutchan): Generate a stable id. PcDescriptors hang off a Code
    // object but do not have a back reference to generate an ID.
    jsobj->AddServiceId(*this);
    if (ref) {
      return;
    }
    JSONArray members(jsobj, "members");
    Iterator iter(*this, UntaggedPcDescriptors::kAnyKind);
    while (iter.MoveNext()) {
      JSONObject descriptor(&members);
      descriptor.AddPropertyF("pcOffset", "%" Px "", iter.PcOffset());
      descriptor.AddProperty("kind", KindAsStr(iter.Kind()));
      descriptor.AddProperty("deoptId", iter.DeoptId());
      // TODO(turnidge): Use AddLocation instead.
      descriptor.AddProperty("tokenPos", iter.TokenPos());
      descriptor.AddProperty("tryIndex", iter.TryIndex());
    }
  }
  
  void PcDescriptors::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    PrintToJSONObject(&jsobj, ref);
  }
  
  void CodeSourceMap::PrintJSONImpl(JSONStream* stream, bool ref) const {
    Object::PrintJSONImpl(stream, ref);
  }
  
  void CompressedStackMaps::PrintJSONImpl(JSONStream* stream, bool ref) const {
    Object::PrintJSONImpl(stream, ref);
  }
  
  void LocalVarDescriptors::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    AddCommonObjectProperties(&jsobj, "Object", ref);
    // TODO(johnmccutchan): Generate a stable id. LocalVarDescriptors hang off
    // a Code object but do not have a back reference to generate an ID.
    jsobj.AddServiceId(*this);
    if (ref) {
      return;
    }
    JSONArray members(&jsobj, "members");
    String& var_name = String::Handle();
    for (intptr_t i = 0; i < Length(); i++) {
      UntaggedLocalVarDescriptors::VarInfo info;
      var_name = GetName(i);
      GetInfo(i, &info);
      JSONObject var(&members);
      var.AddProperty("name", var_name.ToCString());
      var.AddProperty("index", static_cast<intptr_t>(info.index()));
      var.AddProperty("declarationTokenPos", info.declaration_pos);
      var.AddProperty("scopeStartTokenPos", info.begin_pos);
      var.AddProperty("scopeEndTokenPos", info.end_pos);
      var.AddProperty("scopeId", static_cast<intptr_t>(info.scope_id));
      var.AddProperty("kind", KindToCString(info.kind()));
    }
  }
  
  void ExceptionHandlers::PrintJSONImpl(JSONStream* stream, bool ref) const {
    Object::PrintJSONImpl(stream, ref);
  }
  
  void SingleTargetCache::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    AddCommonObjectProperties(&jsobj, "Object", ref);
    jsobj.AddServiceId(*this);
    jsobj.AddProperty("_target", Code::Handle(target()));
    if (ref) {
      return;
    }
    jsobj.AddProperty("_lowerLimit", lower_limit());
    jsobj.AddProperty("_upperLimit", upper_limit());
  }
  
  void UnlinkedCall::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    AddCommonObjectProperties(&jsobj, "Object", ref);
    jsobj.AddServiceId(*this);
    jsobj.AddProperty("_selector", String::Handle(target_name()).ToCString());
    if (ref) {
      return;
    }
    jsobj.AddProperty("_argumentsDescriptor",
                      Array::Handle(arguments_descriptor()));
  }
  
  void MonomorphicSmiableCall::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    AddCommonObjectProperties(&jsobj, "Object", ref);
    jsobj.AddServiceId(*this);
    jsobj.AddProperty("_expectedClassId", Smi::Handle(Smi::New(expected_cid())));
    if (ref) {
      return;
    }
  }
  
  void CallSiteData::PrintJSONImpl(JSONStream* stream, bool ref) const {
    UNREACHABLE();
  }
  
  void ICData::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    AddCommonObjectProperties(&jsobj, "Object", ref);
    jsobj.AddServiceId(*this);
    jsobj.AddProperty("_owner", Object::Handle(Owner()));
    jsobj.AddProperty("_selector", String::Handle(target_name()).ToCString());
    if (ref) {
      return;
    }
    jsobj.AddProperty("_argumentsDescriptor",
                      Object::Handle(arguments_descriptor()));
    jsobj.AddProperty("_entries", Object::Handle(entries()));
  }
  
  void ICData::PrintToJSONArray(const JSONArray& jsarray,
                                TokenPosition token_pos) const {
    auto class_table = IsolateGroup::Current()->class_table();
    Class& cls = Class::Handle();
    Function& func = Function::Handle();
  
    JSONObject jsobj(&jsarray);
    jsobj.AddProperty("name", String::Handle(target_name()).ToCString());
    jsobj.AddProperty("tokenPos", static_cast<intptr_t>(token_pos.Serialize()));
    // TODO(rmacnak): Figure out how to stringify DeoptReasons().
    // jsobj.AddProperty("deoptReasons", ...);
  
    JSONArray cache_entries(&jsobj, "cacheEntries");
    for (intptr_t i = 0; i < NumberOfChecks(); i++) {
      JSONObject cache_entry(&cache_entries);
      func = GetTargetAt(i);
      intptr_t count = GetCountAt(i);
      if (!is_static_call()) {
        intptr_t cid = GetReceiverClassIdAt(i);
        cls = class_table->At(cid);
        cache_entry.AddProperty("receiver", cls);
      }
      cache_entry.AddProperty("target", func);
      cache_entry.AddProperty("count", count);
    }
  }
  
  void Code::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    AddCommonObjectProperties(&jsobj, "Code", ref);
    jsobj.AddFixedServiceId("code/%" Px64 "-%" Px "", compile_timestamp(),
                            PayloadStart());
    const char* qualified_name = QualifiedName(
        NameFormattingParams(kUserVisibleName, NameDisambiguation::kNo));
    const char* vm_name = Name();
    AddNameProperties(&jsobj, qualified_name, vm_name);
    const bool is_stub =
        IsStubCode() || IsAllocationStubCode() || IsTypeTestStubCode();
    if (is_stub) {
      jsobj.AddProperty("kind", "Stub");
    } else {
      jsobj.AddProperty("kind", "Dart");
    }
    jsobj.AddProperty("_optimized", is_optimized());
    const Object& obj = Object::Handle(owner());
    if (obj.IsFunction()) {
      const Function& func = Function::Cast(obj);
      jsobj.AddProperty("_intrinsic", func.is_intrinsic());
      jsobj.AddProperty("_native", func.is_native());
    } else {
      jsobj.AddProperty("_intrinsic", false);
      jsobj.AddProperty("_native", false);
    }
    if (ref) {
      return;
    }
    if (obj.IsFunction()) {
      jsobj.AddProperty("function", obj);
    } else {
      // Generate a fake function reference.
      JSONObject func(&jsobj, "function");
      func.AddProperty("type", "@Function");
      func.AddProperty("_kind", "Stub");
      ASSERT(strcmp(qualified_name, vm_name) == 0);
      func.AddProperty("name", vm_name);
      AddNameProperties(&func, vm_name, vm_name);
    }
    jsobj.AddPropertyF("_startAddress", "%" Px "", PayloadStart());
    jsobj.AddPropertyF("_endAddress", "%" Px "", PayloadStart() + Size());
    jsobj.AddProperty("_alive", is_alive());
    const ObjectPool& object_pool = ObjectPool::Handle(GetObjectPool());
    jsobj.AddProperty("_objectPool", object_pool);
    {
      JSONArray jsarr(&jsobj, "_disassembly");
      if (is_alive()) {
        // Only disassemble alive code objects.
        DisassembleToJSONStream formatter(jsarr);
        Disassemble(&formatter);
      }
    }
    const PcDescriptors& descriptors = PcDescriptors::Handle(pc_descriptors());
    if (!descriptors.IsNull()) {
      JSONObject desc(&jsobj, "_descriptors");
      descriptors.PrintToJSONObject(&desc, false);
    }
  
    PrintJSONInlineIntervals(&jsobj);
  }
  
  void Context::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    // TODO(turnidge): Should the user level type for Context be Context
    // or Object?
    AddCommonObjectProperties(&jsobj, "Context", ref);
    jsobj.AddServiceId(*this);
  
    jsobj.AddProperty("length", num_variables());
  
    if (ref) {
      return;
    }
  
    const Context& parent_context = Context::Handle(parent());
    if (!parent_context.IsNull()) {
      jsobj.AddProperty("parent", parent_context);
    }
  
    JSONArray jsarr(&jsobj, "variables");
    Object& var = Object::Handle();
    for (intptr_t index = 0; index < num_variables(); index++) {
      var = At(index);
      JSONObject jselement(&jsarr);
      jselement.AddProperty("value", var);
    }
  }
  
  void ContextScope::PrintJSONImpl(JSONStream* stream, bool ref) const {
    Object::PrintJSONImpl(stream, ref);
  }
  
  void Sentinel::PrintJSONImpl(JSONStream* stream, bool ref) const {
    // Handle certain special sentinel values.
    if (ptr() == Object::sentinel().ptr()) {
      JSONObject jsobj(stream);
      jsobj.AddProperty("type", "Sentinel");
      jsobj.AddProperty("kind", "NotInitialized");
      jsobj.AddProperty("valueAsString", "<not initialized>");
      return;
    } else if (ptr() == Object::transition_sentinel().ptr()) {
      JSONObject jsobj(stream);
      jsobj.AddProperty("type", "Sentinel");
      jsobj.AddProperty("kind", "BeingInitialized");
      jsobj.AddProperty("valueAsString", "<being initialized>");
      return;
    }
  
    Object::PrintJSONImpl(stream, ref);
  }
  
  void MegamorphicCache::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    AddCommonObjectProperties(&jsobj, "Object", ref);
    jsobj.AddServiceId(*this);
    jsobj.AddProperty("_selector", String::Handle(target_name()).ToCString());
    if (ref) {
      return;
    }
    jsobj.AddProperty("_buckets", Object::Handle(buckets()));
    jsobj.AddProperty("_mask", mask());
    jsobj.AddProperty("_argumentsDescriptor",
                      Object::Handle(arguments_descriptor()));
  }
  
  void SubtypeTestCache::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    AddCommonObjectProperties(&jsobj, "Object", ref);
    jsobj.AddServiceId(*this);
    if (ref) {
      return;
    }
    jsobj.AddProperty("_cache", Array::Handle(cache()));
  }
  
  void LoadingUnit::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    AddCommonObjectProperties(&jsobj, "Object", ref);
    jsobj.AddServiceId(*this);
    if (ref) {
      return;
    }
    jsobj.AddProperty("_parent", LoadingUnit::Handle(parent()));
    jsobj.AddProperty("_baseObjects", Array::Handle(base_objects()));
    jsobj.AddProperty("_id", static_cast<intptr_t>(id()));
    jsobj.AddProperty("_loaded", loaded());
    jsobj.AddProperty("_loadOutstanding", load_outstanding());
  }
  
  void Error::PrintJSONImpl(JSONStream* stream, bool ref) const {
    UNREACHABLE();
  }
  
  void ApiError::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    AddCommonObjectProperties(&jsobj, "Error", ref);
    jsobj.AddProperty("kind", "InternalError");
    jsobj.AddServiceId(*this);
    jsobj.AddProperty("message", ToErrorCString());
  }
  
  void LanguageError::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    AddCommonObjectProperties(&jsobj, "Error", ref);
    jsobj.AddProperty("kind", "LanguageError");
    jsobj.AddServiceId(*this);
    jsobj.AddProperty("message", ToErrorCString());
  }
  
  void UnhandledException::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    AddCommonObjectProperties(&jsobj, "Error", ref);
    jsobj.AddProperty("kind", "UnhandledException");
    jsobj.AddServiceId(*this);
    jsobj.AddProperty("message", ToErrorCString());
    if (ref) {
      return;
    }
    Instance& instance = Instance::Handle();
    instance = exception();
    jsobj.AddProperty("exception", instance);
    instance = stacktrace();
    jsobj.AddProperty("stacktrace", instance);
  }
  
  void UnwindError::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    AddCommonObjectProperties(&jsobj, "Error", ref);
    jsobj.AddProperty("kind", "TerminationError");
    jsobj.AddServiceId(*this);
    jsobj.AddProperty("message", ToErrorCString());
    jsobj.AddProperty("_is_user_initiated", is_user_initiated());
  }
  
  void Instance::PrintSharedInstanceJSON(JSONObject* jsobj, bool ref) const {
    AddCommonObjectProperties(jsobj, "Instance", ref);
    {
      NoSafepointScope safepoint_scope;
      uint32_t hash_code = HeapSnapshotWriter::GetHeapSnapshotIdentityHash(
          Thread::Current(), ptr());
      jsobj->AddProperty64("identityHashCode", hash_code);
    }
    if (ref) {
      return;
    }
  
    // Add all fields in layout order, from superclass to subclass.
    GrowableArray<Class*> classes;
    Class& cls = Class::Handle(this->clazz());
    if (IsClosure()) {
      // Closure fields are not instances. Skip them.
      cls = cls.SuperClass();
    }
    do {
      classes.Add(&Class::Handle(cls.ptr()));
      cls = cls.SuperClass();
    } while (!cls.IsNull());
  
    Array& field_array = Array::Handle();
    Field& field = Field::Handle();
    Instance& field_value = Instance::Handle();
    {
      JSONArray jsarr(jsobj, "fields");
      for (intptr_t i = classes.length() - 1; i >= 0; i--) {
        field_array = classes[i]->fields();
        if (!field_array.IsNull()) {
          for (intptr_t j = 0; j < field_array.Length(); j++) {
            field ^= field_array.At(j);
            if (!field.is_static()) {
              field_value ^= GetField(field);
              JSONObject jsfield(&jsarr);
              jsfield.AddProperty("type", "BoundField");
              jsfield.AddProperty("decl", field);
              jsfield.AddProperty("value", field_value);
            }
          }
        }
      }
    }
  
    if (NumNativeFields() > 0) {
      JSONArray jsarr(jsobj, "_nativeFields");
      for (intptr_t i = 0; i < NumNativeFields(); i++) {
        intptr_t value = GetNativeField(i);
        JSONObject jsfield(&jsarr);
        jsfield.AddProperty("index", i);
        jsfield.AddProperty("value", value);
      }
    }
  }
  
  void Instance::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
  
    PrintSharedInstanceJSON(&jsobj, ref);
    // TODO(regis): Wouldn't it be simpler to provide a Closure::PrintJSONImpl()?
    if (IsClosure()) {
      jsobj.AddProperty("kind", "Closure");
    } else {
      jsobj.AddProperty("kind", "PlainInstance");
    }
    jsobj.AddServiceId(*this);
    if (IsClosure()) {
      // TODO(regis): How about closureInstantiatorTypeArguments and
      // closureFunctionTypeArguments?
      jsobj.AddProperty("closureFunction",
                        Function::Handle(Closure::Cast(*this).function()));
      jsobj.AddProperty("closureContext",
                        Context::Handle(Closure::Cast(*this).context()));
    }
    if (ref) {
      return;
    }
    if (IsClosure()) {
      Debugger* debugger = Isolate::Current()->debugger();
      Breakpoint* bpt = debugger->BreakpointAtActivation(*this);
      if (bpt != NULL) {
        jsobj.AddProperty("_activationBreakpoint", bpt);
      }
    }
  }
  
  void AbstractType::PrintJSONImpl(JSONStream* stream, bool ref) const {
    UNREACHABLE();
  }
  
  void Type::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    PrintSharedInstanceJSON(&jsobj, ref);
    jsobj.AddProperty("kind", "Type");
    const Class& type_cls = Class::Handle(type_class());
    if (type_cls.DeclarationType() == ptr()) {
      intptr_t cid = type_cls.id();
      jsobj.AddFixedServiceId("classes/%" Pd "/types/%d", cid, 0);
    } else {
      jsobj.AddServiceId(*this);
    }
    jsobj.AddProperty("typeClass", type_cls);
    const String& user_name = String::Handle(UserVisibleName());
    const String& vm_name = String::Handle(Name());
    AddNameProperties(&jsobj, user_name.ToCString(), vm_name.ToCString());
    if (ref) {
      return;
    }
    const TypeArguments& typeArgs = TypeArguments::Handle(arguments());
    if (!typeArgs.IsNull()) {
      jsobj.AddProperty("typeArguments", typeArgs);
    }
  }
  
  void FunctionType::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    PrintSharedInstanceJSON(&jsobj, ref);
    jsobj.AddProperty("kind", "FunctionType");
    AbstractType& type = AbstractType::Handle(result_type());
    jsobj.AddProperty("returnType", type);
  
    const int type_params_count = NumTypeParameters();
    if (type_params_count > 0) {
      JSONArray arr(&jsobj, "typeParameters");
      TypeParameter& type_param = TypeParameter::Handle();
      for (intptr_t i = 0; i < type_params_count; ++i) {
        type_param = TypeParameterAt(i);
        arr.AddValue(type_param);
      }
    }
  
    {
      JSONArray jsarr(&jsobj, "parameters");
      String& name = String::Handle();
      const intptr_t param_count = NumParameters();
      const intptr_t fixed_param_count = num_fixed_parameters();
      const bool has_named = HasOptionalNamedParameters();
      for (intptr_t i = 0; i < param_count; ++i) {
        JSONObject param(&jsarr);
        type = ParameterTypeAt(i);
        param.AddProperty("parameterType", type);
        bool fixed = i < fixed_param_count;
        param.AddProperty("fixed", fixed);
        if (!fixed && has_named) {
          name = ParameterNameAt(i);
          param.AddProperty("name", name.ToCString());
          param.AddProperty("required", IsRequiredAt(i));
        }
      }
    }
  }
  
  void TypeRef::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    PrintSharedInstanceJSON(&jsobj, ref);
    jsobj.AddProperty("kind", "TypeRef");
    jsobj.AddServiceId(*this);
    const String& user_name = String::Handle(UserVisibleName());
    const String& vm_name = String::Handle(Name());
    AddNameProperties(&jsobj, user_name.ToCString(), vm_name.ToCString());
    if (ref) {
      return;
    }
    jsobj.AddProperty("targetType", AbstractType::Handle(type()));
  }
  
  void TypeParameter::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    PrintSharedInstanceJSON(&jsobj, ref);
    jsobj.AddProperty("kind", "TypeParameter");
    jsobj.AddServiceId(*this);
    const String& user_name = String::Handle(UserVisibleName());
    const String& vm_name = String::Handle(Name());
    AddNameProperties(&jsobj, user_name.ToCString(), vm_name.ToCString());
    // TODO(regis): parameterizedClass is meaningless and always null.
    const Class& param_cls = Class::Handle(parameterized_class());
    jsobj.AddProperty("parameterizedClass", param_cls);
    if (ref) {
      return;
    }
    jsobj.AddProperty("parameterIndex", index());
    const AbstractType& upper_bound = AbstractType::Handle(bound());
    jsobj.AddProperty("bound", upper_bound);
  }
  
  void Number::PrintJSONImpl(JSONStream* stream, bool ref) const {
    UNREACHABLE();
  }
  
  void Integer::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    PrintSharedInstanceJSON(&jsobj, ref);
    jsobj.AddProperty("kind", "Int");
    jsobj.AddServiceId(*this);
    jsobj.AddProperty("valueAsString", ToCString());
  }
  
  void Smi::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    PrintSharedInstanceJSON(&jsobj, ref);
    jsobj.AddProperty("kind", "Int");
    jsobj.AddFixedServiceId("objects/int-%" Pd "", Value());
    jsobj.AddPropertyF("valueAsString", "%" Pd "", Value());
  }
  
  void Mint::PrintJSONImpl(JSONStream* stream, bool ref) const {
    Integer::PrintJSONImpl(stream, ref);
  }
  
  void Double::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    PrintSharedInstanceJSON(&jsobj, ref);
    jsobj.AddProperty("kind", "Double");
    jsobj.AddServiceId(*this);
    jsobj.AddProperty("valueAsString", ToCString());
  }
  
  void String::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    if (ptr() == Symbols::OptimizedOut().ptr()) {
      // TODO(turnidge): This is a hack.  The user could have this
      // special string in their program.  Fixing this involves updating
      // the debugging api a bit.
      jsobj.AddProperty("type", "Sentinel");
      jsobj.AddProperty("kind", "OptimizedOut");
      jsobj.AddProperty("valueAsString", "<optimized out>");
      return;
    }
    PrintSharedInstanceJSON(&jsobj, ref);
    jsobj.AddProperty("kind", "String");
    jsobj.AddServiceId(*this);
    jsobj.AddProperty("length", Length());
    if (ref) {
      // String refs always truncate to a fixed count;
      const intptr_t kFixedCount = 128;
      if (jsobj.AddPropertyStr("valueAsString", *this, 0, kFixedCount)) {
        jsobj.AddProperty("count", kFixedCount);
        jsobj.AddProperty("valueAsStringIsTruncated", true);
      }
      return;
    }
  
    intptr_t offset;
    intptr_t count;
    stream->ComputeOffsetAndCount(Length(), &offset, &count);
    if (offset > 0) {
      jsobj.AddProperty("offset", offset);
    }
    if (count < Length()) {
      jsobj.AddProperty("count", count);
    }
    jsobj.AddPropertyStr("valueAsString", *this, offset, count);
  }
  
  void Bool::PrintJSONImpl(JSONStream* stream, bool ref) const {
    const char* str = ToCString();
    JSONObject jsobj(stream);
    PrintSharedInstanceJSON(&jsobj, ref);
    jsobj.AddProperty("kind", "Bool");
    jsobj.AddFixedServiceId("objects/bool-%s", str);
    jsobj.AddPropertyF("valueAsString", "%s", str);
  }
  
  void Array::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    PrintSharedInstanceJSON(&jsobj, ref);
    jsobj.AddProperty("kind", "List");
    jsobj.AddServiceId(*this);
    jsobj.AddProperty("length", Length());
    if (ref) {
      return;
    }
    intptr_t offset;
    intptr_t count;
    stream->ComputeOffsetAndCount(Length(), &offset, &count);
    if (offset > 0) {
      jsobj.AddProperty("offset", offset);
    }
    if (count < Length()) {
      jsobj.AddProperty("count", count);
    }
    intptr_t limit = offset + count;
    ASSERT(limit <= Length());
    {
      JSONArray jsarr(&jsobj, "elements");
      Object& element = Object::Handle();
      for (intptr_t index = offset; index < limit; index++) {
        element = At(index);
        jsarr.AddValue(element);
      }
    }
  }
  
  void GrowableObjectArray::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    PrintSharedInstanceJSON(&jsobj, ref);
    jsobj.AddProperty("kind", "List");
    jsobj.AddServiceId(*this);
    jsobj.AddProperty("length", Length());
    if (ref) {
      return;
    }
    intptr_t offset;
    intptr_t count;
    stream->ComputeOffsetAndCount(Length(), &offset, &count);
    if (offset > 0) {
      jsobj.AddProperty("offset", offset);
    }
    if (count < Length()) {
      jsobj.AddProperty("count", count);
    }
    intptr_t limit = offset + count;
    ASSERT(limit <= Length());
    {
      JSONArray jsarr(&jsobj, "elements");
      Object& element = Object::Handle();
      for (intptr_t index = offset; index < limit; index++) {
        element = At(index);
        jsarr.AddValue(element);
      }
    }
  }
  
  void LinkedHashMap::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    PrintSharedInstanceJSON(&jsobj, ref);
    jsobj.AddProperty("kind", "Map");
    jsobj.AddServiceId(*this);
    jsobj.AddProperty("length", Length());
    if (ref) {
      return;
    }
    intptr_t offset;
    intptr_t count;
    stream->ComputeOffsetAndCount(Length(), &offset, &count);
    if (offset > 0) {
      jsobj.AddProperty("offset", offset);
    }
    if (count < Length()) {
      jsobj.AddProperty("count", count);
    }
    intptr_t limit = offset + count;
    ASSERT(limit <= Length());
    {
      JSONArray jsarr(&jsobj, "associations");
      Object& object = Object::Handle();
      LinkedHashMap::Iterator iterator(*this);
      int i = 0;
      while (iterator.MoveNext() && i < limit) {
        if (i >= offset) {
          JSONObject jsassoc(&jsarr);
          object = iterator.CurrentKey();
          jsassoc.AddProperty("key", object);
          object = iterator.CurrentValue();
          jsassoc.AddProperty("value", object);
        }
        i++;
      }
    }
  }
  
  void LinkedHashSet::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    PrintSharedInstanceJSON(&jsobj, ref);
    jsobj.AddProperty("kind", "PlainInstance");
    jsobj.AddServiceId(*this);
    jsobj.AddProperty("length", Length());
    if (ref) {
      return;
    }
    intptr_t offset;
    intptr_t count;
    stream->ComputeOffsetAndCount(Length(), &offset, &count);
    if (offset > 0) {
      jsobj.AddProperty("offset", offset);
    }
    if (count < Length()) {
      jsobj.AddProperty("count", count);
    }
    intptr_t limit = offset + count;
    ASSERT(limit <= Length());
    {
      JSONArray jsarr(&jsobj, "elements");
      Object& object = Object::Handle();
      LinkedHashSet::Iterator iterator(*this);
      int i = 0;
      while (iterator.MoveNext() && i < limit) {
        if (i >= offset) {
          object = iterator.CurrentKey();
          jsarr.AddValue(object);
        }
        i++;
      }
    }
  }
  
  void Float32x4::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    PrintSharedInstanceJSON(&jsobj, ref);
    jsobj.AddProperty("kind", "Float32x4");
    jsobj.AddServiceId(*this);
    jsobj.AddProperty("valueAsString", ToCString());
  }
  
  void Int32x4::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    PrintSharedInstanceJSON(&jsobj, ref);
    jsobj.AddProperty("kind", "Int32x4");
    jsobj.AddServiceId(*this);
    jsobj.AddProperty("valueAsString", ToCString());
  }
  
  void Float64x2::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    PrintSharedInstanceJSON(&jsobj, ref);
    jsobj.AddProperty("kind", "Float64x2");
    jsobj.AddServiceId(*this);
    jsobj.AddProperty("valueAsString", ToCString());
  }
  
  void TypedDataBase::PrintJSONImpl(JSONStream* stream, bool ref) const {
    UNREACHABLE();
  }
  
  void TypedData::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    PrintSharedInstanceJSON(&jsobj, ref);
    const Class& cls = Class::Handle(clazz());
    const char* kind = cls.UserVisibleNameCString();
    jsobj.AddProperty("kind", kind);
    jsobj.AddServiceId(*this);
    jsobj.AddProperty("length", Length());
    if (ref) {
      return;
    }
    intptr_t offset;
    intptr_t count;
    stream->ComputeOffsetAndCount(Length(), &offset, &count);
    if (offset > 0) {
      jsobj.AddProperty("offset", offset);
    }
    if (count < Length()) {
      jsobj.AddProperty("count", count);
    }
    if (count == 0) {
      jsobj.AddProperty("bytes", "");
    } else {
      NoSafepointScope no_safepoint;
      jsobj.AddPropertyBase64("bytes",
                              reinterpret_cast<const uint8_t*>(
                                  DataAddr(offset * ElementSizeInBytes())),
                              count * ElementSizeInBytes());
    }
  }
  
  void TypedDataView::PrintJSONImpl(JSONStream* stream, bool ref) const {
    Instance::PrintJSONImpl(stream, ref);
  }
  
  void ExternalTypedData::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    PrintSharedInstanceJSON(&jsobj, ref);
    const Class& cls = Class::Handle(clazz());
    const char* kind = cls.UserVisibleNameCString();
    jsobj.AddProperty("kind", kind);
    jsobj.AddServiceId(*this);
    jsobj.AddProperty("length", Length());
    if (ref) {
      return;
    }
    intptr_t offset;
    intptr_t count;
    stream->ComputeOffsetAndCount(Length(), &offset, &count);
    if (offset > 0) {
      jsobj.AddProperty("offset", offset);
    }
    if (count < Length()) {
      jsobj.AddProperty("count", count);
    }
    if (count == 0) {
      jsobj.AddProperty("bytes", "");
    } else {
      NoSafepointScope no_safepoint;
      jsobj.AddPropertyBase64("bytes",
                              reinterpret_cast<const uint8_t*>(
                                  DataAddr(offset * ElementSizeInBytes())),
                              count * ElementSizeInBytes());
    }
  }
  
  void Pointer::PrintJSONImpl(JSONStream* stream, bool ref) const {
    // TODO(dacoharkes): what is the JSONStream used for?
    // should it fail because it's not supported?
    // or should it print something reasonable as default?
    Instance::PrintJSONImpl(stream, ref);
  }
  
  void DynamicLibrary::PrintJSONImpl(JSONStream* stream, bool ref) const {
    // TODO(dacoharkes): what is the JSONStream used for?
    // should it fail because it's not supported?
    // or should it print something reasonable as default?
    Instance::PrintJSONImpl(stream, ref);
  }
  
  void Capability::PrintJSONImpl(JSONStream* stream, bool ref) const {
    Instance::PrintJSONImpl(stream, ref);
  }
  
  void ReceivePort::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject obj(stream);
    Instance::PrintSharedInstanceJSON(&obj, ref);
    const StackTrace& allocation_location_ =
        StackTrace::Handle(allocation_location());
    const String& debug_name_ = String::Handle(debug_name());
    obj.AddServiceId(*this);
    obj.AddProperty("kind", "ReceivePort");
    obj.AddProperty64("portId", Id());
    obj.AddProperty("debugName", debug_name_.ToCString());
    obj.AddProperty("allocationLocation", allocation_location_);
  }
  
  void SendPort::PrintJSONImpl(JSONStream* stream, bool ref) const {
    Instance::PrintJSONImpl(stream, ref);
  }
  
  void TransferableTypedData::PrintJSONImpl(JSONStream* stream, bool ref) const {
    Instance::PrintJSONImpl(stream, ref);
  }
  
  void ClosureData::PrintJSONImpl(JSONStream* stream, bool ref) const {
    Object::PrintJSONImpl(stream, ref);
  }
  
  void Closure::PrintJSONImpl(JSONStream* stream, bool ref) const {
    Instance::PrintJSONImpl(stream, ref);
  }
  
  void StackTrace::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    PrintSharedInstanceJSON(&jsobj, ref);
    jsobj.AddProperty("kind", "StackTrace");
    jsobj.AddServiceId(*this);
    jsobj.AddProperty("valueAsString", ToCString());
  }
  
  void RegExp::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    PrintSharedInstanceJSON(&jsobj, ref);
    jsobj.AddProperty("kind", "RegExp");
    jsobj.AddServiceId(*this);
  
    jsobj.AddProperty("pattern", String::Handle(pattern()));
  
    if (ref) {
      return;
    }
  
    jsobj.AddProperty("isCaseSensitive", !flags().IgnoreCase());
    jsobj.AddProperty("isMultiLine", flags().IsMultiLine());
  
    if (!FLAG_interpret_irregexp) {
      Function& func = Function::Handle();
      func = function(kOneByteStringCid, /*sticky=*/false);
      jsobj.AddProperty("_oneByteFunction", func);
      func = function(kTwoByteStringCid, /*sticky=*/false);
      jsobj.AddProperty("_twoByteFunction", func);
      func = function(kExternalOneByteStringCid, /*sticky=*/false);
      jsobj.AddProperty("_externalOneByteFunction", func);
      func = function(kExternalTwoByteStringCid, /*sticky=*/false);
      jsobj.AddProperty("_externalTwoByteFunction", func);
      func = function(kOneByteStringCid, /*sticky=*/true);
      jsobj.AddProperty("_oneByteFunctionSticky", func);
      func = function(kTwoByteStringCid, /*sticky=*/true);
      jsobj.AddProperty("_twoByteFunctionSticky", func);
      func = function(kExternalOneByteStringCid, /*sticky=*/true);
      jsobj.AddProperty("_externalOneByteFunctionSticky", func);
      func = function(kExternalTwoByteStringCid, /*sticky=*/true);
      jsobj.AddProperty("_externalTwoByteFunctionSticky", func);
    } else {
      TypedData& bc = TypedData::Handle();
      bc = bytecode(/*is_one_byte=*/true, /*sticky=*/false);
      jsobj.AddProperty("_oneByteBytecode", bc);
      bc = bytecode(/*is_one_byte=*/false, /*sticky=*/false);
      jsobj.AddProperty("_twoByteBytecode", bc);
      bc = bytecode(/*is_one_byte=*/true, /*sticky=*/true);
      jsobj.AddProperty("_oneByteBytecodeSticky", bc);
      bc = bytecode(/*is_one_byte=*/false, /*sticky=*/true);
      jsobj.AddProperty("_twoByteBytecodeSticky", bc);
    }
  }
  
  void WeakProperty::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    PrintSharedInstanceJSON(&jsobj, ref);
    jsobj.AddProperty("kind", "WeakProperty");
    jsobj.AddServiceId(*this);
    if (ref) {
      return;
    }
  
    const Object& key_handle = Object::Handle(key());
    jsobj.AddProperty("propertyKey", key_handle);
    const Object& value_handle = Object::Handle(value());
    jsobj.AddProperty("propertyValue", value_handle);
  }
  
  void MirrorReference::PrintJSONImpl(JSONStream* stream, bool ref) const {
    JSONObject jsobj(stream);
    PrintSharedInstanceJSON(&jsobj, ref);
    jsobj.AddProperty("kind", "MirrorReference");
    jsobj.AddServiceId(*this);
  
    if (ref) {
      return;
    }
  
    const Object& referent_handle = Object::Handle(referent());
    jsobj.AddProperty("mirrorReferent", referent_handle);
  }
  
  void UserTag::PrintJSONImpl(JSONStream* stream, bool ref) const {
    Instance::PrintJSONImpl(stream, ref);
  }
  
  void FutureOr::PrintJSONImpl(JSONStream* stream, bool ref) const {
    Instance::PrintJSONImpl(stream, ref);
  }
  
  #endif
  
  }  // namespace dart
  