Bug 742188 - Implement ToInt64 and ToUint64 per the WebIDL standard. (r=jorendorff)
authorEric Faust <efaust@mozilla.com>
Fri, 03 Aug 2012 15:15:04 -0700
changeset 101368 83c9437ff26db74dab81cf5352b7f247b2c1471e
parent 101367 cda60803eb9185886caa16c437cc2194f665aeaf
child 101369 6609595c84eb7819a4166102296cb73c9a4e139a
push id12975
push userefaust@mozilla.com
push dateFri, 03 Aug 2012 22:59:42 +0000
treeherdermozilla-inbound@83c9437ff26d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs742188
milestone17.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 742188 - Implement ToInt64 and ToUint64 per the WebIDL standard. (r=jorendorff)
dom/bindings/PrimitiveConversions.h
dom/file/LockedFile.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsnum.cpp
js/src/vm/NumericConversions.h
js/xpconnect/src/XPCConvert.cpp
js/xpconnect/src/codegen.py
js/xpconnect/src/qsgen.py
js/xpconnect/src/xpcpublic.h
--- a/dom/bindings/PrimitiveConversions.h
+++ b/dom/bindings/PrimitiveConversions.h
@@ -86,26 +86,26 @@ struct PrimitiveConversionTraits<bool> {
   }
 };
 
 template<>
 struct PrimitiveConversionTraits<int64_t> {
   typedef int64_t jstype;
   typedef int64_t intermediateType;
   static inline bool converter(JSContext* cx, JS::Value v, jstype* retval) {
-    return xpc::ValueToInt64(cx, v, retval);
+    return JS::ToInt64(cx, v, retval);
   }
 };
 
 template<>
 struct PrimitiveConversionTraits<uint64_t> {
   typedef uint64_t jstype;
   typedef uint64_t intermediateType;
   static inline bool converter(JSContext* cx, JS::Value v, jstype* retval) {
-    return xpc::ValueToUint64(cx, v, retval);
+    return JS::ToUint64(cx, v, retval);
   }
 };
 
 struct PrimitiveConversionTraits_float {
   typedef double jstype;
   typedef double intermediateType;
   static inline bool converter(JSContext* cx, JS::Value v, jstype* retval) {
     return JS::ToNumber(cx, v, retval);
--- a/dom/file/LockedFile.cpp
+++ b/dom/file/LockedFile.cpp
@@ -530,17 +530,17 @@ LockedFile::SetLocation(JSContext* aCx,
 
   // Null means the end-of-file.
   if (JSVAL_IS_NULL(aLocation)) {
     mLocation = LL_MAXUINT;
     return NS_OK;
   }
 
   uint64_t location;
-  if (!xpc::ValueToUint64(aCx, aLocation, &location)) {
+  if (!JS::ToUint64(aCx, aLocation, &location)) {
     return NS_ERROR_TYPE_ERR;
   }
 
   mLocation = location;
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -577,16 +577,30 @@ JS_ValueToECMAInt32(JSContext *cx, jsval
 JS_PUBLIC_API(JSBool)
 JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32_t *ip)
 {
     RootedValue value(cx, v);
     return JS::ToUint32(cx, value, ip);
 }
 
 JS_PUBLIC_API(JSBool)
+JS_ValueToInt64(JSContext *cx, jsval v, int64_t *ip)
+{
+    RootedValue value(cx, v);
+    return JS::ToInt64(cx, value, ip);
+}
+
+JS_PUBLIC_API(JSBool)
+JS_ValueToUint64(JSContext *cx, jsval v, uint64_t *ip)
+{
+    RootedValue value(cx, v);
+    return JS::ToUint64(cx, value, ip);
+}
+
+JS_PUBLIC_API(JSBool)
 JS_ValueToInt32(JSContext *cx, jsval v, int32_t *ip)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, v);
 
     if (v.isInt32()) {
         *ip = v.toInt32();
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2749,29 +2749,51 @@ JS_DoubleToUint32(double d);
 
 /*
  * Convert a value to a number, then to an int32_t, according to the ECMA rules
  * for ToInt32.
  */
 extern JS_PUBLIC_API(JSBool)
 JS_ValueToECMAInt32(JSContext *cx, jsval v, int32_t *ip);
 
+/*
+ * Convert a value to a number, then to an int64_t, according to the WebIDL
+ * rules for ToInt64: http://dev.w3.org/2006/webapi/WebIDL/#es-long-long
+ */
+extern JS_PUBLIC_API(JSBool)
+JS_ValueToInt64(JSContext *cx, jsval v, int64_t *ip);
+
+/*
+ * Convert a value to a number, then to an uint64_t, according to the WebIDL
+ * rules for ToUint64: http://dev.w3.org/2006/webapi/WebIDL/#es-unsigned-long-long
+ */
+extern JS_PUBLIC_API(JSBool)
+JS_ValueToUint64(JSContext *cx, jsval v, uint64_t *ip);
+
 #ifdef __cplusplus
 namespace js {
 /* DO NOT CALL THIS.  Use JS::ToInt16. */
 extern JS_PUBLIC_API(bool)
 ToUint16Slow(JSContext *cx, const JS::Value &v, uint16_t *out);
 
 /* DO NOT CALL THIS.  Use JS::ToInt32. */
 extern JS_PUBLIC_API(bool)
 ToInt32Slow(JSContext *cx, const JS::Value &v, int32_t *out);
 
 /* DO NOT CALL THIS.  Use JS::ToUint32. */
 extern JS_PUBLIC_API(bool)
 ToUint32Slow(JSContext *cx, const JS::Value &v, uint32_t *out);
+
+/* DO NOT CALL THIS. Use JS::ToInt64. */
+extern JS_PUBLIC_API(bool)
+ToInt64Slow(JSContext *cx, const JS::Value &v, int64_t *out);
+
+/* DO NOT CALL THIS. Use JS::ToUint64. */
+extern JS_PUBLIC_API(bool)
+ToUint64Slow(JSContext *cx, const JS::Value &v, uint64_t *out);
 } /* namespace js */
 
 namespace JS {
 
 JS_ALWAYS_INLINE bool
 ToUint16(JSContext *cx, const js::Value &v, uint16_t *out)
 {
     AssertArgumentsAreSane(cx, v);
@@ -2814,16 +2836,52 @@ ToUint32(JSContext *cx, const js::Value 
 
     if (v.isInt32()) {
         *out = uint32_t(v.toInt32());
         return true;
     }
     return js::ToUint32Slow(cx, v, out);
 }
 
+JS_ALWAYS_INLINE bool
+ToInt64(JSContext *cx, const js::Value &v, int64_t *out)
+{
+    AssertArgumentsAreSane(cx, v);
+    {
+        JS::SkipRoot skip(cx, &v);
+        MaybeCheckStackRoots(cx);
+    }
+
+    if (v.isInt32()) {
+        *out = int64_t(v.toInt32());
+        return true;
+    }
+
+    return js::ToInt64Slow(cx, v, out);
+}
+
+JS_ALWAYS_INLINE bool
+ToUint64(JSContext *cx, const js::Value &v, uint64_t *out)
+{
+    AssertArgumentsAreSane(cx, v);
+    {
+        SkipRoot skip(cx, &v);
+        MaybeCheckStackRoots(cx);
+    }
+
+    if (v.isInt32()) {
+        // Account for sign extension of negatives into the longer 64bit space.
+        *out = uint64_t(int64_t(v.toInt32()));
+        return true;
+    }
+
+    return js::ToUint64Slow(cx, v, out);
+}
+
+
 } /* namespace JS */
 #endif /* __cplusplus */
 
 /*
  * Convert a value to a number, then to a uint32_t, according to the ECMA rules
  * for ToUint32.
  */
 extern JS_PUBLIC_API(JSBool)
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -1395,16 +1395,54 @@ ToNumberSlow(JSContext *cx, Value v, dou
         if (v.isObject())
             break;
     }
 
     *out = js_NaN;
     return true;
 }
 
+/*
+ * Convert a value to an int64_t, according to the WebIDL rules for long long
+ * conversion. Return converted value in *out on success, false on failure.
+ */
+JS_PUBLIC_API(bool)
+ToInt64Slow(JSContext *cx, const Value &v, int64_t *out)
+{
+    JS_ASSERT(!v.isInt32());
+    double d;
+    if (v.isDouble()) {
+        d = v.toDouble();
+    } else {
+        if (!ToNumberSlow(cx, v, &d))
+            return false;
+    }
+    *out = ToInt64(d);
+    return true;
+}
+
+/*
+ * Convert a value to an uint64_t, according to the WebIDL rules for unsigned long long
+ * conversion. Return converted value in *out on success, false on failure.
+ */
+JS_PUBLIC_API(bool)
+ToUint64Slow(JSContext *cx, const Value &v, uint64_t *out)
+{
+    JS_ASSERT(!v.isInt32());
+    double d;
+    if (v.isDouble()) {
+        d = v.toDouble();
+    } else {
+        if (!ToNumberSlow(cx, v, &d))
+            return false;
+    }
+    *out = ToUint64(d);
+    return true;
+}
+
 JS_PUBLIC_API(bool)
 ToInt32Slow(JSContext *cx, const Value &v, int32_t *out)
 {
     JS_ASSERT(!v.isInt32());
     double d;
     if (v.isDouble()) {
         d = v.toDouble();
     } else {
--- a/js/src/vm/NumericConversions.h
+++ b/js/src/vm/NumericConversions.h
@@ -28,94 +28,138 @@ union DoublePun {
 #endif
     } s;
     uint64_t u64;
     double d;
 };
 
 } /* namespace detail */
 
-/* ES5 9.5 ToInt32 (specialized for doubles). */
-inline int32_t
-ToInt32(double d)
+/* Numeric Conversion base. Round doubles to Ints according to ECMA or WEBIDL standards. */
+template<size_t width, typename ResultType>
+inline ResultType
+ToIntWidth(double d)
 {
 #if defined(__i386__) || defined(__i386) || defined(__x86_64__) || \
     defined(_M_IX86) || defined(_M_X64)
-    detail::DoublePun du, duh, two32;
+    detail::DoublePun du, duh, twoWidth;
     uint32_t di_h, u_tmp, expon, shift_amount;
     int32_t mask32;
 
     /*
      * Algorithm Outline
-     *  Step 1. If d is NaN, +/-Inf or |d|>=2^84 or |d|<1, then return 0
-     *          All of this is implemented based on an exponent comparison.
-     *  Step 2. If |d|<2^31, then return (int)d
+     *  Step 1. If d is NaN, +/-Inf or |d|>=2^(width + 52) or |d|<1, then return 0
+     *          All of this is implemented based on an exponent comparison,
+     *          since anything with a higher exponent is either not finite, or
+     *          going to round to 0..
+     *  Step 2. If |d|<2^(width - 1), then return (int)d
      *          The cast to integer (conversion in RZ mode) returns the correct result.
-     *  Step 3. If |d|>=2^32, d:=fmod(d, 2^32) is taken  -- but without a call
-     *  Step 4. If |d|>=2^31, then the fractional bits are cleared before
-     *          applying the correction by 2^32:  d - sign(d)*2^32
+     *  Step 3. If |d|>=2^width, d:=fmod(d, 2^width) is taken  -- but without a call
+     *  Step 4. If |d|>=2^(width - 1), then the fractional bits are cleared before
+     *          applying the correction by 2^width:  d - sign(d)*2^width
      *  Step 5. Return (int)d
      */
 
     du.d = d;
     di_h = du.s.hi;
 
     u_tmp = (di_h & 0x7ff00000) - 0x3ff00000;
-    if (u_tmp >= (0x45300000-0x3ff00000)) {
-        // d is Nan, +/-Inf or +/-0, or |d|>=2^(32+52) or |d|<1, in which case result=0
+    if (u_tmp >= ((width + 52) << 20)) {
+        // d is Nan, +/-Inf or +/-0, or |d|>=2^(width+52) or |d|<1, in which case result=0
+        // If we need to shift by more than (width + 52), there are no data bits
+        // to preserve, and the mod will turn out 0.
         return 0;
     }
 
-    if (u_tmp < 0x01f00000) {
-        // |d|<2^31
-        return int32_t(d);
+    if (u_tmp < ((width - 1) << 20)) {
+        // |d|<2^(width - 1)
+        return ResultType(d);
     }
 
-    if (u_tmp > 0x01f00000) {
-        // |d|>=2^32
+    if (u_tmp > ((width - 1) << 20)) {
+        // |d|>=2^width
+        // Throw away multiples of 2^width.
+        //
+        // That is, compute du.d = the value in (-2^width, 2^width)
+        // that has the same sign as d and is equal to d modulo 2^width.
+        //
+        // This can't be done simply by masking away bits of du because
+        // the implicit one-bit of the mantissa is one of the ones we want to
+        // eliminate. So instead we compute duh.d = the appropriate multiple
+        // of 2^width, which *can* be computed by masking, and then we
+        // subtract that from du.d.
         expon = u_tmp >> 20;
-        shift_amount = expon - 21;
-        duh.u64 = du.u64;
+        shift_amount = expon - (width - 11);
         mask32 = 0x80000000;
         if (shift_amount < 32) {
+            // Shift only affects top word.
             mask32 >>= shift_amount;
             duh.s.hi = du.s.hi & mask32;
             duh.s.lo = 0;
         } else {
+            // Top word all 1s, shift affects bottom word.
             mask32 >>= (shift_amount-32);
             duh.s.hi = du.s.hi;
             duh.s.lo = du.s.lo & mask32;
         }
         du.d -= duh.d;
     }
 
     di_h = du.s.hi;
 
-    // eliminate fractional bits
+    // Eliminate fractional bits
     u_tmp = (di_h & 0x7ff00000);
-    if (u_tmp >= 0x41e00000) {
-        // |d|>=2^31
+    if (u_tmp >= (0x3ff00000 + ((width - 1) << 20))) {
+        // |d|>=2^(width - 1)
         expon = u_tmp >> 20;
+
+        // Same idea as before, except save everything non-fractional.
         shift_amount = expon - (0x3ff - 11);
         mask32 = 0x80000000;
         if (shift_amount < 32) {
+            // Top word only
             mask32 >>= shift_amount;
             du.s.hi &= mask32;
             du.s.lo = 0;
         } else {
+            // Bottom word. Top word all 1s.
             mask32 >>= (shift_amount-32);
             du.s.lo &= mask32;
         }
-        two32.s.hi = 0x41f00000 ^ (du.s.hi & 0x80000000);
-        two32.s.lo = 0;
-        du.d -= two32.d;
+        // Apply step 4's 2^width correction.
+        twoWidth.s.hi = (0x3ff00000 + (width << 20)) ^ (du.s.hi & 0x80000000);
+        twoWidth.s.lo = 0;
+        du.d -= twoWidth.d;
     }
 
-    return int32_t(du.d);
-#elif defined (__arm__) && defined (__GNUC__)
+    return ResultType(du.d);
+#else
+    double twoWidth, twoWidthMin1;
+
+    if (!MOZ_DOUBLE_IS_FINITE(d))
+        return 0;
+
+    /* FIXME: This relies on undefined behavior; see bug 667739. */
+    ResultType i = (ResultType) d;
+    if ((double) i == d)
+        return ResultType(i);
+
+    twoWidth = width == 32 ? 4294967296.0 : 18446744073709551616.0;
+    twoWidthMin1 = width == 32 ? 2147483648.0 : 9223372036854775808.0;
+    d = fmod(d, twoWidth);
+    d = (d >= 0) ? floor(d) : ceil(d) + twoWidth;
+    return (ResultType) (d >= twoWidthMin1 ? d - twoWidth : d);
+#endif
+}
+
+/* ES5 9.5 ToInt32 (specialized for doubles). */
+inline int32_t
+ToInt32(double d)
+{
+#if defined (__arm__) && defined (__GNUC__)
     int32_t i;
     uint32_t    tmp0;
     uint32_t    tmp1;
     uint32_t    tmp2;
     asm (
     // We use a pure integer solution here. In the 'softfp' ABI, the argument
     // will start in r0 and r1, and VFP can't do all of the necessary ECMA
     // conversions by itself so some integer code will be required anyway. A
@@ -227,42 +271,41 @@ ToInt32(double d)
 "   mov     %0, #0\n"
 "9:\n"
     : "=r" (i), "=&r" (tmp0), "=&r" (tmp1), "=&r" (tmp2), "=&r" (d)
     : "4" (d)
     : "cc"
         );
     return i;
 #else
-    int32_t i;
-    double two32, two31;
-
-    if (!MOZ_DOUBLE_IS_FINITE(d))
-        return 0;
-
-    /* FIXME: This relies on undefined behavior; see bug 667739. */
-    i = (int32_t) d;
-    if ((double) i == d)
-        return i;
-
-    two32 = 4294967296.0;
-    two31 = 2147483648.0;
-    d = fmod(d, two32);
-    d = (d >= 0) ? floor(d) : ceil(d) + two32;
-    return (int32_t) (d >= two31 ? d - two32 : d);
+    return ToIntWidth<32, int32_t>(d);
 #endif
 }
 
 /* ES5 9.6 (specialized for doubles). */
 inline uint32_t
 ToUint32(double d)
 {
     return uint32_t(ToInt32(d));
 }
 
+/* WEBIDL 4.2.10 */
+inline int64_t
+ToInt64(double d)
+{
+    return ToIntWidth<64, int64_t>(d);
+}
+
+/* WEBIDL 4.2.11 */
+inline uint64_t
+ToUint64(double d)
+{
+    return uint64_t(ToInt64(d));
+}
+
 /* ES5 9.4 ToInteger (specialized for doubles). */
 inline double
 ToInteger(double d)
 {
     if (d == 0)
         return d;
 
     if (!MOZ_DOUBLE_IS_FINITE(d)) {
--- a/js/xpconnect/src/XPCConvert.cpp
+++ b/js/xpconnect/src/XPCConvert.cpp
@@ -392,34 +392,34 @@ XPCConvert::JSData2Native(XPCCallContext
             return false;
         *((int16_t*)d)  = int16_t(ti);
         break;
     case nsXPTType::T_I32    :
         if (!JS_ValueToECMAInt32(cx, s, (int32_t*)d))
             return false;
         break;
     case nsXPTType::T_I64    :
-        return ValueToInt64(cx, s, (int64_t*)d);
+        return JS::ToInt64(cx, s, (int64_t*)d);
 
     case nsXPTType::T_U8     :
         if (!JS_ValueToECMAUint32(cx, s, &tu))
             return false;
         *((uint8_t*)d)  = uint8_t(tu);
         break;
     case nsXPTType::T_U16    :
         if (!JS_ValueToECMAUint32(cx, s, &tu))
             return false;
         *((uint16_t*)d)  = uint16_t(tu);
         break;
     case nsXPTType::T_U32    :
         if (!JS_ValueToECMAUint32(cx, s, (uint32_t*)d))
             return false;
         break;
     case nsXPTType::T_U64    :
-        return ValueToUint64(cx, s, (uint64_t*)d);
+        return JS::ToUint64(cx, s, (uint64_t*)d);
 
     case nsXPTType::T_FLOAT  :
         if (!JS_ValueToNumber(cx, s, &td))
             return false;
         *((float*)d) = (float) td;
         break;
     case nsXPTType::T_DOUBLE :
         if (!JS_ValueToNumber(cx, s, (double*)d))
--- a/js/xpconnect/src/codegen.py
+++ b/js/xpconnect/src/codegen.py
@@ -90,22 +90,22 @@ argumentUnboxingTemplates = {
 
     'unsigned long':
         "    uint32_t ${name};\n"
         "    if (!JS_ValueToECMAUint32(cx, ${argVal}, &${name}))\n"
         "        return JS_FALSE;\n",
 
     'long long':
         "    int64_t ${name};\n"
-        "    if (!xpc::ValueToInt64(cx, ${argVal}, &${name}))\n"
+        "    if (!JS::ToInt64(cx, ${argVal}, &${name}))\n"
         "        return JS_FALSE;\n",
 
     'unsigned long long':
         "    uint64_t ${name};\n"
-        "    if (!xpc::ValueToUint64(cx, ${argVal}, &${name}))\n"
+        "    if (!JS::ToUint64(cx, ${argVal}, &${name}))\n"
         "        return JS_FALSE;\n",
 
     'float':
         "    double ${name}_dbl;\n"
         "    if (!JS_ValueToNumber(cx, ${argVal}, &${name}_dbl))\n"
         "        return JS_FALSE;\n"
         "    float ${name} = (float) ${name}_dbl;\n",
 
--- a/js/xpconnect/src/qsgen.py
+++ b/js/xpconnect/src/qsgen.py
@@ -419,22 +419,22 @@ argumentUnboxingTemplates = {
 
     'unsigned long':
         "    uint32_t ${name};\n"
         "    if (!JS_ValueToECMAUint32(cx, ${argVal}, &${name}))\n"
         "        return JS_FALSE;\n",
 
     'long long':
         "    int64_t ${name};\n"
-        "    if (!xpc::ValueToInt64(cx, ${argVal}, &${name}))\n"
+        "    if (!JS::ToInt64(cx, ${argVal}, &${name}))\n"
         "        return JS_FALSE;\n",
 
     'unsigned long long':
         "    uint64_t ${name};\n"
-        "    if (!xpc::ValueToUint64(cx, ${argVal}, &${name}))\n"
+        "    if (!JS::ToUint64(cx, ${argVal}, &${name}))\n"
         "        return JS_FALSE;\n",
 
     'float':
         "    double ${name}_dbl;\n"
         "    if (!JS_ValueToNumber(cx, ${argVal}, &${name}_dbl))\n"
         "        return JS_FALSE;\n"
         "    float ${name} = (float) ${name}_dbl;\n",
 
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -276,66 +276,16 @@ DOM_DefineQuickStubs(JSContext *cx, JSOb
 // (which isn't all of them).
 nsresult
 ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats,
                                  const nsACString &rtPath,
                                  nsIMemoryMultiReporterCallback *cb,
                                  nsISupports *closure, size_t *rtTotal = NULL);
 
 /**
- * Convert a jsval to PRInt64. Return true on success.
- */
-inline bool
-ValueToInt64(JSContext *cx, JS::Value v, int64_t *result)
-{
-    if (JSVAL_IS_INT(v)) {
-        int32_t intval;
-        if (!JS_ValueToECMAInt32(cx, v, &intval))
-            return false;
-        *result = static_cast<int64_t>(intval);
-    } else {
-        double doubleval;
-        if (!JS_ValueToNumber(cx, v, &doubleval))
-            return false;
-        // Be careful with non-finite doubles
-        if (NS_finite(doubleval))
-            // XXXbz this isn't quite right either; need to do the mod thing
-            *result = static_cast<int64_t>(doubleval);
-        else
-            *result = 0;
-    }
-    return true;
-}
-
-/**
- * Convert a jsval to uint64_t. Return true on success.
- */
-inline bool
-ValueToUint64(JSContext *cx, JS::Value v, uint64_t *result)
-{
-    if (JSVAL_IS_INT(v)) {
-        uint32_t intval;
-        if (!JS_ValueToECMAUint32(cx, v, &intval))
-            return false;
-        *result = static_cast<uint64_t>(intval);
-    } else {
-        double doubleval;
-        if (!JS_ValueToNumber(cx, v, &doubleval))
-            return false;
-        // Be careful with non-finite doubles
-        if (NS_finite(doubleval))
-            // XXXbz this isn't quite right either; need to do the mod thing
-            *result = static_cast<uint64_t>(doubleval);
-        else
-            *result = 0;
-    }
-    return true;
-}
-
-/**
  * Given an arbitrary object, Unwrap will return the wrapped object if the
  * passed-in object is a wrapper that Unwrap knows about *and* the
  * currently running code has permission to access both the wrapper and
  * wrapped object.
  *
  * Since this is meant to be called from functions like
  * XPCWrappedNative::GetWrappedNativeOfJSObject, it does not set an
  * exception on |cx|.