Bug 1037869 - Fix remaining Latin1 string issues. r=terrence
authorJan de Mooij <jdemooij@mozilla.com>
Mon, 14 Jul 2014 22:19:36 +0200
changeset 215862 0f25f3ccb7b309f8042bbbe0ab436e6fd42db766
parent 215861 a4d962f5f0766d4630b706d6a07c579d0fd866c7
child 215863 8bfe7afa1996aa4770793eb892d43b346c3b4c56
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs1037869
milestone33.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 1037869 - Fix remaining Latin1 string issues. r=terrence
dom/base/nsJSEnvironment.cpp
dom/bindings/BindingUtils.cpp
js/src/ctypes/CTypes.cpp
js/src/jsapi-tests/testConservativeGC.cpp
js/src/jsapi-tests/testOOM.cpp
js/src/jsapi-tests/testUTF8.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsexn.cpp
js/src/jsfriendapi.h
js/src/jswrapper.cpp
js/src/vm/String-inl.h
js/src/vm/String.cpp
js/src/vm/String.h
js/src/vm/Symbol.cpp
toolkit/mozapps/extensions/AddonPathService.cpp
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -365,20 +365,19 @@ AsyncErrorReporter::AsyncErrorReporter(J
   if (!aErrorReport->filename) {
     mFileName.SetIsVoid(true);
   } else {
     mFileName.AssignWithConversion(aErrorReport->filename);
   }
 
   const char16_t* m = static_cast<const char16_t*>(aErrorReport->ucmessage);
   if (m) {
-    const char16_t* n = static_cast<const char16_t*>
-      (js::GetErrorTypeName(aRuntime, aErrorReport->exnType));
-    if (n) {
-      mErrorMsg.Assign(n);
+    JSFlatString* name = js::GetErrorTypeName(aRuntime, aErrorReport->exnType);
+    if (name) {
+      AssignJSFlatString(mErrorMsg, name);
       mErrorMsg.AppendLiteral(": ");
     }
     mErrorMsg.Append(m);
   }
 
   if (mErrorMsg.IsEmpty() && aFallbackMessage) {
     mErrorMsg.AssignWithConversion(aFallbackMessage);
   }
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -360,31 +360,34 @@ InterfaceObjectToString(JSContext* cx, u
     return false;
   }
 
   JS::Value v = js::GetFunctionNativeReserved(callee,
                                               TOSTRING_CLASS_RESERVED_SLOT);
   const JSClass* clasp = static_cast<const JSClass*>(v.toPrivate());
 
   v = js::GetFunctionNativeReserved(callee, TOSTRING_NAME_RESERVED_SLOT);
-  JSString* jsname = static_cast<JSString*>(v.toString());
-  size_t length;
-  const jschar* name = JS_GetInternedStringCharsAndLength(jsname, &length);
+  JSString* jsname = v.toString();
+
+  nsAutoJSString name;
+  if (!name.init(cx, jsname)) {
+    return false;
+  }
 
   if (js::GetObjectJSClass(&args.thisv().toObject()) != clasp) {
     JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                          JSMSG_INCOMPATIBLE_PROTO,
                          NS_ConvertUTF16toUTF8(name).get(), "toString",
                          "object");
     return false;
   }
 
   nsString str;
   str.AppendLiteral("function ");
-  str.Append(name, length);
+  str.Append(name);
   str.AppendLiteral("() {");
   str.Append('\n');
   str.AppendLiteral("    [native code]");
   str.Append('\n');
   str.Append('}');
 
   return xpc::NonVoidStringToJsval(cx, str, args.rval());
 }
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -4746,18 +4746,22 @@ AddFieldToArray(JSContext* cx,
   RootedObject typeObj(cx, typeObj_);
   Rooted<JSFlatString*> name(cx, name_);
   RootedObject fieldObj(cx, JS_NewObject(cx, nullptr, NullPtr(), NullPtr()));
   if (!fieldObj)
     return false;
 
   *element = OBJECT_TO_JSVAL(fieldObj);
 
+  AutoStableStringChars nameChars(cx);
+  if (!nameChars.initTwoByte(cx, name))
+      return false;
+
   if (!JS_DefineUCProperty(cx, fieldObj,
-         name->chars(), name->length(),
+         nameChars.twoByteChars(), name->length(),
          typeObj,
          JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
     return false;
 
   return JS_FreezeObject(cx, fieldObj);
 }
 
 bool
--- a/js/src/jsapi-tests/testConservativeGC.cpp
+++ b/js/src/jsapi-tests/testConservativeGC.cpp
@@ -60,29 +60,9 @@ bool checkObjectFields(JSObject *savedCo
 {
     /* Ignore fields which are unstable across GCs. */
     CHECK(savedCopy->lastProperty() == obj->lastProperty());
     return true;
 }
 
 END_TEST(testConservativeGC)
 
-BEGIN_TEST(testDerivedValues)
-{
-  JSString *str = JS_NewStringCopyZ(cx, "once upon a midnight dreary");
-  JS::Anchor<JSString *> str_anchor(str);
-  static const jschar expected[] = { 'o', 'n', 'c', 'e' };
-  const jschar *ch = JS_GetStringCharsZ(cx, str);
-  str = nullptr;
-
-  /* Do a lot of allocation and collection. */
-  for (int i = 0; i < 3; i++) {
-    for (int j = 0; j < 1000; j++)
-      JS_NewStringCopyZ(cx, "as I pondered weak and weary");
-    JS_GC(rt);
-  }
-
-  CHECK(!memcmp(ch, expected, sizeof(expected)));
-  return true;
-}
-END_TEST(testDerivedValues)
-
 #endif /* !defined(JSGC_USE_EXACT_ROOTING) */
--- a/js/src/jsapi-tests/testOOM.cpp
+++ b/js/src/jsapi-tests/testOOM.cpp
@@ -5,18 +5,20 @@
 #include "mozilla/DebugOnly.h"
 
 #include "jsapi-tests/tests.h"
 
 BEGIN_TEST(testOOM)
 {
     JS::RootedValue v(cx, JS::Int32Value(9));
     JS::RootedString jsstr(cx, JS::ToString(cx, v));
-    mozilla::DebugOnly<const jschar *> s = JS_GetStringCharsZ(cx, jsstr);
-    JS_ASSERT(s[0] == '9' && s[1] == '\0');
+    jschar ch;
+    if (!JS_GetStringCharAt(cx, jsstr, 0, &ch))
+        return false;
+    JS_ASSERT(ch == '9');
     return true;
 }
 
 virtual JSRuntime * createRuntime()
 {
     JSRuntime *rt = JS_NewRuntime(0);
     if (!rt)
         return nullptr;
--- a/js/src/jsapi-tests/testUTF8.cpp
+++ b/js/src/jsapi-tests/testUTF8.cpp
@@ -13,31 +13,33 @@
 #include "js/CharacterEncoding.h"
 #include "jsapi-tests/tests.h"
 
 BEGIN_TEST(testUTF8_badUTF8)
 {
     static const char badUTF8[] = "...\xC0...";
     JSString *str = JS_NewStringCopyZ(cx, badUTF8);
     CHECK(str);
-    const jschar *chars = JS_GetStringCharsZ(cx, str);
-    CHECK(chars);
-    CHECK(chars[3] == 0x00C0);
+    jschar ch;
+    if (!JS_GetStringCharAt(cx, str, 3, &ch))
+        return false;
+    CHECK(ch == 0x00C0);
     return true;
 }
 END_TEST(testUTF8_badUTF8)
 
 BEGIN_TEST(testUTF8_bigUTF8)
 {
     static const char bigUTF8[] = "...\xFB\xBF\xBF\xBF\xBF...";
     JSString *str = JS_NewStringCopyZ(cx, bigUTF8);
     CHECK(str);
-    const jschar *chars = JS_GetStringCharsZ(cx, str);
-    CHECK(chars);
-    CHECK(chars[3] == 0x00FB);
+    jschar ch;
+    if (!JS_GetStringCharAt(cx, str, 3, &ch))
+        return false;
+    CHECK(ch == 0x00FB);
     return true;
 }
 END_TEST(testUTF8_bigUTF8)
 
 BEGIN_TEST(testUTF8_badSurrogate)
 {
     static const jschar badSurrogate[] = { 'A', 'B', 'C', 0xDEEE, 'D', 'E', 0 };
     mozilla::Range<const jschar> tbchars(badSurrogate, js_strlen(badSurrogate));
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -265,29 +265,21 @@ JS_ConvertArgumentsVA(JSContext *cx, con
                 return false;
             break;
           case 'I':
             if (!ToNumber(cx, arg, &d))
                 return false;
             *va_arg(ap, double *) = ToInteger(d);
             break;
           case 'S':
-          case 'W':
             str = ToString<CanGC>(cx, arg);
             if (!str)
                 return false;
             arg.setString(str);
-            if (c == 'W') {
-                JSFlatString *flat = str->ensureFlat(cx);
-                if (!flat)
-                    return false;
-                *va_arg(ap, const jschar **) = flat->chars();
-            } else {
-                *va_arg(ap, JSString **) = str;
-            }
+            *va_arg(ap, JSString **) = str;
             break;
           case 'o':
             if (arg.isNullOrUndefined()) {
                 obj = nullptr;
             } else {
                 obj = ToObject(cx, arg);
                 if (!obj)
                     return false;
@@ -1016,22 +1008,16 @@ JS_GetCompartmentPrivate(JSCompartment *
 }
 
 JS_PUBLIC_API(JSAddonId *)
 JS::NewAddonId(JSContext *cx, HandleString str)
 {
     return static_cast<JSAddonId *>(JS_InternJSString(cx, str));
 }
 
-JS_PUBLIC_API(const jschar *)
-JS::CharsZOfAddonId(JSAddonId *id)
-{
-    return id->charsZ();
-}
-
 JS_PUBLIC_API(JSString *)
 JS::StringOfAddonId(JSAddonId *id)
 {
     return id;
 }
 
 JS_PUBLIC_API(JSAddonId *)
 JS::AddonIdOfObject(JSObject *obj)
@@ -5316,59 +5302,27 @@ JS_InternUCString(JSContext *cx, const j
 
 JS_PUBLIC_API(size_t)
 JS_GetStringLength(JSString *str)
 {
     return str->length();
 }
 
 JS_PUBLIC_API(bool)
+JS_StringIsFlat(JSString *str)
+{
+    return str->isFlat();
+}
+
+JS_PUBLIC_API(bool)
 JS_StringHasLatin1Chars(JSString *str)
 {
     return str->hasLatin1Chars();
 }
 
-JS_PUBLIC_API(const jschar *)
-JS_GetStringCharsZ(JSContext *cx, JSString *str)
-{
-    size_t dummy;
-    return JS_GetStringCharsZAndLength(cx, str, &dummy);
-}
-
-JS_PUBLIC_API(const jschar *)
-JS_GetStringCharsZAndLength(JSContext *cx, JSString *str, size_t *plength)
-{
-    /*
-     * Don't require |cx->compartment()| to be |str|'s compartment. We don't need
-     * it, and it's annoying for callers.
-     */
-    JS_ASSERT(plength);
-    AssertHeapIsIdleOrStringIsFlat(cx, str);
-    CHECK_REQUEST(cx);
-    JSFlatString *flat = str->ensureFlat(cx);
-    if (!flat)
-        return nullptr;
-    *plength = flat->length();
-    return flat->chars();
-}
-
-JS_PUBLIC_API(const jschar *)
-JS_GetStringCharsAndLength(JSContext *cx, JSString *str, size_t *plength)
-{
-    JS_ASSERT(plength);
-    AssertHeapIsIdleOrStringIsFlat(cx, str);
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, str);
-    JSLinearString *linear = str->ensureLinear(cx);
-    if (!linear)
-        return nullptr;
-    *plength = linear->length();
-    return linear->chars();
-}
-
 JS_PUBLIC_API(const JS::Latin1Char *)
 JS_GetLatin1StringCharsAndLength(JSContext *cx, const JS::AutoCheckCannotGC &nogc, JSString *str,
                                  size_t *plength)
 {
     JS_ASSERT(plength);
     AssertHeapIsIdleOrStringIsFlat(cx, str);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, str);
@@ -5432,54 +5386,58 @@ JS_CopyStringChars(JSContext *cx, mozill
     if (!linear)
         return false;
 
     MOZ_ASSERT(linear->length() <= dest.length());
     CopyChars(dest.start().get(), *linear);
     return true;
 }
 
-JS_PUBLIC_API(const jschar *)
-JS_GetInternedStringChars(JSString *str)
+JS_PUBLIC_API(const Latin1Char *)
+JS_Latin1InternedStringChars(const JS::AutoCheckCannotGC &nogc, JSString *str)
 {
     JS_ASSERT(str->isAtom());
     JSFlatString *flat = str->ensureFlat(nullptr);
     if (!flat)
         return nullptr;
-    return flat->chars();
+    return flat->latin1Chars(nogc);
 }
 
 JS_PUBLIC_API(const jschar *)
-JS_GetInternedStringCharsAndLength(JSString *str, size_t *plength)
+JS_GetTwoByteInternedStringChars(const JS::AutoCheckCannotGC &nogc, JSString *str)
 {
     JS_ASSERT(str->isAtom());
-    JS_ASSERT(plength);
     JSFlatString *flat = str->ensureFlat(nullptr);
     if (!flat)
         return nullptr;
-    *plength = flat->length();
-    return flat->chars();
+    return flat->twoByteChars(nogc);
 }
 
 extern JS_PUBLIC_API(JSFlatString *)
 JS_FlattenString(JSContext *cx, JSString *str)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, str);
     JSFlatString *flat = str->ensureFlat(cx);
     if (!flat)
         return nullptr;
     return flat;
 }
 
+extern JS_PUBLIC_API(const Latin1Char *)
+JS_GetLatin1FlatStringChars(const JS::AutoCheckCannotGC &nogc, JSFlatString *str)
+{
+    return str->latin1Chars(nogc);
+}
+
 extern JS_PUBLIC_API(const jschar *)
-JS_GetFlatStringChars(JSFlatString *str)
-{
-    return str->chars();
+JS_GetTwoByteFlatStringChars(const JS::AutoCheckCannotGC &nogc, JSFlatString *str)
+{
+    return str->twoByteChars(nogc);
 }
 
 JS_PUBLIC_API(bool)
 JS_CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32_t *result)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
 
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -4137,53 +4137,58 @@ JS_FileEscapedString(FILE *fp, JSString 
 /*
  * Extracting string characters and length.
  *
  * While getting the length of a string is infallible, getting the chars can
  * fail. As indicated by the lack of a JSContext parameter, there are two
  * special cases where getting the chars is infallible:
  *
  * The first case is interned strings, i.e., strings from JS_InternString or
- * JSID_TO_STRING(id), using JS_GetInternedStringChars*.
+ * JSID_TO_STRING(id), using JS_GetLatin1InternedStringChars or
+ * JS_GetTwoByteInternedStringChars.
  *
  * The second case is "flat" strings that have been explicitly prepared in a
  * fallible context by JS_FlattenString. To catch errors, a separate opaque
  * JSFlatString type is returned by JS_FlattenString and expected by
  * JS_GetFlatStringChars. Note, though, that this is purely a syntactic
  * distinction: the input and output of JS_FlattenString are the same actual
- * GC-thing so only one needs to be rooted. If a JSString is known to be flat,
- * JS_ASSERT_STRING_IS_FLAT can be used to make a debug-checked cast. Example:
+ * GC-thing. If a JSString is known to be flat, JS_ASSERT_STRING_IS_FLAT can be
+ * used to make a debug-checked cast. Example:
  *
  *   // in a fallible context
  *   JSFlatString *fstr = JS_FlattenString(cx, str);
  *   if (!fstr)
  *     return false;
  *   JS_ASSERT(fstr == JS_ASSERT_STRING_IS_FLAT(str));
  *
  *   // in an infallible context, for the same 'str'
- *   const jschar *chars = JS_GetFlatStringChars(fstr)
+ *   AutoCheckCannotGC nogc;
+ *   const jschar *chars = JS_GetTwoByteFlatStringChars(nogc, fstr)
  *   JS_ASSERT(chars);
  *
- * The CharsZ APIs guarantee that the returned array has a null character at
- * chars[length]. This can require additional copying so clients should prefer
- * APIs without CharsZ if possible. The infallible functions also return
- * null-terminated arrays. (There is no additional cost or non-Z alternative
- * for the infallible functions, so 'Z' is left out of the identifier.)
+ * Flat strings and interned strings are always null-terminated, so
+ * JS_FlattenString can be used to get a null-terminated string.
+ *
+ * Additionally, string characters are stored as either Latin1Char (8-bit)
+ * or jschar (16-bit). Clients can use JS_StringHasLatin1Chars and can then
+ * call either the Latin1* or TwoByte* functions. Some functions like
+ * JS_CopyStringChars and JS_GetStringCharAt accept both Latin1 and TwoByte
+ * strings.
  */
 
 extern JS_PUBLIC_API(size_t)
 JS_GetStringLength(JSString *str);
 
+extern JS_PUBLIC_API(bool)
+JS_StringIsFlat(JSString *str);
+
 /* Returns true iff the string's characters are stored as Latin1. */
 extern JS_PUBLIC_API(bool)
 JS_StringHasLatin1Chars(JSString *str);
 
-extern JS_PUBLIC_API(const jschar *)
-JS_GetStringCharsAndLength(JSContext *cx, JSString *str, size_t *length);
-
 extern JS_PUBLIC_API(const JS::Latin1Char *)
 JS_GetLatin1StringCharsAndLength(JSContext *cx, const JS::AutoCheckCannotGC &nogc, JSString *str,
                                  size_t *length);
 
 extern JS_PUBLIC_API(const jschar *)
 JS_GetTwoByteStringCharsAndLength(JSContext *cx, const JS::AutoCheckCannotGC &nogc, JSString *str,
                                   size_t *length);
 
@@ -4194,45 +4199,42 @@ extern JS_PUBLIC_API(jschar)
 JS_GetFlatStringCharAt(JSFlatString *str, size_t index);
 
 extern JS_PUBLIC_API(const jschar *)
 JS_GetTwoByteExternalStringChars(JSString *str);
 
 extern JS_PUBLIC_API(bool)
 JS_CopyStringChars(JSContext *cx, mozilla::Range<jschar> dest, JSString *str);
 
-extern JS_PUBLIC_API(const jschar *)
-JS_GetInternedStringChars(JSString *str);
+extern JS_PUBLIC_API(const JS::Latin1Char *)
+JS_GetLatin1InternedStringChars(const JS::AutoCheckCannotGC &nogc, JSString *str);
 
 extern JS_PUBLIC_API(const jschar *)
-JS_GetInternedStringCharsAndLength(JSString *str, size_t *length);
-
-extern JS_PUBLIC_API(const jschar *)
-JS_GetStringCharsZ(JSContext *cx, JSString *str);
-
-extern JS_PUBLIC_API(const jschar *)
-JS_GetStringCharsZAndLength(JSContext *cx, JSString *str, size_t *length);
+JS_GetTwoByteInternedStringChars(const JS::AutoCheckCannotGC &nogc, JSString *str);
 
 extern JS_PUBLIC_API(JSFlatString *)
 JS_FlattenString(JSContext *cx, JSString *str);
 
+extern JS_PUBLIC_API(const JS::Latin1Char *)
+JS_GetLatin1FlatStringChars(const JS::AutoCheckCannotGC &nogc, JSFlatString *str);
+
 extern JS_PUBLIC_API(const jschar *)
-JS_GetFlatStringChars(JSFlatString *str);
+JS_GetTwoByteFlatStringChars(const JS::AutoCheckCannotGC &nogc, JSFlatString *str);
 
 static MOZ_ALWAYS_INLINE JSFlatString *
 JSID_TO_FLAT_STRING(jsid id)
 {
     JS_ASSERT(JSID_IS_STRING(id));
     return (JSFlatString *)(JSID_BITS(id));
 }
 
 static MOZ_ALWAYS_INLINE JSFlatString *
 JS_ASSERT_STRING_IS_FLAT(JSString *str)
 {
-    JS_ASSERT(JS_GetFlatStringChars((JSFlatString *)str));
+    JS_ASSERT(JS_StringIsFlat(str));
     return (JSFlatString *)str;
 }
 
 static MOZ_ALWAYS_INLINE JSString *
 JS_FORGET_STRING_FLATNESS(JSFlatString *fstr)
 {
     return (JSString *)fstr;
 }
@@ -4383,19 +4385,16 @@ class JSAutoByteString
     JSAutoByteString &operator=(const JSAutoByteString &another);
 };
 
 namespace JS {
 
 extern JS_PUBLIC_API(JSAddonId *)
 NewAddonId(JSContext *cx, JS::HandleString str);
 
-extern JS_PUBLIC_API(const jschar *)
-CharsZOfAddonId(JSAddonId *id);
-
 extern JS_PUBLIC_API(JSString *)
 StringOfAddonId(JSAddonId *id);
 
 extern JS_PUBLIC_API(JSAddonId *)
 AddonIdOfObject(JSObject *obj);
 
 } // namespace JS
 
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -596,30 +596,30 @@ js_GetLocalizedErrorMessage(ExclusiveCon
         errorString = callbacks->localeGetErrorMessage(userRef, locale, errorNumber);
     }
 
     if (!errorString)
         errorString = js_GetErrorMessage(userRef, locale, errorNumber);
     return errorString;
 }
 
-JS_FRIEND_API(const jschar*)
-js::GetErrorTypeName(JSRuntime* rt, int16_t exnType)
+JS_FRIEND_API(JSFlatString *)
+js::GetErrorTypeName(JSRuntime *rt, int16_t exnType)
 {
     /*
      * JSEXN_INTERNALERR returns null to prevent that "InternalError: "
      * is prepended before "uncaught exception: "
      */
     if (exnType <= JSEXN_NONE || exnType >= JSEXN_LIMIT ||
         exnType == JSEXN_INTERNALERR)
     {
         return nullptr;
     }
     JSProtoKey key = GetExceptionProtoKey(JSExnType(exnType));
-    return ClassName(key, rt)->chars();
+    return ClassName(key, rt);
 }
 
 bool
 js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp,
                     JSErrorCallback callback, void *userRef)
 {
     // Tell our caller to report immediately if this report is just a warning.
     JS_ASSERT(reportp);
@@ -765,16 +765,17 @@ js_ReportUncaughtException(JSContext *cx
     // still quack like one. Give duck-typing a chance.  We start by looking for
     // "filename" (all lowercase), since that's where DOMExceptions store their
     // filename.  Then we check "fileName", which is where Errors store it.  We
     // have to do it in that order, because DOMExceptions have Error.prototype
     // on their proto chain, and hence also have a "fileName" property, but its
     // value is "".
     const char *filename_str = "filename";
     JSAutoByteString filename;
+    AutoStableStringChars strChars(cx);
     if (!reportp && exnObject && IsDuckTypedErrorObject(cx, exnObject, &filename_str))
     {
         // Temporary value for pulling properties off of duck-typed objects.
         RootedValue val(cx);
 
         RootedString name(cx);
         if (JS_GetProperty(cx, exnObject, js_name_str, &val) && val.isString())
             name = val.toString();
@@ -835,18 +836,18 @@ js_ReportUncaughtException(JSContext *cx
         if (str) {
             // Note that using |str| for |ucmessage| here is kind of wrong,
             // because |str| is supposed to be of the format
             // |ErrorName: ErrorMessage|, and |ucmessage| is supposed to
             // correspond to |ErrorMessage|. But this is what we've historically
             // done for duck-typed error objects.
             //
             // If only this stuff could get specced one day...
-            if (JSFlatString *flat = str->ensureFlat(cx))
-                report.ucmessage = flat->chars();
+            if (str->ensureFlat(cx) && strChars.initTwoByte(cx, str))
+                report.ucmessage = strChars.twoByteChars();
         }
     }
 
     JSAutoByteString bytesStorage;
     const char *bytes = nullptr;
     if (str)
         bytes = bytesStorage.encodeLatin1(cx, str);
     if (!bytes)
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -1092,18 +1092,18 @@ CastToJSFreeOp(FreeOp *fop)
 }
 
 /* Implemented in jsexn.cpp. */
 
 /*
  * Get an error type name from a JSExnType constant.
  * Returns nullptr for invalid arguments and JSEXN_INTERNALERR
  */
-extern JS_FRIEND_API(const jschar*)
-GetErrorTypeName(JSRuntime* rt, int16_t exnType);
+extern JS_FRIEND_API(JSFlatString *)
+GetErrorTypeName(JSRuntime *rt, int16_t exnType);
 
 #ifdef JS_DEBUG
 extern JS_FRIEND_API(unsigned)
 GetEnterCompartmentDepth(JSContext* cx);
 #endif
 
 /* Implemented in jswrapper.cpp. */
 typedef enum NukeReferencesToWindow {
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -688,17 +688,20 @@ SecurityWrapper<Base>::regexp_toShared(J
 
 template <class Base>
 bool
 SecurityWrapper<Base>::defineProperty(JSContext *cx, HandleObject wrapper,
                                       HandleId id, MutableHandle<PropertyDescriptor> desc) const
 {
     if (desc.getter() || desc.setter()) {
         JSString *str = IdToString(cx, id);
-        const jschar *prop = str ? str->getCharsZ(cx) : nullptr;
+        AutoStableStringChars chars(cx);
+        const jschar *prop = nullptr;
+        if (str->ensureFlat(cx) && chars.initTwoByte(cx, str))
+            prop = chars.twoByteChars();
         JS_ReportErrorNumberUC(cx, js_GetErrorMessage, nullptr,
                                JSMSG_ACCESSOR_DEF_DENIED, prop);
         return false;
     }
 
     return Base::defineProperty(cx, wrapper, id, desc);
 }
 
--- a/js/src/vm/String-inl.h
+++ b/js/src/vm/String-inl.h
@@ -448,12 +448,12 @@ JSAtom::finalize(js::FreeOp *fop)
     if (!isInline())
         fop->free_(nonInlineCharsRaw());
 }
 
 inline void
 JSExternalString::finalize(js::FreeOp *fop)
 {
     const JSStringFinalizer *fin = externalFinalizer();
-    fin->finalize(fin, const_cast<jschar *>(nonInlineChars()));
+    fin->finalize(fin, const_cast<jschar *>(rawTwoByteChars()));
 }
 
 #endif /* vm_String_inl_h */
--- a/js/src/vm/String.cpp
+++ b/js/src/vm/String.cpp
@@ -135,46 +135,41 @@ JSString::dump()
         fprintf(stderr, "(oom in JSString::dump)");
     }
     fputc('\n', stderr);
 }
 
 bool
 JSString::equals(const char *s)
 {
-    const jschar *c = getChars(nullptr);
-    if (!c) {
+    JSLinearString *linear = ensureLinear(nullptr);
+    if (!linear) {
         fprintf(stderr, "OOM in JSString::equals!\n");
         return false;
     }
-    while (*c && *s) {
-        if (*c != *s)
-            return false;
-        c++;
-        s++;
-    }
-    return *c == *s;
+
+    return StringEqualsAscii(linear, s);
 }
 #endif /* DEBUG */
 
 void
 JSLinearString::debugUnsafeConvertToLatin1()
 {
     // Temporary helper function to test changes for bug 998392.
 
     MOZ_ASSERT(hasTwoByteChars());
     MOZ_ASSERT(!hasBase());
 
     size_t len = length();
-    const jschar *twoByteChars = chars();
-    char *latin1Chars = (char *)twoByteChars;
+    const jschar *twoByteChars = rawTwoByteChars();
+    Latin1Char *latin1Chars = (Latin1Char *)twoByteChars;
 
     for (size_t i = 0; i < len; i++) {
         MOZ_ASSERT((twoByteChars[i] & 0xff00) == 0);
-        latin1Chars[i] = char(twoByteChars[i]);
+        latin1Chars[i] = Latin1Char(twoByteChars[i]);
     }
 
     latin1Chars[len] = '\0';
     d.u1.flags |= LATIN1_CHARS_BIT;
 }
 
 template <typename CharT>
 static MOZ_ALWAYS_INLINE bool
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -318,23 +318,16 @@ class JSString : public js::gc::Barriere
         return d.u1.length;
     }
 
     MOZ_ALWAYS_INLINE
     bool empty() const {
         return d.u1.length == 0;
     }
 
-    /*
-     * All strings have a fallible operation to get an array of chars.
-     * getCharsZ additionally ensures the array is null terminated.
-     */
-
-    inline const jschar *getChars(js::ExclusiveContext *cx);
-    inline const jschar *getCharsZ(js::ExclusiveContext *cx);
     inline bool getChar(js::ExclusiveContext *cx, size_t index, jschar *code);
 
     /* Strings have either Latin1 or TwoByte chars. */
     bool hasLatin1Chars() const {
         return d.u1.flags & LATIN1_CHARS_BIT;
     }
     bool hasTwoByteChars() const {
         return !(d.u1.flags & LATIN1_CHARS_BIT);
@@ -602,23 +595,16 @@ class JSLinearString : public JSString
                       "nonInlineCharsTwoByte and nonInlineCharsLatin1 must have same offset");
         return (void *)d.s.u2.nonInlineCharsTwoByte;
     }
 
     MOZ_ALWAYS_INLINE const JS::Latin1Char *rawLatin1Chars() const;
     MOZ_ALWAYS_INLINE const jschar *rawTwoByteChars() const;
 
   public:
-    MOZ_ALWAYS_INLINE
-    const jschar *nonInlineChars() const {
-        JS_ASSERT(!isInline());
-        JS_ASSERT(hasTwoByteChars());
-        return d.s.u2.nonInlineCharsTwoByte;
-    }
-
     template<typename CharT>
     MOZ_ALWAYS_INLINE
     const CharT *nonInlineChars(const JS::AutoCheckCannotGC &nogc) const;
 
     MOZ_ALWAYS_INLINE
     const JS::Latin1Char *nonInlineLatin1Chars(const JS::AutoCheckCannotGC &nogc) const {
         JS_ASSERT(!isInline());
         JS_ASSERT(hasLatin1Chars());
@@ -627,38 +613,30 @@ class JSLinearString : public JSString
 
     MOZ_ALWAYS_INLINE
     const jschar *nonInlineTwoByteChars(const JS::AutoCheckCannotGC &nogc) const {
         JS_ASSERT(!isInline());
         JS_ASSERT(hasTwoByteChars());
         return d.s.u2.nonInlineCharsTwoByte;
     }
 
-    MOZ_ALWAYS_INLINE
-    const jschar *chars() const;
-
     template<typename CharT>
     MOZ_ALWAYS_INLINE
     const CharT *chars(const JS::AutoCheckCannotGC &nogc) const;
 
     MOZ_ALWAYS_INLINE
     const JS::Latin1Char *latin1Chars(const JS::AutoCheckCannotGC &nogc) const {
         return rawLatin1Chars();
     }
 
     MOZ_ALWAYS_INLINE
     const jschar *twoByteChars(const JS::AutoCheckCannotGC &nogc) const {
         return rawTwoByteChars();
     }
 
-    JS::TwoByteChars range() const {
-        JS_ASSERT(JSString::isLinear());
-        return JS::TwoByteChars(chars(), length());
-    }
-
     mozilla::Range<const JS::Latin1Char> latin1Range(const JS::AutoCheckCannotGC &nogc) const {
         JS_ASSERT(JSString::isLinear());
         return mozilla::Range<const JS::Latin1Char>(latin1Chars(nogc), length());
     }
 
     mozilla::Range<const jschar> twoByteRange(const JS::AutoCheckCannotGC &nogc) const {
         JS_ASSERT(JSString::isLinear());
         return mozilla::Range<const jschar>(twoByteChars(nogc), length());
@@ -688,19 +666,16 @@ class JSDependentString : public JSLinea
 
     void init(js::ThreadSafeContext *cx, JSLinearString *base, size_t start,
               size_t length);
 
     /* Vacuous and therefore unimplemented. */
     bool isDependent() const MOZ_DELETE;
     JSDependentString &asDependent() const MOZ_DELETE;
 
-    /* Hide chars(), nonInlineChars() is more efficient. */
-    const jschar *chars() const MOZ_DELETE;
-
     /* The offset of this string's chars in base->chars(). */
     size_t baseOffset() const {
         MOZ_ASSERT(JSString::isDependent());
         JS::AutoCheckCannotGC nogc;
         size_t offset;
         if (hasTwoByteChars())
             offset = twoByteChars(nogc) - base()->twoByteChars(nogc);
         else
@@ -729,22 +704,16 @@ class JSFlatString : public JSLinearStri
     void init(const jschar *chars, size_t length);
     void init(const JS::Latin1Char *chars, size_t length);
 
   public:
     template <js::AllowGC allowGC, typename CharT>
     static inline JSFlatString *new_(js::ThreadSafeContext *cx,
                                      const CharT *chars, size_t length);
 
-    MOZ_ALWAYS_INLINE
-    const jschar *charsZ() const {
-        JS_ASSERT(JSString::isFlat());
-        return chars();
-    }
-
     /*
      * Returns true if this string's characters store an unsigned 32-bit
      * integer value, initializing *indexp to that value if so.  (Thus if
      * calling isIndex returns true, js::IndexToString(cx, *indexp) will be a
      * string equal to this string.)
      */
     inline bool isIndex(uint32_t *indexp) const {
         JS_ASSERT(JSString::isFlat());
@@ -783,19 +752,16 @@ class JSFlatString : public JSLinearStri
 JS_STATIC_ASSERT(sizeof(JSFlatString) == sizeof(JSString));
 
 class JSExtensibleString : public JSFlatString
 {
     /* Vacuous and therefore unimplemented. */
     bool isExtensible() const MOZ_DELETE;
     JSExtensibleString &asExtensible() const MOZ_DELETE;
 
-    /* Hide chars(), nonInlineChars() is more efficient. */
-    const jschar *chars() const MOZ_DELETE;
-
   public:
     MOZ_ALWAYS_INLINE
     size_t capacity() const {
         JS_ASSERT(JSString::isExtensible());
         return d.s.u3.capacity;
     }
 };
 
@@ -819,23 +785,16 @@ class JSInlineString : public JSFlatStri
     inline JS::Latin1Char *initLatin1(size_t length);
 
     template <typename CharT>
     inline CharT *init(size_t length);
 
     inline void resetLength(size_t length);
 
     MOZ_ALWAYS_INLINE
-    const jschar *chars() const {
-        JS_ASSERT(JSString::isInline());
-        JS_ASSERT(hasTwoByteChars());
-        return d.inlineStorageTwoByte;
-    }
-
-    MOZ_ALWAYS_INLINE
     const JS::Latin1Char *latin1Chars(const JS::AutoCheckCannotGC &nogc) const {
         JS_ASSERT(JSString::isInline());
         JS_ASSERT(hasLatin1Chars());
         return d.inlineStorageLatin1;
     }
 
     MOZ_ALWAYS_INLINE
     const jschar *twoByteChars(const JS::AutoCheckCannotGC &nogc) const {
@@ -884,19 +843,16 @@ class JSFatInlineString : public JSInlin
         JS_STATIC_ASSERT(MAX_LENGTH_TWO_BYTE + 1 ==
                          (sizeof(JSFatInlineString) -
                           offsetof(JSFatInlineString, d.inlineStorageTwoByte)) / sizeof(jschar));
         JS_STATIC_ASSERT(MAX_LENGTH_LATIN1 + 1 ==
                          (sizeof(JSFatInlineString) -
                           offsetof(JSFatInlineString, d.inlineStorageLatin1)) / sizeof(char));
     }
 
-    /* Hide chars(), inlineChars() is more efficient. */
-    const jschar *chars() const MOZ_DELETE;
-
   protected: /* to fool clang into not warning this is unused */
     union {
         char   inlineStorageExtensionLatin1[INLINE_EXTENSION_CHARS_LATIN1];
         jschar inlineStorageExtensionTwoByte[INLINE_EXTENSION_CHARS_TWO_BYTE];
     };
 
   public:
     template <js::AllowGC allowGC>
@@ -936,19 +892,16 @@ JS_STATIC_ASSERT(sizeof(JSFatInlineStrin
 class JSExternalString : public JSFlatString
 {
     void init(const jschar *chars, size_t length, const JSStringFinalizer *fin);
 
     /* Vacuous and therefore unimplemented. */
     bool isExternal() const MOZ_DELETE;
     JSExternalString &asExternal() const MOZ_DELETE;
 
-    /* Hide chars(), nonInlineChars() is more efficient. */
-    const jschar *chars() const MOZ_DELETE;
-
   public:
     static inline JSExternalString *new_(JSContext *cx, const jschar *chars, size_t length,
                                          const JSStringFinalizer *fin);
 
     const JSStringFinalizer *externalFinalizer() const {
         JS_ASSERT(JSString::isExternal());
         return d.s.u3.externalFinalizer;
     }
@@ -1291,26 +1244,16 @@ CopyChars(CharT *dest, const JSLinearStr
 
 } /* namespace js */
 
 // Addon IDs are interned atoms which are never destroyed. This detail is
 // not exposed outside the API.
 class JSAddonId : public JSAtom
 {};
 
-/* Avoid requiring vm/String-inl.h just to call getChars. */
-
-MOZ_ALWAYS_INLINE const jschar *
-JSString::getChars(js::ExclusiveContext *cx)
-{
-    if (JSLinearString *str = ensureLinear(cx))
-        return str->chars();
-    return nullptr;
-}
-
 MOZ_ALWAYS_INLINE bool
 JSString::getChar(js::ExclusiveContext *cx, size_t index, jschar *code)
 {
     JS_ASSERT(index < length());
 
     /*
      * Optimization for one level deep ropes.
      * This is common for the following pattern:
@@ -1335,24 +1278,16 @@ JSString::getChar(js::ExclusiveContext *
 
     if (!str->ensureLinear(cx))
         return false;
 
     *code = str->asLinear().latin1OrTwoByteChar(index);
     return true;
 }
 
-MOZ_ALWAYS_INLINE const jschar *
-JSString::getCharsZ(js::ExclusiveContext *cx)
-{
-    if (JSFlatString *str = ensureFlat(cx))
-        return str->chars();
-    return nullptr;
-}
-
 MOZ_ALWAYS_INLINE JSLinearString *
 JSString::ensureLinear(js::ExclusiveContext *cx)
 {
     return isLinear()
            ? &asLinear()
            : asRope().flatten(cx);
 }
 
@@ -1454,24 +1389,16 @@ JSString::setNonInlineChars(const jschar
 
 template<>
 MOZ_ALWAYS_INLINE void
 JSString::setNonInlineChars(const JS::Latin1Char *chars)
 {
     d.s.u2.nonInlineCharsLatin1 = chars;
 }
 
-MOZ_ALWAYS_INLINE const jschar *
-JSLinearString::chars() const
-{
-    JS_ASSERT(JSString::isLinear());
-    JS_ASSERT(hasTwoByteChars());
-    return isInline() ? asInline().chars() : nonInlineChars();
-}
-
 MOZ_ALWAYS_INLINE const JS::Latin1Char *
 JSLinearString::rawLatin1Chars() const
 {
     JS_ASSERT(JSString::isLinear());
     JS_ASSERT(hasLatin1Chars());
     return isInline() ? d.inlineStorageLatin1 : d.s.u2.nonInlineCharsLatin1;
 }
 
--- a/js/src/vm/Symbol.cpp
+++ b/js/src/vm/Symbol.cpp
@@ -79,25 +79,22 @@ Symbol::for_(js::ExclusiveContext *cx, H
 }
 
 #ifdef DEBUG
 void
 Symbol::dump(FILE *fp)
 {
     if (isWellKnownSymbol()) {
         // All the well-known symbol names are ASCII.
-        const jschar *desc = description_->chars();
-        size_t len = description_->length();
-        for (size_t i = 0; i < len; i++)
-            fputc(char(desc[i]), fp);
+        description_->dumpCharsNoNewline(fp);
     } else if (code_ == SymbolCode::InSymbolRegistry || code_ == SymbolCode::UniqueSymbol) {
         fputs(code_ == SymbolCode::InSymbolRegistry ? "Symbol.for(" : "Symbol(", fp);
 
         if (description_)
-            JSString::dumpChars(description_->chars(), description_->length(), fp);
+            description_->dumpCharsNoNewline(fp);
         else
             fputs("undefined", fp);
 
         fputc(')', fp);
 
         if (code_ == SymbolCode::UniqueSymbol)
             fprintf(fp, "@%p", (void *) this);
     } else {
--- a/toolkit/mozapps/extensions/AddonPathService.cpp
+++ b/toolkit/mozapps/extensions/AddonPathService.cpp
@@ -13,16 +13,17 @@
 #include "nsServiceManagerUtils.h"
 #include "nsLiteralString.h"
 #include "nsThreadUtils.h"
 #include "nsIIOService.h"
 #include "nsNetUtil.h"
 #include "nsIResProtocolHandler.h"
 #include "nsIChromeRegistry.h"
 #include "nsIJARURI.h"
+#include "nsJSUtils.h"
 #include "mozilla/AddonPathService.h"
 #include "mozilla/Omnijar.h"
 
 #include <algorithm>
 
 namespace mozilla {
 
 struct PathEntryComparator
@@ -80,17 +81,18 @@ AddonPathService::Find(const nsAString& 
   }
   return nullptr;
 }
 
 NS_IMETHODIMP
 AddonPathService::FindAddonId(const nsAString& path, nsAString& addonIdString)
 {
   if (JSAddonId* id = Find(path)) {
-    addonIdString = JS::CharsZOfAddonId(id);
+    JSFlatString* flat = JS_ASSERT_STRING_IS_FLAT(JS::StringOfAddonId(id));
+    AssignJSFlatString(addonIdString, flat);
   }
   return NS_OK;
 }
 
 /* static */ JSAddonId*
 AddonPathService::FindAddonId(const nsAString& path)
 {
   // If no service has been created, then we're not going to find anything.