Bug 1034627 part 8 - Fix XPCConvert to work with Latin1 strings and nursery strings. r=bholley
☠☠ backed out by 1166b5c0e916 ☠ ☠
authorJan de Mooij <jdemooij@mozilla.com>
Fri, 11 Jul 2014 16:22:39 +0200
changeset 193596 bcba40acc0ac6dd9fb287f72a36653bdb166734b
parent 193595 110fbc2ebc1a74d5aa424c911aebd792550745ce
child 193597 a58ad2a9b06170bb0fa6c2d2af5f4f529298ee6a
push idunknown
push userunknown
push dateunknown
reviewersbholley
bugs1034627
milestone33.0a1
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;
     }
 }