Bug 1464845 - Remove js_strdup and JS_strdup. r=Waldo
☠☠ backed out by 3aaea8874060 ☠ ☠
authorAndré Bargull <andre.bargull@gmail.com>
Mon, 04 Jun 2018 01:30:51 -0700
changeset 421173 2426ac9d78fe52b238345e286b000424cbab4989
parent 421172 0c71a38c8c5c49d3041a3a8d06116b07ba1279b3
child 421174 9ea974e9f568ec4ef151702c35333d850c38ab5d
push id34089
push userdluca@mozilla.com
push dateMon, 04 Jun 2018 18:11:55 +0000
treeherdermozilla-central@d8f180ab7492 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs1464845
milestone62.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 1464845 - Remove js_strdup and JS_strdup. r=Waldo
js/public/Utility.h
js/src/builtin/TestingFunctions.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/shell/js.cpp
js/src/util/Text.cpp
js/src/vm/BytecodeUtil.cpp
js/src/vm/BytecodeUtil.h
js/src/vm/CodeCoverage.cpp
js/src/vm/CodeCoverage.h
js/src/vm/Debugger.cpp
js/src/vm/Debugger.h
js/src/vm/JSScript.cpp
js/src/vm/Runtime.cpp
js/src/vm/SelfHosting.cpp
js/src/vm/SharedImmutableStringsCache.h
js/src/vm/TraceLogging.cpp
js/src/vm/TraceLogging.h
js/src/vm/UbiNodeCensus.cpp
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -413,18 +413,16 @@ static inline void* js_realloc(void* p, 
 
 static inline void js_free(void* p)
 {
     // TODO: This should call |moz_arena_free(js::MallocArena, p)| but we
     // currently can't enforce that all memory freed here was allocated by
     // js_malloc().
     free(p);
 }
-
-JS_PUBLIC_API(char*) js_strdup(const char* s);
 #endif/* JS_USE_CUSTOM_ALLOCATOR */
 
 #include <new>
 
 /*
  * Low-level memory management in SpiderMonkey:
  *
  *  ** Do not use the standard malloc/free/realloc: SpiderMonkey allows these
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -2382,18 +2382,18 @@ ReadGeckoProfilingStack(JSContext* cx, u
     // stack.
     if (!cx->isProfilerSamplingEnabled()) {
       args.rval().setObject(*stack);
       return true;
     }
 
     struct InlineFrameInfo
     {
-        InlineFrameInfo(const char* kind, char* label)
-          : kind(kind), label(label) {}
+        InlineFrameInfo(const char* kind, UniqueChars label)
+          : kind(kind), label(std::move(label)) {}
         const char* kind;
         UniqueChars label;
     };
 
     Vector<Vector<InlineFrameInfo, 0, TempAllocPolicy>, 0, TempAllocPolicy> frameInfo(cx);
 
     JS::ProfilingFrameIterator::RegisterState state;
     for (JS::ProfilingFrameIterator i(cx, state); !i.done(); ++i) {
@@ -2417,21 +2417,21 @@ ReadGeckoProfilingStack(JSContext* cx, u
                 break;
               case JS::ProfilingFrameIterator::Frame_Wasm:
                 frameKindStr = "wasm";
                 break;
               default:
                 frameKindStr = "unknown";
             }
 
-            char* label = JS_strdup(cx, frames[i].label);
+            UniqueChars label = DuplicateString(cx, frames[i].label);
             if (!label)
                 return false;
 
-            if (!frameInfo.back().emplaceBack(frameKindStr, label))
+            if (!frameInfo.back().emplaceBack(frameKindStr, std::move(label)))
                 return false;
         }
     }
 
     RootedObject inlineFrameInfo(cx);
     RootedString frameKind(cx);
     RootedString frameLabel(cx);
     RootedId idx(cx);
@@ -4375,17 +4375,17 @@ GetLcovInfo(JSContext* cx, unsigned argc
         AutoRealm ar(cx, global);
         content = js::GetCodeCoverageSummary(cx, &length);
     }
 
     if (!content)
         return false;
 
     JSString* str = JS_NewStringCopyN(cx, content, length);
-    free(content);
+    js_free(content);
 
     if (!str)
         return false;
 
     args.rval().setString(str);
     return true;
 }
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1377,23 +1377,16 @@ JS_freeop(JSFreeOp* fop, void* p)
 }
 
 JS_PUBLIC_API(void)
 JS_updateMallocCounter(JSContext* cx, size_t nbytes)
 {
     return cx->updateMallocCounter(nbytes);
 }
 
-JS_PUBLIC_API(char*)
-JS_strdup(JSContext* cx, const char* s)
-{
-    AssertHeapIsIdle();
-    return DuplicateString(cx, s).release();
-}
-
 #undef JS_AddRoot
 
 JS_PUBLIC_API(bool)
 JS_AddExtraGCRootsTracer(JSContext* cx, JSTraceDataOp traceOp, void* data)
 {
     return cx->runtime()->gc.addBlackRootsTracer(traceOp, data);
 }
 
@@ -3988,17 +3981,17 @@ JS::OwningCompileOptions::copy(JSContext
            setIntroducerFilename(cx, rhs.introducerFilename());
 }
 
 bool
 JS::OwningCompileOptions::setFile(JSContext* cx, const char* f)
 {
     char* copy = nullptr;
     if (f) {
-        copy = JS_strdup(cx, f);
+        copy = DuplicateString(cx, f).release();
         if (!copy)
             return false;
     }
 
     // OwningCompileOptions always owns filename_, so this cast is okay.
     js_free(const_cast<char*>(filename_));
 
     filename_ = copy;
@@ -4032,17 +4025,17 @@ JS::OwningCompileOptions::setSourceMapUR
     return true;
 }
 
 bool
 JS::OwningCompileOptions::setIntroducerFilename(JSContext* cx, const char* s)
 {
     char* copy = nullptr;
     if (s) {
-        copy = JS_strdup(cx, s);
+        copy = DuplicateString(cx, s).release();
         if (!copy)
             return false;
     }
 
     // OwningCompileOptions always owns introducerFilename_, so this cast is okay.
     js_free(const_cast<char*>(introducerFilename_));
 
     introducerFilename_ = copy;
@@ -6846,17 +6839,17 @@ JS_SetDefaultLocale(JSRuntime* rt, const
     return rt->setDefaultLocale(locale);
 }
 
 JS_PUBLIC_API(UniqueChars)
 JS_GetDefaultLocale(JSContext* cx)
 {
     AssertHeapIsIdle();
     if (const char* locale = cx->runtime()->getDefaultLocale())
-        return UniqueChars(JS_strdup(cx, locale));
+        return DuplicateString(cx, locale);
 
     return nullptr;
 }
 
 JS_PUBLIC_API(void)
 JS_ResetDefaultLocale(JSRuntime* rt)
 {
     AssertHeapIsIdle();
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1352,19 +1352,16 @@ JS_free(JSContext* cx, void* p);
  * performance optimization as specified by the given JSFreeOp instance.
  */
 extern JS_PUBLIC_API(void)
 JS_freeop(JSFreeOp* fop, void* p);
 
 extern JS_PUBLIC_API(void)
 JS_updateMallocCounter(JSContext* cx, size_t nbytes);
 
-extern JS_PUBLIC_API(char*)
-JS_strdup(JSContext* cx, const char* s);
-
 /**
  * Set the size of the native stack that should not be exceed. To disable
  * stack size checking pass 0.
  *
  * SpiderMonkey allows for a distinction between system code (such as GCs, which
  * may incidentally be triggered by script but are not strictly performed on
  * behalf of such script), trusted script (as determined by JS_SetTrustedPrincipals),
  * and untrusted script. Each kind of code may have a different stack quota,
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -12,16 +12,17 @@
 #include "mozilla/DebugOnly.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/mozalloc.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/TimeStamp.h"
+#include "mozilla/Unused.h"
 
 #include <chrono>
 #ifdef JS_POSIX_NSPR
 # include <dlfcn.h>
 #endif
 #ifdef XP_WIN
 # include <direct.h>
 # include <process.h>
@@ -38,16 +39,17 @@
 #include <math.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <thread>
+#include <utility>
 #ifdef XP_UNIX
 # include <sys/mman.h>
 # include <sys/stat.h>
 # include <sys/wait.h>
 # include <unistd.h>
 #endif
 
 #include "jsapi.h"
@@ -4965,81 +4967,82 @@ class AutoCStringVector
 {
     Vector<char*> argv_;
   public:
     explicit AutoCStringVector(JSContext* cx) : argv_(cx) {}
     ~AutoCStringVector() {
         for (size_t i = 0; i < argv_.length(); i++)
             js_free(argv_[i]);
     }
-    bool append(char* arg) {
-        if (!argv_.append(arg)) {
-            js_free(arg);
-            return false;
-        }
+    bool append(UniqueChars&& arg) {
+        if (!argv_.append(arg.get()))
+            return false;
+
+        // Now owned by this vector.
+        mozilla::Unused << arg.release();
         return true;
     }
     char* const* get() const {
         return argv_.begin();
     }
     size_t length() const {
         return argv_.length();
     }
     char* operator[](size_t i) const {
         return argv_[i];
     }
-    void replace(size_t i, char* arg) {
+    void replace(size_t i, UniqueChars arg) {
         js_free(argv_[i]);
-        argv_[i] = arg;
-    }
-    char* back() const {
+        argv_[i] = arg.release();
+    }
+    const char* back() const {
         return argv_.back();
     }
-    void replaceBack(char* arg) {
+    void replaceBack(UniqueChars arg) {
         js_free(argv_.back());
-        argv_.back() = arg;
+        argv_.back() = arg.release();
     }
 };
 
 #if defined(XP_WIN)
 static bool
-EscapeForShell(AutoCStringVector& argv)
+EscapeForShell(JSContext* cx, AutoCStringVector& argv)
 {
     // Windows will break arguments in argv by various spaces, so we wrap each
     // argument in quotes and escape quotes within. Even with quotes, \ will be
     // treated like an escape character, so inflate each \ to \\.
 
     for (size_t i = 0; i < argv.length(); i++) {
         if (!argv[i])
             continue;
 
         size_t newLen = 3;  // quotes before and after and null-terminator
         for (char* p = argv[i]; *p; p++) {
             newLen++;
             if (*p == '\"' || *p == '\\')
                 newLen++;
         }
 
-        char* escaped = (char*)js_malloc(newLen);
+        UniqueChars escaped(cx->pod_malloc<char>(newLen));
         if (!escaped)
             return false;
 
         char* src = argv[i];
-        char* dst = escaped;
+        char* dst = escaped.get();
         *dst++ = '\"';
         while (*src) {
             if (*src == '\"' || *src == '\\')
                 *dst++ = '\\';
             *dst++ = *src++;
         }
         *dst++ = '\"';
         *dst++ = '\0';
-        MOZ_ASSERT(escaped + newLen == dst);
-
-        argv.replace(i, escaped);
+        MOZ_ASSERT(escaped.get() + newLen == dst);
+
+        argv.replace(i, std::move(escaped));
     }
     return true;
 }
 #endif
 
 static Vector<const char*, 4, js::SystemAllocPolicy> sPropagatedFlags;
 
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
@@ -5058,50 +5061,57 @@ NestedShell(JSContext* cx, unsigned argc
     AutoCStringVector argv(cx);
 
     // The first argument to the shell is its path, which we assume is our own
     // argv[0].
     if (sArgc < 1) {
         JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, JSSMSG_NESTED_FAIL);
         return false;
     }
-    if (!argv.append(js_strdup(sArgv[0])))
+    UniqueChars shellPath = DuplicateString(cx, sArgv[0]);
+    if (!shellPath || !argv.append(std::move(shellPath)))
         return false;
 
     // Propagate selected flags from the current shell
     for (unsigned i = 0; i < sPropagatedFlags.length(); i++) {
-        char* cstr = js_strdup(sPropagatedFlags[i]);
-        if (!cstr || !argv.append(cstr))
+        UniqueChars flags = DuplicateString(cx, sPropagatedFlags[i]);
+        if (!flags || !argv.append(std::move(flags)))
             return false;
     }
 
     // The arguments to nestedShell are stringified and append to argv.
     RootedString str(cx);
     for (unsigned i = 0; i < args.length(); i++) {
         str = ToString(cx, args[i]);
-        if (!str || !argv.append(JS_EncodeString(cx, str)))
+        if (!str)
+            return false;
+
+        UniqueChars arg(JS_EncodeString(cx, str));
+        if (!arg || !argv.append(std::move(arg)))
             return false;
 
         // As a special case, if the caller passes "--js-cache", replace that
         // with "--js-cache=$(jsCacheDir)"
         if (!strcmp(argv.back(), "--js-cache") && jsCacheDir) {
             UniqueChars newArg = JS_smprintf("--js-cache=%s", jsCacheDir);
-            if (!newArg)
+            if (!newArg) {
+                JS_ReportOutOfMemory(cx);
                 return false;
-            argv.replaceBack(newArg.release());
+            }
+            argv.replaceBack(std::move(newArg));
         }
     }
 
     // execv assumes argv is null-terminated
     if (!argv.append(nullptr))
         return false;
 
     int status = 0;
 #if defined(XP_WIN)
-    if (!EscapeForShell(argv))
+    if (!EscapeForShell(cx, argv))
         return false;
     status = _spawnv(_P_WAIT, sArgv[0], argv.get());
 #else
     pid_t pid = fork();
     switch (pid) {
       case -1:
         JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, JSSMSG_NESTED_FAIL);
         return false;
@@ -8771,24 +8781,25 @@ SetContextOptions(JSContext* cx, const O
 
     reportWarnings = op.getBoolOption('w');
     compileOnly = op.getBoolOption('c');
     printTiming = op.getBoolOption('b');
     enableCodeCoverage = op.getBoolOption("code-coverage");
     enableDisassemblyDumps = op.getBoolOption('D');
     cx->runtime()->profilingScripts = enableCodeCoverage || enableDisassemblyDumps;
 
-    jsCacheDir = op.getStringOption("js-cache");
-    if (jsCacheDir) {
+    if (const char* jsCacheOpt = op.getStringOption("js-cache")) {
+        UniqueChars jsCacheChars;
         if (!op.getBoolOption("no-js-cache-per-process"))
-            jsCacheDir = JS_smprintf("%s/%u", jsCacheDir, (unsigned)getpid()).release();
+            jsCacheChars = JS_smprintf("%s/%u", jsCacheOpt, (unsigned)getpid());
         else
-            jsCacheDir = JS_strdup(cx, jsCacheDir);
-        if (!jsCacheDir)
-            return false;
+            jsCacheChars = DuplicateString(jsCacheOpt);
+        if (!jsCacheChars)
+            return false;
+        jsCacheDir = jsCacheChars.release();
         jsCacheAsmJSPath = JS_smprintf("%s/asmjs.cache", jsCacheDir).release();
     }
 
 #ifdef DEBUG
     dumpEntrainedVariables = op.getBoolOption("dump-entrained-variables");
 #endif
 
 #ifdef JS_GC_ZEAL
--- a/js/src/util/Text.cpp
+++ b/js/src/util/Text.cpp
@@ -31,22 +31,16 @@ js_strchr_limit(const CharT* s, char16_t
 }
 
 template const Latin1Char*
 js_strchr_limit(const Latin1Char* s, char16_t c, const Latin1Char* limit);
 
 template const char16_t*
 js_strchr_limit(const char16_t* s, char16_t c, const char16_t* limit);
 
-JS_PUBLIC_API(char*)
-js_strdup(const char* s)
-{
-    return DuplicateString(s).release();
-}
-
 int32_t
 js_fputs(const char16_t* s, FILE* f)
 {
     while (*s != 0) {
         if (fputwc(wchar_t(*s), f) == WEOF)
             return WEOF;
         s++;
     }
--- a/js/src/vm/BytecodeUtil.cpp
+++ b/js/src/vm/BytecodeUtil.cpp
@@ -31,16 +31,17 @@
 #include "builtin/String.h"
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/SourceNotes.h"
 #include "gc/FreeOp.h"
 #include "gc/GCInternals.h"
 #include "js/CharacterEncoding.h"
 #include "js/Printf.h"
 #include "util/StringBuffer.h"
+#include "util/Text.h"
 #include "vm/CodeCoverage.h"
 #include "vm/EnvironmentObject.h"
 #include "vm/JSAtom.h"
 #include "vm/JSCompartment.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 #include "vm/JSObject.h"
 #include "vm/JSScript.h"
@@ -2314,20 +2315,20 @@ js::DecompileValueGenerator(JSContext* c
         if (result) {
             if (strcmp(result, "(intermediate value)"))
                 return UniqueChars(result);
             js_free(result);
         }
     }
     if (!fallback) {
         if (v.isUndefined())
-            return UniqueChars(JS_strdup(cx, js_undefined_str)); // Prevent users from seeing "(void 0)"
+            return DuplicateString(cx, js_undefined_str); // Prevent users from seeing "(void 0)"
         fallback = ValueToSource(cx, v);
         if (!fallback)
-            return UniqueChars(nullptr);
+            return nullptr;
     }
 
     return UniqueChars(JS_EncodeString(cx, fallback));
 }
 
 static bool
 DecompileArgumentFromStack(JSContext* cx, int formalIndex, char** res)
 {
@@ -2392,37 +2393,37 @@ DecompileArgumentFromStack(JSContext* cx
     if (!ed.init())
         return false;
     if (!ed.decompilePCForStackOperand(current, formalStackIndex))
         return false;
 
     return ed.getOutput(res);
 }
 
-char*
+UniqueChars
 js::DecompileArgument(JSContext* cx, int formalIndex, HandleValue v)
 {
     {
         char* result;
         if (!DecompileArgumentFromStack(cx, formalIndex, &result))
             return nullptr;
         if (result) {
             if (strcmp(result, "(intermediate value)"))
-                return result;
+                return UniqueChars(result);
             js_free(result);
         }
     }
     if (v.isUndefined())
-        return JS_strdup(cx, js_undefined_str); // Prevent users from seeing "(void 0)"
+        return DuplicateString(cx, js_undefined_str); // Prevent users from seeing "(void 0)"
 
     RootedString fallback(cx, ValueToSource(cx, v));
     if (!fallback)
         return nullptr;
 
-    return JS_EncodeString(cx, fallback);
+    return UniqueChars(JS_EncodeString(cx, fallback));
 }
 
 bool
 js::CallResultEscapes(jsbytecode* pc)
 {
     /*
      * If we see any of these sequences, the result is unused:
      * - call / pop
--- a/js/src/vm/BytecodeUtil.h
+++ b/js/src/vm/BytecodeUtil.h
@@ -604,17 +604,17 @@ GetVariableBytecodeLength(jsbytecode* pc
 UniqueChars
 DecompileValueGenerator(JSContext* cx, int spindex, HandleValue v,
                         HandleString fallback, int skipStackHits = 0);
 
 /*
  * Decompile the formal argument at formalIndex in the nearest non-builtin
  * stack frame, falling back with converting v to source.
  */
-char*
+UniqueChars
 DecompileArgument(JSContext* cx, int formalIndex, HandleValue v);
 
 extern bool
 CallResultEscapes(jsbytecode* pc);
 
 static inline unsigned
 GetDecomposeLength(jsbytecode* pc, size_t len)
 {
--- a/js/src/vm/CodeCoverage.cpp
+++ b/js/src/vm/CodeCoverage.cpp
@@ -58,63 +58,57 @@
 //     LH:<sum of lines hits>
 //   }
 //
 // [1] http://ltp.sourceforge.net/coverage/lcov/geninfo.1.php
 //
 namespace js {
 namespace coverage {
 
-LCovSource::LCovSource(LifoAlloc* alloc, const char* name)
-  : name_(name),
+LCovSource::LCovSource(LifoAlloc* alloc, UniqueChars name)
+  : name_(std::move(name)),
     outFN_(alloc),
     outFNDA_(alloc),
     numFunctionsFound_(0),
     numFunctionsHit_(0),
     outBRDA_(alloc),
     numBranchesFound_(0),
     numBranchesHit_(0),
     numLinesInstrumented_(0),
     numLinesHit_(0),
     maxLineHit_(0),
     hasTopLevelScript_(false)
 {
 }
 
 LCovSource::LCovSource(LCovSource&& src)
-  : name_(src.name_),
+  : name_(std::move(src.name_)),
     outFN_(src.outFN_),
     outFNDA_(src.outFNDA_),
     numFunctionsFound_(src.numFunctionsFound_),
     numFunctionsHit_(src.numFunctionsHit_),
     outBRDA_(src.outBRDA_),
     numBranchesFound_(src.numBranchesFound_),
     numBranchesHit_(src.numBranchesHit_),
     linesHit_(std::move(src.linesHit_)),
     numLinesInstrumented_(src.numLinesInstrumented_),
     numLinesHit_(src.numLinesHit_),
     maxLineHit_(src.maxLineHit_),
     hasTopLevelScript_(src.hasTopLevelScript_)
 {
-    src.name_ = nullptr;
-}
-
-LCovSource::~LCovSource()
-{
-    js_delete(name_);
 }
 
 void
 LCovSource::exportInto(GenericPrinter& out) const
 {
     // Only write if everything got recorded.
     if (!hasTopLevelScript_)
         return;
 
-    out.printf("SF:%s\n", name_);
+    out.printf("SF:%s\n", name_.get());
 
     outFN_.exportInto(out);
     outFNDA_.exportInto(out);
     out.printf("FNF:%zu\n", numFunctionsFound_);
     out.printf("FNH:%zu\n", numFunctionsHit_);
 
     outBRDA_.exportInto(out);
     out.printf("BRF:%zu\n", numBranchesFound_);
@@ -504,24 +498,24 @@ LCovRealm::lookupOrAdd(JS::Realm* realm,
     } else {
         // Find the first matching source.
         for (LCovSource& source : *sources_) {
             if (source.match(name))
                 return &source;
         }
     }
 
-    char* source_name = js_strdup(name);
+    UniqueChars source_name = DuplicateString(name);
     if (!source_name) {
         outTN_.reportOutOfMemory();
         return nullptr;
     }
 
     // Allocate a new LCovSource for the current top-level.
-    if (!sources_->append(std::move(LCovSource(&alloc_, source_name)))) {
+    if (!sources_->emplaceBack(LCovSource(&alloc_, std::move(source_name)))) {
         outTN_.reportOutOfMemory();
         return nullptr;
     }
 
     return &sources_->back();
 }
 
 void
--- a/js/src/vm/CodeCoverage.h
+++ b/js/src/vm/CodeCoverage.h
@@ -8,35 +8,36 @@
 #define vm_CodeCoverage_h
 
 #include "mozilla/Vector.h"
 
 #include "ds/LifoAlloc.h"
 
 #include "js/HashTable.h"
 #include "js/TypeDecls.h"
+#include "js/Utility.h"
 
 #include "vm/Printer.h"
 
 namespace js {
 
 class ScriptSourceObject;
 
 namespace coverage {
 
 class LCovSource
 {
   public:
-    LCovSource(LifoAlloc* alloc, const char* name);
+    LCovSource(LifoAlloc* alloc, JS::UniqueChars name);
     LCovSource(LCovSource&& src);
-    ~LCovSource();
+    ~LCovSource() = default;
 
     // Whether the given script name matches this LCovSource.
     bool match(const char* name) const {
-        return strcmp(name_, name) == 0;
+        return strcmp(name_.get(), name) == 0;
     }
 
     // Whether the current source is complete and if it can be flushed.
     bool isComplete() const {
         return hasTopLevelScript_;
     }
 
     // Iterate over the bytecode and collect the lcov output based on the
@@ -48,17 +49,17 @@ class LCovSource
     void exportInto(GenericPrinter& out) const;
 
   private:
     // Write the script name in out.
     bool writeScriptName(LSprinter& out, JSScript* script);
 
   private:
     // Name of the source file.
-    const char* name_;
+    JS::UniqueChars name_;
 
     // LifoAlloc strings which hold the filename of each function as
     // well as the number of hits for each function.
     LSprinter outFN_;
     LSprinter outFNDA_;
     size_t numFunctionsFound_;
     size_t numFunctionsHit_;
 
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -6,16 +6,18 @@
 
 #include "vm/Debugger-inl.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/TypeTraits.h"
 
+#include <utility>
+
 #include "jsfriendapi.h"
 #include "jsnum.h"
 
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/Parser.h"
 #include "gc/FreeOp.h"
 #include "gc/HashUtil.h"
 #include "gc/Marking.h"
@@ -23,16 +25,17 @@
 #include "gc/PublicIterators.h"
 #include "jit/BaselineDebugModeOSR.h"
 #include "jit/BaselineJIT.h"
 #include "js/Date.h"
 #include "js/UbiNodeBreadthFirst.h"
 #include "js/Vector.h"
 #include "js/Wrapper.h"
 #include "proxy/ScriptedProxyHandler.h"
+#include "util/Text.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/AsyncFunction.h"
 #include "vm/AsyncIteration.h"
 #include "vm/DebuggerMemory.h"
 #include "vm/GeckoProfiler.h"
 #include "vm/GeneratorObject.h"
 #include "vm/JSCompartment.h"
 #include "vm/JSContext.h"
@@ -443,35 +446,27 @@ ValueToStableChars(JSContext* cx, const 
     RootedLinearString linear(cx, value.toString()->ensureLinear(cx));
     if (!linear)
         return false;
     if (!stableChars.initTwoByte(cx, linear))
         return false;
     return true;
 }
 
-EvalOptions::~EvalOptions()
-{
-    js_free(const_cast<char*>(filename_));
-}
-
 bool
 EvalOptions::setFilename(JSContext* cx, const char* filename)
 {
-    char* copy = nullptr;
+    JS::UniqueChars copy;
     if (filename) {
-        copy = JS_strdup(cx, filename);
+        copy = DuplicateString(cx, filename);
         if (!copy)
             return false;
     }
 
-    // EvalOptions always owns filename_, so this cast is okay.
-    js_free(const_cast<char*>(filename_));
-
-    filename_ = copy;
+    filename_ = std::move(copy);
     return true;
 }
 
 static bool
 ParseEvalOptions(JSContext* cx, HandleValue value, EvalOptions& options)
 {
     if (!value.isObject())
         return true;
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -16,16 +16,17 @@
 
 #include "builtin/Promise.h"
 #include "ds/TraceableFifo.h"
 #include "gc/Barrier.h"
 #include "gc/WeakMap.h"
 #include "js/Debug.h"
 #include "js/GCVariant.h"
 #include "js/HashTable.h"
+#include "js/Utility.h"
 #include "js/Wrapper.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSCompartment.h"
 #include "vm/JSContext.h"
 #include "vm/SavedStacks.h"
 #include "vm/Stack.h"
 #include "wasm/WasmJS.h"
 
@@ -250,23 +251,23 @@ class AutoSuppressDebuggeeNoExecuteCheck
 
     ~AutoSuppressDebuggeeNoExecuteChecks() {
         MOZ_ASSERT(!*stack_);
         *stack_ = prev_;
     }
 };
 
 class MOZ_RAII EvalOptions {
-    const char* filename_;
-    unsigned lineno_;
+    JS::UniqueChars filename_;
+    unsigned lineno_ = 1;
 
   public:
-    EvalOptions() : filename_(nullptr), lineno_(1) {}
-    ~EvalOptions();
-    const char* filename() const { return filename_; }
+    EvalOptions() = default;
+    ~EvalOptions() = default;
+    const char* filename() const { return filename_.get(); }
     unsigned lineno() const { return lineno_; }
     MOZ_MUST_USE bool setFilename(JSContext* cx, const char* filename);
     void setLineno(unsigned lineno) { lineno_ = lineno; }
 };
 
 /*
  * Env is the type of what ES5 calls "lexical environments" (runtime activations
  * of lexical scopes). This is currently just JSObject, and is implemented by
--- a/js/src/vm/JSScript.cpp
+++ b/js/src/vm/JSScript.cpp
@@ -2696,17 +2696,17 @@ JSScript::initScriptName(JSContext* cx)
         if (!map || !map->init()) {
             ReportOutOfMemory(cx);
             return false;
         }
 
         realm()->scriptNameMap = std::move(map);
     }
 
-    UniqueChars name(js_strdup(filename()));
+    UniqueChars name = DuplicateString(filename());
     if (!name) {
         ReportOutOfMemory(cx);
         return false;
     }
 
     // Register the script name in the realm's map.
     if (!realm()->scriptNameMap->putNew(this, std::move(name))) {
         ReportOutOfMemory(cx);
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -522,19 +522,24 @@ JSContext::handleInterrupt()
     return true;
 }
 
 bool
 JSRuntime::setDefaultLocale(const char* locale)
 {
     if (!locale)
         return false;
+
+    char* newLocale = DuplicateString(mainContextFromOwnThread(), locale).release();
+    if (!newLocale)
+        return false;
+
     resetDefaultLocale();
-    defaultLocale = JS_strdup(mainContextFromOwnThread(), locale);
-    return defaultLocale != nullptr;
+    defaultLocale = newLocale;
+    return true;
 }
 
 void
 JSRuntime::resetDefaultLocale()
 {
     js_free(defaultLocale);
     defaultLocale = nullptr;
 }
@@ -546,17 +551,17 @@ JSRuntime::getDefaultLocale()
         return defaultLocale;
 
     const char* locale = setlocale(LC_ALL, nullptr);
 
     // convert to a well-formed BCP 47 language tag
     if (!locale || !strcmp(locale, "C"))
         locale = "und";
 
-    char* lang = JS_strdup(mainContextFromOwnThread(), locale);
+    char* lang = DuplicateString(mainContextFromOwnThread(), locale).release();
     if (!lang)
         return nullptr;
 
     char* p;
     if ((p = strchr(lang, '.')))
         *p = '\0';
     while ((p = strchr(lang, '_')))
         *p = '-';
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -519,24 +519,24 @@ intrinsic_FinishBoundFunctionInit(JSCont
  * _cannot_ be automatically determined.
  */
 static bool
 intrinsic_DecompileArg(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 2);
 
-    RootedValue value(cx, args[1]);
-    ScopedJSFreePtr<char> str(DecompileArgument(cx, args[0].toInt32(), value));
+    HandleValue value = args[1];
+    UniqueChars str = DecompileArgument(cx, args[0].toInt32(), value);
     if (!str)
         return false;
-    JSAtom* atom = Atomize(cx, str, strlen(str));
-    if (!atom)
+    JSString* result = NewStringCopyZ<CanGC>(cx, str.get());
+    if (!result)
         return false;
-    args.rval().setString(atom);
+    args.rval().setString(result);
     return true;
 }
 
 static bool
 intrinsic_DefineDataProperty(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
--- a/js/src/vm/SharedImmutableStringsCache.h
+++ b/js/src/vm/SharedImmutableStringsCache.h
@@ -39,29 +39,29 @@ class SharedImmutableTwoByteString;
  */
 class SharedImmutableStringsCache
 {
     friend class SharedImmutableString;
     friend class SharedImmutableTwoByteString;
     struct Hasher;
 
   public:
-    using OwnedChars = mozilla::UniquePtr<char[], JS::FreePolicy>;
-    using OwnedTwoByteChars = mozilla::UniquePtr<char16_t[], JS::FreePolicy>;
+    using OwnedChars = JS::UniqueChars;
+    using OwnedTwoByteChars = JS::UniqueTwoByteChars;
 
     /**
      * Get the canonical, shared, and de-duplicated version of the given `const
      * char*` string. If such a string does not exist, call `intoOwnedChars` and
      * add the string it returns to the cache.
      *
      * `intoOwnedChars` must create an owned version of the given string, and
      * must have one of the following types:
      *
-     *     mozilla::UniquePtr<char[], JS::FreePolicy>   intoOwnedChars();
-     *     mozilla::UniquePtr<char[], JS::FreePolicy>&& intoOwnedChars();
+     *     JS::UniqueChars   intoOwnedChars();
+     *     JS::UniqueChars&& intoOwnedChars();
      *
      * It can be used by callers to elide a copy of the string when it is safe
      * to give up ownership of the lookup string to the cache. It must return a
      * `nullptr` on failure.
      *
      * On success, `Some` is returned. In the case of OOM failure, `Nothing` is
      * returned.
      */
@@ -93,18 +93,18 @@ class SharedImmutableStringsCache
     /**
      * Get the canonical, shared, and de-duplicated version of the given `const
      * char16_t*` string. If such a string does not exist, call `intoOwnedChars`
      * and add the string it returns to the cache.
      *
      * `intoOwnedTwoByteChars` must create an owned version of the given string,
      * and must have one of the following types:
      *
-     *     mozilla::UniquePtr<char16_t[], JS::FreePolicy>   intoOwnedTwoByteChars();
-     *     mozilla::UniquePtr<char16_t[], JS::FreePolicy>&& intoOwnedTwoByteChars();
+     *     JS::UniqueTwoByteChars   intoOwnedTwoByteChars();
+     *     JS::UniqueTwoByteChars&& intoOwnedTwoByteChars();
      *
      * It can be used by callers to elide a copy of the string when it is safe
      * to give up ownership of the lookup string to the cache. It must return a
      * `nullptr` on failure.
      *
      * On success, `Some` is returned. In the case of OOM failure, `Nothing` is
      * returned.
      */
--- a/js/src/vm/TraceLogging.cpp
+++ b/js/src/vm/TraceLogging.cpp
@@ -7,22 +7,25 @@
 #include "vm/TraceLogging.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/EndianUtils.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/ScopeExit.h"
 
 #include <string.h>
+#include <utility>
 
 #include "jsapi.h"
 
 #include "jit/BaselineJIT.h"
 #include "jit/CompileWrappers.h"
+#include "js/Printf.h"
 #include "threading/LockGuard.h"
+#include "util/Text.h"
 #include "vm/JSScript.h"
 #include "vm/Runtime.h"
 #include "vm/Time.h"
 #include "vm/TraceLoggingGraph.h"
 
 #include "jit/JitFrames-inl.h"
 
 using namespace js;
@@ -413,27 +416,25 @@ TraceLoggerThreadState::getOrCreateEvent
 
     PointerHashMap::AddPtr p = pointerMap.lookupForAdd((const void*)text);
     if (p) {
         MOZ_ASSERT(p->value()->textId() < nextTextId); // Sanity check.
         p->value()->use();
         return p->value();
     }
 
-    char* str = js_strdup(text);
+    UniqueChars str = DuplicateString(text);
     if (!str)
         return nullptr;
 
     uint32_t textId = nextTextId;
 
-    TraceLoggerEventPayload* payload = js_new<TraceLoggerEventPayload>(textId, str);
-    if (!payload) {
-        js_free(str);
+    auto* payload = js_new<TraceLoggerEventPayload>(textId, std::move(str));
+    if (!payload)
         return nullptr;
-    }
 
     if (!textIdPayloads.putNew(textId, payload)) {
         js_delete(payload);
         payload = nullptr;
         return nullptr;
     }
 
     payload->use();
@@ -445,56 +446,41 @@ TraceLoggerThreadState::getOrCreateEvent
 
     payload->incPointerCount();
 
     return payload;
 }
 
 TraceLoggerEventPayload*
 TraceLoggerThreadState::getOrCreateEventPayload(const char* filename,
-                                                size_t lineno, size_t colno, const void* ptr)
+                                                uint32_t lineno, uint32_t colno, const void* ptr)
 {
     if (!filename)
         filename = "<unknown>";
 
     LockGuard<Mutex> guard(lock);
 
     PointerHashMap::AddPtr p;
     if (ptr) {
         p = pointerMap.lookupForAdd(ptr);
         if (p) {
             MOZ_ASSERT(p->value()->textId() < nextTextId); // Sanity check.
             p->value()->use();
             return p->value();
         }
     }
 
-    // Compute the length of the string to create.
-    size_t lenFilename = strlen(filename);
-    size_t lenLineno = 1;
-    for (size_t i = lineno; i /= 10; lenLineno++);
-    size_t lenColno = 1;
-    for (size_t i = colno; i /= 10; lenColno++);
-
-    size_t len = 7 + lenFilename + 1 + lenLineno + 1 + lenColno;
-    char* str = js_pod_malloc<char>(len + 1);
+    UniqueChars str = JS_smprintf("script %s:%u:%u", filename, lineno, colno);
     if (!str)
         return nullptr;
 
-    DebugOnly<size_t> ret =
-        snprintf(str, len + 1, "script %s:%zu:%zu", filename, lineno, colno);
-    MOZ_ASSERT(ret == len);
-    MOZ_ASSERT(strlen(str) == len);
-
     uint32_t textId = nextTextId;
-    TraceLoggerEventPayload* payload = js_new<TraceLoggerEventPayload>(textId, str);
-    if (!payload) {
-        js_free(str);
+    auto* payload = js_new<TraceLoggerEventPayload>(textId, std::move(str));
+    if (!payload)
         return nullptr;
-    }
 
     if (!textIdPayloads.putNew(textId, payload)) {
         js_delete(payload);
         payload = nullptr;
         return nullptr;
     }
 
     payload->use();
@@ -1025,18 +1011,18 @@ js::TraceLogDisableTextId(JSContext* cx,
         return;
     traceLoggerState->disableTextId(cx, textId);
 }
 
 TraceLoggerEvent::TraceLoggerEvent(TraceLoggerTextId type, JSScript* script)
   : TraceLoggerEvent(type, script->filename(), script->lineno(), script->column())
 { }
 
-TraceLoggerEvent::TraceLoggerEvent(TraceLoggerTextId type, const char* filename, size_t line,
-                                   size_t column)
+TraceLoggerEvent::TraceLoggerEvent(TraceLoggerTextId type, const char* filename, uint32_t line,
+                                   uint32_t column)
   : payload_()
 {
     MOZ_ASSERT(type == TraceLogger_Scripts || type == TraceLogger_AnnotateScripts ||
                type == TraceLogger_InlinedScripts || type == TraceLogger_Frontend);
 
     if (!traceLoggerState)
         return;
 
--- a/js/src/vm/TraceLogging.h
+++ b/js/src/vm/TraceLogging.h
@@ -6,16 +6,18 @@
 
 #ifndef TraceLogging_h
 #define TraceLogging_h
 
 #include "mozilla/GuardObjects.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/MemoryReporting.h"
 
+#include <utility>
+
 #include "js/AllocPolicy.h"
 #include "js/HashTable.h"
 #include "js/TypeDecls.h"
 #include "js/Vector.h"
 #include "vm/MutexIDs.h"
 #include "vm/TraceLoggingGraph.h"
 #include "vm/TraceLoggingTypes.h"
 
@@ -126,17 +128,17 @@ class TraceLoggerEvent {
     EventPayloadOrTextId payload_;
 
   public:
     TraceLoggerEvent()
       : payload_()
     {}
     explicit TraceLoggerEvent(TraceLoggerTextId textId);
     TraceLoggerEvent(TraceLoggerTextId type, JSScript* script);
-    TraceLoggerEvent(TraceLoggerTextId type, const char* filename, size_t line, size_t column);
+    TraceLoggerEvent(TraceLoggerTextId type, const char* filename, uint32_t line, uint32_t column);
     explicit TraceLoggerEvent(const char* text);
     TraceLoggerEvent(const TraceLoggerEvent& event);
     TraceLoggerEvent& operator=(const TraceLoggerEvent& other);
     ~TraceLoggerEvent();
     uint32_t textId() const;
     bool hasTextId() const {
         return hasExtPayload() || payload_.isTextId();
     }
@@ -149,17 +151,17 @@ class TraceLoggerEvent {
     bool hasExtPayload() const {
         return payload_.isEventPayload() && !!payload_.eventPayload();
     }
 #else
   public:
     TraceLoggerEvent() {}
     explicit TraceLoggerEvent(TraceLoggerTextId textId) {}
     TraceLoggerEvent(TraceLoggerTextId type, JSScript* script) {}
-    TraceLoggerEvent(TraceLoggerTextId type, const char* filename, size_t line, size_t column) {}
+    TraceLoggerEvent(TraceLoggerTextId type, const char* filename, uint32_t line, uint32_t column) {}
     explicit TraceLoggerEvent(const char* text) {}
     TraceLoggerEvent(const TraceLoggerEvent& event) {}
     TraceLoggerEvent& operator=(const TraceLoggerEvent& other) { return *this; };
     ~TraceLoggerEvent() {}
     uint32_t textId() const { return 0; }
     bool hasTextId() const { return false; }
 #endif
 
@@ -180,19 +182,19 @@ bool CurrentThreadOwnsTraceLoggerThreadS
  */
 class TraceLoggerEventPayload {
     uint32_t textId_;
     UniqueChars string_;
     mozilla::Atomic<uint32_t> uses_;
     mozilla::Atomic<uint32_t> pointerCount_;
 
   public:
-    TraceLoggerEventPayload(uint32_t textId, char* string)
+    TraceLoggerEventPayload(uint32_t textId, UniqueChars string)
       : textId_(textId),
-        string_(string),
+        string_(std::move(string)),
         uses_(0)
     { }
 
     ~TraceLoggerEventPayload() {
         MOZ_ASSERT(uses_ == 0);
     }
 
     uint32_t textId() {
@@ -437,18 +439,18 @@ class TraceLoggerThreadState
     void purgeUnusedPayloads();
 
     // These functions map a unique input to a logger ID.
     // This can be used to give start and stop events. Calls to these functions should be
     // limited if possible, because of the overhead.
     // Note: it is not allowed to use them in logTimestamp.
     TraceLoggerEventPayload* getOrCreateEventPayload(const char* text);
     TraceLoggerEventPayload* getOrCreateEventPayload(JSScript* script);
-    TraceLoggerEventPayload* getOrCreateEventPayload(const char* filename, size_t lineno,
-                                                     size_t colno, const void* p);
+    TraceLoggerEventPayload* getOrCreateEventPayload(const char* filename, uint32_t lineno,
+                                                     uint32_t colno, const void* p);
 
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) {
         return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
     }
 #endif
 };
 
--- a/js/src/vm/UbiNodeCensus.cpp
+++ b/js/src/vm/UbiNodeCensus.cpp
@@ -783,17 +783,17 @@ ByAllocationStack::report(JSContext* cx,
     MOZ_ASSERT(generation == count.table.generation());
 
     report.setObject(*map);
     return true;
 }
 
 // A count type that categorizes nodes by their script's filename.
 class ByFilename : public CountType {
-    using UniqueCString = UniquePtr<char, JS::FreePolicy>;
+    using UniqueCString = JS::UniqueChars;
 
     struct UniqueCStringHasher {
         using Lookup = UniqueCString;
 
         static js::HashNumber hash(const Lookup& lookup) {
             return CStringHasher::hash(lookup.get());
         }
 
@@ -875,17 +875,17 @@ bool
 ByFilename::count(CountBase& countBase, mozilla::MallocSizeOf mallocSizeOf, const Node& node)
 {
     Count& count = static_cast<Count&>(countBase);
 
     const char* filename = node.scriptFilename();
     if (!filename)
         return count.noFilename->count(mallocSizeOf, node);
 
-    UniqueCString myFilename(js_strdup(filename));
+    UniqueCString myFilename = DuplicateString(filename);
     if (!myFilename)
         return false;
 
     Table::AddPtr p = count.table.lookupForAdd(myFilename);
     if (!p) {
         CountBasePtr thenCount(thenType->makeCount());
         if (!thenCount || !count.table.add(p, std::move(myFilename), std::move(thenCount)))
             return false;