Bug 1485066 - Part 12: Change the decompiler to return UTF-8 encoded strings. r=Waldo
☠☠ backed out by 3d23c2f43b8a ☠ ☠
authorAndré Bargull <andre.bargull@gmail.com>
Wed, 05 Sep 2018 01:25:12 -0700
changeset 434768 939e27aa2d591fd8aa16c4c1488e2277d053f8f8
parent 434767 d50fcf82556c2c259ba818d51dc08aadfcea2ef4
child 434769 f09bc4d5fdcc36f4dccd2cc399819469c5c657f2
push id107490
push usercsabou@mozilla.com
push dateWed, 05 Sep 2018 12:25:08 +0000
treeherdermozilla-inbound@e40f67f15bf1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs1485066
milestone64.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 1485066 - Part 12: Change the decompiler to return UTF-8 encoded strings. r=Waldo
js/public/CharacterEncoding.h
js/src/builtin/Object.cpp
js/src/builtin/Stream.cpp
js/src/perf/jsperf.cpp
js/src/shell/js.cpp
js/src/vm/BytecodeUtil.cpp
js/src/vm/ForOfIterator.cpp
js/src/vm/JSContext.cpp
js/src/vm/JSObject.cpp
js/src/vm/Printer.cpp
js/src/vm/SelfHosting.cpp
js/src/vm/TypedArrayObject.cpp
--- a/js/public/CharacterEncoding.h
+++ b/js/public/CharacterEncoding.h
@@ -112,17 +112,17 @@ class UTF8CharsZ : public mozilla::Range
 
     char* c_str() { return reinterpret_cast<char*>(get()); }
 };
 
 /*
  * A wrapper for a "const char*" that is encoded using UTF-8.
  * This class does not manage ownership of the data; that is left
  * to others.  This differs from UTF8CharsZ in that the chars are
- * const and it allows assignment.
+ * const and it disallows assignment.
  */
 class JS_PUBLIC_API(ConstUTF8CharsZ)
 {
     const char* data_;
 
   public:
     using CharT = unsigned char;
 
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -1052,18 +1052,18 @@ js::obj_create(JSContext* cx, unsigned a
         return false;
     }
 
     if (!args[0].isObjectOrNull()) {
         UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, args[0], nullptr);
         if (!bytes)
             return false;
 
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
-                                   bytes.get(), "not an object or null");
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
+                                 bytes.get(), "not an object or null");
         return false;
     }
 
     // Step 2.
     RootedObject proto(cx, args[0].toObjectOrNull());
     RootedPlainObject obj(cx, ObjectCreateImpl(cx, proto));
     if (!obj)
         return false;
--- a/js/src/builtin/Stream.cpp
+++ b/js/src/builtin/Stream.cpp
@@ -272,18 +272,18 @@ PromiseRejectedWithPendingError(JSContex
 
 static void
 ReportArgTypeError(JSContext* cx, const char* funName, const char* expectedType, HandleValue arg)
 {
     UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, arg, nullptr);
     if (!bytes)
         return;
 
-    JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, funName,
-                               expectedType, bytes.get());
+    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, funName,
+                             expectedType, bytes.get());
 }
 
 static MOZ_MUST_USE bool
 RejectWithPendingError(JSContext* cx, Handle<PromiseObject*> promise) {
     // Not much we can do about uncatchable exceptions, just bail.
     RootedValue exn(cx);
     if (!GetAndClearException(cx, &exn))
         return false;
--- a/js/src/perf/jsperf.cpp
+++ b/js/src/perf/jsperf.cpp
@@ -218,17 +218,17 @@ pm_finalize(JSFreeOp* fop, JSObject* obj
 
 static PerfMeasurement*
 GetPM(JSContext* cx, JS::HandleValue value, const char* fname)
 {
     if (!value.isObject()) {
         UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, value, nullptr);
         if (!bytes)
             return nullptr;
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, 0, JSMSG_NOT_NONNULL_OBJECT, bytes.get());
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, 0, JSMSG_NOT_NONNULL_OBJECT, bytes.get());
         return nullptr;
     }
     RootedObject obj(cx, &value.toObject());
     PerfMeasurement* p = (PerfMeasurement*)
         JS_GetInstancePrivate(cx, obj, &pm_class, nullptr);
     if (p)
         return p;
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3095,17 +3095,18 @@ DisassembleToString(JSContext* cx, unsig
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     Sprinter sprinter(cx);
     if (!sprinter.init())
         return false;
     if (!DisassembleToSprinter(cx, args.length(), vp, &sprinter))
         return false;
 
-    JSString* str = JS_NewStringCopyZ(cx, sprinter.string());
+    JS::ConstUTF8CharsZ utf8chars(sprinter.string(), strlen(sprinter.string()));
+    JSString* str = JS_NewStringCopyUTF8Z(cx, utf8chars);
     if (!str)
         return false;
     args.rval().setString(str);
     return true;
 }
 
 static bool
 Disassemble(JSContext* cx, unsigned argc, Value* vp)
--- a/js/src/vm/BytecodeUtil.cpp
+++ b/js/src/vm/BytecodeUtil.cpp
@@ -1142,24 +1142,24 @@ ToDisassemblySource(JSContext* cx, Handl
     if (v.isObject()) {
         JSObject& obj = v.toObject();
 
         if (obj.is<JSFunction>()) {
             RootedFunction fun(cx, &obj.as<JSFunction>());
             JSString* str = JS_DecompileFunction(cx, fun);
             if (!str)
                 return nullptr;
-            return EncodeLatin1(cx, str);
+            return StringToNewUTF8CharsZ(cx, *str);
         }
 
         if (obj.is<RegExpObject>()) {
             JSString* source = obj.as<RegExpObject>().toString(cx);
             if (!source)
                 return nullptr;
-            return EncodeLatin1(cx, source);
+            return StringToNewUTF8CharsZ(cx, *source);
         }
     }
 
     JSString* str = ValueToSource(cx, v);
     if (!str)
         return nullptr;
     return QuoteString(cx, str);
 }
@@ -2294,17 +2294,17 @@ js::DecompileValueGenerator(JSContext* c
     if (!fallback) {
         if (v.isUndefined())
             return DuplicateString(cx, js_undefined_str); // Prevent users from seeing "(void 0)"
         fallback = ValueToSource(cx, v);
         if (!fallback)
             return nullptr;
     }
 
-    return EncodeLatin1(cx, fallback);
+    return StringToNewUTF8CharsZ(cx, *fallback);
 }
 
 static bool
 DecompileArgumentFromStack(JSContext* cx, int formalIndex, UniqueChars* res)
 {
     MOZ_ASSERT(formalIndex >= 0);
 
     *res = nullptr;
@@ -2374,18 +2374,20 @@ DecompileArgumentFromStack(JSContext* cx
 
 JSString*
 js::DecompileArgument(JSContext* cx, int formalIndex, HandleValue v)
 {
     {
         UniqueChars result;
         if (!DecompileArgumentFromStack(cx, formalIndex, &result))
             return nullptr;
-        if (result && strcmp(result.get(), "(intermediate value)"))
-            return NewStringCopyZ<CanGC>(cx, result.get());
+        if (result && strcmp(result.get(), "(intermediate value)")) {
+            JS::ConstUTF8CharsZ utf8chars(result.get(), strlen(result.get()));
+            return NewStringCopyUTF8Z<CanGC>(cx, utf8chars);
+        }
     }
     if (v.isUndefined())
         return cx->names().undefined; // Prevent users from seeing "(void 0)"
 
     return ValueToSource(cx, v);
 }
 
 extern bool
@@ -2687,17 +2689,18 @@ GetPCCountJSON(JSContext* cx, const Scri
             if (!ed.init())
                 return false;
             // defIndex passed here is not used.
             if (!ed.decompilePC(pc, /* defIndex = */ 0))
                 return false;
             UniqueChars text = ed.getOutput();
             if (!text)
                 return false;
-            JSString* str = NewLatin1StringZ(cx, std::move(text));
+            JS::ConstUTF8CharsZ utf8chars(text.get(), strlen(text.get()));
+            JSString* str = NewStringCopyUTF8Z<CanGC>(cx, utf8chars);
             if (!AppendJSONProperty(buf, "text"))
                 return false;
             if (!str || !(str = StringToSource(cx, str)))
                 return false;
             if (!buf.append(str))
                 return false;
         }
 
--- a/js/src/vm/ForOfIterator.cpp
+++ b/js/src/vm/ForOfIterator.cpp
@@ -62,17 +62,17 @@ ForOfIterator::init(HandleValue iterable
     // Throw if obj[@@iterator] isn't callable.
     // js::Invoke is about to check for this kind of error anyway, but it would
     // throw an inscrutable error message about |method| rather than this nice
     // one about |obj|.
     if (!callee.isObject() || !callee.toObject().isCallable()) {
         UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, iterable, nullptr);
         if (!bytes)
             return false;
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE, bytes.get());
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE, bytes.get());
         return false;
     }
 
     RootedValue res(cx);
     if (!js::Call(cx, callee, iterable, &res))
         return false;
 
     if (!res.isObject())
--- a/js/src/vm/JSContext.cpp
+++ b/js/src/vm/JSContext.cpp
@@ -914,104 +914,91 @@ js::ReportIsNullOrUndefinedForPropertyAc
         return;
     }
 
     UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr);
     if (!bytes)
         return;
 
     if (strcmp(bytes.get(), js_undefined_str) == 0 || strcmp(bytes.get(), js_null_str) == 0) {
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NO_PROPERTIES,
-                                   bytes.get());
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NO_PROPERTIES, bytes.get());
     } else if (v.isUndefined()) {
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
-                                   bytes.get(), js_undefined_str);
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE, bytes.get(),
+                                 js_undefined_str);
     } else {
         MOZ_ASSERT(v.isNull());
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
-                                   bytes.get(), js_null_str);
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE, bytes.get(),
+                                 js_null_str);
     }
 }
 
-static UniqueChars
-EncodeIdAsLatin1(JSContext* cx, HandleId id)
-{
-    RootedValue idVal(cx, IdToValue(id));
-    JSString* idStr = ValueToSource(cx, idVal);
-    if (!idStr)
-        return nullptr;
-
-    return EncodeLatin1(cx, idStr);
-}
-
 void
 js::ReportIsNullOrUndefinedForPropertyAccess(JSContext* cx, HandleValue v, HandleId key,
                                              bool reportScanStack)
 {
     MOZ_ASSERT(v.isNullOrUndefined());
 
-    UniqueChars keyBytes = EncodeIdAsLatin1(cx, key);
+    UniqueChars keyBytes = IdToPrintableUTF8(cx, key, IdToPrintableBehavior::IdIsPropertyKey);
     if (!keyBytes)
         return;
 
     if (!reportScanStack) {
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL,
-                                   keyBytes.get(),
-                                   v.isUndefined() ? js_undefined_str : js_null_str);
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL,
+                                 keyBytes.get(),
+                                 v.isUndefined() ? js_undefined_str : js_null_str);
         return;
     }
 
     UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr);
     if (!bytes)
         return;
 
     if (strcmp(bytes.get(), js_undefined_str) == 0 || strcmp(bytes.get(), js_null_str) == 0) {
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL,
-                                   keyBytes.get(), bytes.get());
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL,
+                                 keyBytes.get(), bytes.get());
     } else if (v.isUndefined()) {
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL_EXPR,
-                                   bytes.get(), js_undefined_str, keyBytes.get());
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL_EXPR,
+                                 bytes.get(), js_undefined_str, keyBytes.get());
     } else {
         MOZ_ASSERT(v.isNull());
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL_EXPR,
-                                   bytes.get(), js_null_str, keyBytes.get());
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL_EXPR,
+                                 bytes.get(), js_null_str, keyBytes.get());
     }
 }
 
 void
 js::ReportMissingArg(JSContext* cx, HandleValue v, unsigned arg)
 {
     char argbuf[11];
     UniqueChars bytes;
 
     SprintfLiteral(argbuf, "%u", arg);
     if (IsFunctionObject(v)) {
         RootedAtom name(cx, v.toObject().as<JSFunction>().explicitName());
         bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, name);
         if (!bytes)
             return;
     }
-    JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
-                               JSMSG_MISSING_FUN_ARG,
-                               argbuf, bytes ? bytes.get() : "");
+    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_MISSING_FUN_ARG,
+                             argbuf, bytes ? bytes.get() : "");
 }
 
 bool
 js::ReportValueErrorFlags(JSContext* cx, unsigned flags, const unsigned errorNumber,
                           int spindex, HandleValue v, HandleString fallback,
                           const char* arg1, const char* arg2)
 {
     MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount >= 1);
     MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount <= 3);
     UniqueChars bytes = DecompileValueGenerator(cx, spindex, v, fallback);
     if (!bytes)
         return false;
 
-    return JS_ReportErrorFlagsAndNumberLatin1(cx, flags, GetErrorMessage, nullptr, errorNumber,
-                                              bytes.get(), arg1, arg2);
+    return JS_ReportErrorFlagsAndNumberUTF8(cx, flags, GetErrorMessage, nullptr, errorNumber,
+                                            bytes.get(), arg1, arg2);
 }
 
 JSObject*
 js::CreateErrorNotesArray(JSContext* cx, JSErrorReport* report)
 {
     RootedArrayObject notesArray(cx, NewDenseEmptyArray(cx));
     if (!notesArray)
         return nullptr;
--- a/js/src/vm/JSObject.cpp
+++ b/js/src/vm/JSObject.cpp
@@ -79,18 +79,18 @@ using namespace js;
 using namespace js::gc;
 
 void
 js::ReportNotObject(JSContext* cx, HandleValue v)
 {
     MOZ_ASSERT(!v.isObject());
 
     if (UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr)) {
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
-                                   bytes.get());
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
+                                 bytes.get());
     }
 }
 
 void
 js::ReportNotObjectArg(JSContext* cx, const char* nth, const char* fun, HandleValue v)
 {
     MOZ_ASSERT(!v.isObject());
 
@@ -223,18 +223,18 @@ js::GetFirstArgumentAsObject(JSContext* 
         return false;
     }
 
     HandleValue v = args[0];
     if (!v.isObject()) {
         UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr);
         if (!bytes)
             return false;
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
-                                   bytes.get(), "not an object");
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
+                                 bytes.get(), "not an object");
         return false;
     }
 
     objp.set(&v.toObject());
     return true;
 }
 
 static bool
--- a/js/src/vm/Printer.cpp
+++ b/js/src/vm/Printer.cpp
@@ -3,24 +3,26 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/Printer.h"
 
 #include "mozilla/PodOperations.h"
 #include "mozilla/Printf.h"
+#include "mozilla/RangedPtr.h"
 
 #include <ctype.h>
 #include <stdarg.h>
 #include <stdio.h>
 
 #include "jsutil.h"
 
 #include "ds/LifoAlloc.h"
+#include "js/CharacterEncoding.h"
 #include "util/Text.h"
 #include "util/Windows.h"
 #include "vm/JSContext.h"
 
 using mozilla::PodCopy;
 
 namespace
 {
@@ -218,34 +220,27 @@ Sprinter::put(const char* s, size_t len)
     return true;
 }
 
 bool
 Sprinter::putString(JSString* s)
 {
     InvariantChecker ic(this);
 
-    size_t length = s->length();
+    JSFlatString* flat = s->ensureFlat(context);
+    if (!flat)
+        return false;
+
+    size_t length = JS::GetDeflatedUTF8StringLength(flat);
 
     char* buffer = reserve(length);
     if (!buffer)
         return false;
 
-    JSLinearString* linear = s->ensureLinear(context);
-    if (!linear)
-        return false;
-
-    JS::AutoCheckCannotGC nogc;
-    if (linear->hasLatin1Chars()) {
-        PodCopy(reinterpret_cast<Latin1Char*>(buffer), linear->latin1Chars(nogc), length);
-    } else {
-        const char16_t* src = linear->twoByteChars(nogc);
-        for (size_t i = 0; i < length; i++)
-            buffer[i] = char(src[i]);
-    }
+    JS::DeflateStringToUTF8Buffer(flat, mozilla::RangedPtr<char>(buffer, length));
 
     buffer[length] = '\0';
     return true;
 }
 
 ptrdiff_t
 Sprinter::getOffset() const
 {
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -294,26 +294,26 @@ ThrowErrorWithType(JSContext* cx, JSExnT
 
     UniqueChars errorArgs[3];
     for (unsigned i = 1; i < 4 && i < args.length(); i++) {
         HandleValue val = args[i];
         if (val.isInt32() || val.isString()) {
             JSString* str = ToString<CanGC>(cx, val);
             if (!str)
                 return;
-            errorArgs[i - 1] = EncodeLatin1(cx, str);
+            errorArgs[i - 1] = StringToNewUTF8CharsZ(cx, *str);
         } else {
             errorArgs[i - 1] = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, val, nullptr);
         }
         if (!errorArgs[i - 1])
             return;
     }
 
-    JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, errorNumber,
-                               errorArgs[0].get(), errorArgs[1].get(), errorArgs[2].get());
+    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber,
+                             errorArgs[0].get(), errorArgs[1].get(), errorArgs[2].get());
 }
 
 static bool
 intrinsic_ThrowRangeError(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() >= 1);
 
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -1319,18 +1319,18 @@ TypedArrayObjectTemplate<T>::fromObject(
     RootedObject arrayLike(cx);
     if (!callee.isNullOrUndefined()) {
         // Throw if other[Symbol.iterator] isn't callable.
         if (!callee.isObject() || !callee.toObject().isCallable()) {
             RootedValue otherVal(cx, ObjectValue(*other));
             UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, otherVal, nullptr);
             if (!bytes)
                 return nullptr;
-            JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE,
-                                       bytes.get());
+            JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE,
+                                     bytes.get());
             return nullptr;
         }
 
         FixedInvokeArgs<2> args2(cx);
         args2[0].setObject(*other);
         args2[1].set(callee);
 
         // Step 6.a.