Bug 1034627 part 8 - Fix XPCConvert to work with Latin1 strings and nursery strings. r=bholley
authorJan de Mooij <jdemooij@mozilla.com>
Fri, 11 Jul 2014 16:22:39 +0200
changeset 214414 6f10bdf152448668e5f7157673bbaee3bce2aae7
parent 214413 52291e750b098ebacd926875e5d4977e9ce948b1
child 214415 d257e3805fc79aaf1c023144881ff3d9527c6611
push id3857
push userraliiev@mozilla.com
push dateTue, 02 Sep 2014 16:39:23 +0000
treeherdermozilla-beta@5638b907b505 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley
bugs1034627
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 1034627 part 8 - Fix XPCConvert to work with Latin1 strings and nursery strings. r=bholley
js/xpconnect/src/XPCConvert.cpp
--- a/js/xpconnect/src/XPCConvert.cpp
+++ b/js/xpconnect/src/XPCConvert.cpp
@@ -2,27 +2,29 @@
 /* vim: set ts=8 sts=4 et sw=4 tw=99: */
 /* 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/. */
 
 /* Data conversion between native and JavaScript types. */
 
 #include "mozilla/ArrayUtils.h"
+#include "mozilla/Range.h"
 
 #include "xpcprivate.h"
 #include "nsIAtom.h"
 #include "nsWrapperCache.h"
 #include "nsJSUtils.h"
 #include "WrapperFactory.h"
 
 #include "nsWrapperCacheInlines.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
+#include "js/CharacterEncoding.h"
 #include "jsprf.h"
 #include "JavaScriptParent.h"
 
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/PrimitiveConversions.h"
 
 using namespace xpc;
@@ -359,16 +361,26 @@ CheckJSCharInCharRange(jschar c)
         char msg[MSG_BUF_SIZE];
         JS_snprintf(msg, MSG_BUF_SIZE, "jschar out of char range; high bits of data lost: 0x%x", c);
         NS_WARNING(msg);
         return false;
     }
 
     return true;
 }
+
+template<typename CharT>
+static void
+CheckCharsInCharRange(const CharT *chars, size_t len)
+{
+    for (size_t i = 0; i < len; i++) {
+        if (!CheckJSCharInCharRange(chars[i]))
+            break;
+    }
+}
 #endif
 
 template<typename T>
 bool ConvertToPrimitive(JSContext *cx, HandleValue v, T *retval)
 {
     return ValueToPrimitive<T, eDefault>(cx, v, retval);
 }
 
@@ -409,44 +421,47 @@ XPCConvert::JSData2Native(void* d, Handl
     case nsXPTType::T_BOOL   :
         return ConvertToPrimitive(cx, s, static_cast<bool*>(d));
     case nsXPTType::T_CHAR   :
     {
         JSString* str = ToString(cx, s);
         if (!str) {
             return false;
         }
-        size_t length;
-        const jschar* chars = JS_GetStringCharsAndLength(cx, str, &length);
-        if (!chars) {
-            return false;
+
+        jschar ch;
+        if (JS_GetStringLength(str) == 0) {
+            ch = 0;
+        } else {
+            if (!JS_GetStringCharAt(cx, str, 0, &ch))
+                return false;
         }
-        jschar ch = length ? chars[0] : 0;
 #ifdef DEBUG
         CheckJSCharInCharRange(ch);
 #endif
         *((char*)d) = char(ch);
         break;
     }
     case nsXPTType::T_WCHAR  :
     {
         JSString* str;
         if (!(str = ToString(cx, s))) {
             return false;
         }
-        size_t length;
-        const jschar* chars = JS_GetStringCharsAndLength(cx, str, &length);
-        if (!chars) {
-            return false;
-        }
+        size_t length = JS_GetStringLength(str);
         if (length == 0) {
             *((uint16_t*)d) = 0;
             break;
         }
-        *((uint16_t*)d) = uint16_t(chars[0]);
+
+        jschar ch;
+        if (!JS_GetStringCharAt(cx, str, 0, &ch))
+            return false;
+
+        *((uint16_t*)d) = uint16_t(ch);
         break;
     }
     case nsXPTType::T_JSVAL :
         *((jsval*)d) = s;
         break;
     case nsXPTType::T_VOID:
         XPC_LOG_ERROR(("XPCConvert::JSData2Native : void* params not supported"));
         NS_ERROR("void* params not supported");
@@ -487,28 +502,23 @@ XPCConvert::JSData2Native(void* d, Handl
         if (s.isNull()) {
             if (useAllocator)
                 *((const nsAString**)d) = &NullString();
             else
                 (**((nsAString**)d)).SetIsVoid(true);
             return true;
         }
         size_t length = 0;
-        const char16_t* chars = nullptr;
         JSString* str = nullptr;
         if (!s.isUndefined()) {
             str = ToString(cx, s);
             if (!str)
                 return false;
 
-            chars = useAllocator ? JS_GetStringCharsZAndLength(cx, str, &length)
-                                 : JS_GetStringCharsAndLength(cx, str, &length);
-            if (!chars)
-                return false;
-
+            length = JS_GetStringLength(str);
             if (!length) {
                 if (useAllocator)
                     *((const nsAString**)d) = &EmptyString();
                 else
                     (**((nsAString**)d)).Truncate();
                 return true;
             }
         }
@@ -521,53 +531,54 @@ XPCConvert::JSData2Native(void* d, Handl
             ws = *((nsString**)d);
         }
 
         if (!str) {
             ws->AssignLiteral(MOZ_UTF16("undefined"));
         } else if (XPCStringConvert::IsDOMString(str)) {
             // The characters represent an existing nsStringBuffer that
             // was shared by XPCStringConvert::ReadableToJSVal.
+            const jschar *chars = JS_GetTwoByteExternalStringChars(str);
             nsStringBuffer::FromData((void *)chars)->ToString(length, *ws);
         } else if (XPCStringConvert::IsLiteral(str)) {
             // The characters represent a literal char16_t string constant
             // compiled into libxul, such as the string "undefined" above.
+            const jschar *chars = JS_GetTwoByteExternalStringChars(str);
             ws->AssignLiteral(chars, length);
-        } else if (useAllocator && STRING_TO_JSVAL(str) == s) {
-            // The JS string will exist over the function call.
-            // We don't need to copy the characters in this case.
-            ws->Rebind(chars, length);
         } else {
-            ws->Assign(chars, length);
+            if (!AssignJSString(cx, *ws, str))
+                return false;
         }
         return true;
     }
 
     case nsXPTType::T_CHAR_STR:
     {
         if (s.isUndefined() || s.isNull()) {
             *((char**)d) = nullptr;
             return true;
         }
 
         JSString* str = ToString(cx, s);
         if (!str) {
             return false;
         }
 #ifdef DEBUG
-        const jschar* chars=nullptr;
-        if (nullptr != (chars = JS_GetStringCharsZ(cx, str))) {
-            bool legalRange = true;
-            int len = JS_GetStringLength(str);
-            const jschar* t;
-            int32_t i=0;
-            for (t=chars; (i< len) && legalRange ; i++,t++) {
-                if (!CheckJSCharInCharRange(*t))
-                    break;
-            }
+        if (JS_StringHasLatin1Chars(str)) {
+            size_t len;
+            AutoCheckCannotGC nogc;
+            const Latin1Char *chars = JS_GetLatin1StringCharsAndLength(cx, nogc, str, &len);
+            if (chars)
+                CheckCharsInCharRange(chars, len);
+        } else {
+            size_t len;
+            AutoCheckCannotGC nogc;
+            const jschar *chars = JS_GetTwoByteStringCharsAndLength(cx, nogc, str, &len);
+            if (chars)
+                CheckCharsInCharRange(chars, len);
         }
 #endif // DEBUG
         size_t length = JS_GetStringEncodingLength(cx, str);
         if (length == size_t(-1)) {
             return false;
         }
         char *buffer = static_cast<char *>(nsMemory::Alloc(length + 1));
         if (!buffer) {
@@ -576,66 +587,58 @@ XPCConvert::JSData2Native(void* d, Handl
         JS_EncodeStringToBuffer(cx, str, buffer, length);
         buffer[length] = '\0';
         *((void**)d) = buffer;
         return true;
     }
 
     case nsXPTType::T_WCHAR_STR:
     {
-        const jschar* chars=nullptr;
         JSString* str;
 
         if (s.isUndefined() || s.isNull()) {
             *((jschar**)d) = nullptr;
             return true;
         }
 
         if (!(str = ToString(cx, s))) {
             return false;
         }
-        if (!(chars = JS_GetStringCharsZ(cx, str))) {
-            return false;
-        }
         int len = JS_GetStringLength(str);
         int byte_len = (len+1)*sizeof(jschar);
         if (!(*((void**)d) = nsMemory::Alloc(byte_len))) {
             // XXX should report error
             return false;
         }
-        jschar* destchars = *((jschar**)d);
-        memcpy(destchars, chars, byte_len);
-        destchars[len] = 0;
+        mozilla::Range<jschar> destChars(*((jschar**)d), len + 1);
+        if (!JS_CopyStringChars(cx, destChars, str))
+            return false;
+        destChars[len] = 0;
 
         return true;
     }
 
     case nsXPTType::T_UTF8STRING:
     {
-        const jschar* chars;
-        size_t length;
-        JSString* str;
-
         if (s.isNull() || s.isUndefined()) {
             if (useAllocator) {
                 *((const nsACString**)d) = &NullCString();
             } else {
                 nsCString* rs = *((nsCString**)d);
                 rs->SetIsVoid(true);
             }
             return true;
         }
 
         // The JS val is neither null nor void...
+        JSString *str = ToString(cx, s);
+        if (!str)
+            return false;
 
-        if (!(str = ToString(cx, s))||
-            !(chars = JS_GetStringCharsAndLength(cx, str, &length))) {
-            return false;
-        }
-
+        size_t length = JS_GetStringLength(str);
         if (!length) {
             if (useAllocator) {
                 *((const nsACString**)d) = &EmptyCString();
             } else {
                 nsCString* rs = *((nsCString**)d);
                 rs->Truncate();
             }
             return true;
@@ -647,17 +650,26 @@ XPCConvert::JSData2Native(void* d, Handl
             rs = new nsCString();
             if (!rs)
                 return false;
 
             *((const nsCString**)d) = rs;
         } else {
             rs = *((nsCString**)d);
         }
-        CopyUTF16toUTF8(Substring(chars, length), *rs);
+
+        JSFlatString *flat = JS_FlattenString(cx, str);
+        if (!flat)
+            return false;
+
+        size_t utf8Length = JS::GetDeflatedUTF8StringLength(flat);
+        rs->SetLength(utf8Length);
+
+        JS::DeflateStringToUTF8Buffer(flat, mozilla::RangedPtr<char>(rs->BeginWriting(), utf8Length));
+        MOZ_ASSERT(rs->get()[utf8Length] == '\0');
         return true;
     }
 
     case nsXPTType::T_CSTRING:
     {
         if (s.isNull() || s.isUndefined()) {
             if (useAllocator) {
                 nsACString *rs = new nsCString();
@@ -724,25 +736,23 @@ XPCConvert::JSData2Native(void* d, Handl
             if (!variant)
                 return false;
 
             variant.forget(static_cast<nsISupports**>(d));
             return true;
         } else if (iid->Equals(NS_GET_IID(nsIAtom)) && s.isString()) {
             // We're trying to pass a string as an nsIAtom.  Let's atomize!
             JSString* str = s.toString();
-            const char16_t* chars = JS_GetStringCharsZ(cx, str);
-            if (!chars) {
+            nsAutoJSString autoStr;
+            if (!autoStr.init(cx, str)) {
                 if (pErr)
                     *pErr = NS_ERROR_XPC_BAD_CONVERT_JS_NULL_REF;
                 return false;
             }
-            uint32_t length = JS_GetStringLength(str);
-            nsCOMPtr<nsIAtom> atom =
-                NS_NewAtom(nsDependentSubstring(chars, chars + length));
+            nsCOMPtr<nsIAtom> atom = NS_NewAtom(autoStr);
             atom.forget((nsISupports**)d);
             return true;
         }
         //else ...
 
         if (s.isNullOrUndefined()) {
             *((nsISupports**)d) = nullptr;
             return true;
@@ -1788,17 +1798,16 @@ XPCConvert::JSStringWithSize2Native(void
             buffer[len] = '\0';
             *((char**)d) = buffer;
 
             return true;
         }
 
         case nsXPTType::T_PWSTRING_SIZE_IS:
         {
-            const jschar* chars=nullptr;
             JSString* str;
 
             if (s.isUndefined() || s.isNull()) {
                 if (0 != count) {
                     if (pErr)
                         *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
                     return false;
                 }
@@ -1820,29 +1829,28 @@ XPCConvert::JSStringWithSize2Native(void
             }
 
             len = JS_GetStringLength(str);
             if (len > count) {
                 if (pErr)
                     *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
                 return false;
             }
-            if (len < count)
-                len = count;
 
-            if (!(chars = JS_GetStringCharsZ(cx, str))) {
-                return false;
-            }
+            len = count;
+
             uint32_t alloc_len = (len + 1) * sizeof(jschar);
             if (!(*((void**)d) = nsMemory::Alloc(alloc_len))) {
                 // XXX should report error
                 return false;
             }
-            memcpy(*((jschar**)d), chars, alloc_len);
-            (*((jschar**)d))[count] = 0;
+            mozilla::Range<jschar> destChars(*((jschar**)d), len + 1);
+            if (!JS_CopyStringChars(cx, destChars, str))
+                return false;
+            destChars[count] = 0;
 
             return true;
         }
         default:
             XPC_LOG_ERROR(("XPCConvert::JSStringWithSize2Native : unsupported type"));
             return false;
     }
 }