Bug 1052579 - Add explicit arena to several char-copying JS functions r=sfink
authorChris Martin <cmartin@mozilla.com>
Thu, 11 Apr 2019 17:53:07 +0000
changeset 469077 1b3804497a8e5c76cf14984d62425f79f27dd3a2
parent 469076 acb04833e713061274e2f2221aa0b84cf2c2a8ab
child 469078 a047b64590a4926c919b299f883cec2e00d1dedd
push id35856
push usercsabou@mozilla.com
push dateFri, 12 Apr 2019 03:19:48 +0000
treeherdermozilla-central@940684cd1065 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1052579
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1052579 - Add explicit arena to several char-copying JS functions r=sfink Several of these JS char-copying functions allocate memory, and currently they implicitly do it in the MallocArena. They now take an explicit argument about which arena they perform their allocation in. Differential Revision: https://phabricator.services.mozilla.com/D25708
js/public/CharacterEncoding.h
js/src/builtin/TestingFunctions.cpp
js/src/ctypes/CTypes.cpp
js/src/shell/js.cpp
js/src/util/Text.cpp
js/src/util/Text.h
js/src/vm/CharacterEncoding.cpp
js/src/vm/Compartment.cpp
js/src/vm/CompilationAndEvaluation.cpp
js/src/vm/MemoryMetrics.cpp
js/src/vm/StringType.cpp
js/src/vm/StringType.h
--- a/js/public/CharacterEncoding.h
+++ b/js/public/CharacterEncoding.h
@@ -242,41 +242,46 @@ JS_PUBLIC_API uint32_t Utf8ToOneUcs4Char
                                          int utf8Length);
 
 /*
  * Inflate bytes in UTF-8 encoding to char16_t.
  * - On error, returns an empty TwoByteCharsZ.
  * - On success, returns a malloc'd TwoByteCharsZ, and updates |outlen| to hold
  *   its length;  the length value excludes the trailing null.
  */
-extern JS_PUBLIC_API TwoByteCharsZ UTF8CharsToNewTwoByteCharsZ(
-    JSContext* cx, const UTF8Chars utf8, size_t* outlen);
+extern JS_PUBLIC_API TwoByteCharsZ
+UTF8CharsToNewTwoByteCharsZ(JSContext* cx, const UTF8Chars utf8, size_t* outlen,
+                            arena_id_t destArenaId);
 
 /*
  * Like UTF8CharsToNewTwoByteCharsZ, but for WTF8Chars.
  */
-extern JS_PUBLIC_API TwoByteCharsZ WTF8CharsToNewTwoByteCharsZ(
-    JSContext* cx, const WTF8Chars wtf8, size_t* outlen);
+extern JS_PUBLIC_API TwoByteCharsZ
+WTF8CharsToNewTwoByteCharsZ(JSContext* cx, const WTF8Chars wtf8, size_t* outlen,
+                            arena_id_t destArenaId);
 
 /*
  * Like UTF8CharsToNewTwoByteCharsZ, but for ConstUTF8CharsZ.
  */
-extern JS_PUBLIC_API TwoByteCharsZ UTF8CharsToNewTwoByteCharsZ(
-    JSContext* cx, const ConstUTF8CharsZ& utf8, size_t* outlen);
+extern JS_PUBLIC_API TwoByteCharsZ
+UTF8CharsToNewTwoByteCharsZ(JSContext* cx, const ConstUTF8CharsZ& utf8,
+                            size_t* outlen, arena_id_t destArenaId);
 
 /*
  * The same as UTF8CharsToNewTwoByteCharsZ(), except that any malformed UTF-8
  * characters will be replaced by \uFFFD. No exception will be thrown for
  * malformed UTF-8 input.
  */
-extern JS_PUBLIC_API TwoByteCharsZ LossyUTF8CharsToNewTwoByteCharsZ(
-    JSContext* cx, const UTF8Chars utf8, size_t* outlen);
+extern JS_PUBLIC_API TwoByteCharsZ
+LossyUTF8CharsToNewTwoByteCharsZ(JSContext* cx, const UTF8Chars utf8,
+                                 size_t* outlen, arena_id_t destArenaId);
 
-extern JS_PUBLIC_API TwoByteCharsZ LossyUTF8CharsToNewTwoByteCharsZ(
-    JSContext* cx, const ConstUTF8CharsZ& utf8, size_t* outlen);
+extern JS_PUBLIC_API TwoByteCharsZ
+LossyUTF8CharsToNewTwoByteCharsZ(JSContext* cx, const ConstUTF8CharsZ& utf8,
+                                 size_t* outlen, arena_id_t destArenaId);
 
 /*
  * Returns the length of the char buffer required to encode |s| as UTF8.
  * Does not include the null-terminator.
  */
 JS_PUBLIC_API size_t GetDeflatedUTF8StringLength(JSFlatString* s);
 
 /*
@@ -311,25 +316,27 @@ JS_PUBLIC_API SmallestEncoding FindSmall
 
 /*
  * Return a null-terminated Latin-1 string copied from the input string,
  * storing its length (excluding null terminator) in |*outlen|.  Fail and
  * report an error if the string contains non-Latin-1 codepoints.  Returns
  * Latin1CharsZ() on failure.
  */
 extern JS_PUBLIC_API Latin1CharsZ
-UTF8CharsToNewLatin1CharsZ(JSContext* cx, const UTF8Chars utf8, size_t* outlen);
+UTF8CharsToNewLatin1CharsZ(JSContext* cx, const UTF8Chars utf8, size_t* outlen,
+                           arena_id_t destArenaId);
 
 /*
  * Return a null-terminated Latin-1 string copied from the input string,
  * storing its length (excluding null terminator) in |*outlen|.  Non-Latin-1
  * codepoints are replaced by '?'.  Returns Latin1CharsZ() on failure.
  */
-extern JS_PUBLIC_API Latin1CharsZ LossyUTF8CharsToNewLatin1CharsZ(
-    JSContext* cx, const UTF8Chars utf8, size_t* outlen);
+extern JS_PUBLIC_API Latin1CharsZ
+LossyUTF8CharsToNewLatin1CharsZ(JSContext* cx, const UTF8Chars utf8,
+                                size_t* outlen, arena_id_t destArenaId);
 
 /*
  * Returns true if all characters in the given null-terminated string are
  * ASCII, i.e. < 0x80, false otherwise.
  */
 extern JS_PUBLIC_API bool StringIsASCII(const char* s);
 
 }  // namespace JS
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -2480,17 +2480,18 @@ static bool ReadGeckoProfilingStack(JSCo
           break;
         case JS::ProfilingFrameIterator::Frame_Wasm:
           frameKindStr = "wasm";
           break;
         default:
           frameKindStr = "unknown";
       }
 
-      UniqueChars label = DuplicateString(cx, frames[i].label);
+      UniqueChars label =
+          DuplicateStringToArena(js::MallocArena, cx, frames[i].label);
       if (!label) {
         return false;
       }
 
       if (!frameInfo.back().emplaceBack(frameKindStr, std::move(label))) {
         return false;
       }
     }
@@ -3542,17 +3543,18 @@ struct FindPathHandler {
     // We take care of each node the first time we visit it, so there's
     // nothing to be done on subsequent visits.
     if (!first) {
       return true;
     }
 
     // Record how we reached this node. This is the last edge on a
     // shortest path to this node.
-    EdgeName edgeName = DuplicateString(cx, edge.name.get());
+    EdgeName edgeName =
+        DuplicateStringToArena(js::MallocArena, cx, edge.name.get());
     if (!edgeName) {
       return false;
     }
     *backEdge = BackEdge(origin, std::move(edgeName));
 
     // Have we reached our final target node?
     if (edge.referent == target) {
       // Record the path that got us here, which must be a shortest path.
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -7662,20 +7662,21 @@ bool CData::GetRuntime(JSContext* cx, un
     return false;
   }
 
   args.rval().setObject(*result);
   return true;
 }
 
 typedef JS::TwoByteCharsZ (*InflateUTF8Method)(JSContext*, const JS::UTF8Chars,
-                                               size_t*);
+                                               size_t*, arena_id_t);
 
 static bool ReadStringCommon(JSContext* cx, InflateUTF8Method inflateUTF8,
-                             unsigned argc, Value* vp, const char* funName) {
+                             unsigned argc, Value* vp, const char* funName,
+                             arena_id_t destArenaId) {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() != 0) {
     return ArgumentLengthError(cx, funName, "no", "s");
   }
 
   RootedObject obj(cx, GetThisObject(cx, args, funName));
   if (!obj) {
     return IncompatibleThisProto(cx, funName, args.thisv());
@@ -7738,17 +7739,18 @@ static bool ReadStringCommon(JSContext* 
     case TYPE_char:
     case TYPE_signed_char:
     case TYPE_unsigned_char: {
       char* bytes = static_cast<char*>(data);
       size_t length = strnlen(bytes, maxLength);
 
       // Determine the length.
       UniqueTwoByteChars dst(
-          inflateUTF8(cx, JS::UTF8Chars(bytes, length), &length).get());
+          inflateUTF8(cx, JS::UTF8Chars(bytes, length), &length, destArenaId)
+              .get());
       if (!dst) {
         return false;
       }
 
       result = JS_NewUCString(cx, std::move(dst), length);
       if (!result) {
         return false;
       }
@@ -7774,29 +7776,31 @@ static bool ReadStringCommon(JSContext* 
   }
 
   args.rval().setString(result);
   return true;
 }
 
 bool CData::ReadString(JSContext* cx, unsigned argc, Value* vp) {
   return ReadStringCommon(cx, JS::UTF8CharsToNewTwoByteCharsZ, argc, vp,
-                          "CData.prototype.readString");
+                          "CData.prototype.readString", js::MallocArena);
 }
 
 bool CDataFinalizer::Methods::ReadString(JSContext* cx, unsigned argc,
                                          Value* vp) {
   return ReadStringCommon(cx, JS::UTF8CharsToNewTwoByteCharsZ, argc, vp,
-                          "CDataFinalizer.prototype.readString");
+                          "CDataFinalizer.prototype.readString",
+                          js::MallocArena);
 }
 
 bool CData::ReadStringReplaceMalformed(JSContext* cx, unsigned argc,
                                        Value* vp) {
   return ReadStringCommon(cx, JS::LossyUTF8CharsToNewTwoByteCharsZ, argc, vp,
-                          "CData.prototype.readStringReplaceMalformed");
+                          "CData.prototype.readStringReplaceMalformed",
+                          js::MallocArena);
 }
 
 JSString* CData::GetSourceString(JSContext* cx, HandleObject typeObj,
                                  void* data) {
   // Walk the types, building up the toSource() string.
   // First, we build up the type expression:
   // 't.ptr' for pointers;
   // 't.array([n])' for arrays;
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2409,19 +2409,20 @@ JSString* js::shell::FileAsString(JSCont
       if (!pathname) {
         return nullptr;
       }
       JS_ReportErrorUTF8(cx, "can't read %s: short read", pathname.get());
     }
     return nullptr;
   }
 
-  UniqueTwoByteChars ucbuf(JS::LossyUTF8CharsToNewTwoByteCharsZ(
-                               cx, JS::UTF8Chars(buf.get(), len), &len)
-                               .get());
+  UniqueTwoByteChars ucbuf(
+      JS::LossyUTF8CharsToNewTwoByteCharsZ(cx, JS::UTF8Chars(buf.get(), len),
+                                           &len, js::MallocArena)
+          .get());
   if (!ucbuf) {
     pathname = JS_EncodeStringToUTF8(cx, pathnameStr);
     if (!pathname) {
       return nullptr;
     }
     JS_ReportErrorUTF8(cx, "Invalid UTF-8 in file '%s'", pathname.get());
     return nullptr;
   }
--- a/js/src/util/Text.cpp
+++ b/js/src/util/Text.cpp
@@ -52,70 +52,100 @@ int32_t js_fputs(const char16_t* s, FILE
     if (fputwc(wchar_t(*s), f) == WEOF) {
       return WEOF;
     }
     s++;
   }
   return 1;
 }
 
-UniqueChars js::DuplicateString(JSContext* cx, const char* s) {
+UniqueChars js::DuplicateStringToArena(arena_id_t destArenaId, JSContext* cx,
+                                       const char* s) {
   size_t n = strlen(s) + 1;
-  auto ret = cx->make_pod_array<char>(n);
+  auto ret = cx->make_pod_array<char>(n, destArenaId);
   if (!ret) {
     return ret;
   }
   PodCopy(ret.get(), s, n);
   return ret;
 }
 
-UniqueTwoByteChars js::DuplicateString(JSContext* cx, const char16_t* s) {
+UniqueTwoByteChars js::DuplicateStringToArena(arena_id_t destArenaId,
+                                              JSContext* cx,
+                                              const char16_t* s) {
   size_t n = js_strlen(s) + 1;
-  auto ret = cx->make_pod_array<char16_t>(n);
+  auto ret = cx->make_pod_array<char16_t>(n, destArenaId);
   if (!ret) {
     return ret;
   }
   PodCopy(ret.get(), s, n);
   return ret;
 }
 
-UniqueChars js::DuplicateString(const char* s) {
+UniqueChars js::DuplicateStringToArena(arena_id_t destArenaId, const char* s) {
   size_t n = strlen(s) + 1;
-  UniqueChars ret(js_pod_malloc<char>(n));
+  UniqueChars ret(js_pod_arena_malloc<char>(destArenaId, n));
   if (!ret) {
     return ret;
   }
   PodCopy(ret.get(), s, n);
   return ret;
 }
 
-UniqueChars js::DuplicateString(const char* s, size_t n) {
-  UniqueChars ret(js_pod_malloc<char>(n + 1));
+UniqueChars js::DuplicateStringToArena(arena_id_t destArenaId, const char* s,
+                                       size_t n) {
+  UniqueChars ret(js_pod_arena_malloc<char>(destArenaId, n + 1));
   if (!ret) {
     return nullptr;
   }
   PodCopy(ret.get(), s, n);
   ret[n] = 0;
   return ret;
 }
 
-UniqueTwoByteChars js::DuplicateString(const char16_t* s) {
-  return DuplicateString(s, js_strlen(s));
+UniqueTwoByteChars js::DuplicateStringToArena(arena_id_t destArenaId,
+                                              const char16_t* s) {
+  return DuplicateStringToArena(destArenaId, s, js_strlen(s));
 }
 
-UniqueTwoByteChars js::DuplicateString(const char16_t* s, size_t n) {
-  UniqueTwoByteChars ret(js_pod_malloc<char16_t>(n + 1));
+UniqueTwoByteChars js::DuplicateStringToArena(arena_id_t destArenaId,
+                                              const char16_t* s, size_t n) {
+  UniqueTwoByteChars ret(js_pod_arena_malloc<char16_t>(destArenaId, n + 1));
   if (!ret) {
     return nullptr;
   }
   PodCopy(ret.get(), s, n);
   ret[n] = 0;
   return ret;
 }
 
+UniqueChars js::DuplicateString(JSContext* cx, const char* s) {
+  return DuplicateStringToArena(js::MallocArena, cx, s);
+}
+
+UniqueTwoByteChars js::DuplicateString(JSContext* cx, const char16_t* s) {
+  return DuplicateStringToArena(js::MallocArena, cx, s);
+}
+
+UniqueChars js::DuplicateString(const char* s) {
+  return DuplicateStringToArena(js::MallocArena, s);
+}
+
+UniqueChars js::DuplicateString(const char* s, size_t n) {
+  return DuplicateStringToArena(js::MallocArena, s, n);
+}
+
+UniqueTwoByteChars js::DuplicateString(const char16_t* s) {
+  return DuplicateStringToArena(js::MallocArena, s);
+}
+
+UniqueTwoByteChars js::DuplicateString(const char16_t* s, size_t n) {
+  return DuplicateStringToArena(js::MallocArena, s, n);
+}
+
 char16_t* js::InflateString(JSContext* cx, const char* bytes, size_t length) {
   char16_t* chars = cx->pod_malloc<char16_t>(length + 1);
   if (!chars) {
     return nullptr;
   }
   CopyAndInflateChars(chars, bytes, length);
   chars[length] = 0;
   return chars;
--- a/js/src/util/Text.h
+++ b/js/src/util/Text.h
@@ -83,16 +83,39 @@ static inline const CharT* SkipSpace(con
 
   while (s < end && unicode::IsSpace(*s)) {
     s++;
   }
 
   return s;
 }
 
+extern UniqueChars DuplicateStringToArena(arena_id_t destArenaId, JSContext* cx,
+                                          const char* s);
+
+extern UniqueTwoByteChars DuplicateStringToArena(arena_id_t destArenaId,
+                                                 JSContext* cx,
+                                                 const char16_t* s);
+
+/*
+ * These variants do not report OOMs, you must arrange for OOMs to be reported
+ * yourself.
+ */
+extern UniqueChars DuplicateStringToArena(arena_id_t destArenaId,
+                                          const char* s);
+
+extern UniqueChars DuplicateStringToArena(arena_id_t destArenaId, const char* s,
+                                          size_t n);
+
+extern UniqueTwoByteChars DuplicateStringToArena(arena_id_t destArenaId,
+                                                 const char16_t* s);
+
+extern UniqueTwoByteChars DuplicateStringToArena(arena_id_t destArenaId,
+                                                 const char16_t* s, size_t n);
+
 extern UniqueChars DuplicateString(JSContext* cx, const char* s);
 
 extern UniqueTwoByteChars DuplicateString(JSContext* cx, const char16_t* s);
 
 /*
  * These variants do not report OOMs, you must arrange for OOMs to be reported
  * yourself.
  */
--- a/js/src/vm/CharacterEncoding.cpp
+++ b/js/src/vm/CharacterEncoding.cpp
@@ -418,17 +418,17 @@ static void CopyAndInflateUTF8IntoBuffer
     MOZ_ALWAYS_TRUE((InflateUTF8ToUTF16<ErrorAction>(cx, src, push)));
     MOZ_ASSERT(j == outlen);
   }
   dst[outlen] = CharT('\0');  // NUL char
 }
 
 template <OnUTF8Error ErrorAction, typename CharsT, class InputCharsT>
 static CharsT InflateUTF8StringHelper(JSContext* cx, const InputCharsT src,
-                                      size_t* outlen) {
+                                      size_t* outlen, arena_id_t destArenaId) {
   using CharT = typename CharsT::CharT;
   static_assert(std::is_same<CharT, char16_t>::value ||
                     std::is_same<CharT, Latin1Char>::value,
                 "bad CharT");
 
   *outlen = 0;
 
   size_t len = 0;
@@ -438,65 +438,72 @@ static CharsT InflateUTF8StringHelper(JS
     allASCII &= (c < 0x80);
     return LoopDisposition::Continue;
   };
   if (!InflateUTF8ToUTF16<ErrorAction>(cx, src, count)) {
     return CharsT();
   }
   *outlen = len;
 
-  CharT* dst = cx->template pod_malloc<CharT>(*outlen + 1);  // +1 for NUL
+  CharT* dst =
+      cx->template pod_malloc<CharT>(*outlen + 1, destArenaId);  // +1 for NUL
+
   if (!dst) {
     ReportOutOfMemory(cx);
     return CharsT();
   }
 
   constexpr OnUTF8Error errorMode =
       std::is_same<CharT, Latin1Char>::value
           ? OnUTF8Error::InsertQuestionMark
           : OnUTF8Error::InsertReplacementCharacter;
   CopyAndInflateUTF8IntoBuffer<errorMode>(cx, src, dst, *outlen, allASCII);
 
   return CharsT(dst, *outlen);
 }
 
 TwoByteCharsZ JS::UTF8CharsToNewTwoByteCharsZ(JSContext* cx,
                                               const UTF8Chars utf8,
-                                              size_t* outlen) {
-  return InflateUTF8StringHelper<OnUTF8Error::Throw, TwoByteCharsZ>(cx, utf8,
-                                                                    outlen);
+                                              size_t* outlen,
+                                              arena_id_t destArenaId) {
+  return InflateUTF8StringHelper<OnUTF8Error::Throw, TwoByteCharsZ>(
+      cx, utf8, outlen, destArenaId);
 }
 
 TwoByteCharsZ JS::WTF8CharsToNewTwoByteCharsZ(JSContext* cx,
                                               const WTF8Chars wtf8,
-                                              size_t* outlen) {
-  return InflateUTF8StringHelper<OnUTF8Error::Throw, TwoByteCharsZ>(cx, wtf8,
-                                                                    outlen);
+                                              size_t* outlen,
+                                              arena_id_t destArenaId) {
+  return InflateUTF8StringHelper<OnUTF8Error::Throw, TwoByteCharsZ>(
+      cx, wtf8, outlen, destArenaId);
 }
 
 TwoByteCharsZ JS::UTF8CharsToNewTwoByteCharsZ(JSContext* cx,
                                               const ConstUTF8CharsZ& utf8,
-                                              size_t* outlen) {
+                                              size_t* outlen,
+                                              arena_id_t destArenaId) {
   UTF8Chars chars(utf8.c_str(), strlen(utf8.c_str()));
-  return InflateUTF8StringHelper<OnUTF8Error::Throw, TwoByteCharsZ>(cx, chars,
-                                                                    outlen);
+  return InflateUTF8StringHelper<OnUTF8Error::Throw, TwoByteCharsZ>(
+      cx, chars, outlen, destArenaId);
 }
 
 TwoByteCharsZ JS::LossyUTF8CharsToNewTwoByteCharsZ(JSContext* cx,
                                                    const JS::UTF8Chars utf8,
-                                                   size_t* outlen) {
+                                                   size_t* outlen,
+                                                   arena_id_t destArenaId) {
   return InflateUTF8StringHelper<OnUTF8Error::InsertReplacementCharacter,
-                                 TwoByteCharsZ>(cx, utf8, outlen);
+                                 TwoByteCharsZ>(cx, utf8, outlen, destArenaId);
 }
 
 TwoByteCharsZ JS::LossyUTF8CharsToNewTwoByteCharsZ(
-    JSContext* cx, const JS::ConstUTF8CharsZ& utf8, size_t* outlen) {
+    JSContext* cx, const JS::ConstUTF8CharsZ& utf8, size_t* outlen,
+    arena_id_t destArenaId) {
   UTF8Chars chars(utf8.c_str(), strlen(utf8.c_str()));
   return InflateUTF8StringHelper<OnUTF8Error::InsertReplacementCharacter,
-                                 TwoByteCharsZ>(cx, chars, outlen);
+                                 TwoByteCharsZ>(cx, chars, outlen, destArenaId);
 }
 
 static void UpdateSmallestEncodingForChar(char16_t c,
                                           JS::SmallestEncoding* encoding) {
   JS::SmallestEncoding newEncoding = JS::SmallestEncoding::ASCII;
   if (c >= 0x80) {
     if (c < 0x100) {
       newEncoding = JS::SmallestEncoding::Latin1;
@@ -517,26 +524,28 @@ JS::SmallestEncoding JS::FindSmallestEnc
                                                    : LoopDisposition::Continue;
   };
   MOZ_ALWAYS_TRUE((InflateUTF8ToUTF16<OnUTF8Error::InsertReplacementCharacter>(
       /* cx = */ nullptr, utf8, onChar)));
   return encoding;
 }
 
 Latin1CharsZ JS::UTF8CharsToNewLatin1CharsZ(JSContext* cx, const UTF8Chars utf8,
-                                            size_t* outlen) {
-  return InflateUTF8StringHelper<OnUTF8Error::Throw, Latin1CharsZ>(cx, utf8,
-                                                                   outlen);
+                                            size_t* outlen,
+                                            arena_id_t destArenaId) {
+  return InflateUTF8StringHelper<OnUTF8Error::Throw, Latin1CharsZ>(
+      cx, utf8, outlen, destArenaId);
 }
 
 Latin1CharsZ JS::LossyUTF8CharsToNewLatin1CharsZ(JSContext* cx,
                                                  const UTF8Chars utf8,
-                                                 size_t* outlen) {
+                                                 size_t* outlen,
+                                                 arena_id_t destArenaId) {
   return InflateUTF8StringHelper<OnUTF8Error::InsertQuestionMark, Latin1CharsZ>(
-      cx, utf8, outlen);
+      cx, utf8, outlen, destArenaId);
 }
 
 /**
  * Atomization Helpers.
  *
  * These functions are extremely single-use, and are not intended for general
  * consumption.
  */
--- a/js/src/vm/Compartment.cpp
+++ b/js/src/vm/Compartment.cpp
@@ -107,25 +107,26 @@ static JSString* CopyStringPure(JSContex
     return chars.isLatin1() ? NewStringCopyN<CanGC>(
                                   cx, chars.latin1Range().begin().get(), len)
                             : NewStringCopyNDontDeflate<CanGC>(
                                   cx, chars.twoByteRange().begin().get(), len);
   }
 
   if (str->hasLatin1Chars()) {
     UniquePtr<Latin1Char[], JS::FreePolicy> copiedChars =
-        str->asRope().copyLatin1CharsZ(cx);
+        str->asRope().copyLatin1CharsZ(cx, js::MallocArena);
     if (!copiedChars) {
       return nullptr;
     }
 
     return NewString<CanGC>(cx, std::move(copiedChars), len);
   }
 
-  UniqueTwoByteChars copiedChars = str->asRope().copyTwoByteCharsZ(cx);
+  UniqueTwoByteChars copiedChars =
+      str->asRope().copyTwoByteCharsZ(cx, js::MallocArena);
   if (!copiedChars) {
     return nullptr;
   }
 
   return NewStringDontDeflate<CanGC>(cx, std::move(copiedChars), len);
 }
 
 bool Compartment::wrap(JSContext* cx, MutableHandleString strp) {
--- a/js/src/vm/CompilationAndEvaluation.cpp
+++ b/js/src/vm/CompilationAndEvaluation.cpp
@@ -68,17 +68,19 @@ static JSScript* CompileSourceBuffer(JSC
   frontend::GlobalScriptInfo info(cx, options, scopeKind);
   return frontend::CompileGlobalScript(info, srcBuf);
 }
 
 static JSScript* CompileUtf8(JSContext* cx,
                              const ReadOnlyCompileOptions& options,
                              const char* bytes, size_t length) {
   auto chars = UniqueTwoByteChars(
-      UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(bytes, length), &length).get());
+      UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(bytes, length), &length,
+                                  js::MallocArena)
+          .get());
   if (!chars) {
     return nullptr;
   }
 
   SourceText<char16_t> source;
   if (!source.init(cx, std::move(chars), length)) {
     return nullptr;
   }
@@ -181,17 +183,19 @@ JS_PUBLIC_API bool JS_Utf8BufferIsCompil
                                                  size_t length) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
   cx->check(obj);
 
   cx->clearPendingException();
 
   JS::UniqueTwoByteChars chars{
-      UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(utf8, length), &length).get()};
+      UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(utf8, length), &length,
+                                  js::MallocArena)
+          .get()};
   if (!chars) {
     return true;
   }
 
   // Return true on any out-of-memory error or non-EOF-related syntax error, so
   // our caller doesn't try to collect more buffered source.
   bool result = true;
 
@@ -587,17 +591,18 @@ JS_PUBLIC_API bool JS::EvaluateUtf8Path(
   }
 
   CompileOptions options(cx, optionsArg);
   options.setFileAndLine(filename, 1);
 
   auto contents = reinterpret_cast<const char*>(buffer.begin());
   size_t length = buffer.length();
   auto chars = UniqueTwoByteChars(
-      UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(contents, length), &length)
+      UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(contents, length), &length,
+                                  js::MallocArena)
           .get());
   if (!chars) {
     return false;
   }
 
   SourceText<char16_t> srcBuf;
   if (!srcBuf.init(cx, std::move(chars), length)) {
     return false;
--- a/js/src/vm/MemoryMetrics.cpp
+++ b/js/src/vm/MemoryMetrics.cpp
@@ -71,29 +71,31 @@ static bool EqualStringsPure(JSString* s
   }
 
   const Char1* c1;
   UniquePtr<Char1[], JS::FreePolicy> ownedChars1;
   JS::AutoCheckCannotGC nogc;
   if (s1->isLinear()) {
     c1 = s1->asLinear().chars<Char1>(nogc);
   } else {
-    ownedChars1 = s1->asRope().copyChars<Char1>(/* tcx */ nullptr);
+    ownedChars1 =
+        s1->asRope().copyChars<Char1>(/* tcx */ nullptr, js::MallocArena);
     if (!ownedChars1) {
       MOZ_CRASH("oom");
     }
     c1 = ownedChars1.get();
   }
 
   const Char2* c2;
   UniquePtr<Char2[], JS::FreePolicy> ownedChars2;
   if (s2->isLinear()) {
     c2 = s2->asLinear().chars<Char2>(nogc);
   } else {
-    ownedChars2 = s2->asRope().copyChars<Char2>(/* tcx */ nullptr);
+    ownedChars2 =
+        s2->asRope().copyChars<Char2>(/* tcx */ nullptr, js::MallocArena);
     if (!ownedChars2) {
       MOZ_CRASH("oom");
     }
     c2 = ownedChars2.get();
   }
 
   return EqualChars(c1, c2, s1->length());
 }
@@ -121,17 +123,18 @@ NotableStringInfo::NotableStringInfo() :
 template <typename CharT>
 static void StoreStringChars(char* buffer, size_t bufferSize, JSString* str) {
   const CharT* chars;
   UniquePtr<CharT[], JS::FreePolicy> ownedChars;
   JS::AutoCheckCannotGC nogc;
   if (str->isLinear()) {
     chars = str->asLinear().chars<CharT>(nogc);
   } else {
-    ownedChars = str->asRope().copyChars<CharT>(/* tcx */ nullptr);
+    ownedChars =
+        str->asRope().copyChars<CharT>(/* tcx */ nullptr, js::MallocArena);
     if (!ownedChars) {
       MOZ_CRASH("oom");
     }
     chars = ownedChars.get();
   }
 
   // We might truncate |str| even if it's much shorter than 1024 chars, if
   // |str| contains unicode chars.  Since this is just for a memory reporter,
--- a/js/src/vm/StringType.cpp
+++ b/js/src/vm/StringType.cpp
@@ -292,46 +292,50 @@ static MOZ_ALWAYS_INLINE bool AllocChars
   /* Like length, capacity does not include the null char, so take it out. */
   *capacity = numChars - 1;
 
   JS_STATIC_ASSERT(JSString::MAX_LENGTH * sizeof(CharT) < UINT32_MAX);
   *chars = str->zone()->pod_malloc<CharT>(numChars);
   return *chars != nullptr;
 }
 
-UniqueLatin1Chars JSRope::copyLatin1CharsZ(JSContext* maybecx) const {
-  return copyCharsInternal<Latin1Char>(maybecx, true);
+UniqueLatin1Chars JSRope::copyLatin1CharsZ(JSContext* maybecx,
+                                           arena_id_t destArenaId) const {
+  return copyCharsInternal<Latin1Char>(maybecx, true, destArenaId);
 }
 
-UniqueTwoByteChars JSRope::copyTwoByteCharsZ(JSContext* maybecx) const {
-  return copyCharsInternal<char16_t>(maybecx, true);
+UniqueTwoByteChars JSRope::copyTwoByteCharsZ(JSContext* maybecx,
+                                             arena_id_t destArenaId) const {
+  return copyCharsInternal<char16_t>(maybecx, true, destArenaId);
 }
 
-UniqueLatin1Chars JSRope::copyLatin1Chars(JSContext* maybecx) const {
-  return copyCharsInternal<Latin1Char>(maybecx, false);
+UniqueLatin1Chars JSRope::copyLatin1Chars(JSContext* maybecx,
+                                          arena_id_t destArenaId) const {
+  return copyCharsInternal<Latin1Char>(maybecx, false, destArenaId);
 }
 
-UniqueTwoByteChars JSRope::copyTwoByteChars(JSContext* maybecx) const {
-  return copyCharsInternal<char16_t>(maybecx, false);
+UniqueTwoByteChars JSRope::copyTwoByteChars(JSContext* maybecx,
+                                            arena_id_t destArenaId) const {
+  return copyCharsInternal<char16_t>(maybecx, false, destArenaId);
 }
 
 template <typename CharT>
 UniquePtr<CharT[], JS::FreePolicy> JSRope::copyCharsInternal(
-    JSContext* maybecx, bool nullTerminate) const {
+    JSContext* maybecx, bool nullTerminate, arena_id_t destArenaId) const {
   // Left-leaning ropes are far more common than right-leaning ropes, so
   // perform a non-destructive traversal of the rope, right node first,
   // splatting each node's characters into a contiguous buffer.
 
   size_t n = length();
 
   UniquePtr<CharT[], JS::FreePolicy> out;
   if (maybecx) {
-    out.reset(maybecx->pod_malloc<CharT>(n + 1));
+    out.reset(maybecx->pod_malloc<CharT>(n + 1, destArenaId));
   } else {
-    out.reset(js_pod_malloc<CharT>(n + 1));
+    out.reset(js_pod_arena_malloc<CharT>(destArenaId, n + 1));
   }
 
   if (!out) {
     return nullptr;
   }
 
   Vector<const JSString*, 8, SystemAllocPolicy> nodeStack;
   const JSString* str = this;
@@ -1836,28 +1840,28 @@ JSFlatString* NewStringCopyUTF8N(JSConte
   JS::SmallestEncoding encoding = JS::FindSmallestEncoding(utf8);
   if (encoding == JS::SmallestEncoding::ASCII) {
     return NewStringCopyN<allowGC>(cx, utf8.begin().get(), utf8.length());
   }
 
   size_t length;
   if (encoding == JS::SmallestEncoding::Latin1) {
     UniqueLatin1Chars latin1(
-        UTF8CharsToNewLatin1CharsZ(cx, utf8, &length).get());
+        UTF8CharsToNewLatin1CharsZ(cx, utf8, &length, js::MallocArena).get());
     if (!latin1) {
       return nullptr;
     }
 
     return NewString<allowGC>(cx, std::move(latin1), length);
   }
 
   MOZ_ASSERT(encoding == JS::SmallestEncoding::UTF16);
 
   UniqueTwoByteChars utf16(
-      UTF8CharsToNewTwoByteCharsZ(cx, utf8, &length).get());
+      UTF8CharsToNewTwoByteCharsZ(cx, utf8, &length, js::MallocArena).get());
   if (!utf16) {
     return nullptr;
   }
 
   return NewString<allowGC>(cx, std::move(utf16), length);
 }
 
 template JSFlatString* NewStringCopyUTF8N<CanGC>(JSContext* cx,
--- a/js/src/vm/StringType.h
+++ b/js/src/vm/StringType.h
@@ -744,17 +744,17 @@ class JSString : public js::gc::Cell {
   JSString() = delete;
   JSString(const JSString& other) = delete;
   void operator=(const JSString& other) = delete;
 };
 
 class JSRope : public JSString {
   template <typename CharT>
   js::UniquePtr<CharT[], JS::FreePolicy> copyCharsInternal(
-      JSContext* cx, bool nullTerminate) const;
+      JSContext* cx, bool nullTerminate, arena_id_t destArenaId) const;
 
   enum UsingBarrier { WithIncrementalBarrier, NoBarrier };
 
   template <UsingBarrier b, typename CharT>
   JSFlatString* flattenInternal(JSContext* cx);
 
   template <UsingBarrier b>
   JSFlatString* flattenInternal(JSContext* cx);
@@ -768,25 +768,28 @@ class JSRope : public JSString {
   template <js::AllowGC allowGC>
   static inline JSRope* new_(
       JSContext* cx,
       typename js::MaybeRooted<JSString*, allowGC>::HandleType left,
       typename js::MaybeRooted<JSString*, allowGC>::HandleType right,
       size_t length, js::gc::InitialHeap = js::gc::DefaultHeap);
 
   js::UniquePtr<JS::Latin1Char[], JS::FreePolicy> copyLatin1Chars(
-      JSContext* maybecx) const;
-  JS::UniqueTwoByteChars copyTwoByteChars(JSContext* maybecx) const;
+      JSContext* maybecx, arena_id_t destArenaId) const;
+  JS::UniqueTwoByteChars copyTwoByteChars(JSContext* maybecx,
+                                          arena_id_t destArenaId) const;
 
   js::UniquePtr<JS::Latin1Char[], JS::FreePolicy> copyLatin1CharsZ(
-      JSContext* maybecx) const;
-  JS::UniqueTwoByteChars copyTwoByteCharsZ(JSContext* maybecx) const;
+      JSContext* maybecx, arena_id_t destArenaId) const;
+  JS::UniqueTwoByteChars copyTwoByteCharsZ(JSContext* maybecx,
+                                           arena_id_t destArenaId) const;
 
   template <typename CharT>
-  js::UniquePtr<CharT[], JS::FreePolicy> copyChars(JSContext* maybecx) const;
+  js::UniquePtr<CharT[], JS::FreePolicy> copyChars(
+      JSContext* maybecx, arena_id_t destArenaId) const;
 
   // Hash function specific for ropes that avoids allocating a temporary
   // string. There are still allocations internally so it's technically
   // fallible.
   //
   // Returns the same value as if this were a linear string being hashed.
   MOZ_MUST_USE bool hash(uint32_t* outhHash) const;
 
@@ -1827,24 +1830,25 @@ MOZ_ALWAYS_INLINE const char16_t* JSLine
 template <>
 MOZ_ALWAYS_INLINE const JS::Latin1Char* JSLinearString::chars(
     const JS::AutoRequireNoGC& nogc) const {
   return rawLatin1Chars();
 }
 
 template <>
 MOZ_ALWAYS_INLINE js::UniquePtr<JS::Latin1Char[], JS::FreePolicy>
-JSRope::copyChars<JS::Latin1Char>(JSContext* maybecx) const {
-  return copyLatin1Chars(maybecx);
+JSRope::copyChars<JS::Latin1Char>(JSContext* maybecx,
+                                  arena_id_t destArenaId) const {
+  return copyLatin1Chars(maybecx, destArenaId);
 }
 
 template <>
 MOZ_ALWAYS_INLINE JS::UniqueTwoByteChars JSRope::copyChars<char16_t>(
-    JSContext* maybecx) const {
-  return copyTwoByteChars(maybecx);
+    JSContext* maybecx, arena_id_t destArenaId) const {
+  return copyTwoByteChars(maybecx, destArenaId);
 }
 
 template <>
 MOZ_ALWAYS_INLINE bool JSThinInlineString::lengthFits<JS::Latin1Char>(
     size_t length) {
   return length <= MAX_LENGTH_LATIN1;
 }