Version 2.19.0-41.0.dev

Merge commit '3b8817e7fcd4abea2c8274543c101ad785788a20' into 'dev'
diff --git a/pkg/native_stack_traces/CHANGELOG.md b/pkg/native_stack_traces/CHANGELOG.md
index 39f1612..a1b47ce 100644
--- a/pkg/native_stack_traces/CHANGELOG.md
+++ b/pkg/native_stack_traces/CHANGELOG.md
@@ -1,7 +1,3 @@
-## 0.5.1
-
-- Exported more ELF utilities for use in Dart tests.
-
 ## 0.5.0
 
 - Require Dart >= 2.17 (enhanced enum support)
diff --git a/pkg/native_stack_traces/lib/elf.dart b/pkg/native_stack_traces/lib/elf.dart
index 7c6e4bb..31272b1 100644
--- a/pkg/native_stack_traces/lib/elf.dart
+++ b/pkg/native_stack_traces/lib/elf.dart
@@ -8,12 +8,4 @@
         isolateSymbolName,
         vmDataSymbolName,
         vmSymbolName;
-export 'src/elf.dart'
-    show
-        DynamicTable,
-        DynamicTableTag,
-        Elf,
-        Section,
-        Symbol,
-        SymbolBinding,
-        SymbolType;
+export 'src/elf.dart' show DynamicTable, DynamicTableTag, Elf, Section, Symbol;
diff --git a/pkg/native_stack_traces/lib/src/elf.dart b/pkg/native_stack_traces/lib/src/elf.dart
index 6b8bbbe..d8328eb 100644
--- a/pkg/native_stack_traces/lib/src/elf.dart
+++ b/pkg/native_stack_traces/lib/src/elf.dart
@@ -696,19 +696,16 @@
   }
 }
 
-/// An enumeration of recognized symbol binding values used by the ELF format.
 enum SymbolBinding {
   STB_LOCAL,
   STB_GLOBAL,
   STB_WEAK,
 }
 
-/// An enumeration of recognized symbol types used by the ELF format.
 enum SymbolType {
   STT_NOTYPE,
   STT_OBJECT,
   STT_FUNC,
-  STT_SECTION,
 }
 
 enum SymbolVisibility {
@@ -1009,17 +1006,6 @@
     return null;
   }
 
-  /// Returns an iterable of the symbols in the dynamic symbol table(s).
-  /// The ordering of the symbols is not guaranteed.
-  Iterable<Symbol> get dynamicSymbols sync* {
-    for (final section in namedSections('.dynsym')) {
-      final dynsym = section as SymbolTable;
-      for (final symbol in dynsym.values) {
-        yield symbol;
-      }
-    }
-  }
-
   /// Reverse lookup of the static symbol that contains the given virtual
   /// address. Returns null if no static symbol matching the address is found.
   @override
@@ -1042,17 +1028,6 @@
     return bestSym;
   }
 
-  /// Returns an iterable of the symbols in the static symbol table(s).
-  /// The ordering of the symbols is not guaranteed.
-  Iterable<Symbol> get staticSymbols sync* {
-    for (final section in namedSections('.symtab')) {
-      final symtab = section as SymbolTable;
-      for (final symbol in symtab.values) {
-        yield symbol;
-      }
-    }
-  }
-
   /// Creates an [Elf] from the data pointed to by [reader].
   ///
   /// After succesful completion, the [endian] and [wordSize] fields of the
diff --git a/runtime/tests/vm/dart/readonly_data_symbols_test.dart b/runtime/tests/vm/dart/readonly_data_symbols_test.dart
deleted file mode 100644
index 3ccad9b..0000000
--- a/runtime/tests/vm/dart/readonly_data_symbols_test.dart
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright (c) 2022, 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.
-
-// This test checks that gen_snapshot outputs static symbols for read-only
-// data objects.
-
-// OtherResources=use_save_debugging_info_flag_program.dart
-
-import "dart:async";
-import "dart:io";
-
-import 'package:expect/expect.dart';
-import 'package:native_stack_traces/elf.dart';
-import 'package:path/path.dart' as path;
-
-import 'use_flag_test_helper.dart';
-
-main(List<String> args) async {
-  if (!isAOTRuntime) {
-    return; // Running in JIT: AOT binaries not available.
-  }
-
-  if (Platform.isAndroid) {
-    return; // SDK tree and dart_bootstrap not available on the test device.
-  }
-
-  // These are the tools we need to be available to run on a given platform:
-  if (!await testExecutable(genSnapshot)) {
-    throw "Cannot run test as $genSnapshot not available";
-  }
-  if (!await testExecutable(aotRuntime)) {
-    throw "Cannot run test as $aotRuntime not available";
-  }
-  if (!File(platformDill).existsSync()) {
-    throw "Cannot run test as $platformDill does not exist";
-  }
-
-  await withTempDir('readonly-symbols-flag-test', (String tempDir) async {
-    final cwDir = path.dirname(Platform.script.toFilePath());
-    final script =
-        path.join(cwDir, 'use_save_debugging_info_flag_program.dart');
-    final scriptDill = path.join(tempDir, 'flag_program.dill');
-
-    // Compile script to Kernel IR.
-    await run(genKernel, <String>[
-      '--aot',
-      '--platform=$platformDill',
-      '-o',
-      scriptDill,
-      script,
-    ]);
-
-    final scriptSnapshot = path.join(tempDir, 'dwarf.so');
-    final scriptDebuggingInfo = path.join(tempDir, 'debug_info.so');
-    await run(genSnapshot, <String>[
-      '--dwarf-stack-traces-mode',
-      '--save-debugging-info=$scriptDebuggingInfo',
-      '--snapshot-kind=app-aot-elf',
-      '--elf=$scriptSnapshot',
-      scriptDill,
-    ]);
-
-    checkElf(scriptSnapshot);
-    checkElf(scriptDebuggingInfo);
-  });
-}
-
-void checkElf(String filename) {
-  // Check that the static symbol table contains entries that are not in the
-  // dynamic symbol table, have STB_LOCAL binding, and are of type STT_OBJECT.
-  final elf = Elf.fromFile(filename);
-  Expect.isNotNull(elf);
-  final dynamicSymbols = elf!.dynamicSymbols.toList();
-  for (final symbol in dynamicSymbols) {
-    // All symbol tables have an initial entry with zero-valued fields.
-    if (symbol.name == '') {
-      Expect.equals(SymbolBinding.STB_LOCAL, symbol.bind);
-      Expect.equals(SymbolType.STT_NOTYPE, symbol.type);
-      Expect.equals(0, symbol.value);
-    } else {
-      Expect.equals(SymbolBinding.STB_GLOBAL, symbol.bind);
-      Expect.equals(SymbolType.STT_OBJECT, symbol.type);
-      Expect.isTrue(symbol.name.startsWith('_kDart'),
-          'unexpected symbol name ${symbol.name}');
-    }
-  }
-  final onlyStaticSymbols = elf.staticSymbols
-      .where((s1) => !dynamicSymbols.any((s2) => s1.name == s2.name));
-  Expect.isNotEmpty(onlyStaticSymbols, 'no static-only symbols');
-  final objectSymbols =
-      onlyStaticSymbols.where((s) => s.type == SymbolType.STT_OBJECT);
-  Expect.isNotEmpty(objectSymbols, 'no static-only object symbols');
-  for (final symbol in objectSymbols) {
-    // Currently we only write local object symbols.
-    Expect.equals(SymbolBinding.STB_LOCAL, symbol.bind);
-    // All object symbols are prefixed with the type of the C++ object.
-    final objectType = symbol.name.substring(0, symbol.name.indexOf('_'));
-    switch (objectType) {
-      // Used for entries in the non-clustered portion of the read-only data
-      // section that don't correspond to a specific Dart object.
-      case 'RawBytes':
-      // Currently the only types of objects written to the non-clustered
-      // portion of the read-only data section.
-      case 'OneByteString':
-      case 'TwoByteString':
-      case 'CodeSourceMap':
-      case 'PcDescriptors':
-      case 'CompressedStackMaps':
-        break;
-      default:
-        Expect.fail('unexpected object type $objectType');
-    }
-  }
-}
diff --git a/runtime/tests/vm/dart_2/readonly_data_symbols_test.dart b/runtime/tests/vm/dart_2/readonly_data_symbols_test.dart
deleted file mode 100644
index c646840..0000000
--- a/runtime/tests/vm/dart_2/readonly_data_symbols_test.dart
+++ /dev/null
@@ -1,117 +0,0 @@
-// Copyright (c) 2022, 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.
-
-// @dart = 2.9
-
-// This test checks that gen_snapshot outputs static symbols for read-only data
-// objects.
-
-// OtherResources=use_save_debugging_info_flag_program.dart
-
-import "dart:async";
-import "dart:io";
-
-import 'package:expect/expect.dart';
-import 'package:native_stack_traces/elf.dart';
-import 'package:path/path.dart' as path;
-
-import 'use_flag_test_helper.dart';
-
-main(List<String> args) async {
-  if (!isAOTRuntime) {
-    return; // Running in JIT: AOT binaries not available.
-  }
-
-  if (Platform.isAndroid) {
-    return; // SDK tree and dart_bootstrap not available on the test device.
-  }
-
-  // These are the tools we need to be available to run on a given platform:
-  if (!await testExecutable(genSnapshot)) {
-    throw "Cannot run test as $genSnapshot not available";
-  }
-  if (!await testExecutable(aotRuntime)) {
-    throw "Cannot run test as $aotRuntime not available";
-  }
-  if (!File(platformDill).existsSync()) {
-    throw "Cannot run test as $platformDill does not exist";
-  }
-
-  await withTempDir('readonly-symbols-flag-test', (String tempDir) async {
-    final cwDir = path.dirname(Platform.script.toFilePath());
-    final script =
-        path.join(cwDir, 'use_save_debugging_info_flag_program.dart');
-    final scriptDill = path.join(tempDir, 'flag_program.dill');
-
-    // Compile script to Kernel IR.
-    await run(genKernel, <String>[
-      '--aot',
-      '--platform=$platformDill',
-      '-o',
-      scriptDill,
-      script,
-    ]);
-
-    final scriptSnapshot = path.join(tempDir, 'dwarf.so');
-    final scriptDebuggingInfo = path.join(tempDir, 'debug_info.so');
-    await run(genSnapshot, <String>[
-      '--dwarf-stack-traces-mode',
-      '--save-debugging-info=$scriptDebuggingInfo',
-      '--snapshot-kind=app-aot-elf',
-      '--elf=$scriptSnapshot',
-      scriptDill,
-    ]);
-
-    checkElf(scriptSnapshot);
-    checkElf(scriptDebuggingInfo);
-  });
-}
-
-void checkElf(String filename) {
-  // Check that the static symbol table contains entries that are not in the
-  // dynamic symbol table, have STB_LOCAL binding, and are of type STT_OBJECT.
-  final elf = Elf.fromFile(filename);
-  Expect.isNotNull(elf);
-  final dynamicSymbols = elf.dynamicSymbols.toList();
-  for (final symbol in dynamicSymbols) {
-    // All symbol tables have an initial entry with zero-valued fields.
-    if (symbol.name == '') {
-      Expect.equals(SymbolBinding.STB_LOCAL, symbol.bind);
-      Expect.equals(SymbolType.STT_NOTYPE, symbol.type);
-      Expect.equals(0, symbol.value);
-    } else {
-      Expect.equals(SymbolBinding.STB_GLOBAL, symbol.bind);
-      Expect.equals(SymbolType.STT_OBJECT, symbol.type);
-      Expect.isTrue(symbol.name.startsWith('_kDart'),
-          'unexpected symbol name ${symbol.name}');
-    }
-  }
-  final onlyStaticSymbols = elf.staticSymbols
-      .where((s1) => !dynamicSymbols.any((s2) => s1.name == s2.name));
-  Expect.isNotEmpty(onlyStaticSymbols, 'no static-only symbols');
-  final objectSymbols =
-      onlyStaticSymbols.where((s) => s.type == SymbolType.STT_OBJECT);
-  Expect.isNotEmpty(objectSymbols, 'no static-only object symbols');
-  for (final symbol in objectSymbols) {
-    // Currently we only write local object symbols.
-    Expect.equals(SymbolBinding.STB_LOCAL, symbol.bind);
-    // All object symbols are prefixed with the type of the C++ object.
-    final objectType = symbol.name.substring(0, symbol.name.indexOf('_'));
-    switch (objectType) {
-      // Used for entries in the non-clustered portion of the read-only data
-      // section that don't correspond to a specific Dart object.
-      case 'RawBytes':
-      // Currently the only types of objects written to the non-clustered
-      // portion of the read-only data section.
-      case 'OneByteString':
-      case 'TwoByteString':
-      case 'CodeSourceMap':
-      case 'PcDescriptors':
-      case 'CompressedStackMaps':
-        break;
-      default:
-        Expect.fail('unexpected object type $objectType');
-    }
-  }
-}
diff --git a/runtime/vm/app_snapshot.cc b/runtime/vm/app_snapshot.cc
index 14cc87c..ea36ec5 100644
--- a/runtime/vm/app_snapshot.cc
+++ b/runtime/vm/app_snapshot.cc
@@ -7288,11 +7288,7 @@
 }
 
 uint32_t Serializer::GetDataOffset(ObjectPtr object) const {
-#if defined(SNAPSHOT_BACKTRACE)
-  return image_writer_->GetDataOffsetFor(object, ParentOf(object));
-#else
   return image_writer_->GetDataOffsetFor(object);
-#endif
 }
 
 intptr_t Serializer::GetDataSize() const {
@@ -7397,16 +7393,7 @@
 }
 
 #if defined(SNAPSHOT_BACKTRACE)
-ObjectPtr Serializer::ParentOf(ObjectPtr object) const {
-  for (intptr_t i = 0; i < parent_pairs_.length(); i += 2) {
-    if (parent_pairs_[i]->ptr() == object) {
-      return parent_pairs_[i + 1]->ptr();
-    }
-  }
-  return Object::null();
-}
-
-ObjectPtr Serializer::ParentOf(const Object& object) const {
+ObjectPtr Serializer::ParentOf(const Object& object) {
   for (intptr_t i = 0; i < parent_pairs_.length(); i += 2) {
     if (parent_pairs_[i]->ptr() == object.ptr()) {
       return parent_pairs_[i + 1]->ptr();
diff --git a/runtime/vm/app_snapshot.h b/runtime/vm/app_snapshot.h
index 3445cb4..922c8ac 100644
--- a/runtime/vm/app_snapshot.h
+++ b/runtime/vm/app_snapshot.h
@@ -19,6 +19,10 @@
 #include "vm/snapshot.h"
 #include "vm/version.h"
 
+#if defined(DEBUG)
+#define SNAPSHOT_BACKTRACE
+#endif
+
 namespace dart {
 
 // For full snapshots, we use a clustered snapshot format that trades longer
@@ -240,8 +244,7 @@
 
   void UnexpectedObject(ObjectPtr object, const char* message);
 #if defined(SNAPSHOT_BACKTRACE)
-  ObjectPtr ParentOf(ObjectPtr object) const;
-  ObjectPtr ParentOf(const Object& object) const;
+  ObjectPtr ParentOf(const Object& object);
 #endif
 
   SerializationCluster* NewClusterForClass(intptr_t cid, bool is_canonical);
diff --git a/runtime/vm/image_snapshot.cc b/runtime/vm/image_snapshot.cc
index 508fb3e4..8115931 100644
--- a/runtime/vm/image_snapshot.cc
+++ b/runtime/vm/image_snapshot.cc
@@ -179,9 +179,6 @@
       next_text_offset_(0),
       objects_(),
       instructions_(),
-#if defined(DART_PRECOMPILER)
-      namer_(t->zone()),
-#endif
       image_type_(TagObjectTypeAsReadOnly(zone_, "Image")),
       instructions_section_type_(
           TagObjectTypeAsReadOnly(zone_, "InstructionsSection")),
@@ -289,20 +286,11 @@
   }
 }
 
-#if defined(SNAPSHOT_BACKTRACE)
-uint32_t ImageWriter::GetDataOffsetFor(ObjectPtr raw_object,
-                                       ObjectPtr raw_parent) {
-#else
 uint32_t ImageWriter::GetDataOffsetFor(ObjectPtr raw_object) {
-#endif
   const intptr_t snap_size = SizeInSnapshot(raw_object);
   const intptr_t offset = next_data_offset_;
   next_data_offset_ += snap_size;
-#if defined(SNAPSHOT_BACKTRACE)
-  objects_.Add(ObjectData(raw_object, raw_parent));
-#else
   objects_.Add(ObjectData(raw_object));
-#endif
   return offset;
 }
 
@@ -467,11 +455,8 @@
     heap->SetObjectId(data.insns_->ptr(), 0);
   }
   for (auto& data : objects_) {
-    if (data.is_object()) {
+    if (data.is_object) {
       data.obj = &Object::Handle(zone_, data.raw_obj);
-#if defined(SNAPSHOT_BACKTRACE)
-      data.parent = &Object::Handle(zone_, data.raw_parent);
-#endif
     }
   }
 
@@ -479,14 +464,11 @@
   // to string objects. String is used for simplicity as a bit container,
   // can't use TypedData because it has an internal pointer (data_) field.
   for (auto& data : objects_) {
-    if (!data.is_object()) {
+    if (!data.is_object) {
       const auto bytes = data.bytes;
       data.obj = &Object::Handle(
           zone_, OneByteString::New(bytes.buf, bytes.length, Heap::kOld));
-#if defined(SNAPSHOT_BACKTRACE)
-      data.parent = &Object::null_object();
-#endif
-      data.set_is_object(true);
+      data.is_object = true;
       String::Cast(*data.obj).Hash();
       free(bytes.buf);
     }
@@ -507,8 +489,13 @@
 }
 
 void ImageWriter::WriteROData(NonStreamingWriteStream* stream, bool vm) {
-  ASSERT(Utils::IsAligned(stream->Position(), kRODataAlignment));
+#if defined(DART_PRECOMPILER)
+  const intptr_t start_position = stream->Position();
+#endif
+  stream->Align(ImageWriter::kRODataAlignment);
+
   // Heap page starts here.
+
   intptr_t section_start = stream->Position();
 
   stream->WriteWord(next_data_offset_);  // Data length.
@@ -518,20 +505,20 @@
   ASSERT_EQUAL(stream->Position() - section_start, Image::kHeaderSize);
 #if defined(DART_PRECOMPILER)
   if (profile_writer_ != nullptr) {
-    // Attribute the Image header to the artificial root.
+    const intptr_t end_position = stream->Position();
     profile_writer_->AttributeBytesTo(
-        V8SnapshotProfileWriter::kArtificialRootId, Image::kHeaderSize);
+        V8SnapshotProfileWriter::kArtificialRootId,
+        end_position - start_position);
   }
 #endif
 
   // Heap page objects start here.
 
   for (auto entry : objects_) {
-    ASSERT(entry.is_object());
+    ASSERT(entry.is_object);
     const Object& obj = *entry.obj;
 #if defined(DART_PRECOMPILER)
     AutoTraceImage(obj, section_start, stream);
-    const char* object_name = namer_.SnapshotNameFor(entry);
 #endif
     auto const object_start = stream->Position();
 
@@ -581,9 +568,6 @@
     }
     stream->Align(compiler::target::ObjectAlignment::kObjectAlignment);
     ASSERT_EQUAL(stream->Position() - object_start, SizeInSnapshot(obj));
-#if defined(DART_PRECOMPILER)
-    AddDataSymbol(object_name, object_start, stream->Position() - object_start);
-#endif
   }
 }
 
@@ -788,6 +772,7 @@
 
 #if defined(DART_PRECOMPILER)
   PcDescriptors& descriptors = PcDescriptors::Handle(zone_);
+  SnapshotTextObjectNamer namer(zone_);
 #endif
 
   ASSERT(offset_space_ != IdSpace::kSnapshot);
@@ -797,7 +782,10 @@
     ASSERT_EQUAL(data.text_offset_, text_offset);
 
 #if defined(DART_PRECOMPILER)
-    const char* object_name = namer_.SnapshotNameFor(data);
+    // We won't add trampolines as symbols, so their name need not be unique
+    // across different WriteText() calls.
+    const char* object_name = namer.SnapshotNameFor(
+        is_trampoline ? i : unique_symbol_counter_++, data);
 
     if (profile_writer_ != nullptr) {
       const V8SnapshotProfileWriter::ObjectId id(offset_space_, text_offset);
@@ -1118,7 +1106,7 @@
   }
 }
 
-static void AddAssemblerIdentifier(BaseTextBuffer* printer, const char* label) {
+static void AddAssemblerIdentifier(ZoneTextBuffer* printer, const char* label) {
   ASSERT(label[0] != '.');
   if (label[0] == 'L' && printer->length() == 0) {
     // Assembler treats labels starting with `L` as local which can cause
@@ -1171,89 +1159,47 @@
   }
 }
 
-void ImageWriter::SnapshotTextObjectNamer::AddNonUniqueNameFor(
-    BaseTextBuffer* buffer,
-    const Object& object) {
-  if (object.IsCode()) {
-    const Code& code = Code::Cast(object);
-    owner_ = WeakSerializationReference::Unwrap(code.owner());
-    if (owner_.IsNull()) {
-      buffer->AddString("Stub_");
-      insns_ = code.instructions();
-      const char* name = StubCode::NameOfStub(insns_.EntryPoint());
-      ASSERT(name != nullptr);
-      buffer->AddString(name);
-    } else {
-      if (owner_.IsClass()) {
-        buffer->AddString("AllocationStub_");
-      } else if (!owner_.IsAbstractType() && !owner_.IsFunction()) {
-        // Double-check that we didn't get an invalid owner for the Code object.
-        UNREACHABLE();
-      }
-      AddNonUniqueNameFor(buffer, owner_);
-    }
-  } else if (object.IsClass()) {
-    const char* name = Class::Cast(object).ScrubbedNameCString();
-    AddAssemblerIdentifier(buffer, name);
-  } else if (object.IsAbstractType()) {
-    namer_.WriteStubNameForTypeTo(buffer, AbstractType::Cast(object));
-  } else if (object.IsFunction()) {
-    const char* name = Function::Cast(object).QualifiedScrubbedNameCString();
-    AddAssemblerIdentifier(buffer, name);
-  } else if (object.IsCompressedStackMaps()) {
-    buffer->AddString("CompressedStackMaps");
-  } else if (object.IsPcDescriptors()) {
-    buffer->AddString("PcDescriptors");
-  } else if (object.IsCodeSourceMap()) {
-    buffer->AddString("CodeSourceMap");
-  } else if (object.IsString()) {
-    const String& str = String::Cast(object);
-    if (str.IsOneByteString()) {
-      buffer->AddString("OneByteString");
-    } else if (str.IsTwoByteString()) {
-      buffer->AddString("TwoByteString");
-    }
+const char* SnapshotTextObjectNamer::SnapshotNameFor(intptr_t code_index,
+                                                     const Code& code) {
+  ASSERT(!code.IsNull());
+  owner_ = code.owner();
+  if (owner_.IsNull()) {
+    insns_ = code.instructions();
+    const char* name = StubCode::NameOfStub(insns_.EntryPoint());
+    ASSERT(name != nullptr);
+    return OS::SCreate(zone_, "Stub_%s", name);
+  }
+  // The weak reference to the Code's owner should never have been removed via
+  // an intermediate serialization, since WSRs are only introduced during
+  // precompilation.
+  owner_ = WeakSerializationReference::Unwrap(owner_);
+  ASSERT(!owner_.IsNull());
+  ZoneTextBuffer printer(zone_);
+  if (owner_.IsClass()) {
+    const char* name = Class::Cast(owner_).ScrubbedNameCString();
+    printer.AddString("AllocationStub_");
+    AddAssemblerIdentifier(&printer, name);
+  } else if (owner_.IsAbstractType()) {
+    const char* name = namer_.StubNameForType(AbstractType::Cast(owner_));
+    printer.AddString(name);
+  } else if (owner_.IsFunction()) {
+    const char* name = Function::Cast(owner_).QualifiedScrubbedNameCString();
+    AddAssemblerIdentifier(&printer, name);
   } else {
     UNREACHABLE();
   }
+
+  printer.Printf("_%" Pd, code_index);
+  return printer.buffer();
 }
 
-const char* ImageWriter::SnapshotTextObjectNamer::SnapshotNameFor(
-    const InstructionsData& data) {
-  ZoneTextBuffer printer(zone_);
+const char* SnapshotTextObjectNamer::SnapshotNameFor(
+    intptr_t index,
+    const ImageWriter::InstructionsData& data) {
   if (data.trampoline_bytes != nullptr) {
-    printer.AddString("Trampoline");
-  } else {
-    AddNonUniqueNameFor(&printer, *data.code_);
+    return OS::SCreate(zone_, "Trampoline_%" Pd "", index);
   }
-  printer.Printf("_%" Pd, nonce_++);
-  return printer.buffer();
-}
-
-const char* ImageWriter::SnapshotTextObjectNamer::SnapshotNameFor(
-    const ObjectData& data) {
-  ASSERT(data.is_object());
-  ZoneTextBuffer printer(zone_);
-  if (data.is_original_object()) {
-    const Object& obj = *data.obj;
-    AddNonUniqueNameFor(&printer, obj);
-#if defined(SNAPSHOT_BACKTRACE)
-    // It's less useful knowing the parent of a String than other read-only
-    // data objects, and this avoids us having to handle other classes
-    // in AddNonUniqueNameFor.
-    if (!obj.IsString()) {
-      const Object& parent = *data.parent;
-      if (!parent.IsNull()) {
-        printer.AddString("_");
-        AddNonUniqueNameFor(&printer, parent);
-      }
-    }
-#endif
-  } else {
-    printer.AddString("RawBytes");
-  }
-  printer.Printf("_%" Pd, nonce_++);
-  return printer.buffer();
+  return SnapshotNameFor(index, *data.code_);
 }
 
 void AssemblyImageWriter::WriteBss(bool vm) {
@@ -1269,35 +1215,12 @@
 
 void AssemblyImageWriter::WriteROData(NonStreamingWriteStream* clustered_stream,
                                       bool vm) {
+  ImageWriter::WriteROData(clustered_stream, vm);
   if (!EnterSection(ProgramSection::Data, vm, ImageWriter::kRODataAlignment)) {
     return;
   }
-  // The clustered stream already has some data on it from the serializer, so
-  // make sure that the read-only objects start at the appropriate alignment
-  // within the stream, as we'll write the entire clustered stream to the
-  // assembly output (which was aligned in EnterSection).
-  const intptr_t start_position = clustered_stream->Position();
-  clustered_stream->Align(ImageWriter::kRODataAlignment);
-  if (profile_writer_ != nullptr) {
-    // Attribute any padding needed to the artificial root.
-    const intptr_t padding = clustered_stream->Position() - start_position;
-    profile_writer_->AttributeBytesTo(
-        V8SnapshotProfileWriter::kArtificialRootId, padding);
-  }
-  // First write the read-only data objects to the clustered stream.
-  ImageWriter::WriteROData(clustered_stream, vm);
-  // Next, write the bytes of the clustered stream (along with any symbols
-  // if appropriate) to the assembly output.
-  const uint8_t* bytes = clustered_stream->buffer();
-  const intptr_t len = clustered_stream->bytes_written();
-  intptr_t last_position = 0;
-  for (const auto& symbol : *current_symbols_) {
-    WriteBytes(bytes + last_position, symbol.offset - last_position);
-    assembly_stream_->Printf("%s:\n", symbol.name);
-    last_position = symbol.offset;
-  }
-  WriteBytes(bytes + last_position, len - last_position);
-  ExitSection(ProgramSection::Data, vm, len);
+  WriteBytes(clustered_stream->buffer(), clustered_stream->bytes_written());
+  ExitSection(ProgramSection::Data, vm, clustered_stream->bytes_written());
 }
 
 bool AssemblyImageWriter::EnterSection(ProgramSection section,
@@ -1317,14 +1240,10 @@
       global_symbol = true;
       break;
     case ProgramSection::Data:
-      // We create a SymbolData array even if there is no debug_elf_ because we
-      // may be writing RO data symbols, and RO data is written in two steps:
-      // 1. Serializing the read-only data objects to the clustered stream
-      // 2. Writing the bytes of the clustered stream to the assembly output.
-      // Thus, we'll need to interleave the symbols with the cluster bytes
-      // during step 2.
-      current_symbols_ =
-          new (zone_) ZoneGrowableArray<Elf::SymbolData>(zone_, 0);
+      if (debug_elf_ != nullptr) {
+        current_symbols_ =
+            new (zone_) ZoneGrowableArray<Elf::SymbolData>(zone_, 0);
+      }
 #if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) ||        \
     defined(DART_TARGET_OS_FUCHSIA)
       assembly_stream_->WriteString(".section .rodata\n");
@@ -1462,12 +1381,6 @@
   assembly_stream_->Printf("%s:\n", symbol);
 }
 
-void AssemblyImageWriter::AddDataSymbol(const char* symbol,
-                                        intptr_t offset,
-                                        size_t size) {
-  current_symbols_->Add({symbol, elf::STT_OBJECT, offset, size});
-}
-
 void AssemblyImageWriter::FrameUnwindPrologue() {
   // Creates DWARF's .debug_frame
   // CFI = Call frame information
@@ -1591,22 +1504,11 @@
 
 void BlobImageWriter::WriteROData(NonStreamingWriteStream* clustered_stream,
                                   bool vm) {
-#if defined(DART_PRECOMPILER)
-  const intptr_t start_position = clustered_stream->Position();
-#endif
-  current_section_stream_ = ASSERT_NOTNULL(clustered_stream);
+  ImageWriter::WriteROData(clustered_stream, vm);
+  current_section_stream_ = clustered_stream;
   if (!EnterSection(ProgramSection::Data, vm, ImageWriter::kRODataAlignment)) {
     return;
   }
-#if defined(DART_PRECOMPILER)
-  if (profile_writer_ != nullptr) {
-    // Attribute any padding needed to the artificial root.
-    const intptr_t padding = clustered_stream->Position() - start_position;
-    profile_writer_->AttributeBytesTo(
-        V8SnapshotProfileWriter::kArtificialRootId, padding);
-  }
-#endif
-  ImageWriter::WriteROData(clustered_stream, vm);
   ExitSection(ProgramSection::Data, vm, clustered_stream->bytes_written());
 }
 
@@ -1618,6 +1520,7 @@
   ASSERT(current_relocations_ == nullptr);
   ASSERT(current_symbols_ == nullptr);
 #endif
+  // For now, we set current_section_stream_ in ::WriteData.
   ASSERT(section == ProgramSection::Data || current_section_stream_ == nullptr);
   ASSERT(current_section_symbol_ == nullptr);
   switch (section) {
@@ -1632,8 +1535,6 @@
 #endif
       break;
     case ProgramSection::Data:
-      // The stream to use is passed into WriteROData and set there.
-      ASSERT(current_section_stream_ != nullptr);
 #if defined(DART_PRECOMPILER)
       current_relocations_ =
           new (zone_) ZoneGrowableArray<Elf::Relocation>(zone_, 0);
@@ -1713,12 +1614,6 @@
     debug_elf_->dwarf()->AddCode(code, symbol);
   }
 }
-
-void BlobImageWriter::AddDataSymbol(const char* symbol,
-                                    intptr_t offset,
-                                    size_t size) {
-  current_symbols_->Add({symbol, elf::STT_OBJECT, offset, size});
-}
 #endif  // defined(DART_PRECOMPILER)
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
diff --git a/runtime/vm/image_snapshot.h b/runtime/vm/image_snapshot.h
index c1ce151..730b861 100644
--- a/runtime/vm/image_snapshot.h
+++ b/runtime/vm/image_snapshot.h
@@ -22,10 +22,6 @@
 #include "vm/type_testing_stubs.h"
 #include "vm/v8_snapshot_writer.h"
 
-#if defined(DEBUG)
-#define SNAPSHOT_BACKTRACE
-#endif
-
 namespace dart {
 
 // Forward declarations.
@@ -274,11 +270,7 @@
            offset_space_ == IdSpace::kIsolateText;
   }
   int32_t GetTextOffsetFor(InstructionsPtr instructions, CodePtr code);
-#if defined(SNAPSHOT_BACKTRACE)
-  uint32_t GetDataOffsetFor(ObjectPtr raw_object, ObjectPtr raw_parent);
-#else
   uint32_t GetDataOffsetFor(ObjectPtr raw_object);
-#endif
 
   uint32_t AddBytesToData(uint8_t* bytes, intptr_t length);
 
@@ -364,27 +356,10 @@
   };
 
   struct ObjectData {
-#if defined(SNAPSHOT_BACKTRACE)
-    explicit ObjectData(ObjectPtr raw_obj, ObjectPtr raw_parent)
-        : raw_obj(raw_obj),
-          raw_parent(raw_parent),
-          flags(IsObjectField::encode(true) |
-                IsOriginalObjectField::encode(true)) {}
-    ObjectData(uint8_t* buf, intptr_t length)
-        : bytes({buf, length}),
-          raw_parent(Object::null()),
-          flags(IsObjectField::encode(false) |
-                IsOriginalObjectField::encode(false)) {}
-#else
     explicit ObjectData(ObjectPtr raw_obj)
-        : raw_obj(raw_obj),
-          flags(IsObjectField::encode(true) |
-                IsOriginalObjectField::encode(true)) {}
+        : raw_obj(raw_obj), is_object(true) {}
     ObjectData(uint8_t* buf, intptr_t length)
-        : bytes({buf, length}),
-          flags(IsObjectField::encode(false) |
-                IsOriginalObjectField::encode(false)) {}
-#endif
+        : bytes({buf, length}), is_object(false) {}
 
     union {
       struct {
@@ -394,26 +369,7 @@
       ObjectPtr raw_obj;
       const Object* obj;
     };
-#if defined(SNAPSHOT_BACKTRACE)
-    union {
-      ObjectPtr raw_parent;
-      const Object* parent;
-    };
-#endif
-    uint8_t flags;
-
-    bool is_object() const { return IsObjectField::decode(flags); }
-    bool is_original_object() const {
-      return IsOriginalObjectField::decode(flags);
-    }
-
-    void set_is_object(bool value) {
-      flags = IsObjectField::update(value, flags);
-    }
-
-    using IsObjectField = BitField<uint8_t, bool, 0, 1>;
-    using IsOriginalObjectField =
-        BitField<uint8_t, bool, IsObjectField::kNextBit, 1>;
+    bool is_object;
   };
 
   // Methods abstracting out the particulars of the underlying concrete writer.
@@ -459,10 +415,6 @@
   virtual void AddCodeSymbol(const Code& code,
                              const char* symbol,
                              intptr_t section_offset) = 0;
-  // Creates a static symbol for a read-only data object when appropriate.
-  virtual void AddDataSymbol(const char* symbol,
-                             intptr_t section_offset,
-                             size_t size) = 0;
 
   // Overloaded convenience versions of the above virtual methods.
 
@@ -490,47 +442,6 @@
   GrowableArray<ObjectData> objects_;
   GrowableArray<InstructionsData> instructions_;
 
-#if defined(DART_PRECOMPILER)
-  class SnapshotTextObjectNamer : ValueObject {
-   public:
-    explicit SnapshotTextObjectNamer(Zone* zone)
-        : zone_(ASSERT_NOTNULL(zone)),
-          owner_(Object::Handle(zone)),
-          string_(String::Handle(zone)),
-          insns_(Instructions::Handle(zone)),
-          store_(IsolateGroup::Current()->object_store()) {}
-
-    const char* StubNameForType(const AbstractType& type) const;
-
-    // Returns a unique assembly-safe name for text data to use in symbols.
-    // Assumes that code in the InstructionsData has been allocated a handle.
-    const char* SnapshotNameFor(const InstructionsData& data);
-    // Returns a unique assembly-safe name for read-only data to use in symbols.
-    // Assumes that the ObjectData has already been converted to object handles.
-    const char* SnapshotNameFor(const ObjectData& data);
-
-   private:
-    // Returns a unique assembly-safe name for the given code or read-only
-    // data object for use in symbols.
-    const char* SnapshotNameFor(const Object& object);
-    // Adds a non-unique assembly-safe name for the given object to the given
-    // buffer.
-    void AddNonUniqueNameFor(BaseTextBuffer* buffer, const Object& object);
-
-    Zone* const zone_;
-    Object& owner_;
-    String& string_;
-    Instructions& insns_;
-    ObjectStore* const store_;
-    TypeTestingStubNamer namer_;
-    intptr_t nonce_ = 0;
-
-    DISALLOW_COPY_AND_ASSIGN(SnapshotTextObjectNamer);
-  };
-
-  SnapshotTextObjectNamer namer_;
-#endif
-
   IdSpace offset_space_ = IdSpace::kSnapshot;
   V8SnapshotProfileWriter* profile_writer_ = nullptr;
   const char* const image_type_;
@@ -538,8 +449,12 @@
   const char* const instructions_type_;
   const char* const trampoline_type_;
 
+  // Used to make sure Code symbols are unique across text sections.
+  intptr_t unique_symbol_counter_ = 0;
+
   template <class T>
   friend class TraceImageObjectScope;
+  friend class SnapshotTextObjectNamer;  // For InstructionsData.
 
  private:
   static intptr_t SizeInSnapshotForBytes(intptr_t length);
@@ -588,6 +503,32 @@
   DISALLOW_COPY_AND_ASSIGN(TraceImageObjectScope);
 };
 
+class SnapshotTextObjectNamer : ValueObject {
+ public:
+  explicit SnapshotTextObjectNamer(Zone* zone)
+      : zone_(ASSERT_NOTNULL(zone)),
+        owner_(Object::Handle(zone)),
+        string_(String::Handle(zone)),
+        insns_(Instructions::Handle(zone)),
+        store_(IsolateGroup::Current()->object_store()) {}
+
+  const char* StubNameForType(const AbstractType& type) const;
+
+  const char* SnapshotNameFor(intptr_t code_index, const Code& code);
+  const char* SnapshotNameFor(intptr_t index,
+                              const ImageWriter::InstructionsData& data);
+
+ private:
+  Zone* const zone_;
+  Object& owner_;
+  String& string_;
+  Instructions& insns_;
+  ObjectStore* const store_;
+  TypeTestingStubNamer namer_;
+
+  DISALLOW_COPY_AND_ASSIGN(SnapshotTextObjectNamer);
+};
+
 class AssemblyImageWriter : public ImageWriter {
  public:
   AssemblyImageWriter(Thread* thread,
@@ -622,7 +563,6 @@
   virtual void AddCodeSymbol(const Code& code,
                              const char* symbol,
                              intptr_t offset);
-  virtual void AddDataSymbol(const char* symbol, intptr_t offset, size_t size);
 
   BaseWriteStream* const assembly_stream_;
   Dwarf* const assembly_dwarf_;
@@ -631,8 +571,8 @@
   // Used in Relocation to output "(.)" for relocations involving the current
   // section position and creating local symbols in AddCodeSymbol.
   const char* current_section_symbol_ = nullptr;
-  // Used for creating local symbols for code and data objects in the
-  // debugging info, if separately written.
+  // Used for creating local symbols for code objects in the debugging info,
+  // if separately written.
   ZoneGrowableArray<Elf::SymbolData>* current_symbols_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(AssemblyImageWriter);
@@ -676,7 +616,6 @@
   virtual void AddCodeSymbol(const Code& code,
                              const char* symbol,
                              intptr_t offset);
-  virtual void AddDataSymbol(const char* symbol, intptr_t offset, size_t size);
 
   // Set on section entrance to a new array containing the relocations for the
   // current section.
diff --git a/runtime/vm/type_testing_stubs.cc b/runtime/vm/type_testing_stubs.cc
index a62a5e4..41ac9d1 100644
--- a/runtime/vm/type_testing_stubs.cc
+++ b/runtime/vm/type_testing_stubs.cc
@@ -14,7 +14,6 @@
 #include "vm/stub_code.h"
 #include "vm/timeline.h"
 #include "vm/type_testing_stubs.h"
-#include "vm/zone_text_buffer.h"
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
 #include "vm/compiler/backend/flow_graph_compiler.h"
@@ -33,60 +32,57 @@
 
 const char* TypeTestingStubNamer::StubNameForType(
     const AbstractType& type) const {
-  ZoneTextBuffer buffer(Thread::Current()->zone());
-  WriteStubNameForTypeTo(&buffer, type);
-  return buffer.buffer();
+  Zone* Z = Thread::Current()->zone();
+  return OS::SCreate(Z, "TypeTestingStub_%s", StringifyType(type));
 }
 
-void TypeTestingStubNamer::WriteStubNameForTypeTo(
-    BaseTextBuffer* buffer,
+const char* TypeTestingStubNamer::StringifyType(
     const AbstractType& type) const {
-  buffer->AddString("TypeTestingStub_");
-  StringifyTypeTo(buffer, type);
-}
-
-void TypeTestingStubNamer::StringifyTypeTo(BaseTextBuffer* buffer,
-                                           const AbstractType& type) const {
   NoSafepointScope no_safepoint;
+  Zone* Z = Thread::Current()->zone();
   if (type.IsType()) {
     const intptr_t cid = Type::Cast(type).type_class_id();
     ClassTable* class_table = IsolateGroup::Current()->class_table();
     klass_ = class_table->At(cid);
     ASSERT(!klass_.IsNull());
 
+    const char* curl = "";
     lib_ = klass_.library();
     if (!lib_.IsNull()) {
       string_ = lib_.url();
-      buffer->AddString(string_.ToCString());
+      curl = OS::SCreate(Z, "%s_", string_.ToCString());
     } else {
-      buffer->Printf("nolib%" Pd "_", nonce_++);
+      static std::atomic<intptr_t> counter = 0;
+      curl = OS::SCreate(Z, "nolib%" Pd "_", counter++);
     }
 
-    buffer->AddString("_");
-    buffer->AddString(klass_.ScrubbedNameCString());
+    const char* concatenated = AssemblerSafeName(
+        OS::SCreate(Z, "%s_%s", curl, klass_.ScrubbedNameCString()));
 
     const intptr_t type_parameters = klass_.NumTypeParameters();
-    auto& type_arguments = TypeArguments::Handle(type.arguments());
-    if (!type_arguments.IsNull() && type_parameters > 0) {
+    auto& type_arguments = TypeArguments::Handle();
+    if (type.arguments() != TypeArguments::null() && type_parameters > 0) {
       type_arguments = type.arguments();
       ASSERT(type_arguments.Length() >= type_parameters);
       const intptr_t length = type_arguments.Length();
       for (intptr_t i = 0; i < type_parameters; ++i) {
         type_ = type_arguments.TypeAt(length - type_parameters + i);
-        buffer->AddString("__");
-        StringifyTypeTo(buffer, type_);
+        concatenated =
+            OS::SCreate(Z, "%s__%s", concatenated, StringifyType(type_));
       }
     }
+
+    return concatenated;
   } else if (type.IsTypeParameter()) {
-    buffer->AddString(TypeParameter::Cast(type).CanonicalNameCString());
+    return AssemblerSafeName(
+        OS::SCreate(Z, "%s", TypeParameter::Cast(type).CanonicalNameCString()));
   } else {
-    buffer->AddString(type.ToCString());
+    return AssemblerSafeName(OS::SCreate(Z, "%s", type.ToCString()));
   }
-  MakeNameAssemblerSafe(buffer);
 }
 
-void TypeTestingStubNamer::MakeNameAssemblerSafe(BaseTextBuffer* buffer) {
-  char* cursor = buffer->buffer();
+const char* TypeTestingStubNamer::AssemblerSafeName(char* cname) {
+  char* cursor = cname;
   while (*cursor != '\0') {
     char c = *cursor;
     if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
@@ -95,6 +91,7 @@
     }
     cursor++;
   }
+  return cname;
 }
 
 CodePtr TypeTestingStubGenerator::DefaultCodeForType(
diff --git a/runtime/vm/type_testing_stubs.h b/runtime/vm/type_testing_stubs.h
index b823215..7807bc1 100644
--- a/runtime/vm/type_testing_stubs.h
+++ b/runtime/vm/type_testing_stubs.h
@@ -24,19 +24,15 @@
   //
   // (only during dart_boostrap).
   const char* StubNameForType(const AbstractType& type) const;
-  void WriteStubNameForTypeTo(BaseTextBuffer* buffer,
-                              const AbstractType& type) const;
 
  private:
-  void StringifyTypeTo(BaseTextBuffer* buffer, const AbstractType& type) const;
-  // Converts the contents of the buffer to an assembly-safe name.
-  static void MakeNameAssemblerSafe(BaseTextBuffer* buffer);
+  const char* StringifyType(const AbstractType& type) const;
+  static const char* AssemblerSafeName(char* cname);
 
   Library& lib_;
   Class& klass_;
   AbstractType& type_;
   String& string_;
-  mutable intptr_t nonce_ = 0;
 };
 
 class TypeTestingStubGenerator {
diff --git a/tools/VERSION b/tools/VERSION
index 6e500fc..8969d93 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 19
 PATCH 0
-PRERELEASE 40
+PRERELEASE 41
 PRERELEASE_PATCH 0
\ No newline at end of file