Bug 1334278 - change JS_smprintf to return UniqueChars; r?froydnj draft
authorTom Tromey <tom@tromey.com>
Fri, 03 Mar 2017 15:10:11 -0700
changeset 568734 6bc390539e800a48281e77a04cfb727696b9cbf1
parent 568733 41f33e577fe1afae8b9ce59d0bbc03ff6edae158
child 568735 d8c53d11dd277f4f5ddd3e74a971957c332021be
push id55961
push userbmo:ttromey@mozilla.com
push dateWed, 26 Apr 2017 14:11:48 +0000
reviewersfroydnj
bugs1334278
milestone55.0a1
Bug 1334278 - change JS_smprintf to return UniqueChars; r?froydnj This changes JS_smprintf to return UniqueChars, rather than relying on manual memory management. MozReview-Commit-ID: ENjQJODYdD1
dom/bindings/nsScriptError.cpp
js/src/builtin/Intl.cpp
js/src/jit/BacktrackingAllocator.cpp
js/src/jit/Ion.cpp
js/src/jit/LIR.cpp
js/src/jit/MacroAssembler.cpp
js/src/jsapi-tests/testParseJSON.cpp
js/src/jsapi-tests/testPrintf.cpp
js/src/jsapi.h
js/src/jscntxt.cpp
js/src/jsfriendapi.cpp
js/src/jsopcode.cpp
js/src/jsprf.cpp
js/src/jsprf.h
js/src/shell/js.cpp
js/src/vm/SelfHosting.cpp
js/src/wasm/AsmJS.cpp
js/src/wasm/WasmTextToBinary.cpp
js/xpconnect/loader/mozJSComponentLoader.cpp
js/xpconnect/src/XPCShellImpl.cpp
js/xpconnect/src/XPCThrower.cpp
js/xpconnect/src/XPCWrappedJSClass.cpp
js/xpconnect/src/XPCWrappedNative.cpp
js/xpconnect/src/XPCWrappedNativeJSOps.cpp
--- a/dom/bindings/nsScriptError.cpp
+++ b/dom/bindings/nsScriptError.cpp
@@ -252,17 +252,17 @@ ToStringHelper(const char* aSeverity, co
 {
     static const char format0[] =
         "[%s: \"%s\" {file: \"%s\" line: %d column: %d source: \"%s\"}]";
     static const char format1[] =
         "[%s: \"%s\" {file: \"%s\" line: %d}]";
     static const char format2[] =
         "[%s: \"%s\"]";
 
-    char* temp;
+    UniqueChars temp;
     char* tempMessage = nullptr;
     char* tempSourceName = nullptr;
     char* tempSourceLine = nullptr;
 
     if (!aMessage.IsEmpty())
         tempMessage = ToNewUTF8String(aMessage);
     if (!aSourceName.IsEmpty())
         // Use at most 512 characters from mSourceName.
@@ -296,18 +296,17 @@ ToStringHelper(const char* aSeverity, co
     if (nullptr != tempSourceName)
         free(tempSourceName);
     if (nullptr != tempSourceLine)
         free(tempSourceLine);
 
     if (!temp)
         return NS_ERROR_OUT_OF_MEMORY;
 
-    aResult.Assign(temp);
-    JS_smprintf_free(temp);
+    aResult.Assign(temp.get());
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsScriptErrorBase::ToString(nsACString& /*UTF8*/ aResult)
 {
     static const char error[] = "JavaScript Error";
     static const char warning[] = "JavaScript Warning";
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -1303,17 +1303,17 @@ NewUCollator(JSContext* cx, Handle<Colla
         size_t insertLen = strlen(insert);
         char* newLocale = cx->pod_malloc<char>(localeLen + insertLen + 1);
         if (!newLocale)
             return nullptr;
         memcpy(newLocale, oldLocale, index);
         memcpy(newLocale + index, insert, insertLen);
         memcpy(newLocale + index + insertLen, oldLocale + index, localeLen - index + 1); // '\0'
         locale.clear();
-        locale.initBytes(newLocale);
+        locale.initBytes(JS::UniqueChars(newLocale));
     } else {
         MOZ_ASSERT(StringEqualsAscii(usage, "sort"));
     }
 
     // We don't need to look at the collation property - it can only be set
     // via the Unicode locale extension and is therefore already set on
     // locale.
 
--- a/js/src/jit/BacktrackingAllocator.cpp
+++ b/js/src/jit/BacktrackingAllocator.cpp
@@ -2308,51 +2308,51 @@ BacktrackingAllocator::annotateMoveGroup
 
 #ifdef JS_JITSPEW
 
 UniqueChars
 LiveRange::toString() const
 {
     AutoEnterOOMUnsafeRegion oomUnsafe;
 
-    char* buf = JS_smprintf("v%u [%u,%u)", hasVreg() ? vreg() : 0, from().bits(), to().bits());
+    UniqueChars buf = JS_smprintf("v%u [%u,%u)", hasVreg() ? vreg() : 0, from().bits(), to().bits());
 
     if (buf && bundle() && !bundle()->allocation().isBogus())
-        buf = JS_sprintf_append(buf, " %s", bundle()->allocation().toString().get());
+        buf = JS_sprintf_append(Move(buf), " %s", bundle()->allocation().toString().get());
 
     if (buf && hasDefinition())
-        buf = JS_sprintf_append(buf, " (def)");
+        buf = JS_sprintf_append(Move(buf), " (def)");
 
     for (UsePositionIterator iter = usesBegin(); buf && iter; iter++)
-        buf = JS_sprintf_append(buf, " %s@%u", iter->use()->toString().get(), iter->pos.bits());
+        buf = JS_sprintf_append(Move(buf), " %s@%u", iter->use()->toString().get(), iter->pos.bits());
 
     if (!buf)
         oomUnsafe.crash("LiveRange::toString()");
 
-    return UniqueChars(buf);
+    return buf;
 }
 
 UniqueChars
 LiveBundle::toString() const
 {
     AutoEnterOOMUnsafeRegion oomUnsafe;
 
     // Suppress -Wformat warning.
-    char *buf = JS_smprintf("%s", "");
+    UniqueChars buf = JS_smprintf("%s", "");
 
     for (LiveRange::BundleLinkIterator iter = rangesBegin(); buf && iter; iter++) {
-        buf = JS_sprintf_append(buf, "%s %s",
+        buf = JS_sprintf_append(Move(buf), "%s %s",
                                 (iter == rangesBegin()) ? "" : " ##",
                                 LiveRange::get(*iter)->toString().get());
     }
 
     if (!buf)
         oomUnsafe.crash("LiveBundle::toString()");
 
-    return UniqueChars(buf);
+    return buf;
 }
 
 #endif // JS_JITSPEW
 
 void
 BacktrackingAllocator::dumpVregs()
 {
 #ifdef JS_JITSPEW
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -3320,22 +3320,21 @@ jit::Invalidate(JSContext* cx, JSScript*
         //      "<filename>:<lineno>"
 
         // Get the script filename, if any, and its length.
         const char* filename = script->filename();
         if (filename == nullptr)
             filename = "<unknown>";
 
         // Construct the descriptive string.
-        char* buf = JS_smprintf("Invalidate %s:%" PRIuSIZE, filename, script->lineno());
+        UniqueChars buf = JS_smprintf("Invalidate %s:%" PRIuSIZE, filename, script->lineno());
 
         // Ignore the event on allocation failure.
         if (buf) {
-            cx->runtime()->geckoProfiler().markEvent(buf);
-            JS_smprintf_free(buf);
+            cx->runtime()->geckoProfiler().markEvent(buf.get());
         }
     }
 
     // RecompileInfoVector has inline space for at least one element.
     RecompileInfoVector scripts;
     MOZ_ASSERT(script->hasIonScript());
     MOZ_RELEASE_ASSERT(scripts.reserve(1));
     scripts.infallibleAppend(script->ionScript()->recompileInfo());
--- a/js/src/jit/LIR.cpp
+++ b/js/src/jit/LIR.cpp
@@ -371,36 +371,36 @@ typeName(LDefinition::Type type)
     MOZ_CRASH("Invalid type");
 }
 
 UniqueChars
 LDefinition::toString() const
 {
     AutoEnterOOMUnsafeRegion oomUnsafe;
 
-    char* buf;
+    UniqueChars buf;
     if (isBogusTemp()) {
         buf = JS_smprintf("bogus");
     } else {
         buf = JS_smprintf("v%u<%s>", virtualRegister(), typeName(type()));
         if (buf) {
             if (policy() == LDefinition::FIXED)
-                buf = JS_sprintf_append(buf, ":%s", output()->toString().get());
+                buf = JS_sprintf_append(Move(buf), ":%s", output()->toString().get());
             else if (policy() == LDefinition::MUST_REUSE_INPUT)
-                buf = JS_sprintf_append(buf, ":tied(%u)", getReusedInput());
+                buf = JS_sprintf_append(Move(buf), ":tied(%u)", getReusedInput());
         }
     }
 
     if (!buf)
         oomUnsafe.crash("LDefinition::toString()");
 
-    return UniqueChars(buf);
+    return buf;
 }
 
-static char*
+static UniqueChars
 PrintUse(const LUse* use)
 {
     switch (use->policy()) {
       case LUse::REGISTER:
         return JS_smprintf("v%d:r", use->virtualRegister());
       case LUse::FIXED:
         return JS_smprintf("v%d:%s", use->virtualRegister(),
                            AnyRegister::FromCode(use->registerCode()).name());
@@ -415,17 +415,17 @@ PrintUse(const LUse* use)
     }
 }
 
 UniqueChars
 LAllocation::toString() const
 {
     AutoEnterOOMUnsafeRegion oomUnsafe;
 
-    char* buf;
+    UniqueChars buf;
     if (isBogus()) {
         buf = JS_smprintf("bogus");
     } else {
         switch (kind()) {
           case LAllocation::CONSTANT_VALUE:
           case LAllocation::CONSTANT_INDEX:
             buf = JS_smprintf("c");
             break;
@@ -447,17 +447,17 @@ LAllocation::toString() const
           default:
             MOZ_CRASH("what?");
         }
     }
 
     if (!buf)
         oomUnsafe.crash("LAllocation::toString()");
 
-    return UniqueChars(buf);
+    return buf;
 }
 
 void
 LAllocation::dump() const
 {
     fprintf(stderr, "%s\n", toString().get());
 }
 
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1728,21 +1728,20 @@ MacroAssembler::printf(const char* outpu
     callWithABI(JS_FUNC_TO_DATA_PTR(void*, Printf0_));
 
     PopRegsInMask(save);
 }
 
 static void
 Printf1_(const char* output, uintptr_t value) {
     AutoEnterOOMUnsafeRegion oomUnsafe;
-    char* line = JS_sprintf_append(nullptr, output, value);
+    js::UniqueChars line = JS_sprintf_append(nullptr, output, value);
     if (!line)
         oomUnsafe.crash("OOM at masm.printf");
-    fprintf(stderr, "%s", line);
-    js_free(line);
+    fprintf(stderr, "%s", line.get());
 }
 
 void
 MacroAssembler::printf(const char* output, Register value)
 {
     AllocatableRegisterSet regs(RegisterSet::Volatile());
     LiveRegisterSet save(regs.asLiveSet());
     PushRegsInMask(save);
--- a/js/src/jsapi-tests/testParseJSON.cpp
+++ b/js/src/jsapi-tests/testParseJSON.cpp
@@ -301,19 +301,18 @@ Error(JSContext* cx, const char (&input)
     RootedValue exn(cx);
     CHECK(JS_GetPendingException(cx, &exn));
     JS_ClearPendingException(cx);
 
     js::ErrorReport report(cx);
     CHECK(report.init(cx, exn, js::ErrorReport::WithSideEffects));
     CHECK(report.report()->errorNumber == JSMSG_JSON_BAD_PARSE);
 
-    const char* lineAndColumnASCII = JS_smprintf("line %d column %d", expectedLine, expectedColumn);
-    CHECK(strstr(report.toStringResult().c_str(), lineAndColumnASCII) != nullptr);
-    js_free((void*)lineAndColumnASCII);
+    UniqueChars lineAndColumnASCII = JS_smprintf("line %d column %d", expectedLine, expectedColumn);
+    CHECK(strstr(report.toStringResult().c_str(), lineAndColumnASCII.get()) != nullptr);
 
     /* We do not execute JS, so there should be no exception thrown. */
     CHECK(!JS_IsExceptionPending(cx));
 
     return true;
 }
 END_TEST(testParseJSON_error)
 
--- a/js/src/jsapi-tests/testPrintf.cpp
+++ b/js/src/jsapi-tests/testPrintf.cpp
@@ -17,23 +17,20 @@
 
 static bool
 MOZ_FORMAT_PRINTF(2, 3)
 print_one (const char *expect, const char *fmt, ...)
 {
     va_list ap;
 
     va_start(ap, fmt);
-    char *output = JS_vsmprintf (fmt, ap);
+    JS::UniqueChars output = JS_vsmprintf (fmt, ap);
     va_end(ap);
 
-    bool result = output && !strcmp(output, expect);
-    JS_smprintf_free(output);
-
-    return result;
+    return output && !strcmp(output.get(), expect);
 }
 
 static const char *
 zero()
 {
     return nullptr;
 }
 
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -5068,19 +5068,19 @@ class MOZ_RAII JSAutoByteString
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     ~JSAutoByteString() {
         JS_free(nullptr, mBytes);
     }
 
     /* Take ownership of the given byte array. */
-    void initBytes(char* bytes) {
+    void initBytes(JS::UniqueChars&& bytes) {
         MOZ_ASSERT(!mBytes);
-        mBytes = bytes;
+        mBytes = bytes.release();
     }
 
     char* encodeLatin1(JSContext* cx, JSString* str) {
         MOZ_ASSERT(!mBytes);
         MOZ_ASSERT(cx);
         mBytes = JS_EncodeString(cx, str);
         return mBytes;
     }
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -536,24 +536,23 @@ PrintErrorLine(JSContext* cx, FILE* file
 {
 }
 
 template <typename T>
 static bool
 PrintSingleError(JSContext* cx, FILE* file, JS::ConstUTF8CharsZ toStringResult,
                  T* report, PrintErrorKind kind)
 {
-    UniquePtr<char> prefix;
+    UniqueChars prefix;
     if (report->filename)
-        prefix.reset(JS_smprintf("%s:", report->filename));
+        prefix = JS_smprintf("%s:", report->filename);
 
     if (report->lineno) {
-        UniquePtr<char> tmp(JS_smprintf("%s%u:%u ", prefix ? prefix.get() : "", report->lineno,
-                                        report->column));
-        prefix = Move(tmp);
+        prefix = JS_smprintf("%s%u:%u ", prefix ? prefix.get() : "", report->lineno,
+                                        report->column);
     }
 
     if (kind != PrintErrorKind::Error) {
         const char* kindPrefix = nullptr;
         switch (kind) {
           case PrintErrorKind::Error:
             MOZ_CRASH("unreachable");
           case PrintErrorKind::Warning:
@@ -562,18 +561,17 @@ PrintSingleError(JSContext* cx, FILE* fi
           case PrintErrorKind::StrictWarning:
             kindPrefix = "strict warning";
             break;
           case PrintErrorKind::Note:
             kindPrefix = "note";
             break;
         }
 
-        UniquePtr<char> tmp(JS_smprintf("%s%s: ", prefix ? prefix.get() : "", kindPrefix));
-        prefix = Move(tmp);
+        prefix = JS_smprintf("%s%s: ", prefix ? prefix.get() : "", kindPrefix);
     }
 
     const char* message = toStringResult ? toStringResult.c_str() : report->message().c_str();
 
     /* embedded newlines -- argh! */
     const char* ctmp;
     while ((ctmp = strchr(message, '\n')) != 0) {
         ctmp++;
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -786,17 +786,17 @@ FormatValue(JSContext* cx, const Value& 
 // context.
 static char*
 MOZ_FORMAT_PRINTF(3, 4)
 sprintf_append(JSContext* cx, char* buf, const char* fmt, ...)
 {
     va_list ap;
 
     va_start(ap, fmt);
-    char* result = JS_vsprintf_append(buf, fmt, ap);
+    char* result = JS_vsprintf_append(UniqueChars(buf), fmt, ap).release();
     va_end(ap);
 
     if (!result) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
     return result;
@@ -1041,17 +1041,17 @@ JS::FormatStackDump(JSContext* cx, char*
         else
             buf = FormatWasmFrame(cx, i, buf, num, showArgs);
         if (!buf)
             return nullptr;
         num++;
     }
 
     if (!num)
-        buf = JS_sprintf_append(buf, "JavaScript stack is empty\n");
+        buf = JS_sprintf_append(UniqueChars(buf), "JavaScript stack is empty\n").release();
 
     return buf;
 }
 
 extern JS_FRIEND_API(bool)
 JS::ForceLexicalInitialization(JSContext *cx, HandleObject obj)
 {
     AssertHeapIsIdle();
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -1179,32 +1179,32 @@ ToDisassemblySource(JSContext* cx, Handl
 {
     if (v.isString()) {
         Sprinter sprinter(cx);
         if (!sprinter.init())
             return false;
         char* nbytes = QuoteString(&sprinter, v.toString(), '"');
         if (!nbytes)
             return false;
-        nbytes = JS_sprintf_append(nullptr, "%s", nbytes);
+        UniqueChars copy = JS_smprintf("%s", nbytes);
         if (!nbytes) {
             ReportOutOfMemory(cx);
             return false;
         }
-        bytes->initBytes(nbytes);
+        bytes->initBytes(Move(copy));
         return true;
     }
 
     if (JS::CurrentThreadIsHeapBusy() || !cx->isAllocAllowed()) {
-        char* source = JS_sprintf_append(nullptr, "<value>");
+        UniqueChars source = JS_smprintf("<value>");
         if (!source) {
             ReportOutOfMemory(cx);
             return false;
         }
-        bytes->initBytes(source);
+        bytes->initBytes(Move(source));
         return true;
     }
 
     if (v.isObject()) {
         JSObject& obj = v.toObject();
 
         if (obj.is<JSFunction>()) {
             RootedFunction fun(cx, &obj.as<JSFunction>());
@@ -1223,81 +1223,81 @@ ToDisassemblySource(JSContext* cx, Handl
     }
 
     return !!ValueToPrintable(cx, v, bytes, true);
 }
 
 static bool
 ToDisassemblySource(JSContext* cx, HandleScope scope, JSAutoByteString* bytes)
 {
-    char* source = JS_sprintf_append(nullptr, "%s {", ScopeKindString(scope->kind()));
+    UniqueChars source = JS_smprintf("%s {", ScopeKindString(scope->kind()));
     if (!source) {
         ReportOutOfMemory(cx);
         return false;
     }
 
     for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++) {
         JSAutoByteString nameBytes;
         if (!AtomToPrintableString(cx, bi.name(), &nameBytes))
             return false;
 
-        source = JS_sprintf_append(source, "%s: ", nameBytes.ptr());
+        source = JS_sprintf_append(Move(source), "%s: ", nameBytes.ptr());
         if (!source) {
             ReportOutOfMemory(cx);
             return false;
         }
 
         BindingLocation loc = bi.location();
         switch (loc.kind()) {
           case BindingLocation::Kind::Global:
-            source = JS_sprintf_append(source, "global");
+            source = JS_sprintf_append(Move(source), "global");
             break;
 
           case BindingLocation::Kind::Frame:
-            source = JS_sprintf_append(source, "frame slot %u", loc.slot());
+            source = JS_sprintf_append(Move(source), "frame slot %u", loc.slot());
             break;
 
           case BindingLocation::Kind::Environment:
-            source = JS_sprintf_append(source, "env slot %u", loc.slot());
+            source = JS_sprintf_append(Move(source), "env slot %u", loc.slot());
             break;
 
           case BindingLocation::Kind::Argument:
-            source = JS_sprintf_append(source, "arg slot %u", loc.slot());
+            source = JS_sprintf_append(Move(source), "arg slot %u", loc.slot());
             break;
 
           case BindingLocation::Kind::NamedLambdaCallee:
-            source = JS_sprintf_append(source, "named lambda callee");
+            source = JS_sprintf_append(Move(source), "named lambda callee");
             break;
 
           case BindingLocation::Kind::Import:
-            source = JS_sprintf_append(source, "import");
+            source = JS_sprintf_append(Move(source), "import");
             break;
         }
 
         if (!source) {
             ReportOutOfMemory(cx);
             return false;
         }
 
         if (!bi.isLast()) {
-            source = JS_sprintf_append(source, ", ");
+            source = JS_sprintf_append(Move(source), ", ");
             if (!source) {
                 ReportOutOfMemory(cx);
                 return false;
             }
         }
     }
 
-    source = JS_sprintf_append(source, "}");
+    source = JS_sprintf_append(Move(source), "}");
     if (!source) {
         ReportOutOfMemory(cx);
         return false;
     }
 
-    bytes->initBytes(source);
+    bytes->initBytes(Move(source));
     return true;
 }
 
 static bool
 DumpJumpOrigins(JSContext* cx, HandleScript script, jsbytecode* pc,
                 BytecodeParser* parser, Sprinter* sp)
 {
     bool called = false;
--- a/js/src/jsprf.cpp
+++ b/js/src/jsprf.cpp
@@ -15,42 +15,45 @@
 #include "mozilla/Printf.h"
 
 #include "jsalloc.h"
 
 using namespace js;
 
 typedef mozilla::SmprintfPolicyPointer<js::SystemAllocPolicy> JSSmprintfPointer;
 
-JS_PUBLIC_API(char*) JS_smprintf(const char* fmt, ...)
+JS_PUBLIC_API(JS::UniqueChars) JS_smprintf(const char* fmt, ...)
 {
     va_list ap;
     va_start(ap, fmt);
     JSSmprintfPointer result = mozilla::Vsmprintf<js::SystemAllocPolicy>(fmt, ap);
     va_end(ap);
-    return result.release();
+    return JS::UniqueChars(result.release());
 }
 
 JS_PUBLIC_API(void) JS_smprintf_free(char* mem)
 {
     mozilla::SmprintfFree<js::SystemAllocPolicy>(mem);
 }
 
-JS_PUBLIC_API(char*) JS_sprintf_append(char* last, const char* fmt, ...)
+JS_PUBLIC_API(JS::UniqueChars) JS_sprintf_append(JS::UniqueChars&& last, const char* fmt, ...)
 {
     va_list ap;
     va_start(ap, fmt);
+    JSSmprintfPointer lastPtr(last.release());
     JSSmprintfPointer result =
-        mozilla::VsmprintfAppend<js::SystemAllocPolicy>(JSSmprintfPointer(last), fmt, ap);
+        mozilla::VsmprintfAppend<js::SystemAllocPolicy>(Move(lastPtr), fmt, ap);
     va_end(ap);
-    return result.release();
+    return JS::UniqueChars(result.release());
 }
 
-JS_PUBLIC_API(char*) JS_vsmprintf(const char* fmt, va_list ap)
+JS_PUBLIC_API(JS::UniqueChars) JS_vsmprintf(const char* fmt, va_list ap)
 {
-    return mozilla::Vsmprintf<js::SystemAllocPolicy>(fmt, ap).release();
+    return JS::UniqueChars(mozilla::Vsmprintf<js::SystemAllocPolicy>(fmt, ap).release());
 }
 
-JS_PUBLIC_API(char*) JS_vsprintf_append(char* last, const char* fmt, va_list ap)
+JS_PUBLIC_API(JS::UniqueChars) JS_vsprintf_append(JS::UniqueChars&& last,
+                                                  const char* fmt, va_list ap)
 {
-    return mozilla::VsmprintfAppend<js::SystemAllocPolicy>(JSSmprintfPointer(last),
-                                                           fmt, ap).release();
+    JSSmprintfPointer lastPtr(last.release());
+    return JS::UniqueChars(mozilla::VsmprintfAppend<js::SystemAllocPolicy>(Move(lastPtr),
+                                                                           fmt, ap).release());
 }
--- a/js/src/jsprf.h
+++ b/js/src/jsprf.h
@@ -7,24 +7,27 @@
 #ifndef jsprf_h
 #define jsprf_h
 
 #include "mozilla/Printf.h"
 
 #include <stdarg.h>
 
 #include "jstypes.h"
+#include "js/Utility.h"
 
 /* Wrappers for mozilla::Smprintf and friends that are used throughout
    JS.  */
 
-extern JS_PUBLIC_API(char*) JS_smprintf(const char* fmt, ...)
+extern JS_PUBLIC_API(JS::UniqueChars) JS_smprintf(const char* fmt, ...)
     MOZ_FORMAT_PRINTF(1, 2);
 
 extern JS_PUBLIC_API(void) JS_smprintf_free(char* mem);
 
-extern JS_PUBLIC_API(char*) JS_sprintf_append(char* last, const char* fmt, ...)
+extern JS_PUBLIC_API(JS::UniqueChars) JS_sprintf_append(JS::UniqueChars&& last,
+                                                        const char* fmt, ...)
      MOZ_FORMAT_PRINTF(2, 3);
 
-extern JS_PUBLIC_API(char*) JS_vsmprintf(const char* fmt, va_list ap);
-extern JS_PUBLIC_API(char*) JS_vsprintf_append(char* last, const char* fmt, va_list ap);
+extern JS_PUBLIC_API(JS::UniqueChars) JS_vsmprintf(const char* fmt, va_list ap);
+extern JS_PUBLIC_API(JS::UniqueChars) JS_vsprintf_append(JS::UniqueChars&& last,
+                                                         const char* fmt, va_list ap);
 
 #endif /* jsprf_h */
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1358,41 +1358,40 @@ Options(JSContext* cx, unsigned argc, Va
                                "unknown option name '%s'."
                                " The valid names are strict,"
                                " werror, and strict_mode.",
                                opt.ptr());
             return false;
         }
     }
 
-    char* names = strdup("");
+    UniqueChars names = DuplicateString("");
     bool found = false;
     if (names && oldContextOptions.extraWarnings()) {
-        names = JS_sprintf_append(names, "%s%s", found ? "," : "", "strict");
+        names = JS_sprintf_append(Move(names), "%s%s", found ? "," : "", "strict");
         found = true;
     }
     if (names && oldContextOptions.werror()) {
-        names = JS_sprintf_append(names, "%s%s", found ? "," : "", "werror");
+        names = JS_sprintf_append(Move(names), "%s%s", found ? "," : "", "werror");
         found = true;
     }
     if (names && oldContextOptions.throwOnAsmJSValidationFailure()) {
-        names = JS_sprintf_append(names, "%s%s", found ? "," : "", "throw_on_asmjs_validation_failure");
+        names = JS_sprintf_append(Move(names), "%s%s", found ? "," : "", "throw_on_asmjs_validation_failure");
         found = true;
     }
     if (names && oldContextOptions.strictMode()) {
-        names = JS_sprintf_append(names, "%s%s", found ? "," : "", "strict_mode");
+        names = JS_sprintf_append(Move(names), "%s%s", found ? "," : "", "strict_mode");
         found = true;
     }
     if (!names) {
         JS_ReportOutOfMemory(cx);
         return false;
     }
 
-    JSString* str = JS_NewStringCopyZ(cx, names);
-    free(names);
+    JSString* str = JS_NewStringCopyZ(cx, names.get());
     if (!str)
         return false;
     args.rval().setString(str);
     return true;
 }
 
 static bool
 LoadScript(JSContext* cx, unsigned argc, Value* vp, bool scriptRelative)
@@ -4939,20 +4938,20 @@ NestedShell(JSContext* cx, unsigned argc
     for (unsigned i = 0; i < args.length(); i++) {
         str = ToString(cx, args[i]);
         if (!str || !argv.append(JS_EncodeString(cx, str)))
             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) {
-            char* newArg = JS_smprintf("--js-cache=%s", jsCacheDir);
+            UniqueChars newArg = JS_smprintf("--js-cache=%s", jsCacheDir);
             if (!newArg)
                 return false;
-            argv.replaceBack(newArg);
+            argv.replaceBack(newArg.release());
         }
     }
 
     // execv assumes argv is null-terminated
     if (!argv.append(nullptr))
         return false;
 
     int status = 0;
@@ -8193,22 +8192,22 @@ SetContextOptions(JSContext* cx, const O
     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 (!op.getBoolOption("no-js-cache-per-process"))
-            jsCacheDir = JS_smprintf("%s/%u", jsCacheDir, (unsigned)getpid());
+            jsCacheDir = JS_smprintf("%s/%u", jsCacheDir, (unsigned)getpid()).release();
         else
             jsCacheDir = JS_strdup(cx, jsCacheDir);
         if (!jsCacheDir)
             return false;
-        jsCacheAsmJSPath = JS_smprintf("%s/asmjs.cache", jsCacheDir);
+        jsCacheAsmJSPath = JS_smprintf("%s/asmjs.cache", jsCacheDir).release();
     }
 
 #ifdef DEBUG
     dumpEntrainedVariables = op.getBoolOption("dump-entrained-variables");
 #endif
 
 #ifdef JS_GC_ZEAL
     const char* zealStr = op.getStringOption("gc-zeal");
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -286,17 +286,17 @@ ThrowErrorWithType(JSContext* cx, JSExnT
                 return;
             errorArgs[i - 1].encodeLatin1(cx, str);
         } else if (val.isString()) {
             errorArgs[i - 1].encodeLatin1(cx, val.toString());
         } else {
             UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, val, nullptr);
             if (!bytes)
                 return;
-            errorArgs[i - 1].initBytes(bytes.release());
+            errorArgs[i - 1].initBytes(Move(bytes));
         }
         if (!errorArgs[i - 1])
             return;
     }
 
     JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, errorNumber,
                                errorArgs[0].ptr(), errorArgs[1].ptr(), errorArgs[2].ptr());
 }
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -2268,17 +2268,17 @@ class MOZ_STACK_CLASS ModuleValidator
         return failOffset(pn->pn_pos.begin, str);
     }
 
     bool failfVAOffset(uint32_t offset, const char* fmt, va_list ap) {
         MOZ_ASSERT(!hasAlreadyFailed());
         MOZ_ASSERT(errorOffset_ == UINT32_MAX);
         MOZ_ASSERT(fmt);
         errorOffset_ = offset;
-        errorString_.reset(JS_vsmprintf(fmt, ap));
+        errorString_ = JS_vsmprintf(fmt, ap);
         return false;
     }
 
     bool failfOffset(uint32_t offset, const char* fmt, ...) MOZ_FORMAT_PRINTF(3, 4) {
         va_list ap;
         va_start(ap, fmt);
         failfVAOffset(offset, fmt, ap);
         va_end(ap);
@@ -8583,17 +8583,17 @@ LookupAsmJSModuleInCache(JSContext* cx, 
     asmJSMetadata->strict = parser.pc->sc()->strict() && !parser.pc->sc()->hasExplicitUseStrict();
     asmJSMetadata->scriptSource.reset(parser.ss);
 
     if (!parser.tokenStream.advance(asmJSMetadata->srcEndBeforeCurly()))
         return false;
 
     int64_t after = PRMJ_Now();
     int ms = (after - before) / PRMJ_USEC_PER_MSEC;
-    *compilationTimeReport = UniqueChars(JS_smprintf("loaded from cache in %dms", ms));
+    *compilationTimeReport = JS_smprintf("loaded from cache in %dms", ms);
     if (!*compilationTimeReport)
         return false;
 
     *loadedFromCache = true;
     return true;
 }
 
 /*****************************************************************************/
@@ -8684,17 +8684,17 @@ BuildConsoleMessage(JSContext* cx, unsig
       case JS::AsmJSCache_Disabled_PrivateBrowsing:
         cacheString = "caching disabled by private browsing mode";
         break;
       case JS::AsmJSCache_LIMIT:
         MOZ_CRASH("bad AsmJSCacheResult");
         break;
     }
 
-    return UniqueChars(JS_smprintf("total compilation time %dms; %s", time, cacheString));
+    return JS_smprintf("total compilation time %dms; %s", time, cacheString);
 #else
     return DuplicateString("");
 #endif
 }
 
 bool
 js::CompileAsmJS(JSContext* cx, AsmJSParser& parser, ParseNode* stmtList, bool* validated)
 {
--- a/js/src/wasm/WasmTextToBinary.cpp
+++ b/js/src/wasm/WasmTextToBinary.cpp
@@ -551,21 +551,21 @@ class WasmTokenStream
         end_(text + js_strlen(text)),
         lineStart_(text),
         line_(1),
         lookaheadIndex_(0),
         lookaheadDepth_(0)
     {}
     void generateError(WasmToken token, UniqueChars* error) {
         unsigned column = token.begin() - lineStart_ + 1;
-        error->reset(JS_smprintf("parsing wasm text at %u:%u", line_, column));
+        *error = JS_smprintf("parsing wasm text at %u:%u", line_, column);
     }
     void generateError(WasmToken token, const char* msg, UniqueChars* error) {
         unsigned column = token.begin() - lineStart_ + 1;
-        error->reset(JS_smprintf("parsing wasm text at %u:%u: %s", line_, column, msg));
+        *error = JS_smprintf("parsing wasm text at %u:%u: %s", line_, column, msg);
     }
     WasmToken peek() {
         if (!lookaheadDepth_) {
             lookahead_[lookaheadIndex_] = next();
             lookaheadDepth_ = 1;
         }
         return lookahead_[lookaheadIndex_];
     }
@@ -3405,17 +3405,17 @@ class Resolver
             ref.setIndex(p->value());
             return true;
         }
         return false;
     }
     bool failResolveLabel(const char* kind, AstName name) {
         TwoByteChars chars(name.begin(), name.length());
         UniqueChars utf8Chars(CharsToNewUTF8CharsZ(nullptr, chars).c_str());
-        error_->reset(JS_smprintf("%s label '%s' not found", kind, utf8Chars.get()));
+        *error_ = JS_smprintf("%s label '%s' not found", kind, utf8Chars.get());
         return false;
     }
 
   public:
     explicit Resolver(LifoAlloc& lifo, UniqueChars* error)
       : error_(error),
         varMap_(lifo),
         globalMap_(lifo),
@@ -3489,17 +3489,17 @@ class Resolver
                 ref.setIndex(i);
                 return true;
             }
         }
         return failResolveLabel("branch target", ref.name());
     }
 
     bool fail(const char* message) {
-        error_->reset(JS_smprintf("%s", message));
+        *error_ = JS_smprintf("%s", message);
         return false;
     }
 };
 
 } // end anonymous namespace
 
 static bool
 ResolveExpr(Resolver& r, AstExpr& expr);
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -134,21 +134,21 @@ static const JSFunctionSpec gGlobalFun[]
 };
 
 class MOZ_STACK_CLASS JSCLContextHelper
 {
 public:
     explicit JSCLContextHelper(JSContext* aCx);
     ~JSCLContextHelper();
 
-    void reportErrorAfterPop(char* buf);
+    void reportErrorAfterPop(UniqueChars&& buf);
 
 private:
     JSContext* mContext;
-    char*      mBuf;
+    UniqueChars mBuf;
 
     // prevent copying and assignment
     JSCLContextHelper(const JSCLContextHelper&) = delete;
     const JSCLContextHelper& operator=(const JSCLContextHelper&) = delete;
 };
 
 static nsresult
 MOZ_FORMAT_PRINTF(2, 3)
@@ -156,44 +156,43 @@ ReportOnCallerUTF8(JSContext* callerCont
                    const char* format, ...) {
     if (!callerContext) {
         return NS_ERROR_FAILURE;
     }
 
     va_list ap;
     va_start(ap, format);
 
-    char* buf = JS_vsmprintf(format, ap);
+    UniqueChars buf = JS_vsmprintf(format, ap);
     if (!buf) {
         va_end(ap);
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
-    JS_ReportErrorUTF8(callerContext, "%s", buf);
-    JS_smprintf_free(buf);
+    JS_ReportErrorUTF8(callerContext, "%s", buf.get());
 
     va_end(ap);
     return NS_OK;
 }
 
 static nsresult
 MOZ_FORMAT_PRINTF(2, 3)
 ReportOnCallerUTF8(JSCLContextHelper& helper,
                    const char* format, ...)
 {
     va_list ap;
     va_start(ap, format);
 
-    char* buf = JS_vsmprintf(format, ap);
+    UniqueChars buf = JS_vsmprintf(format, ap);
     if (!buf) {
         va_end(ap);
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
-    helper.reportErrorAfterPop(buf);
+    helper.reportErrorAfterPop(Move(buf));
     va_end(ap);
     return NS_OK;
 }
 
 mozJSComponentLoader::mozJSComponentLoader()
     : mModules(16),
       mImports(16),
       mInProgressImports(16),
@@ -1346,19 +1345,18 @@ JSCLContextHelper::JSCLContextHelper(JSC
     : mContext(aCx)
     , mBuf(nullptr)
 {
 }
 
 JSCLContextHelper::~JSCLContextHelper()
 {
     if (mBuf) {
-        JS_ReportErrorUTF8(mContext, "%s", mBuf);
-        JS_smprintf_free(mBuf);
+        JS_ReportErrorUTF8(mContext, "%s", mBuf.get());
     }
 }
 
 void
-JSCLContextHelper::reportErrorAfterPop(char* buf)
+JSCLContextHelper::reportErrorAfterPop(UniqueChars&& buf)
 {
     MOZ_ASSERT(!mBuf, "Already called reportErrorAfterPop");
-    mBuf = buf;
+    mBuf = Move(buf);
 }
--- a/js/xpconnect/src/XPCShellImpl.cpp
+++ b/js/xpconnect/src/XPCShellImpl.cpp
@@ -515,41 +515,40 @@ Options(JSContext* cx, unsigned argc, Va
             ContextOptionsRef(cx).toggleStrictMode();
         else {
             JS_ReportErrorUTF8(cx, "unknown option name '%s'. The valid names are "
                                "strict, werror, and strict_mode.", opt.ptr());
             return false;
         }
     }
 
-    char* names = nullptr;
+    UniqueChars names;
     if (oldContextOptions.extraWarnings()) {
-        names = JS_sprintf_append(names, "%s", "strict");
+        names = JS_sprintf_append(Move(names), "%s", "strict");
         if (!names) {
             JS_ReportOutOfMemory(cx);
             return false;
         }
     }
     if (oldContextOptions.werror()) {
-        names = JS_sprintf_append(names, "%s%s", names ? "," : "", "werror");
+        names = JS_sprintf_append(Move(names), "%s%s", names ? "," : "", "werror");
         if (!names) {
             JS_ReportOutOfMemory(cx);
             return false;
         }
     }
     if (names && oldContextOptions.strictMode()) {
-        names = JS_sprintf_append(names, "%s%s", names ? "," : "", "strict_mode");
+        names = JS_sprintf_append(Move(names), "%s%s", names ? "," : "", "strict_mode");
         if (!names) {
             JS_ReportOutOfMemory(cx);
             return false;
         }
     }
 
-    str = JS_NewStringCopyZ(cx, names);
-    free(names);
+    str = JS_NewStringCopyZ(cx, names.get());
     if (!str)
         return false;
 
     args.rval().setString(str);
     return true;
 }
 
 static PersistentRootedValue *sScriptedInterruptCallback = nullptr;
@@ -712,17 +711,17 @@ env_setProperty(JSContext* cx, HandleObj
     JSAutoByteString name(cx, idstr);
     if (!name)
         return false;
     JSAutoByteString value(cx, valstr);
     if (!value)
         return false;
 #if defined XP_WIN || defined HPUX || defined OSF1 || defined SCO
     {
-        char* waste = JS_smprintf("%s=%s", name.ptr(), value.ptr());
+        char* waste = JS_smprintf("%s=%s", name.ptr(), value.ptr()).release();
         if (!waste) {
             JS_ReportOutOfMemory(cx);
             return false;
         }
         rv = putenv(waste);
 #ifdef XP_WIN
         /*
          * HPUX9 at least still has the bad old non-copying putenv.
--- a/js/xpconnect/src/XPCThrower.cpp
+++ b/js/xpconnect/src/XPCThrower.cpp
@@ -111,19 +111,19 @@ XPCThrower::ThrowBadResult(nsresult rv, 
         return;
 
     // else...
 
     if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &format) || !format)
         format = "";
 
     if (nsXPCException::NameAndFormatForNSResult(result, &name, nullptr) && name)
-        sz = JS_smprintf("%s 0x%x (%s)", format, (unsigned) result, name);
+        sz = JS_smprintf("%s 0x%x (%s)", format, (unsigned) result, name).release();
     else
-        sz = JS_smprintf("%s 0x%x", format, (unsigned) result);
+        sz = JS_smprintf("%s 0x%x", format, (unsigned) result).release();
     NS_ENSURE_TRUE_VOID(sz);
 
     if (sz && sVerbose)
         Verbosify(ccx, &sz, true);
 
     dom::Throw(ccx, result, nsDependentCString(sz));
 
     if (sz)
@@ -135,17 +135,17 @@ void
 XPCThrower::ThrowBadParam(nsresult rv, unsigned paramNum, XPCCallContext& ccx)
 {
     char* sz;
     const char* format;
 
     if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &format))
         format = "";
 
-    sz = JS_smprintf("%s arg %d", format, paramNum);
+    sz = JS_smprintf("%s arg %d", format, paramNum).release();
     NS_ENSURE_TRUE_VOID(sz);
 
     if (sz && sVerbose)
         Verbosify(ccx, &sz, true);
 
     dom::Throw(ccx, rv, nsDependentCString(sz));
 
     if (sz)
@@ -163,17 +163,17 @@ XPCThrower::Verbosify(XPCCallContext& cc
     if (ccx.HasInterfaceAndMember()) {
         XPCNativeInterface* iface = ccx.GetInterface();
         jsid id = ccx.GetMember()->GetName();
         JSAutoByteString bytes;
         const char* name = JSID_IS_VOID(id) ? "Unknown" : bytes.encodeLatin1(ccx, JSID_TO_STRING(id));
         if (!name) {
             name = "";
         }
-        sz = JS_smprintf("%s [%s.%s]", *psz, iface->GetNameString(), name);
+        sz = JS_smprintf("%s [%s.%s]", *psz, iface->GetNameString(), name).release();
     }
 
     if (sz) {
         if (own)
             JS_smprintf_free(*psz);
         *psz = sz;
     }
 }
--- a/js/xpconnect/src/XPCWrappedJSClass.cpp
+++ b/js/xpconnect/src/XPCWrappedJSClass.cpp
@@ -1215,26 +1215,24 @@ pre_call_clean_up:
         } else {
             // The property was not an object so can't be a function.
             // Let's build and 'throw' an exception.
 
             static const nsresult code =
                     NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED;
             static const char format[] = "%s \"%s\"";
             const char * msg;
-            char* sz = nullptr;
+            UniqueChars sz;
 
             if (nsXPCException::NameAndFormatForNSResult(code, nullptr, &msg) && msg)
                 sz = JS_smprintf(format, msg, name);
 
-            XPCConvert::ConstructException(code, sz, GetInterfaceName(), name,
+            XPCConvert::ConstructException(code, sz.get(), GetInterfaceName(), name,
                                            nullptr, getter_AddRefs(syntheticException),
                                            nullptr, nullptr);
-            if (sz)
-                JS_smprintf_free(sz);
             success = false;
         }
     }
 
     if (!success)
         return CheckForException(ccx, aes, name, GetInterfaceName(),
                                  syntheticException);
 
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -2136,61 +2136,58 @@ XPCWrappedNative::ToString(XPCWrappedNat
 #  define FMT_STR(str) str
 #  define PARAM_ADDR(w) , w
 #else
 #  define FMT_ADDR ""
 #  define FMT_STR(str)
 #  define PARAM_ADDR(w)
 #endif
 
-    char* sz = nullptr;
-    char* name = nullptr;
+    UniqueChars sz;
+    UniqueChars name;
 
     nsCOMPtr<nsIXPCScriptable> scr = GetScriptable();
     if (scr)
         name = JS_smprintf("%s", scr->GetJSClass()->name);
     if (to) {
         const char* fmt = name ? " (%s)" : "%s";
-        name = JS_sprintf_append(name, fmt,
+        name = JS_sprintf_append(Move(name), fmt,
                                  to->GetInterface()->GetNameString());
     } else if (!name) {
         XPCNativeSet* set = GetSet();
         XPCNativeInterface** array = set->GetInterfaceArray();
         RefPtr<XPCNativeInterface> isupp = XPCNativeInterface::GetISupports();
         uint16_t count = set->GetInterfaceCount();
 
         if (count == 1)
-            name = JS_sprintf_append(name, "%s", array[0]->GetNameString());
+            name = JS_sprintf_append(Move(name), "%s", array[0]->GetNameString());
         else if (count == 2 && array[0] == isupp) {
-            name = JS_sprintf_append(name, "%s", array[1]->GetNameString());
+            name = JS_sprintf_append(Move(name), "%s", array[1]->GetNameString());
         } else {
             for (uint16_t i = 0; i < count; i++) {
                 const char* fmt = (i == 0) ?
                                     "(%s" : (i == count-1) ?
                                         ", %s)" : ", %s";
-                name = JS_sprintf_append(name, fmt,
+                name = JS_sprintf_append(Move(name), fmt,
                                          array[i]->GetNameString());
             }
         }
     }
 
     if (!name) {
         return nullptr;
     }
     const char* fmt = "[xpconnect wrapped %s" FMT_ADDR FMT_STR(" (native")
         FMT_ADDR FMT_STR(")") "]";
     if (scr) {
         fmt = "[object %s" FMT_ADDR FMT_STR(" (native") FMT_ADDR FMT_STR(")") "]";
     }
-    sz = JS_smprintf(fmt, name PARAM_ADDR(this) PARAM_ADDR(mIdentity.get()));
+    sz = JS_smprintf(fmt, name.get() PARAM_ADDR(this) PARAM_ADDR(mIdentity.get()));
 
-    JS_smprintf_free(name);
-
-
-    return sz;
+    return sz.release();
 
 #undef FMT_ADDR
 #undef PARAM_ADDR
 }
 
 /***************************************************************************/
 
 #ifdef XPC_CHECK_CLASSINFO_CLAIMS
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -39,31 +39,30 @@ static bool Throw(nsresult errNum, JSCon
         return Throw(NS_ERROR_XPC_HAS_BEEN_SHUTDOWN, cx);                     \
     PR_END_MACRO
 
 /***************************************************************************/
 
 static bool
 ToStringGuts(XPCCallContext& ccx)
 {
-    char* sz;
+    UniqueChars sz;
     XPCWrappedNative* wrapper = ccx.GetWrapper();
 
     if (wrapper)
-        sz = wrapper->ToString(ccx.GetTearOff());
+        sz.reset(wrapper->ToString(ccx.GetTearOff()));
     else
         sz = JS_smprintf("[xpconnect wrapped native prototype]");
 
     if (!sz) {
         JS_ReportOutOfMemory(ccx);
         return false;
     }
 
-    JSString* str = JS_NewStringCopyZ(ccx, sz);
-    JS_smprintf_free(sz);
+    JSString* str = JS_NewStringCopyZ(ccx, sz.get());
     if (!str)
         return false;
 
     ccx.SetRetVal(JS::StringValue(str));
     return true;
 }
 
 /***************************************************************************/