Bug 1490605 - Part 3: Use existing UTF-8 functions from CharacterEncoding instead of near duplicating them. r=arai
authorAndré Bargull <andre.bargull@gmail.com>
Fri, 14 Sep 2018 09:51:25 -0700
changeset 436515 0fed430fe00c2c984a6a2c6cb4be30f62fe53baa
parent 436514 6a993fa3e3d8aff2cb8cd8be4390eda67a3328e7
child 436516 815719383085e20e162450c74002dbcbdff87095
push id34645
push userdluca@mozilla.com
push dateSat, 15 Sep 2018 09:47:39 +0000
treeherdermozilla-central@73a2f427e2fd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarai
bugs1490605
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 1490605 - Part 3: Use existing UTF-8 functions from CharacterEncoding instead of near duplicating them. r=arai
js/src/ctypes/CTypes.cpp
js/src/ctypes/CTypes.h
js/src/ctypes/Library.cpp
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -39,16 +39,17 @@
 #include "gc/FreeOp.h"
 #include "gc/Policy.h"
 #include "jit/AtomicOperations.h"
 #include "js/CharacterEncoding.h"
 #include "js/StableStringChars.h"
 #include "js/UniquePtr.h"
 #include "js/Utility.h"
 #include "js/Vector.h"
+#include "util/Unicode.h"
 #include "util/Windows.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 
 #include "vm/JSObject-inl.h"
 
 using namespace std;
 
@@ -56,175 +57,56 @@ using mozilla::IsAsciiAlpha;
 using mozilla::IsAsciiDigit;
 
 using JS::AutoCheckCannotGC;
 using JS::AutoStableStringChars;
 
 namespace js {
 namespace ctypes {
 
-template <typename CharT>
-size_t
-GetDeflatedUTF8StringLength(JSContext* maybecx, const CharT* chars,
-                            size_t nchars)
-{
-    size_t nbytes;
-    const CharT* end;
-    unsigned c, c2;
-
-    nbytes = nchars;
-    for (end = chars + nchars; chars != end; chars++) {
-        c = *chars;
-        if (c < 0x80) {
-            continue;
-        }
-        if (0xD800 <= c && c <= 0xDFFF) {
-            /* Surrogate pair. */
-            chars++;
-
-            /* nbytes sets 1 length since this is surrogate pair. */
-            nbytes--;
-            if (c >= 0xDC00 || chars == end) {
-                goto bad_surrogate;
-            }
-            c2 = *chars;
-            if (c2 < 0xDC00 || c2 > 0xDFFF) {
-                goto bad_surrogate;
-            }
-            c = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
-        }
-        c >>= 11;
-        nbytes++;
-        while (c) {
-            c >>= 5;
-            nbytes++;
-        }
-    }
-    return nbytes;
-
-  bad_surrogate:
-    if (maybecx) {
-        js::gc::AutoSuppressGC suppress(maybecx);
-        char buffer[10];
-        SprintfLiteral(buffer, "0x%x", c);
-        JS_ReportErrorNumberASCII(maybecx, GetErrorMessage, nullptr, JSMSG_BAD_SURROGATE_CHAR,
-                                  buffer);
-    }
-    return (size_t) -1;
-}
-
-template size_t
-GetDeflatedUTF8StringLength(JSContext* maybecx, const Latin1Char* chars,
-                            size_t nchars);
-
-template size_t
-GetDeflatedUTF8StringLength(JSContext* maybecx, const char16_t* chars,
-                            size_t nchars);
-
-static size_t
-GetDeflatedUTF8StringLength(JSContext* maybecx, JSLinearString* str)
-{
-    size_t length = str->length();
-
-    JS::AutoCheckCannotGC nogc;
-    return str->hasLatin1Chars()
-           ? GetDeflatedUTF8StringLength(maybecx, str->latin1Chars(nogc), length)
-           : GetDeflatedUTF8StringLength(maybecx, str->twoByteChars(nogc), length);
-}
-
-template <typename CharT>
+static bool
+HasUnpairedSurrogate(const char16_t* chars, size_t nchars, char16_t* unpaired)
+{
+  for (const char16_t* end = chars + nchars; chars != end; chars++) {
+    char16_t c = *chars;
+    if (unicode::LeadSurrogateMin <= c && c <= unicode::TrailSurrogateMax) {
+      chars++;
+      if (c >= unicode::TrailSurrogateMin || chars == end) {
+        *unpaired = c;
+        return true;
+      }
+      char16_t c2 = *chars;
+      if (c2 < unicode::TrailSurrogateMin || c2 > unicode::TrailSurrogateMax) {
+        *unpaired = c;
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
 bool
-DeflateStringToUTF8Buffer(JSContext* maybecx, const CharT* src, size_t srclen,
-                          char* dst, size_t* dstlenp)
-{
-    size_t i, utf8Len;
-    char16_t c, c2;
-    uint32_t v;
-    uint8_t utf8buf[6];
-
-    size_t dstlen = *dstlenp;
-    size_t origDstlen = dstlen;
-
-    while (srclen) {
-        c = *src++;
-        srclen--;
-        if (c >= 0xDC00 && c <= 0xDFFF) {
-            goto badSurrogate;
-        }
-        if (c < 0xD800 || c > 0xDBFF) {
-            v = c;
-        } else {
-            if (srclen < 1) {
-                goto badSurrogate;
-            }
-            c2 = *src;
-            if ((c2 < 0xDC00) || (c2 > 0xDFFF)) {
-                goto badSurrogate;
-            }
-            src++;
-            srclen--;
-            v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
-        }
-        if (v < 0x0080) {
-            /* no encoding necessary - performance hack */
-            if (dstlen == 0) {
-                goto bufferTooSmall;
-            }
-            *dst++ = (char) v;
-            utf8Len = 1;
-        } else {
-            utf8Len = js::OneUcs4ToUtf8Char(utf8buf, v);
-            if (utf8Len > dstlen) {
-                goto bufferTooSmall;
-            }
-            for (i = 0; i < utf8Len; i++) {
-                *dst++ = (char) utf8buf[i];
-            }
-        }
-        dstlen -= utf8Len;
-    }
-    *dstlenp = (origDstlen - dstlen);
+ReportErrorIfUnpairedSurrogatePresent(JSContext* cx, JSLinearString* str)
+{
+  if (str->hasLatin1Chars()) {
     return true;
-
-badSurrogate:
-    *dstlenp = (origDstlen - dstlen);
-    /* Delegate error reporting to the measurement function. */
-    if (maybecx) {
-        GetDeflatedUTF8StringLength(maybecx, src - 1, srclen + 1);
-    }
-    return false;
-
-bufferTooSmall:
-    *dstlenp = (origDstlen - dstlen);
-    if (maybecx) {
-        js::gc::AutoSuppressGC suppress(maybecx);
-        JS_ReportErrorNumberASCII(maybecx, GetErrorMessage, nullptr,
-                                  JSMSG_BUFFER_TOO_SMALL);
-    }
-    return false;
-}
-
-template bool
-DeflateStringToUTF8Buffer(JSContext* maybecx, const Latin1Char* src, size_t srclen,
-                          char* dst, size_t* dstlenp);
-
-template bool
-DeflateStringToUTF8Buffer(JSContext* maybecx, const char16_t* src, size_t srclen,
-                          char* dst, size_t* dstlenp);
-
-static bool
-DeflateStringToUTF8Buffer(JSContext* maybecx, JSLinearString* str, char* dst,
-                          size_t* dstlenp)
-{
-    size_t length = str->length();
-
+  }
+
+  char16_t unpaired;
+  {
     JS::AutoCheckCannotGC nogc;
-    return str->hasLatin1Chars()
-           ? DeflateStringToUTF8Buffer(maybecx, str->latin1Chars(nogc), length, dst, dstlenp)
-           : DeflateStringToUTF8Buffer(maybecx, str->twoByteChars(nogc), length, dst, dstlenp);
+    if (!HasUnpairedSurrogate(str->twoByteChars(nogc), str->length(), &unpaired)) {
+      return true;
+    }
+  }
+
+  char buffer[10];
+  SprintfLiteral(buffer, "0x%x", unpaired);
+  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_SURROGATE_CHAR, buffer);
+  return false;
 }
 
 /*******************************************************************************
 ** JSAPI function prototypes
 *******************************************************************************/
 
 // We use an enclosing struct here out of paranoia about the ability of gcc 4.4
 // (and maybe 4.5) to correctly compile this if it were a template function.
@@ -3663,59 +3545,62 @@ ImplicitConvert(JSContext* cx,
       }
 
     } else if (convType == ConversionType::Argument && val.isString()) {
       // Convert the string for the ffi call. This requires allocating space
       // which the caller assumes ownership of.
       // TODO: Extend this so we can safely convert strings at other times also.
       JSString* sourceString = val.toString();
       size_t sourceLength = sourceString->length();
-      JSLinearString* sourceLinear = sourceString->ensureLinear(cx);
-      if (!sourceLinear) {
+      Rooted<JSFlatString*> sourceFlat(cx, sourceString->ensureFlat(cx));
+      if (!sourceFlat) {
         return false;
       }
 
       switch (CType::GetTypeCode(baseType)) {
       case TYPE_char:
       case TYPE_signed_char:
       case TYPE_unsigned_char: {
-        // Convert from UTF-16 to UTF-8.
-        size_t nbytes = GetDeflatedUTF8StringLength(cx, sourceLinear);
-        if (nbytes == (size_t) -1) {
+        // Reject if unpaired surrogate characters are present.
+        if (!ReportErrorIfUnpairedSurrogatePresent(cx, sourceFlat)) {
           return false;
         }
 
+        // Convert from UTF-16 to UTF-8.
+        size_t nbytes = JS::GetDeflatedUTF8StringLength(sourceFlat);
+
         char** charBuffer = static_cast<char**>(buffer);
         *charBuffer = cx->pod_malloc<char>(nbytes + 1);
         if (!*charBuffer) {
           return false;
         }
 
-        ASSERT_OK(DeflateStringToUTF8Buffer(cx, sourceLinear, *charBuffer, &nbytes));
+        JS::DeflateStringToUTF8Buffer(sourceFlat, mozilla::RangedPtr<char>(*charBuffer, nbytes),
+                                      &nbytes);
         (*charBuffer)[nbytes] = 0;
         *freePointer = true;
         break;
       }
       case TYPE_char16_t: {
         // Copy the char16_t string data. (We could provide direct access to the
         // JSString's buffer, but this approach is safer if the caller happens
         // to modify the string.)
         char16_t** char16Buffer = static_cast<char16_t**>(buffer);
         *char16Buffer = cx->pod_malloc<char16_t>(sourceLength + 1);
         if (!*char16Buffer) {
           return false;
         }
 
         *freePointer = true;
-        if (sourceLinear->hasLatin1Chars()) {
+        if (sourceFlat->hasLatin1Chars()) {
             AutoCheckCannotGC nogc;
-            CopyAndInflateChars(*char16Buffer, sourceLinear->latin1Chars(nogc), sourceLength);
+            CopyAndInflateChars(*char16Buffer, sourceFlat->latin1Chars(nogc), sourceLength);
         } else {
             AutoCheckCannotGC nogc;
-            mozilla::PodCopy(*char16Buffer, sourceLinear->twoByteChars(nogc), sourceLength);
+            mozilla::PodCopy(*char16Buffer, sourceFlat->twoByteChars(nogc), sourceLength);
         }
         (*char16Buffer)[sourceLength] = 0;
         break;
       }
       default:
         return ConvError(cx, targetType, val, convType, funObj, argIndex,
                          arrObj, arrIndex);
       }
@@ -3785,41 +3670,42 @@ ImplicitConvert(JSContext* cx,
     MOZ_ASSERT(!funObj);
 
     RootedObject baseType(cx, ArrayType::GetBaseType(targetType));
     size_t targetLength = ArrayType::GetLength(targetType);
 
     if (val.isString()) {
       JSString* sourceString = val.toString();
       size_t sourceLength = sourceString->length();
-      JSLinearString* sourceLinear = sourceString->ensureLinear(cx);
-      if (!sourceLinear) {
+      Rooted<JSFlatString*> sourceFlat(cx, sourceString->ensureFlat(cx));
+      if (!sourceFlat) {
         return false;
       }
 
       switch (CType::GetTypeCode(baseType)) {
       case TYPE_char:
       case TYPE_signed_char:
       case TYPE_unsigned_char: {
-        // Convert from UTF-16 or Latin1 to UTF-8.
-        size_t nbytes =
-          GetDeflatedUTF8StringLength(cx, sourceLinear);
-        if (nbytes == (size_t) -1) {
+        // Reject if unpaired surrogate characters are present.
+        if (!ReportErrorIfUnpairedSurrogatePresent(cx, sourceFlat)) {
           return false;
         }
 
+        // Convert from UTF-16 or Latin1 to UTF-8.
+        size_t nbytes = JS::GetDeflatedUTF8StringLength(sourceFlat);
+
         if (targetLength < nbytes) {
           MOZ_ASSERT(!funObj);
           return ArrayLengthOverflow(cx, targetLength, targetType, nbytes, val,
                                      convType);
         }
 
         char* charBuffer = static_cast<char*>(buffer);
-        ASSERT_OK(DeflateStringToUTF8Buffer(cx, sourceLinear, charBuffer,
-                                            &nbytes));
+        JS::DeflateStringToUTF8Buffer(sourceFlat, mozilla::RangedPtr<char>(charBuffer, nbytes),
+                                      &nbytes);
 
         if (targetLength > nbytes) {
           charBuffer[nbytes] = 0;
         }
 
         break;
       }
       case TYPE_char16_t: {
@@ -3827,22 +3713,22 @@ ImplicitConvert(JSContext* cx,
         // if there's space.
         if (targetLength < sourceLength) {
           MOZ_ASSERT(!funObj);
           return ArrayLengthOverflow(cx, targetLength, targetType,
                                      sourceLength, val, convType);
         }
 
         char16_t* dest = static_cast<char16_t*>(buffer);
-        if (sourceLinear->hasLatin1Chars()) {
+        if (sourceFlat->hasLatin1Chars()) {
             AutoCheckCannotGC nogc;
-            CopyAndInflateChars(dest, sourceLinear->latin1Chars(nogc), sourceLength);
+            CopyAndInflateChars(dest, sourceFlat->latin1Chars(nogc), sourceLength);
         } else {
             AutoCheckCannotGC nogc;
-            mozilla::PodCopy(dest, sourceLinear->twoByteChars(nogc), sourceLength);
+            mozilla::PodCopy(dest, sourceFlat->twoByteChars(nogc), sourceLength);
         }
 
         if (targetLength > sourceLength) {
           dest[sourceLength] = 0;
         }
 
         break;
       }
@@ -5779,31 +5665,33 @@ ArrayType::ConstructData(JSContext* cx,
                                     "an array object or integer");
       }
 
     } else if (args[0].isString()) {
       // We were given a string. Size the array to the appropriate length,
       // including space for the terminator.
       JSString* sourceString = args[0].toString();
       size_t sourceLength = sourceString->length();
-      JSLinearString* sourceLinear = sourceString->ensureLinear(cx);
-      if (!sourceLinear) {
+      Rooted<JSFlatString*> sourceFlat(cx, sourceString->ensureFlat(cx));
+      if (!sourceFlat) {
         return false;
       }
 
       switch (CType::GetTypeCode(baseType)) {
       case TYPE_char:
       case TYPE_signed_char:
       case TYPE_unsigned_char: {
-        // Determine the UTF-8 length.
-        length = GetDeflatedUTF8StringLength(cx, sourceLinear);
-        if (length == (size_t) -1) {
+        // Reject if unpaired surrogate characters are present.
+        if (!ReportErrorIfUnpairedSurrogatePresent(cx, sourceFlat)) {
           return false;
         }
 
+        // Determine the UTF-8 length.
+        length = JS::GetDeflatedUTF8StringLength(sourceFlat);
+
         ++length;
         break;
       }
       case TYPE_char16_t:
         length = sourceLength + 1;
         break;
       default:
         return ConvError(cx, obj, args[0], ConversionType::Construct);
@@ -6283,17 +6171,17 @@ StructType::Create(JSContext* cx, unsign
 
 bool
 StructType::DefineInternal(JSContext* cx, JSObject* typeObj_, JSObject* fieldsObj_)
 {
   RootedObject typeObj(cx, typeObj_);
   RootedObject fieldsObj(cx, fieldsObj_);
 
   uint32_t len;
-  ASSERT_OK(JS_GetArrayLength(cx, fieldsObj, &len));
+  MOZ_ALWAYS_TRUE(JS_GetArrayLength(cx, fieldsObj, &len));
 
   // Get the common prototype for CData objects of this type from
   // ctypes.CType.prototype.
   RootedObject dataProto(cx, CType::GetProtoFromType(cx, typeObj, SLOT_STRUCTDATAPROTO));
   if (!dataProto) {
     return false;
   }
 
@@ -7223,17 +7111,17 @@ FunctionType::Create(JSContext* cx, unsi
 
     if (!isArray) {
       return ArgumentTypeMismatch(cx, "third ", "FunctionType", "an array");
     }
 
     arrayObj = &args[2].toObject();
 
     uint32_t len;
-    ASSERT_OK(JS_GetArrayLength(cx, arrayObj, &len));
+    MOZ_ALWAYS_TRUE(JS_GetArrayLength(cx, arrayObj, &len));
 
     if (!argTypes.resize(len)) {
       JS_ReportOutOfMemory(cx);
       return false;
     }
   }
 
   // Pull out the argument types from the array, if any.
@@ -9135,17 +9023,17 @@ Int64::Construct(JSContext* cx,
       return TypeOverflow(cx, "int64", args[0]);
     }
     return ArgumentConvError(cx, args[0], "Int64", 0);
   }
 
   // Get ctypes.Int64.prototype from the 'prototype' property of the ctor.
   RootedValue slot(cx);
   RootedObject callee(cx, &args.callee());
-  ASSERT_OK(JS_GetProperty(cx, callee, "prototype", &slot));
+  MOZ_ALWAYS_TRUE(JS_GetProperty(cx, callee, "prototype", &slot));
   RootedObject proto(cx, slot.toObjectOrNull());
   MOZ_ASSERT(JS_GetClass(proto) == &sInt64ProtoClass);
 
   JSObject* result = Int64Base::Construct(cx, proto, i, false);
   if (!result) {
     return false;
   }
 
@@ -9326,17 +9214,17 @@ UInt64::Construct(JSContext* cx,
       return TypeOverflow(cx, "uint64", args[0]);
     }
     return ArgumentConvError(cx, args[0], "UInt64", 0);
   }
 
   // Get ctypes.UInt64.prototype from the 'prototype' property of the ctor.
   RootedValue slot(cx);
   RootedObject callee(cx, &args.callee());
-  ASSERT_OK(JS_GetProperty(cx, callee, "prototype", &slot));
+  MOZ_ALWAYS_TRUE(JS_GetProperty(cx, callee, "prototype", &slot));
   RootedObject proto(cx, &slot.toObject());
   MOZ_ASSERT(JS_GetClass(proto) == &sUInt64ProtoClass);
 
   JSObject* result = Int64Base::Construct(cx, proto, u, true);
   if (!result) {
     return false;
   }
 
--- a/js/src/ctypes/CTypes.h
+++ b/js/src/ctypes/CTypes.h
@@ -265,39 +265,26 @@ PrependString(JSContext* cx, StringBuild
     for (size_t i = 0; i < alen; i++) {
       v[i] = chars[i];
     }
   } else {
     memcpy(v.begin(), linear->twoByteChars(nogc), alen * sizeof(char16_t));
   }
 }
 
-template <typename CharT>
-extern size_t
-GetDeflatedUTF8StringLength(JSContext* maybecx, const CharT* chars,
-                            size_t charsLength);
-
-template <typename CharT>
 MOZ_MUST_USE bool
-DeflateStringToUTF8Buffer(JSContext* maybecx, const CharT* src, size_t srclen,
-                          char* dst, size_t* dstlenp);
+ReportErrorIfUnpairedSurrogatePresent(JSContext* cx, JSLinearString* str);
 
 MOZ_MUST_USE JSObject*
 GetThisObject(JSContext* cx, const CallArgs& args, const char* msg);
 
 /*******************************************************************************
 ** Function and struct API definitions
 *******************************************************************************/
 
-MOZ_ALWAYS_INLINE void
-ASSERT_OK(bool ok)
-{
-  MOZ_ASSERT(ok);
-}
-
 // for JS error reporting
 enum ErrorNum {
 #define MSG_DEF(name, count, exception, format) \
   name,
 #include "ctypes/ctypes.msg"
 #undef MSG_DEF
   CTYPESERR_LIMIT
 };
--- a/js/src/ctypes/Library.cpp
+++ b/js/src/ctypes/Library.cpp
@@ -114,66 +114,66 @@ Library::Create(JSContext* cx, HandleVal
     return nullptr;
   }
 
   PRLibSpec libSpec;
   RootedFlatString pathStr(cx, JS_FlattenString(cx, path.toString()));
   if (!pathStr) {
     return nullptr;
   }
+#ifdef XP_WIN
+  // On Windows, converting to native charset may corrupt path string.
+  // So, we have to use Unicode path directly.
   AutoStableStringChars pathStrChars(cx);
   if (!pathStrChars.initTwoByte(cx, pathStr)) {
     return nullptr;
   }
-#ifdef XP_WIN
-  // On Windows, converting to native charset may corrupt path string.
-  // So, we have to use Unicode path directly.
   char16ptr_t pathChars = pathStrChars.twoByteChars();
   libSpec.value.pathname_u = pathChars;
   libSpec.type = PR_LibSpec_PathnameU;
 #else
   // Convert to platform native charset if the appropriate callback has been
   // provided.
-  char* pathBytes;
+  JS::UniqueChars pathBytes;
   if (callbacks && callbacks->unicodeToNative) {
-    pathBytes =
-      callbacks->unicodeToNative(cx, pathStrChars.twoByteChars(), pathStr->length());
+    AutoStableStringChars pathStrChars(cx);
+    if (!pathStrChars.initTwoByte(cx, pathStr)) {
+      return nullptr;
+    }
+
+    pathBytes.reset(callbacks->unicodeToNative(cx, pathStrChars.twoByteChars(),
+                                               pathStr->length()));
+    if (!pathBytes) {
+      return nullptr;
+    }
+  } else {
+    // Fallback: assume the platform native charset is UTF-8. This is true
+    // for Mac OS X, Android, and probably Linux.
+    if (!ReportErrorIfUnpairedSurrogatePresent(cx, pathStr)) {
+      return nullptr;
+    }
+
+    size_t nbytes = JS::GetDeflatedUTF8StringLength(pathStr);
+
+    pathBytes.reset(static_cast<char*>(JS_malloc(cx, nbytes + 1)));
     if (!pathBytes) {
       return nullptr;
     }
 
-  } else {
-    // Fallback: assume the platform native charset is UTF-8. This is true
-    // for Mac OS X, Android, and probably Linux.
-    size_t nbytes =
-      GetDeflatedUTF8StringLength(cx, pathStrChars.twoByteChars(), pathStr->length());
-    if (nbytes == (size_t) -1) {
-      return nullptr;
-    }
-
-    pathBytes = static_cast<char*>(JS_malloc(cx, nbytes + 1));
-    if (!pathBytes) {
-      return nullptr;
-    }
-
-    ASSERT_OK(DeflateStringToUTF8Buffer(cx, pathStrChars.twoByteChars(),
-                pathStr->length(), pathBytes, &nbytes));
+    JS::DeflateStringToUTF8Buffer(pathStr, mozilla::RangedPtr<char>(pathBytes.get(), nbytes),
+                                  &nbytes);
     pathBytes[nbytes] = 0;
   }
 
-  libSpec.value.pathname = pathBytes;
+  libSpec.value.pathname = pathBytes.get();
   libSpec.type = PR_LibSpec_Pathname;
 #endif
 
   PRLibrary* library = PR_LoadLibraryWithFlags(libSpec, PR_LD_NOW);
 
-#ifndef XP_WIN
-  JS_free(cx, pathBytes);
-#endif
-
   if (!library) {
 #define MAX_ERROR_LEN 1024
     char error[MAX_ERROR_LEN] = "Cannot get error from NSPR.";
     uint32_t errorLen = PR_GetErrorTextLength();
     if (errorLen && errorLen < MAX_ERROR_LEN) {
       PR_GetErrorText(error);
     }
 #undef MAX_ERROR_LEN