tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CTypes.cpp (303569B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "ctypes/CTypes.h"
      8 #include "js/experimental/CTypes.h"  // JS::CTypesActivity{Callback,Type}, JS::InitCTypesClass, JS::SetCTypesActivityCallback, JS::SetCTypesCallbacks
      9 
     10 #include "mozilla/CheckedInt.h"
     11 #include "mozilla/MemoryReporting.h"
     12 #include "mozilla/Sprintf.h"
     13 #include "mozilla/TextUtils.h"
     14 #include "mozilla/Vector.h"
     15 #include "mozilla/WrappingOperations.h"
     16 
     17 #if defined(XP_UNIX)
     18 #  include <errno.h>
     19 #endif
     20 #if defined(XP_WIN)
     21 #  include <float.h>
     22 #endif
     23 #if defined(SOLARIS)
     24 #  include <ieeefp.h>
     25 #endif
     26 #include <iterator>
     27 #include <limits>
     28 #include <stdint.h>
     29 #include <sys/types.h>
     30 #include <type_traits>
     31 
     32 #include "jsapi.h"
     33 #include "jsexn.h"
     34 #include "jsnum.h"
     35 
     36 #include "ctypes/Library.h"
     37 #include "gc/GCContext.h"
     38 #include "jit/AtomicOperations.h"
     39 #include "js/Array.h"  // JS::GetArrayLength, JS::IsArrayObject, JS::NewArrayObject
     40 #include "js/ArrayBuffer.h"  // JS::{IsArrayBufferObject,GetArrayBufferData,GetArrayBuffer{ByteLength,Data}}
     41 #include "js/ArrayBufferMaybeShared.h"  // JS::IsImmutableArrayBufferMaybeShared
     42 #include "js/CallAndConstruct.h"        // JS::IsCallable, JS_CallFunctionValue
     43 #include "js/CharacterEncoding.h"
     44 #include "js/experimental/TypedData.h"  // JS_GetArrayBufferView{Type,Data}, JS_GetTypedArrayByteLength, JS_IsArrayBufferViewObject, JS_IsTypedArrayObject
     45 #include "js/friend/ErrorMessages.h"    // js::GetErrorMessage, JSMSG_*
     46 #include "js/GlobalObject.h"            // JS::CurrentGlobalOrNull
     47 #include "js/Object.h"  // JS::GetMaybePtrFromReservedSlot, JS::GetReservedSlot, JS::SetReservedSlot
     48 #include "js/PropertyAndElement.h"  // JS_DefineFunction, JS_DefineFunctions, JS_DefineProperties, JS_DefineProperty, JS_DefinePropertyById, JS_DefineUCProperty, JS_Enumerate, JS_GetElement, JS_GetProperty, JS_GetPropertyById
     49 #include "js/PropertySpec.h"
     50 #include "js/SharedArrayBuffer.h"  // JS::{GetSharedArrayBuffer{ByteLength,Data},IsSharedArrayBufferObject}
     51 #include "js/StableStringChars.h"
     52 #include "js/UniquePtr.h"
     53 #include "js/Utility.h"
     54 #include "js/Vector.h"
     55 #include "util/Text.h"
     56 #include "util/Unicode.h"
     57 #include "util/WindowsWrapper.h"
     58 #include "vm/JSContext.h"
     59 #include "vm/JSFunction.h"
     60 #include "vm/JSObject.h"
     61 
     62 #include "gc/GCContext-inl.h"
     63 #include "vm/JSObject-inl.h"
     64 
     65 using std::numeric_limits;
     66 
     67 using mozilla::CheckedInt;
     68 using mozilla::IsAsciiAlpha;
     69 using mozilla::IsAsciiDigit;
     70 
     71 using JS::AutoCheckCannotGC;
     72 using JS::AutoCTypesActivityCallback;
     73 using JS::AutoStableStringChars;
     74 using JS::CTypesActivityType;
     75 
     76 namespace js::ctypes {
     77 
     78 static bool HasUnpairedSurrogate(const char16_t* chars, size_t nchars,
     79                                 char16_t* unpaired) {
     80  for (const char16_t* end = chars + nchars; chars != end; chars++) {
     81    char16_t c = *chars;
     82    if (unicode::IsSurrogate(c)) {
     83      chars++;
     84      if (unicode::IsTrailSurrogate(c) || chars == end) {
     85        *unpaired = c;
     86        return true;
     87      }
     88      char16_t c2 = *chars;
     89      if (!unicode::IsTrailSurrogate(c2)) {
     90        *unpaired = c;
     91        return true;
     92      }
     93    }
     94  }
     95  return false;
     96 }
     97 
     98 bool ReportErrorIfUnpairedSurrogatePresent(JSContext* cx, JSLinearString* str) {
     99  if (str->hasLatin1Chars()) {
    100    return true;
    101  }
    102 
    103  char16_t unpaired;
    104  {
    105    JS::AutoCheckCannotGC nogc;
    106    if (!HasUnpairedSurrogate(str->twoByteChars(nogc), str->length(),
    107                              &unpaired)) {
    108      return true;
    109    }
    110  }
    111 
    112  char buffer[10];
    113  SprintfLiteral(buffer, "0x%x", unpaired);
    114  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    115                            JSMSG_BAD_SURROGATE_CHAR, buffer);
    116  return false;
    117 }
    118 
    119 /*******************************************************************************
    120 ** JSAPI function prototypes
    121 *******************************************************************************/
    122 
    123 // We use an enclosing struct here out of paranoia about the ability of gcc 4.4
    124 // (and maybe 4.5) to correctly compile this if it were a template function.
    125 // See also the comments in dom/workers/Events.cpp (and other adjacent files) by
    126 // the |struct Property| there.
    127 template <JS::IsAcceptableThis Test, JS::NativeImpl Impl>
    128 struct Property {
    129  static bool Fun(JSContext* cx, unsigned argc, JS::Value* vp) {
    130    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
    131    return JS::CallNonGenericMethod<Test, Impl>(cx, args);
    132  }
    133 };
    134 
    135 static bool ConstructAbstract(JSContext* cx, unsigned argc, Value* vp);
    136 
    137 namespace CType {
    138 static bool ConstructData(JSContext* cx, unsigned argc, Value* vp);
    139 static bool ConstructBasic(JSContext* cx, HandleObject obj,
    140                           const CallArgs& args);
    141 
    142 static void Trace(JSTracer* trc, JSObject* obj);
    143 static void Finalize(JS::GCContext* gcx, JSObject* obj);
    144 
    145 bool IsCType(HandleValue v);
    146 bool IsCTypeOrProto(HandleValue v);
    147 
    148 bool PrototypeGetter(JSContext* cx, const JS::CallArgs& args);
    149 bool NameGetter(JSContext* cx, const JS::CallArgs& args);
    150 bool SizeGetter(JSContext* cx, const JS::CallArgs& args);
    151 bool PtrGetter(JSContext* cx, const JS::CallArgs& args);
    152 
    153 static bool CreateArray(JSContext* cx, unsigned argc, Value* vp);
    154 static bool ToString(JSContext* cx, unsigned argc, Value* vp);
    155 static bool ToSource(JSContext* cx, unsigned argc, Value* vp);
    156 
    157 /*
    158 * Get the global "ctypes" object.
    159 *
    160 * |obj| must be a CType object.
    161 *
    162 * This function never returns nullptr.
    163 */
    164 static JSObject* GetGlobalCTypes(JSContext* cx, JSObject* obj);
    165 
    166 }  // namespace CType
    167 
    168 namespace ABI {
    169 bool IsABI(JSObject* obj);
    170 static bool ToSource(JSContext* cx, unsigned argc, Value* vp);
    171 }  // namespace ABI
    172 
    173 namespace PointerType {
    174 static bool Create(JSContext* cx, unsigned argc, Value* vp);
    175 static bool ConstructData(JSContext* cx, HandleObject obj,
    176                          const CallArgs& args);
    177 
    178 bool IsPointerType(HandleValue v);
    179 bool IsPointer(HandleValue v);
    180 
    181 bool TargetTypeGetter(JSContext* cx, const JS::CallArgs& args);
    182 bool ContentsGetter(JSContext* cx, const JS::CallArgs& args);
    183 bool ContentsSetter(JSContext* cx, const JS::CallArgs& args);
    184 
    185 static bool IsNull(JSContext* cx, unsigned argc, Value* vp);
    186 static bool Increment(JSContext* cx, unsigned argc, Value* vp);
    187 static bool Decrement(JSContext* cx, unsigned argc, Value* vp);
    188 // The following is not an instance function, since we don't want to expose
    189 // arbitrary pointer arithmetic at this moment.
    190 static bool OffsetBy(JSContext* cx, const CallArgs& args, int offset,
    191                     const char* name);
    192 }  // namespace PointerType
    193 
    194 namespace ArrayType {
    195 bool IsArrayType(HandleValue v);
    196 bool IsArrayOrArrayType(HandleValue v);
    197 
    198 static bool Create(JSContext* cx, unsigned argc, Value* vp);
    199 static bool ConstructData(JSContext* cx, HandleObject obj,
    200                          const CallArgs& args);
    201 
    202 bool ElementTypeGetter(JSContext* cx, const JS::CallArgs& args);
    203 bool LengthGetter(JSContext* cx, const JS::CallArgs& args);
    204 
    205 static bool Getter(JSContext* cx, HandleObject obj, HandleId idval,
    206                   MutableHandleValue vp, bool* handled);
    207 static bool Setter(JSContext* cx, HandleObject obj, HandleId idval,
    208                   HandleValue v, ObjectOpResult& result, bool* handled);
    209 static bool AddressOfElement(JSContext* cx, unsigned argc, Value* vp);
    210 }  // namespace ArrayType
    211 
    212 namespace StructType {
    213 bool IsStruct(HandleValue v);
    214 
    215 static bool Create(JSContext* cx, unsigned argc, Value* vp);
    216 static bool ConstructData(JSContext* cx, HandleObject obj,
    217                          const CallArgs& args);
    218 
    219 bool FieldsArrayGetter(JSContext* cx, const JS::CallArgs& args);
    220 
    221 enum { SLOT_FIELDNAME };
    222 
    223 static bool FieldGetter(JSContext* cx, unsigned argc, Value* vp);
    224 static bool FieldSetter(JSContext* cx, unsigned argc, Value* vp);
    225 static bool AddressOfField(JSContext* cx, unsigned argc, Value* vp);
    226 static bool Define(JSContext* cx, unsigned argc, Value* vp);
    227 }  // namespace StructType
    228 
    229 namespace FunctionType {
    230 static bool Create(JSContext* cx, unsigned argc, Value* vp);
    231 static bool ConstructData(JSContext* cx, HandleObject typeObj,
    232                          HandleObject dataObj, HandleObject fnObj,
    233                          HandleObject thisObj, HandleValue errVal);
    234 
    235 static bool Call(JSContext* cx, unsigned argc, Value* vp);
    236 
    237 bool IsFunctionType(HandleValue v);
    238 
    239 bool ArgTypesGetter(JSContext* cx, const JS::CallArgs& args);
    240 bool ReturnTypeGetter(JSContext* cx, const JS::CallArgs& args);
    241 bool ABIGetter(JSContext* cx, const JS::CallArgs& args);
    242 bool IsVariadicGetter(JSContext* cx, const JS::CallArgs& args);
    243 }  // namespace FunctionType
    244 
    245 namespace CClosure {
    246 static void Trace(JSTracer* trc, JSObject* obj);
    247 static void Finalize(JS::GCContext* gcx, JSObject* obj);
    248 
    249 // libffi callback
    250 static void ClosureStub(ffi_cif* cif, void* result, void** args,
    251                        void* userData);
    252 
    253 struct ArgClosure : public ScriptEnvironmentPreparer::Closure {
    254  ArgClosure(ffi_cif* cifArg, void* resultArg, void** argsArg,
    255             ClosureInfo* cinfoArg)
    256      : cif(cifArg), result(resultArg), args(argsArg), cinfo(cinfoArg) {}
    257 
    258  bool operator()(JSContext* cx) override;
    259 
    260  ffi_cif* cif;
    261  void* result;
    262  void** args;
    263  ClosureInfo* cinfo;
    264 };
    265 }  // namespace CClosure
    266 
    267 namespace CData {
    268 static void Finalize(JS::GCContext* gcx, JSObject* obj);
    269 
    270 bool ValueGetter(JSContext* cx, const JS::CallArgs& args);
    271 bool ValueSetter(JSContext* cx, const JS::CallArgs& args);
    272 
    273 static bool Address(JSContext* cx, unsigned argc, Value* vp);
    274 static bool ReadString(JSContext* cx, unsigned argc, Value* vp);
    275 static bool ReadStringReplaceMalformed(JSContext* cx, unsigned argc, Value* vp);
    276 static bool ReadTypedArray(JSContext* cx, unsigned argc, Value* vp);
    277 static bool ToSource(JSContext* cx, unsigned argc, Value* vp);
    278 static JSString* GetSourceString(JSContext* cx, HandleObject typeObj,
    279                                 void* data);
    280 
    281 bool ErrnoGetter(JSContext* cx, const JS::CallArgs& args);
    282 
    283 #if defined(XP_WIN)
    284 bool LastErrorGetter(JSContext* cx, const JS::CallArgs& args);
    285 #endif  // defined(XP_WIN)
    286 }  // namespace CData
    287 
    288 namespace CDataFinalizer {
    289 /*
    290 * Attach a C function as a finalizer to a JS object.
    291 *
    292 * This function is available from JS as |ctypes.withFinalizer|.
    293 *
    294 * JavaScript signature:
    295 * function(CData, CData):   CDataFinalizer
    296 *          value  finalizer finalizable
    297 *
    298 * Where |finalizer| is a one-argument function taking a value
    299 * with the same type as |value|.
    300 */
    301 static bool Construct(JSContext* cx, unsigned argc, Value* vp);
    302 
    303 /*
    304 * Private data held by |CDataFinalizer|.
    305 *
    306 * See also |enum CDataFinalizerSlot| for the slots of
    307 * |CDataFinalizer|.
    308 *
    309 * Note: the private data may be nullptr, if |dispose|, |forget| or the
    310 * finalizer has already been called.
    311 */
    312 struct Private {
    313  /*
    314   * The C data to pass to the code.
    315   * Finalization/|dispose|/|forget| release this memory.
    316   */
    317  void* cargs;
    318 
    319  /*
    320   * The total size of the buffer pointed by |cargs|
    321   */
    322  size_t cargs_size;
    323 
    324  /*
    325   * Low-level signature information.
    326   * Finalization/|dispose|/|forget| release this memory.
    327   */
    328  ffi_cif CIF;
    329 
    330  /*
    331   * The C function to invoke during finalization.
    332   * Do not deallocate this.
    333   */
    334  uintptr_t code;
    335 
    336  /*
    337   * A buffer for holding the return value.
    338   * Finalization/|dispose|/|forget| release this memory.
    339   */
    340  void* rvalue;
    341 };
    342 
    343 /*
    344 * Methods of instances of |CDataFinalizer|
    345 */
    346 namespace Methods {
    347 static bool Dispose(JSContext* cx, unsigned argc, Value* vp);
    348 static bool Forget(JSContext* cx, unsigned argc, Value* vp);
    349 static bool ReadString(JSContext* cx, unsigned argc, Value* vp);
    350 static bool ReadTypedArray(JSContext* cx, unsigned argc, Value* vp);
    351 static bool ToSource(JSContext* cx, unsigned argc, Value* vp);
    352 static bool ToString(JSContext* cx, unsigned argc, Value* vp);
    353 }  // namespace Methods
    354 
    355 /*
    356 * Utility functions
    357 *
    358 * @return true if |obj| is a CDataFinalizer, false otherwise.
    359 */
    360 static bool IsCDataFinalizer(JSObject* obj);
    361 
    362 /*
    363 * Clean up the finalization information of a CDataFinalizer.
    364 *
    365 * Used by |Finalize|, |Dispose| and |Forget|.
    366 *
    367 * @param p The private information of the CDataFinalizer. If nullptr,
    368 * this function does nothing.
    369 * @param obj Either nullptr, if the object should not be cleaned up (i.e.
    370 * during finalization) or a CDataFinalizer JSObject. Always use nullptr
    371 * if you are calling from a finalizer.
    372 */
    373 static void Cleanup(Private* p, JSObject* obj);
    374 
    375 /*
    376 * Perform the actual call to the finalizer code.
    377 */
    378 static void CallFinalizer(CDataFinalizer::Private* p, int* errnoStatus,
    379                          int32_t* lastErrorStatus);
    380 
    381 /*
    382 * Return the CType of a CDataFinalizer object, or nullptr if the object
    383 * has been cleaned-up already.
    384 */
    385 static JSObject* GetCType(JSContext* cx, JSObject* obj);
    386 
    387 /*
    388 * Perform finalization of a |CDataFinalizer|
    389 */
    390 static void Finalize(JS::GCContext* gcx, JSObject* obj);
    391 
    392 /*
    393 * Return the Value contained by this finalizer.
    394 *
    395 * Note that the Value is actually not recorded, but converted back from C.
    396 */
    397 static bool GetValue(JSContext* cx, JSObject* obj, MutableHandleValue result);
    398 
    399 }  // namespace CDataFinalizer
    400 
    401 // Int64Base provides functions common to Int64 and UInt64.
    402 namespace Int64Base {
    403 JSObject* Construct(JSContext* cx, HandleObject proto, uint64_t data,
    404                    bool isUnsigned);
    405 
    406 uint64_t GetInt(JSObject* obj);
    407 
    408 bool ToString(JSContext* cx, JSObject* obj, const CallArgs& args,
    409              bool isUnsigned);
    410 
    411 bool ToSource(JSContext* cx, JSObject* obj, const CallArgs& args,
    412              bool isUnsigned);
    413 
    414 static void Finalize(JS::GCContext* gcx, JSObject* obj);
    415 }  // namespace Int64Base
    416 
    417 namespace Int64 {
    418 static bool Construct(JSContext* cx, unsigned argc, Value* vp);
    419 
    420 static bool ToString(JSContext* cx, unsigned argc, Value* vp);
    421 static bool ToSource(JSContext* cx, unsigned argc, Value* vp);
    422 
    423 static bool Compare(JSContext* cx, unsigned argc, Value* vp);
    424 static bool Lo(JSContext* cx, unsigned argc, Value* vp);
    425 static bool Hi(JSContext* cx, unsigned argc, Value* vp);
    426 static bool Join(JSContext* cx, unsigned argc, Value* vp);
    427 }  // namespace Int64
    428 
    429 namespace UInt64 {
    430 static bool Construct(JSContext* cx, unsigned argc, Value* vp);
    431 
    432 static bool ToString(JSContext* cx, unsigned argc, Value* vp);
    433 static bool ToSource(JSContext* cx, unsigned argc, Value* vp);
    434 
    435 static bool Compare(JSContext* cx, unsigned argc, Value* vp);
    436 static bool Lo(JSContext* cx, unsigned argc, Value* vp);
    437 static bool Hi(JSContext* cx, unsigned argc, Value* vp);
    438 static bool Join(JSContext* cx, unsigned argc, Value* vp);
    439 }  // namespace UInt64
    440 
    441 /*******************************************************************************
    442 ** JSClass definitions and initialization functions
    443 *******************************************************************************/
    444 
    445 // Class representing the 'ctypes' object itself. This exists to contain the
    446 // JS::CTypesCallbacks set of function pointers.
    447 static const JSClass sCTypesGlobalClass = {
    448    "ctypes",
    449    JSCLASS_HAS_RESERVED_SLOTS(CTYPESGLOBAL_SLOTS),
    450 };
    451 
    452 static const JSClass sCABIClass = {
    453    "CABI",
    454    JSCLASS_HAS_RESERVED_SLOTS(CABI_SLOTS),
    455 };
    456 
    457 // Class representing ctypes.{C,Pointer,Array,Struct,Function}Type.prototype.
    458 // This exists to give said prototypes a class of "CType", and to provide
    459 // reserved slots for stashing various other prototype objects.
    460 static const JSClassOps sCTypeProtoClassOps = {
    461    nullptr,            // addProperty
    462    nullptr,            // delProperty
    463    nullptr,            // enumerate
    464    nullptr,            // newEnumerate
    465    nullptr,            // resolve
    466    nullptr,            // mayResolve
    467    nullptr,            // finalize
    468    ConstructAbstract,  // call
    469    ConstructAbstract,  // construct
    470    nullptr,            // trace
    471 };
    472 static const JSClass sCTypeProtoClass = {
    473    "CType",
    474    JSCLASS_HAS_RESERVED_SLOTS(CTYPEPROTO_SLOTS),
    475    &sCTypeProtoClassOps,
    476 };
    477 
    478 // Class representing ctypes.CData.prototype and the 'prototype' properties
    479 // of CTypes. This exists to give said prototypes a class of "CData".
    480 static const JSClass sCDataProtoClass = {
    481    "CData",
    482    0,
    483 };
    484 
    485 static const JSClassOps sCTypeClassOps = {
    486    nullptr,               // addProperty
    487    nullptr,               // delProperty
    488    nullptr,               // enumerate
    489    nullptr,               // newEnumerate
    490    nullptr,               // resolve
    491    nullptr,               // mayResolve
    492    CType::Finalize,       // finalize
    493    CType::ConstructData,  // call
    494    CType::ConstructData,  // construct
    495    CType::Trace,          // trace
    496 };
    497 static const JSClass sCTypeClass = {
    498    "CType",
    499    JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS) | JSCLASS_FOREGROUND_FINALIZE,
    500    &sCTypeClassOps,
    501 };
    502 
    503 static const JSClassOps sCDataClassOps = {
    504    nullptr,             // addProperty
    505    nullptr,             // delProperty
    506    nullptr,             // enumerate
    507    nullptr,             // newEnumerate
    508    nullptr,             // resolve
    509    nullptr,             // mayResolve
    510    CData::Finalize,     // finalize
    511    FunctionType::Call,  // call
    512    FunctionType::Call,  // construct
    513    nullptr,             // trace
    514 };
    515 static const JSClass sCDataClass = {
    516    "CData",
    517    JSCLASS_HAS_RESERVED_SLOTS(CDATA_SLOTS) | JSCLASS_FOREGROUND_FINALIZE,
    518    &sCDataClassOps,
    519 };
    520 
    521 static const JSClassOps sCClosureClassOps = {
    522    nullptr,             // addProperty
    523    nullptr,             // delProperty
    524    nullptr,             // enumerate
    525    nullptr,             // newEnumerate
    526    nullptr,             // resolve
    527    nullptr,             // mayResolve
    528    CClosure::Finalize,  // finalize
    529    nullptr,             // call
    530    nullptr,             // construct
    531    CClosure::Trace,     // trace
    532 };
    533 static const JSClass sCClosureClass = {
    534    "CClosure",
    535    JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS) | JSCLASS_FOREGROUND_FINALIZE,
    536    &sCClosureClassOps,
    537 };
    538 
    539 /*
    540 * Class representing the prototype of CDataFinalizer.
    541 */
    542 static const JSClass sCDataFinalizerProtoClass = {
    543    "CDataFinalizer",
    544    0,
    545 };
    546 
    547 /*
    548 * Class representing instances of CDataFinalizer.
    549 *
    550 * Instances of CDataFinalizer have both private data (with type
    551 * |CDataFinalizer::Private|) and slots (see |CDataFinalizerSlots|).
    552 */
    553 static const JSClassOps sCDataFinalizerClassOps = {
    554    nullptr,                   // addProperty
    555    nullptr,                   // delProperty
    556    nullptr,                   // enumerate
    557    nullptr,                   // newEnumerate
    558    nullptr,                   // resolve
    559    nullptr,                   // mayResolve
    560    CDataFinalizer::Finalize,  // finalize
    561    nullptr,                   // call
    562    nullptr,                   // construct
    563    nullptr,                   // trace
    564 };
    565 static const JSClass sCDataFinalizerClass = {
    566    "CDataFinalizer",
    567    JSCLASS_HAS_RESERVED_SLOTS(CDATAFINALIZER_SLOTS) |
    568        JSCLASS_FOREGROUND_FINALIZE,
    569    &sCDataFinalizerClassOps,
    570 };
    571 
    572 #define CTYPESFN_FLAGS (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
    573 
    574 #define CTYPESCTOR_FLAGS (CTYPESFN_FLAGS | JSFUN_CONSTRUCTOR)
    575 
    576 #define CTYPESACC_FLAGS (JSPROP_ENUMERATE | JSPROP_PERMANENT)
    577 
    578 #define CABIFN_FLAGS (JSPROP_READONLY | JSPROP_PERMANENT)
    579 
    580 #define CDATAFN_FLAGS (JSPROP_READONLY | JSPROP_PERMANENT)
    581 
    582 #define CDATAFINALIZERFN_FLAGS (JSPROP_READONLY | JSPROP_PERMANENT)
    583 
    584 static const JSPropertySpec sCTypeProps[] = {
    585    JS_PSG("name", (Property<CType::IsCType, CType::NameGetter>::Fun),
    586           CTYPESACC_FLAGS),
    587    JS_PSG("size", (Property<CType::IsCType, CType::SizeGetter>::Fun),
    588           CTYPESACC_FLAGS),
    589    JS_PSG("ptr", (Property<CType::IsCType, CType::PtrGetter>::Fun),
    590           CTYPESACC_FLAGS),
    591    JS_PSG("prototype",
    592           (Property<CType::IsCTypeOrProto, CType::PrototypeGetter>::Fun),
    593           CTYPESACC_FLAGS),
    594    JS_PS_END,
    595 };
    596 
    597 static const JSFunctionSpec sCTypeFunctions[] = {
    598    JS_FN("array", CType::CreateArray, 0, CTYPESFN_FLAGS),
    599    JS_FN("toString", CType::ToString, 0, CTYPESFN_FLAGS),
    600    JS_FN("toSource", CType::ToSource, 0, CTYPESFN_FLAGS),
    601    JS_FS_END,
    602 };
    603 
    604 static const JSFunctionSpec sCABIFunctions[] = {
    605    JS_FN("toSource", ABI::ToSource, 0, CABIFN_FLAGS),
    606    JS_FN("toString", ABI::ToSource, 0, CABIFN_FLAGS),
    607    JS_FS_END,
    608 };
    609 
    610 static const JSPropertySpec sCDataProps[] = {
    611    JS_PSGS("value", (Property<CData::IsCData, CData::ValueGetter>::Fun),
    612            (Property<CData::IsCData, CData::ValueSetter>::Fun),
    613            JSPROP_PERMANENT),
    614    JS_PS_END,
    615 };
    616 
    617 static const JSFunctionSpec sCDataFunctions[] = {
    618    JS_FN("address", CData::Address, 0, CDATAFN_FLAGS),
    619    JS_FN("readString", CData::ReadString, 0, CDATAFN_FLAGS),
    620    JS_FN("readStringReplaceMalformed", CData::ReadStringReplaceMalformed, 0,
    621          CDATAFN_FLAGS),
    622    JS_FN("readTypedArray", CData::ReadTypedArray, 0, CDATAFN_FLAGS),
    623    JS_FN("toSource", CData::ToSource, 0, CDATAFN_FLAGS),
    624    JS_FN("toString", CData::ToSource, 0, CDATAFN_FLAGS),
    625    JS_FS_END,
    626 };
    627 
    628 static const JSFunctionSpec sCDataFinalizerFunctions[] = {
    629    JS_FN("dispose", CDataFinalizer::Methods::Dispose, 0,
    630          CDATAFINALIZERFN_FLAGS),
    631    JS_FN("forget", CDataFinalizer::Methods::Forget, 0, CDATAFINALIZERFN_FLAGS),
    632    JS_FN("readString", CDataFinalizer::Methods::ReadString, 0,
    633          CDATAFINALIZERFN_FLAGS),
    634    JS_FN("readTypedArray", CDataFinalizer::Methods::ReadTypedArray, 0,
    635          CDATAFINALIZERFN_FLAGS),
    636    JS_FN("toString", CDataFinalizer::Methods::ToString, 0,
    637          CDATAFINALIZERFN_FLAGS),
    638    JS_FN("toSource", CDataFinalizer::Methods::ToSource, 0,
    639          CDATAFINALIZERFN_FLAGS),
    640    JS_FS_END,
    641 };
    642 
    643 static const JSFunctionSpec sPointerFunction =
    644    JS_FN("PointerType", PointerType::Create, 1, CTYPESCTOR_FLAGS);
    645 
    646 static const JSPropertySpec sPointerProps[] = {
    647    JS_PSG("targetType",
    648           (Property<PointerType::IsPointerType,
    649                     PointerType::TargetTypeGetter>::Fun),
    650           CTYPESACC_FLAGS),
    651    JS_PS_END,
    652 };
    653 
    654 static const JSFunctionSpec sPointerInstanceFunctions[] = {
    655    JS_FN("isNull", PointerType::IsNull, 0, CTYPESFN_FLAGS),
    656    JS_FN("increment", PointerType::Increment, 0, CTYPESFN_FLAGS),
    657    JS_FN("decrement", PointerType::Decrement, 0, CTYPESFN_FLAGS),
    658    JS_FS_END,
    659 };
    660 
    661 static const JSPropertySpec sPointerInstanceProps[] = {
    662    JS_PSGS(
    663        "contents",
    664        (Property<PointerType::IsPointer, PointerType::ContentsGetter>::Fun),
    665        (Property<PointerType::IsPointer, PointerType::ContentsSetter>::Fun),
    666        JSPROP_PERMANENT),
    667    JS_PS_END,
    668 };
    669 
    670 static const JSFunctionSpec sArrayFunction =
    671    JS_FN("ArrayType", ArrayType::Create, 1, CTYPESCTOR_FLAGS);
    672 
    673 static const JSPropertySpec sArrayProps[] = {
    674    JS_PSG(
    675        "elementType",
    676        (Property<ArrayType::IsArrayType, ArrayType::ElementTypeGetter>::Fun),
    677        CTYPESACC_FLAGS),
    678    JS_PSG(
    679        "length",
    680        (Property<ArrayType::IsArrayOrArrayType, ArrayType::LengthGetter>::Fun),
    681        CTYPESACC_FLAGS),
    682    JS_PS_END,
    683 };
    684 
    685 static const JSFunctionSpec sArrayInstanceFunctions[] = {
    686    JS_FN("addressOfElement", ArrayType::AddressOfElement, 1, CDATAFN_FLAGS),
    687    JS_FS_END,
    688 };
    689 
    690 static const JSPropertySpec sArrayInstanceProps[] = {
    691    JS_PSG(
    692        "length",
    693        (Property<ArrayType::IsArrayOrArrayType, ArrayType::LengthGetter>::Fun),
    694        JSPROP_PERMANENT),
    695    JS_PS_END,
    696 };
    697 
    698 static const JSFunctionSpec sStructFunction =
    699    JS_FN("StructType", StructType::Create, 2, CTYPESCTOR_FLAGS);
    700 
    701 static const JSPropertySpec sStructProps[] = {
    702    JS_PSG("fields",
    703           (Property<StructType::IsStruct, StructType::FieldsArrayGetter>::Fun),
    704           CTYPESACC_FLAGS),
    705    JS_PS_END,
    706 };
    707 
    708 static const JSFunctionSpec sStructFunctions[] = {
    709    JS_FN("define", StructType::Define, 1, CDATAFN_FLAGS),
    710    JS_FS_END,
    711 };
    712 
    713 static const JSFunctionSpec sStructInstanceFunctions[] = {
    714    JS_FN("addressOfField", StructType::AddressOfField, 1, CDATAFN_FLAGS),
    715    JS_FS_END,
    716 };
    717 
    718 static const JSFunctionSpec sFunctionFunction =
    719    JS_FN("FunctionType", FunctionType::Create, 2, CTYPESCTOR_FLAGS);
    720 
    721 static const JSPropertySpec sFunctionProps[] = {
    722    JS_PSG("argTypes",
    723           (Property<FunctionType::IsFunctionType,
    724                     FunctionType::ArgTypesGetter>::Fun),
    725           CTYPESACC_FLAGS),
    726    JS_PSG("returnType",
    727           (Property<FunctionType::IsFunctionType,
    728                     FunctionType::ReturnTypeGetter>::Fun),
    729           CTYPESACC_FLAGS),
    730    JS_PSG(
    731        "abi",
    732        (Property<FunctionType::IsFunctionType, FunctionType::ABIGetter>::Fun),
    733        CTYPESACC_FLAGS),
    734    JS_PSG("isVariadic",
    735           (Property<FunctionType::IsFunctionType,
    736                     FunctionType::IsVariadicGetter>::Fun),
    737           CTYPESACC_FLAGS),
    738    JS_PS_END,
    739 };
    740 
    741 static const JSFunctionSpec sFunctionInstanceFunctions[] = {
    742    JS_FN("call", js::fun_call, 1, CDATAFN_FLAGS),
    743    JS_FN("apply", js::fun_apply, 2, CDATAFN_FLAGS),
    744    JS_FS_END,
    745 };
    746 
    747 static const JSClass sInt64ProtoClass = {
    748    "Int64",
    749    0,
    750 };
    751 
    752 static const JSClass sUInt64ProtoClass = {
    753    "UInt64",
    754    0,
    755 };
    756 
    757 static const JSClassOps sInt64ClassOps = {
    758    nullptr,              // addProperty
    759    nullptr,              // delProperty
    760    nullptr,              // enumerate
    761    nullptr,              // newEnumerate
    762    nullptr,              // resolve
    763    nullptr,              // mayResolve
    764    Int64Base::Finalize,  // finalize
    765    nullptr,              // call
    766    nullptr,              // construct
    767    nullptr,              // trace
    768 };
    769 
    770 static const JSClass sInt64Class = {
    771    "Int64",
    772    JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS) | JSCLASS_FOREGROUND_FINALIZE,
    773    &sInt64ClassOps,
    774 };
    775 
    776 static const JSClass sUInt64Class = {
    777    "UInt64",
    778    JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS) | JSCLASS_FOREGROUND_FINALIZE,
    779    &sInt64ClassOps,
    780 };
    781 
    782 static const JSFunctionSpec sInt64StaticFunctions[] = {
    783    JS_FN("compare", Int64::Compare, 2, CTYPESFN_FLAGS),
    784    JS_FN("lo", Int64::Lo, 1, CTYPESFN_FLAGS),
    785    JS_FN("hi", Int64::Hi, 1, CTYPESFN_FLAGS),
    786    // "join" is defined specially; see InitInt64Class.
    787    JS_FS_END,
    788 };
    789 
    790 static const JSFunctionSpec sUInt64StaticFunctions[] = {
    791    JS_FN("compare", UInt64::Compare, 2, CTYPESFN_FLAGS),
    792    JS_FN("lo", UInt64::Lo, 1, CTYPESFN_FLAGS),
    793    JS_FN("hi", UInt64::Hi, 1, CTYPESFN_FLAGS),
    794    // "join" is defined specially; see InitInt64Class.
    795    JS_FS_END,
    796 };
    797 
    798 static const JSFunctionSpec sInt64Functions[] = {
    799    JS_FN("toString", Int64::ToString, 0, CTYPESFN_FLAGS),
    800    JS_FN("toSource", Int64::ToSource, 0, CTYPESFN_FLAGS),
    801    JS_FS_END,
    802 };
    803 
    804 static const JSFunctionSpec sUInt64Functions[] = {
    805    JS_FN("toString", UInt64::ToString, 0, CTYPESFN_FLAGS),
    806    JS_FN("toSource", UInt64::ToSource, 0, CTYPESFN_FLAGS),
    807    JS_FS_END,
    808 };
    809 
    810 static const JSPropertySpec sModuleProps[] = {
    811    JS_PSG("errno", (Property<IsCTypesGlobal, CData::ErrnoGetter>::Fun),
    812           JSPROP_PERMANENT),
    813 #if defined(XP_WIN)
    814    JS_PSG("winLastError",
    815           (Property<IsCTypesGlobal, CData::LastErrorGetter>::Fun),
    816           JSPROP_PERMANENT),
    817 #endif  // defined(XP_WIN)
    818    JS_PS_END,
    819 };
    820 
    821 static const JSFunctionSpec sModuleFunctions[] = {
    822    JS_FN("CDataFinalizer", CDataFinalizer::Construct, 2, CTYPESFN_FLAGS),
    823    JS_FN("open", Library::Open, 1, CTYPESFN_FLAGS),
    824    JS_FN("cast", CData::Cast, 2, CTYPESFN_FLAGS),
    825    JS_FN("getRuntime", CData::GetRuntime, 1, CTYPESFN_FLAGS),
    826    JS_FN("libraryName", Library::Name, 1, CTYPESFN_FLAGS),
    827    JS_FS_END,
    828 };
    829 
    830 // Wrapper for arrays, to intercept indexed gets/sets.
    831 class CDataArrayProxyHandler : public ForwardingProxyHandler {
    832 public:
    833  static const CDataArrayProxyHandler singleton;
    834  static const char family;
    835 
    836  constexpr CDataArrayProxyHandler() : ForwardingProxyHandler(&family) {}
    837 
    838  bool get(JSContext* cx, HandleObject proxy, HandleValue receiver, HandleId id,
    839           MutableHandleValue vp) const override;
    840  bool set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v,
    841           HandleValue receiver, ObjectOpResult& result) const override;
    842 };
    843 
    844 const CDataArrayProxyHandler CDataArrayProxyHandler::singleton;
    845 const char CDataArrayProxyHandler::family = 0;
    846 
    847 bool CDataArrayProxyHandler::get(JSContext* cx, HandleObject proxy,
    848                                 HandleValue receiver, HandleId id,
    849                                 MutableHandleValue vp) const {
    850  RootedObject target(cx, proxy->as<ProxyObject>().target());
    851  bool handled = false;
    852  if (!ArrayType::Getter(cx, target, id, vp, &handled)) {
    853    return false;
    854  }
    855  if (handled) {
    856    return true;
    857  }
    858  return ForwardingProxyHandler::get(cx, proxy, receiver, id, vp);
    859 }
    860 
    861 bool CDataArrayProxyHandler::set(JSContext* cx, HandleObject proxy, HandleId id,
    862                                 HandleValue v, HandleValue receiver,
    863                                 ObjectOpResult& result) const {
    864  RootedObject target(cx, proxy->as<ProxyObject>().target());
    865  bool handled = false;
    866  if (!ArrayType::Setter(cx, target, id, v, result, &handled)) {
    867    return false;
    868  }
    869  if (handled) {
    870    return true;
    871  }
    872  return ForwardingProxyHandler::set(cx, proxy, id, v, receiver, result);
    873 }
    874 
    875 static JSObject* MaybeUnwrapArrayWrapper(JSObject* obj) {
    876  if (obj->is<ProxyObject>() &&
    877      obj->as<ProxyObject>().handler() == &CDataArrayProxyHandler::singleton) {
    878    return obj->as<ProxyObject>().target();
    879  }
    880  return obj;
    881 }
    882 
    883 static MOZ_ALWAYS_INLINE JSString* NewUCString(JSContext* cx,
    884                                               const AutoStringChars&& from) {
    885  return JS_NewUCStringCopyN(cx, from.begin(), from.length());
    886 }
    887 
    888 /*
    889 * Return a size rounded up to a multiple of a power of two.
    890 *
    891 * Note: |align| must be a power of 2.
    892 */
    893 static MOZ_ALWAYS_INLINE size_t Align(size_t val, size_t align) {
    894  // Ensure that align is a power of two.
    895  MOZ_ASSERT(align != 0 && (align & (align - 1)) == 0);
    896  return ((val - 1) | (align - 1)) + 1;
    897 }
    898 
    899 static ABICode GetABICode(JSObject* obj) {
    900  // make sure we have an object representing a CABI class,
    901  // and extract the enumerated class type from the reserved slot.
    902  if (!obj->hasClass(&sCABIClass)) {
    903    return INVALID_ABI;
    904  }
    905 
    906  Value result = JS::GetReservedSlot(obj, SLOT_ABICODE);
    907  return ABICode(result.toInt32());
    908 }
    909 
    910 static const JSErrorFormatString ErrorFormatString[CTYPESERR_LIMIT] = {
    911 #define MSG_DEF(name, count, exception, format) \
    912  {#name, format, count, exception},
    913 #include "ctypes/ctypes.msg"
    914 #undef MSG_DEF
    915 };
    916 
    917 static const JSErrorFormatString* GetErrorMessage(void* userRef,
    918                                                  const unsigned errorNumber) {
    919  if (0 < errorNumber && errorNumber < CTYPESERR_LIMIT) {
    920    return &ErrorFormatString[errorNumber];
    921  }
    922  return nullptr;
    923 }
    924 
    925 static JS::UniqueChars EncodeUTF8(JSContext* cx, AutoString& str) {
    926  RootedString string(cx, NewUCString(cx, str.finish()));
    927  if (!string) {
    928    return nullptr;
    929  }
    930  return JS_EncodeStringToUTF8(cx, string);
    931 }
    932 
    933 static const char* CTypesToSourceForError(JSContext* cx, HandleValue val,
    934                                          JS::UniqueChars& bytes) {
    935  if (val.isObject()) {
    936    RootedObject obj(cx, &val.toObject());
    937    if (CType::IsCType(obj) || CData::IsCDataMaybeUnwrap(&obj)) {
    938      RootedValue v(cx, ObjectValue(*obj));
    939      RootedString str(cx, JS_ValueToSource(cx, v));
    940      bytes = JS_EncodeStringToUTF8(cx, str);
    941      return bytes.get();
    942    }
    943  }
    944  return ValueToSourceForError(cx, val, bytes);
    945 }
    946 
    947 static void BuildCStyleFunctionTypeSource(JSContext* cx, HandleObject typeObj,
    948                                          HandleString nameStr,
    949                                          unsigned ptrCount,
    950                                          AutoString& source);
    951 
    952 static void BuildCStyleTypeSource(JSContext* cx, JSObject* typeObj_,
    953                                  AutoString& source) {
    954  RootedObject typeObj(cx, typeObj_);
    955 
    956  MOZ_ASSERT(CType::IsCType(typeObj));
    957 
    958  switch (CType::GetTypeCode(typeObj)) {
    959 #define BUILD_SOURCE(name, fromType, ffiType) \
    960  case TYPE_##name:                           \
    961    AppendString(cx, source, #name);          \
    962    break;
    963    CTYPES_FOR_EACH_TYPE(BUILD_SOURCE)
    964 #undef BUILD_SOURCE
    965    case TYPE_void_t:
    966      AppendString(cx, source, "void");
    967      break;
    968    case TYPE_pointer: {
    969      unsigned ptrCount = 0;
    970      TypeCode type;
    971      RootedObject baseTypeObj(cx, typeObj);
    972      do {
    973        baseTypeObj = PointerType::GetBaseType(baseTypeObj);
    974        ptrCount++;
    975        type = CType::GetTypeCode(baseTypeObj);
    976      } while (type == TYPE_pointer || type == TYPE_array);
    977      if (type == TYPE_function) {
    978        BuildCStyleFunctionTypeSource(cx, baseTypeObj, nullptr, ptrCount,
    979                                      source);
    980        break;
    981      }
    982      BuildCStyleTypeSource(cx, baseTypeObj, source);
    983      AppendChars(source, '*', ptrCount);
    984      break;
    985    }
    986    case TYPE_struct: {
    987      RootedString name(cx, CType::GetName(cx, typeObj));
    988      AppendString(cx, source, "struct ");
    989      AppendString(cx, source, name);
    990      break;
    991    }
    992    case TYPE_function:
    993      BuildCStyleFunctionTypeSource(cx, typeObj, nullptr, 0, source);
    994      break;
    995    case TYPE_array:
    996      MOZ_CRASH("TYPE_array shouldn't appear in function type");
    997  }
    998 }
    999 
   1000 static void BuildCStyleFunctionTypeSource(JSContext* cx, HandleObject typeObj,
   1001                                          HandleString nameStr,
   1002                                          unsigned ptrCount,
   1003                                          AutoString& source) {
   1004  MOZ_ASSERT(CType::IsCType(typeObj));
   1005 
   1006  FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
   1007  BuildCStyleTypeSource(cx, fninfo->mReturnType, source);
   1008  AppendString(cx, source, " ");
   1009  if (nameStr) {
   1010    MOZ_ASSERT(ptrCount == 0);
   1011    AppendString(cx, source, nameStr);
   1012  } else if (ptrCount) {
   1013    AppendString(cx, source, "(");
   1014    AppendChars(source, '*', ptrCount);
   1015    AppendString(cx, source, ")");
   1016  }
   1017  AppendString(cx, source, "(");
   1018  if (fninfo->mArgTypes.length() > 0) {
   1019    for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
   1020      BuildCStyleTypeSource(cx, fninfo->mArgTypes[i], source);
   1021      if (i != fninfo->mArgTypes.length() - 1 || fninfo->mIsVariadic) {
   1022        AppendString(cx, source, ", ");
   1023      }
   1024    }
   1025    if (fninfo->mIsVariadic) {
   1026      AppendString(cx, source, "...");
   1027    }
   1028  }
   1029  AppendString(cx, source, ")");
   1030 }
   1031 
   1032 static void BuildFunctionTypeSource(JSContext* cx, HandleObject funObj,
   1033                                    AutoString& source) {
   1034  MOZ_ASSERT(CData::IsCData(funObj) || CType::IsCType(funObj));
   1035 
   1036  if (CData::IsCData(funObj)) {
   1037    Value slot = JS::GetReservedSlot(funObj, SLOT_REFERENT);
   1038    if (!slot.isUndefined() && Library::IsLibrary(&slot.toObject())) {
   1039      slot = JS::GetReservedSlot(funObj, SLOT_FUNNAME);
   1040      MOZ_ASSERT(!slot.isUndefined());
   1041      RootedObject typeObj(cx, CData::GetCType(funObj));
   1042      RootedObject baseTypeObj(cx, PointerType::GetBaseType(typeObj));
   1043      RootedString nameStr(cx, slot.toString());
   1044      BuildCStyleFunctionTypeSource(cx, baseTypeObj, nameStr, 0, source);
   1045      return;
   1046    }
   1047  }
   1048 
   1049  RootedValue funVal(cx, ObjectValue(*funObj));
   1050  RootedString funcStr(cx, JS_ValueToSource(cx, funVal));
   1051  if (!funcStr) {
   1052    JS_ClearPendingException(cx);
   1053    AppendString(cx, source, "<<error converting function to string>>");
   1054    return;
   1055  }
   1056  AppendString(cx, source, funcStr);
   1057 }
   1058 
   1059 enum class ConversionType {
   1060  Argument = 0,
   1061  Construct,
   1062  Finalizer,
   1063  Return,
   1064  Setter
   1065 };
   1066 
   1067 static void BuildConversionPosition(JSContext* cx, ConversionType convType,
   1068                                    HandleObject funObj, unsigned argIndex,
   1069                                    AutoString& source) {
   1070  switch (convType) {
   1071    case ConversionType::Argument: {
   1072      MOZ_ASSERT(funObj);
   1073 
   1074      AppendString(cx, source, " at argument ");
   1075      AppendUInt(source, argIndex + 1);
   1076      AppendString(cx, source, " of ");
   1077      BuildFunctionTypeSource(cx, funObj, source);
   1078      break;
   1079    }
   1080    case ConversionType::Finalizer:
   1081      MOZ_ASSERT(funObj);
   1082 
   1083      AppendString(cx, source, " at argument 1 of ");
   1084      BuildFunctionTypeSource(cx, funObj, source);
   1085      break;
   1086    case ConversionType::Return:
   1087      MOZ_ASSERT(funObj);
   1088 
   1089      AppendString(cx, source, " at the return value of ");
   1090      BuildFunctionTypeSource(cx, funObj, source);
   1091      break;
   1092    default:
   1093      MOZ_ASSERT(!funObj);
   1094      break;
   1095  }
   1096 }
   1097 
   1098 static JSLinearString* GetFieldName(HandleObject structObj,
   1099                                    unsigned fieldIndex) {
   1100  const FieldInfoHash* fields = StructType::GetFieldInfo(structObj);
   1101  for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
   1102    if (r.front().value().mIndex == fieldIndex) {
   1103      return (&r.front())->key();
   1104    }
   1105  }
   1106  return nullptr;
   1107 }
   1108 
   1109 static void BuildTypeSource(JSContext* cx, JSObject* typeObj_, bool makeShort,
   1110                            AutoString& result);
   1111 
   1112 static JS::UniqueChars TypeSourceForError(JSContext* cx, JSObject* typeObj) {
   1113  AutoString source;
   1114  BuildTypeSource(cx, typeObj, true, source);
   1115  if (!source) {
   1116    return nullptr;
   1117  }
   1118  return EncodeUTF8(cx, source);
   1119 }
   1120 
   1121 static JS::UniqueChars FunctionTypeSourceForError(JSContext* cx,
   1122                                                  HandleObject funObj) {
   1123  AutoString funSource;
   1124  BuildFunctionTypeSource(cx, funObj, funSource);
   1125  if (!funSource) {
   1126    return nullptr;
   1127  }
   1128  return EncodeUTF8(cx, funSource);
   1129 }
   1130 
   1131 static JS::UniqueChars ConversionPositionForError(JSContext* cx,
   1132                                                  ConversionType convType,
   1133                                                  HandleObject funObj,
   1134                                                  unsigned argIndex) {
   1135  AutoString posSource;
   1136  BuildConversionPosition(cx, convType, funObj, argIndex, posSource);
   1137  if (!posSource) {
   1138    return nullptr;
   1139  }
   1140  return EncodeUTF8(cx, posSource);
   1141 }
   1142 
   1143 class IndexCString final {
   1144  char indexStr[21];  // space for UINT64_MAX plus terminating null
   1145  static_assert(sizeof(size_t) <= 8, "index array too small");
   1146 
   1147 public:
   1148  explicit IndexCString(size_t index) {
   1149    SprintfLiteral(indexStr, "%zu", index);
   1150  }
   1151 
   1152  const char* get() const { return indexStr; }
   1153 };
   1154 
   1155 static bool ConvError(JSContext* cx, const char* expectedStr,
   1156                      HandleValue actual, ConversionType convType,
   1157                      HandleObject funObj = nullptr, unsigned argIndex = 0,
   1158                      HandleObject arrObj = nullptr, unsigned arrIndex = 0) {
   1159  JS::UniqueChars valBytes;
   1160  const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
   1161  if (!valStr) {
   1162    return false;
   1163  }
   1164 
   1165  if (arrObj) {
   1166    MOZ_ASSERT(CType::IsCType(arrObj));
   1167 
   1168    switch (CType::GetTypeCode(arrObj)) {
   1169      case TYPE_array: {
   1170        MOZ_ASSERT(!funObj);
   1171 
   1172        IndexCString indexStr(arrIndex);
   1173 
   1174        JS::UniqueChars arrStr = TypeSourceForError(cx, arrObj);
   1175        if (!arrStr) {
   1176          return false;
   1177        }
   1178 
   1179        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1180                                 CTYPESMSG_CONV_ERROR_ARRAY, valStr,
   1181                                 indexStr.get(), arrStr.get());
   1182        break;
   1183      }
   1184      case TYPE_struct: {
   1185        RootedString name(cx, GetFieldName(arrObj, arrIndex));
   1186        MOZ_ASSERT(name);
   1187        JS::UniqueChars nameStr = JS_EncodeStringToUTF8(cx, name);
   1188        if (!nameStr) {
   1189          return false;
   1190        }
   1191 
   1192        JS::UniqueChars structStr = TypeSourceForError(cx, arrObj);
   1193        if (!structStr) {
   1194          return false;
   1195        }
   1196 
   1197        JS::UniqueChars posStr;
   1198        if (funObj) {
   1199          posStr = ConversionPositionForError(cx, convType, funObj, argIndex);
   1200          if (!posStr) {
   1201            return false;
   1202          }
   1203        }
   1204 
   1205        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1206                                 CTYPESMSG_CONV_ERROR_STRUCT, valStr,
   1207                                 nameStr.get(), expectedStr, structStr.get(),
   1208                                 (posStr ? posStr.get() : ""));
   1209        break;
   1210      }
   1211      default:
   1212        MOZ_CRASH("invalid arrObj value");
   1213    }
   1214    return false;
   1215  }
   1216 
   1217  switch (convType) {
   1218    case ConversionType::Argument: {
   1219      MOZ_ASSERT(funObj);
   1220 
   1221      IndexCString indexStr(argIndex + 1);
   1222 
   1223      JS::UniqueChars funStr = FunctionTypeSourceForError(cx, funObj);
   1224      if (!funStr) {
   1225        return false;
   1226      }
   1227 
   1228      JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1229                               CTYPESMSG_CONV_ERROR_ARG, valStr, indexStr.get(),
   1230                               funStr.get());
   1231      break;
   1232    }
   1233    case ConversionType::Finalizer: {
   1234      MOZ_ASSERT(funObj);
   1235 
   1236      JS::UniqueChars funStr = FunctionTypeSourceForError(cx, funObj);
   1237      if (!funStr) {
   1238        return false;
   1239      }
   1240 
   1241      JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1242                               CTYPESMSG_CONV_ERROR_FIN, valStr, funStr.get());
   1243      break;
   1244    }
   1245    case ConversionType::Return: {
   1246      MOZ_ASSERT(funObj);
   1247 
   1248      JS::UniqueChars funStr = FunctionTypeSourceForError(cx, funObj);
   1249      if (!funStr) {
   1250        return false;
   1251      }
   1252 
   1253      JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1254                               CTYPESMSG_CONV_ERROR_RET, valStr, funStr.get());
   1255      break;
   1256    }
   1257    case ConversionType::Setter:
   1258    case ConversionType::Construct:
   1259      MOZ_ASSERT(!funObj);
   1260 
   1261      JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1262                               CTYPESMSG_CONV_ERROR_SET, valStr, expectedStr);
   1263      break;
   1264  }
   1265 
   1266  return false;
   1267 }
   1268 
   1269 static bool ConvError(JSContext* cx, HandleObject expectedType,
   1270                      HandleValue actual, ConversionType convType,
   1271                      HandleObject funObj = nullptr, unsigned argIndex = 0,
   1272                      HandleObject arrObj = nullptr, unsigned arrIndex = 0) {
   1273  MOZ_ASSERT(CType::IsCType(expectedType));
   1274 
   1275  JS::UniqueChars expectedStr = TypeSourceForError(cx, expectedType);
   1276  if (!expectedStr) {
   1277    return false;
   1278  }
   1279 
   1280  return ConvError(cx, expectedStr.get(), actual, convType, funObj, argIndex,
   1281                   arrObj, arrIndex);
   1282 }
   1283 
   1284 static bool ArgumentConvError(JSContext* cx, HandleValue actual,
   1285                              const char* funStr, unsigned argIndex) {
   1286  MOZ_ASSERT(JS::StringIsASCII(funStr));
   1287 
   1288  JS::UniqueChars valBytes;
   1289  const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
   1290  if (!valStr) {
   1291    return false;
   1292  }
   1293 
   1294  IndexCString indexStr(argIndex + 1);
   1295 
   1296  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1297                           CTYPESMSG_CONV_ERROR_ARG, valStr, indexStr.get(),
   1298                           funStr);
   1299  return false;
   1300 }
   1301 
   1302 static bool ArgumentLengthError(JSContext* cx, const char* fun,
   1303                                const char* count, const char* s) {
   1304  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1305                           CTYPESMSG_WRONG_ARG_LENGTH, fun, count, s);
   1306  return false;
   1307 }
   1308 
   1309 static bool ArrayLengthMismatch(JSContext* cx, size_t expectedLength,
   1310                                HandleObject arrObj, size_t actualLength,
   1311                                HandleValue actual, ConversionType convType) {
   1312  MOZ_ASSERT(arrObj && CType::IsCType(arrObj));
   1313 
   1314  JS::UniqueChars valBytes;
   1315  const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
   1316  if (!valStr) {
   1317    return false;
   1318  }
   1319 
   1320  IndexCString expectedLengthStr(expectedLength);
   1321  IndexCString actualLengthStr(actualLength);
   1322 
   1323  JS::UniqueChars arrStr = TypeSourceForError(cx, arrObj);
   1324  if (!arrStr) {
   1325    return false;
   1326  }
   1327 
   1328  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1329                           CTYPESMSG_ARRAY_MISMATCH, valStr, arrStr.get(),
   1330                           expectedLengthStr.get(), actualLengthStr.get());
   1331  return false;
   1332 }
   1333 
   1334 static bool ArrayLengthOverflow(JSContext* cx, unsigned expectedLength,
   1335                                HandleObject arrObj, unsigned actualLength,
   1336                                HandleValue actual, ConversionType convType) {
   1337  MOZ_ASSERT(arrObj && CType::IsCType(arrObj));
   1338 
   1339  JS::UniqueChars valBytes;
   1340  const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
   1341  if (!valStr) {
   1342    return false;
   1343  }
   1344 
   1345  IndexCString expectedLengthStr(expectedLength);
   1346  IndexCString actualLengthStr(actualLength);
   1347 
   1348  JS::UniqueChars arrStr = TypeSourceForError(cx, arrObj);
   1349  if (!arrStr) {
   1350    return false;
   1351  }
   1352 
   1353  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1354                           CTYPESMSG_ARRAY_OVERFLOW, valStr, arrStr.get(),
   1355                           expectedLengthStr.get(), actualLengthStr.get());
   1356  return false;
   1357 }
   1358 
   1359 static bool ArgumentRangeMismatch(JSContext* cx, const char* func,
   1360                                  const char* range) {
   1361  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1362                            CTYPESMSG_ARG_RANGE_MISMATCH, func, range);
   1363  return false;
   1364 }
   1365 
   1366 static bool ArgumentTypeMismatch(JSContext* cx, const char* arg,
   1367                                 const char* func, const char* type) {
   1368  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1369                            CTYPESMSG_ARG_TYPE_MISMATCH, arg, func, type);
   1370  return false;
   1371 }
   1372 
   1373 static bool CannotConstructError(JSContext* cx, const char* type) {
   1374  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1375                            CTYPESMSG_CANNOT_CONSTRUCT, type);
   1376  return false;
   1377 }
   1378 
   1379 static bool DuplicateFieldError(JSContext* cx, Handle<JSLinearString*> name) {
   1380  JS::UniqueChars nameStr = JS_EncodeStringToUTF8(cx, name);
   1381  if (!nameStr) {
   1382    return false;
   1383  }
   1384 
   1385  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1386                           CTYPESMSG_DUPLICATE_FIELD, nameStr.get());
   1387  return false;
   1388 }
   1389 
   1390 static bool EmptyFinalizerCallError(JSContext* cx, const char* funName) {
   1391  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1392                            CTYPESMSG_EMPTY_FIN_CALL, funName);
   1393  return false;
   1394 }
   1395 
   1396 static bool EmptyFinalizerError(JSContext* cx, ConversionType convType,
   1397                                HandleObject funObj = nullptr,
   1398                                unsigned argIndex = 0) {
   1399  JS::UniqueChars posStr;
   1400  if (funObj) {
   1401    posStr = ConversionPositionForError(cx, convType, funObj, argIndex);
   1402    if (!posStr) {
   1403      return false;
   1404    }
   1405  }
   1406 
   1407  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, CTYPESMSG_EMPTY_FIN,
   1408                           (posStr ? posStr.get() : ""));
   1409  return false;
   1410 }
   1411 
   1412 static bool FieldCountMismatch(JSContext* cx, unsigned expectedCount,
   1413                               HandleObject structObj, unsigned actualCount,
   1414                               HandleValue actual, ConversionType convType,
   1415                               HandleObject funObj = nullptr,
   1416                               unsigned argIndex = 0) {
   1417  MOZ_ASSERT(structObj && CType::IsCType(structObj));
   1418 
   1419  JS::UniqueChars valBytes;
   1420  const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
   1421  if (!valStr) {
   1422    return false;
   1423  }
   1424 
   1425  JS::UniqueChars structStr = TypeSourceForError(cx, structObj);
   1426  if (!structStr) {
   1427    return false;
   1428  }
   1429 
   1430  IndexCString expectedCountStr(expectedCount);
   1431  IndexCString actualCountStr(actualCount);
   1432 
   1433  JS::UniqueChars posStr;
   1434  if (funObj) {
   1435    posStr = ConversionPositionForError(cx, convType, funObj, argIndex);
   1436    if (!posStr) {
   1437      return false;
   1438    }
   1439  }
   1440 
   1441  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1442                           CTYPESMSG_FIELD_MISMATCH, valStr, structStr.get(),
   1443                           expectedCountStr.get(), actualCountStr.get(),
   1444                           (posStr ? posStr.get() : ""));
   1445  return false;
   1446 }
   1447 
   1448 static bool FieldDescriptorCountError(JSContext* cx, HandleValue typeVal,
   1449                                      size_t length) {
   1450  JS::UniqueChars valBytes;
   1451  const char* valStr = CTypesToSourceForError(cx, typeVal, valBytes);
   1452  if (!valStr) {
   1453    return false;
   1454  }
   1455 
   1456  IndexCString lengthStr(length);
   1457 
   1458  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1459                           CTYPESMSG_FIELD_DESC_COUNT, valStr, lengthStr.get());
   1460  return false;
   1461 }
   1462 
   1463 static bool FieldDescriptorNameError(JSContext* cx, HandleId id) {
   1464  JS::UniqueChars idBytes;
   1465  RootedValue idVal(cx, IdToValue(id));
   1466  const char* propStr = CTypesToSourceForError(cx, idVal, idBytes);
   1467  if (!propStr) {
   1468    return false;
   1469  }
   1470 
   1471  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1472                           CTYPESMSG_FIELD_DESC_NAME, propStr);
   1473  return false;
   1474 }
   1475 
   1476 static bool FieldDescriptorSizeError(JSContext* cx, HandleObject typeObj,
   1477                                     HandleId id) {
   1478  RootedValue typeVal(cx, ObjectValue(*typeObj));
   1479  JS::UniqueChars typeBytes;
   1480  const char* typeStr = CTypesToSourceForError(cx, typeVal, typeBytes);
   1481  if (!typeStr) {
   1482    return false;
   1483  }
   1484 
   1485  RootedString idStr(cx, IdToString(cx, id));
   1486  JS::UniqueChars propStr = JS_EncodeStringToUTF8(cx, idStr);
   1487  if (!propStr) {
   1488    return false;
   1489  }
   1490 
   1491  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1492                           CTYPESMSG_FIELD_DESC_SIZE, typeStr, propStr.get());
   1493  return false;
   1494 }
   1495 
   1496 static bool FieldDescriptorNameTypeError(JSContext* cx, HandleValue typeVal) {
   1497  JS::UniqueChars valBytes;
   1498  const char* valStr = CTypesToSourceForError(cx, typeVal, valBytes);
   1499  if (!valStr) {
   1500    return false;
   1501  }
   1502 
   1503  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1504                           CTYPESMSG_FIELD_DESC_NAMETYPE, valStr);
   1505  return false;
   1506 }
   1507 
   1508 static bool FieldDescriptorTypeError(JSContext* cx, HandleValue poroVal,
   1509                                     HandleId id) {
   1510  JS::UniqueChars typeBytes;
   1511  const char* typeStr = CTypesToSourceForError(cx, poroVal, typeBytes);
   1512  if (!typeStr) {
   1513    return false;
   1514  }
   1515 
   1516  RootedString idStr(cx, IdToString(cx, id));
   1517  JS::UniqueChars propStr = JS_EncodeStringToUTF8(cx, idStr);
   1518  if (!propStr) {
   1519    return false;
   1520  }
   1521 
   1522  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1523                           CTYPESMSG_FIELD_DESC_TYPE, typeStr, propStr.get());
   1524  return false;
   1525 }
   1526 
   1527 static bool FieldMissingError(JSContext* cx, JSObject* typeObj,
   1528                              JSLinearString* name_) {
   1529  JS::UniqueChars typeBytes;
   1530  RootedString name(cx, name_);
   1531  RootedValue typeVal(cx, ObjectValue(*typeObj));
   1532  const char* typeStr = CTypesToSourceForError(cx, typeVal, typeBytes);
   1533  if (!typeStr) {
   1534    return false;
   1535  }
   1536 
   1537  JS::UniqueChars nameStr = JS_EncodeStringToUTF8(cx, name);
   1538  if (!nameStr) {
   1539    return false;
   1540  }
   1541 
   1542  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1543                           CTYPESMSG_FIELD_MISSING, typeStr, nameStr.get());
   1544  return false;
   1545 }
   1546 
   1547 static bool FinalizerSizeError(JSContext* cx, HandleObject funObj,
   1548                               HandleValue actual) {
   1549  MOZ_ASSERT(CType::IsCType(funObj));
   1550 
   1551  JS::UniqueChars valBytes;
   1552  const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
   1553  if (!valStr) {
   1554    return false;
   1555  }
   1556 
   1557  JS::UniqueChars funStr = FunctionTypeSourceForError(cx, funObj);
   1558  if (!funStr) {
   1559    return false;
   1560  }
   1561 
   1562  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1563                           CTYPESMSG_FIN_SIZE_ERROR, funStr.get(), valStr);
   1564  return false;
   1565 }
   1566 
   1567 static bool FunctionArgumentLengthMismatch(
   1568    JSContext* cx, unsigned expectedCount, unsigned actualCount,
   1569    HandleObject funObj, HandleObject typeObj, bool isVariadic) {
   1570  JS::UniqueChars funStr;
   1571  Value slot = JS::GetReservedSlot(funObj, SLOT_REFERENT);
   1572  if (!slot.isUndefined() && Library::IsLibrary(&slot.toObject())) {
   1573    funStr = FunctionTypeSourceForError(cx, funObj);
   1574  } else {
   1575    funStr = FunctionTypeSourceForError(cx, typeObj);
   1576  }
   1577  if (!funStr) {
   1578    return false;
   1579  }
   1580 
   1581  IndexCString expectedCountStr(expectedCount);
   1582  IndexCString actualCountStr(actualCount);
   1583 
   1584  const char* variadicStr = isVariadic ? " or more" : "";
   1585 
   1586  JS_ReportErrorNumberUTF8(
   1587      cx, GetErrorMessage, nullptr, CTYPESMSG_ARG_COUNT_MISMATCH, funStr.get(),
   1588      expectedCountStr.get(), variadicStr, actualCountStr.get());
   1589  return false;
   1590 }
   1591 
   1592 static bool FunctionArgumentTypeError(JSContext* cx, uint32_t index,
   1593                                      HandleValue typeVal, const char* reason) {
   1594  MOZ_ASSERT(JS::StringIsASCII(reason));
   1595 
   1596  JS::UniqueChars valBytes;
   1597  const char* valStr = CTypesToSourceForError(cx, typeVal, valBytes);
   1598  if (!valStr) {
   1599    return false;
   1600  }
   1601 
   1602  IndexCString indexStr(index + 1);
   1603 
   1604  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1605                           CTYPESMSG_ARG_TYPE_ERROR, indexStr.get(), reason,
   1606                           valStr);
   1607  return false;
   1608 }
   1609 
   1610 static bool FunctionReturnTypeError(JSContext* cx, HandleValue type,
   1611                                    const char* reason) {
   1612  MOZ_ASSERT(JS::StringIsASCII(reason));
   1613 
   1614  JS::UniqueChars valBytes;
   1615  const char* valStr = CTypesToSourceForError(cx, type, valBytes);
   1616  if (!valStr) {
   1617    return false;
   1618  }
   1619 
   1620  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1621                           CTYPESMSG_RET_TYPE_ERROR, reason, valStr);
   1622  return false;
   1623 }
   1624 
   1625 static bool IncompatibleCallee(JSContext* cx, const char* funName,
   1626                               HandleObject actualObj) {
   1627  MOZ_ASSERT(JS::StringIsASCII(funName));
   1628 
   1629  JS::UniqueChars valBytes;
   1630  RootedValue val(cx, ObjectValue(*actualObj));
   1631  const char* valStr = CTypesToSourceForError(cx, val, valBytes);
   1632  if (!valStr) {
   1633    return false;
   1634  }
   1635 
   1636  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1637                           CTYPESMSG_INCOMPATIBLE_CALLEE, funName, valStr);
   1638  return false;
   1639 }
   1640 
   1641 static bool IncompatibleThisProto(JSContext* cx, const char* funName,
   1642                                  const char* actualType) {
   1643  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1644                            CTYPESMSG_INCOMPATIBLE_THIS, funName, actualType);
   1645  return false;
   1646 }
   1647 
   1648 static bool IncompatibleThisProto(JSContext* cx, const char* funName,
   1649                                  HandleValue actualVal) {
   1650  MOZ_ASSERT(JS::StringIsASCII(funName));
   1651 
   1652  JS::UniqueChars valBytes;
   1653  const char* valStr = CTypesToSourceForError(cx, actualVal, valBytes);
   1654  if (!valStr) {
   1655    return false;
   1656  }
   1657 
   1658  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1659                           CTYPESMSG_INCOMPATIBLE_THIS_VAL, funName,
   1660                           "incompatible object", valStr);
   1661  return false;
   1662 }
   1663 
   1664 static bool IncompatibleThisType(JSContext* cx, const char* funName,
   1665                                 const char* actualType) {
   1666  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1667                            CTYPESMSG_INCOMPATIBLE_THIS_TYPE, funName,
   1668                            actualType);
   1669  return false;
   1670 }
   1671 
   1672 static bool IncompatibleThisType(JSContext* cx, const char* funName,
   1673                                 const char* actualType,
   1674                                 HandleValue actualVal) {
   1675  MOZ_ASSERT(JS::StringIsASCII(funName));
   1676  MOZ_ASSERT(JS::StringIsASCII(actualType));
   1677 
   1678  JS::UniqueChars valBytes;
   1679  const char* valStr = CTypesToSourceForError(cx, actualVal, valBytes);
   1680  if (!valStr) {
   1681    return false;
   1682  }
   1683 
   1684  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1685                           CTYPESMSG_INCOMPATIBLE_THIS_VAL, funName, actualType,
   1686                           valStr);
   1687  return false;
   1688 }
   1689 
   1690 static bool InvalidIndexError(JSContext* cx, HandleValue val) {
   1691  JS::UniqueChars idBytes;
   1692  const char* indexStr = CTypesToSourceForError(cx, val, idBytes);
   1693  if (!indexStr) {
   1694    return false;
   1695  }
   1696 
   1697  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1698                           CTYPESMSG_INVALID_INDEX, indexStr);
   1699  return false;
   1700 }
   1701 
   1702 static bool InvalidIndexError(JSContext* cx, HandleId id) {
   1703  RootedValue idVal(cx, IdToValue(id));
   1704  return InvalidIndexError(cx, idVal);
   1705 }
   1706 
   1707 static bool InvalidIndexRangeError(JSContext* cx, size_t index, size_t length) {
   1708  IndexCString indexStr(index);
   1709  IndexCString lengthStr(length);
   1710 
   1711  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1712                            CTYPESMSG_INVALID_RANGE, indexStr.get(),
   1713                            lengthStr.get());
   1714  return false;
   1715 }
   1716 
   1717 static bool NonPrimitiveError(JSContext* cx, HandleObject typeObj) {
   1718  MOZ_ASSERT(CType::IsCType(typeObj));
   1719 
   1720  JS::UniqueChars typeStr = TypeSourceForError(cx, typeObj);
   1721  if (!typeStr) {
   1722    return false;
   1723  }
   1724 
   1725  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1726                           CTYPESMSG_NON_PRIMITIVE, typeStr.get());
   1727  return false;
   1728 }
   1729 
   1730 static bool NonStringBaseError(JSContext* cx, HandleValue thisVal) {
   1731  JS::UniqueChars valBytes;
   1732  const char* valStr = CTypesToSourceForError(cx, thisVal, valBytes);
   1733  if (!valStr) {
   1734    return false;
   1735  }
   1736 
   1737  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1738                           CTYPESMSG_NON_STRING_BASE, valStr);
   1739  return false;
   1740 }
   1741 
   1742 static bool NonTypedArrayBaseError(JSContext* cx, HandleValue thisVal) {
   1743  JS::UniqueChars valBytes;
   1744  const char* valStr = CTypesToSourceForError(cx, thisVal, valBytes);
   1745  if (!valStr) {
   1746    return false;
   1747  }
   1748 
   1749  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1750                           CTYPESMSG_NON_TYPEDARRAY_BASE, valStr);
   1751  return false;
   1752 }
   1753 
   1754 static bool NullPointerError(JSContext* cx, const char* action,
   1755                             HandleObject obj) {
   1756  MOZ_ASSERT(JS::StringIsASCII(action));
   1757 
   1758  JS::UniqueChars valBytes;
   1759  RootedValue val(cx, ObjectValue(*obj));
   1760  const char* valStr = CTypesToSourceForError(cx, val, valBytes);
   1761  if (!valStr) {
   1762    return false;
   1763  }
   1764 
   1765  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, CTYPESMSG_NULL_POINTER,
   1766                           action, valStr);
   1767  return false;
   1768 }
   1769 
   1770 static bool PropNameNonStringError(JSContext* cx, HandleId id,
   1771                                   HandleValue actual, ConversionType convType,
   1772                                   HandleObject funObj = nullptr,
   1773                                   unsigned argIndex = 0) {
   1774  JS::UniqueChars valBytes;
   1775  const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
   1776  if (!valStr) {
   1777    return false;
   1778  }
   1779 
   1780  JS::UniqueChars idBytes;
   1781  RootedValue idVal(cx, IdToValue(id));
   1782  const char* propStr = CTypesToSourceForError(cx, idVal, idBytes);
   1783  if (!propStr) {
   1784    return false;
   1785  }
   1786 
   1787  JS::UniqueChars posStr;
   1788  if (funObj) {
   1789    posStr = ConversionPositionForError(cx, convType, funObj, argIndex);
   1790    if (!posStr) {
   1791      return false;
   1792    }
   1793  }
   1794 
   1795  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1796                           CTYPESMSG_PROP_NONSTRING, propStr, valStr,
   1797                           (posStr ? posStr.get() : ""));
   1798  return false;
   1799 }
   1800 
   1801 static bool SizeOverflow(JSContext* cx, const char* name, const char* limit) {
   1802  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1803                            CTYPESMSG_SIZE_OVERFLOW, name, limit);
   1804  return false;
   1805 }
   1806 
   1807 static bool TypeError(JSContext* cx, const char* expected, HandleValue actual) {
   1808  MOZ_ASSERT(JS::StringIsASCII(expected));
   1809 
   1810  JS::UniqueChars bytes;
   1811  const char* src = CTypesToSourceForError(cx, actual, bytes);
   1812  if (!src) {
   1813    return false;
   1814  }
   1815 
   1816  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, CTYPESMSG_TYPE_ERROR,
   1817                           expected, src);
   1818  return false;
   1819 }
   1820 
   1821 static bool TypeOverflow(JSContext* cx, const char* expected,
   1822                         HandleValue actual) {
   1823  MOZ_ASSERT(JS::StringIsASCII(expected));
   1824 
   1825  JS::UniqueChars valBytes;
   1826  const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
   1827  if (!valStr) {
   1828    return false;
   1829  }
   1830 
   1831  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1832                           CTYPESMSG_TYPE_OVERFLOW, valStr, expected);
   1833  return false;
   1834 }
   1835 
   1836 static bool UndefinedSizeCastError(JSContext* cx, HandleObject targetTypeObj) {
   1837  JS::UniqueChars targetTypeStr = TypeSourceForError(cx, targetTypeObj);
   1838  if (!targetTypeStr) {
   1839    return false;
   1840  }
   1841 
   1842  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1843                           CTYPESMSG_UNDEFINED_SIZE_CAST, targetTypeStr.get());
   1844  return false;
   1845 }
   1846 
   1847 static bool SizeMismatchCastError(JSContext* cx, HandleObject sourceTypeObj,
   1848                                  HandleObject targetTypeObj, size_t sourceSize,
   1849                                  size_t targetSize) {
   1850  JS::UniqueChars sourceTypeStr = TypeSourceForError(cx, sourceTypeObj);
   1851  if (!sourceTypeStr) {
   1852    return false;
   1853  }
   1854 
   1855  JS::UniqueChars targetTypeStr = TypeSourceForError(cx, targetTypeObj);
   1856  if (!targetTypeStr) {
   1857    return false;
   1858  }
   1859 
   1860  IndexCString sourceSizeStr(sourceSize);
   1861  IndexCString targetSizeStr(targetSize);
   1862 
   1863  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1864                           CTYPESMSG_SIZE_MISMATCH_CAST, targetTypeStr.get(),
   1865                           sourceTypeStr.get(), targetSizeStr.get(),
   1866                           sourceSizeStr.get());
   1867  return false;
   1868 }
   1869 
   1870 static bool UndefinedSizePointerError(JSContext* cx, const char* action,
   1871                                      HandleObject obj) {
   1872  MOZ_ASSERT(JS::StringIsASCII(action));
   1873 
   1874  JS::UniqueChars valBytes;
   1875  RootedValue val(cx, ObjectValue(*obj));
   1876  const char* valStr = CTypesToSourceForError(cx, val, valBytes);
   1877  if (!valStr) {
   1878    return false;
   1879  }
   1880 
   1881  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1882                           CTYPESMSG_UNDEFINED_SIZE, action, valStr);
   1883  return false;
   1884 }
   1885 
   1886 static bool VariadicArgumentTypeError(JSContext* cx, uint32_t index,
   1887                                      HandleValue actual) {
   1888  JS::UniqueChars valBytes;
   1889  const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
   1890  if (!valStr) {
   1891    return false;
   1892  }
   1893 
   1894  IndexCString indexStr(index + 1);
   1895 
   1896  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1897                           CTYPESMSG_VARG_TYPE_ERROR, indexStr.get(), valStr);
   1898  return false;
   1899 }
   1900 
   1901 [[nodiscard]] JSObject* GetThisObject(JSContext* cx, const CallArgs& args,
   1902                                      const char* msg) {
   1903  if (!args.thisv().isObject()) {
   1904    IncompatibleThisProto(cx, msg, args.thisv());
   1905    return nullptr;
   1906  }
   1907 
   1908  return &args.thisv().toObject();
   1909 }
   1910 
   1911 static bool DefineToStringTag(JSContext* cx, HandleObject obj,
   1912                              const char* toStringTag) {
   1913  RootedString toStringTagStr(cx, JS_NewStringCopyZ(cx, toStringTag));
   1914  if (!toStringTagStr) {
   1915    return false;
   1916  }
   1917 
   1918  RootedId toStringTagId(
   1919      cx, JS::GetWellKnownSymbolKey(cx, JS::SymbolCode::toStringTag));
   1920  return JS_DefinePropertyById(cx, obj, toStringTagId, toStringTagStr,
   1921                               JSPROP_READONLY);
   1922 }
   1923 
   1924 static JSObject* InitCTypeClass(JSContext* cx, HandleObject ctypesObj) {
   1925  JSFunction* fun = JS_DefineFunction(cx, ctypesObj, "CType", ConstructAbstract,
   1926                                      0, CTYPESCTOR_FLAGS);
   1927  if (!fun) {
   1928    return nullptr;
   1929  }
   1930 
   1931  RootedObject ctor(cx, JS_GetFunctionObject(fun));
   1932  RootedObject fnproto(cx);
   1933  if (!JS_GetPrototype(cx, ctor, &fnproto)) {
   1934    return nullptr;
   1935  }
   1936  MOZ_ASSERT(ctor);
   1937  MOZ_ASSERT(fnproto);
   1938 
   1939  // Set up ctypes.CType.prototype.
   1940  RootedObject prototype(
   1941      cx, JS_NewObjectWithGivenProto(cx, &sCTypeProtoClass, fnproto));
   1942  if (!prototype) {
   1943    return nullptr;
   1944  }
   1945 
   1946  if (!JS_DefineProperty(cx, ctor, "prototype", prototype,
   1947                         JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
   1948    return nullptr;
   1949 
   1950  if (!JS_DefineProperty(cx, prototype, "constructor", ctor,
   1951                         JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
   1952    return nullptr;
   1953 
   1954  // Define properties and functions common to all CTypes.
   1955  if (!JS_DefineProperties(cx, prototype, sCTypeProps) ||
   1956      !JS_DefineFunctions(cx, prototype, sCTypeFunctions))
   1957    return nullptr;
   1958 
   1959  if (!DefineToStringTag(cx, prototype, "CType")) {
   1960    return nullptr;
   1961  }
   1962 
   1963  if (!JS_FreezeObject(cx, ctor) || !JS_FreezeObject(cx, prototype)) {
   1964    return nullptr;
   1965  }
   1966 
   1967  return prototype;
   1968 }
   1969 
   1970 static JSObject* InitABIClass(JSContext* cx) {
   1971  RootedObject obj(cx, JS_NewPlainObject(cx));
   1972 
   1973  if (!obj) {
   1974    return nullptr;
   1975  }
   1976 
   1977  if (!JS_DefineFunctions(cx, obj, sCABIFunctions)) {
   1978    return nullptr;
   1979  }
   1980 
   1981  if (!DefineToStringTag(cx, obj, "CABI")) {
   1982    return nullptr;
   1983  }
   1984 
   1985  return obj;
   1986 }
   1987 
   1988 static JSObject* InitCDataClass(JSContext* cx, HandleObject parent,
   1989                                HandleObject CTypeProto) {
   1990  JSFunction* fun = JS_DefineFunction(cx, parent, "CData", ConstructAbstract, 0,
   1991                                      CTYPESCTOR_FLAGS);
   1992  if (!fun) {
   1993    return nullptr;
   1994  }
   1995 
   1996  RootedObject ctor(cx, JS_GetFunctionObject(fun));
   1997  MOZ_ASSERT(ctor);
   1998 
   1999  // Set up ctypes.CData.__proto__ === ctypes.CType.prototype.
   2000  // (Note that 'ctypes.CData instanceof Function' is still true, thanks to the
   2001  // prototype chain.)
   2002  if (!JS_SetPrototype(cx, ctor, CTypeProto)) {
   2003    return nullptr;
   2004  }
   2005 
   2006  // Set up ctypes.CData.prototype.
   2007  RootedObject prototype(cx, JS_NewObject(cx, &sCDataProtoClass));
   2008  if (!prototype) {
   2009    return nullptr;
   2010  }
   2011 
   2012  if (!JS_DefineProperty(cx, ctor, "prototype", prototype,
   2013                         JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
   2014    return nullptr;
   2015 
   2016  if (!JS_DefineProperty(cx, prototype, "constructor", ctor,
   2017                         JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
   2018    return nullptr;
   2019 
   2020  // Define properties and functions common to all CDatas.
   2021  if (!JS_DefineProperties(cx, prototype, sCDataProps) ||
   2022      !JS_DefineFunctions(cx, prototype, sCDataFunctions))
   2023    return nullptr;
   2024 
   2025  if (!DefineToStringTag(cx, prototype, "CData")) {
   2026    return nullptr;
   2027  }
   2028 
   2029  if (  // !JS_FreezeObject(cx, prototype) || // XXX fixme - see bug 541212!
   2030      !JS_FreezeObject(cx, ctor))
   2031    return nullptr;
   2032 
   2033  return prototype;
   2034 }
   2035 
   2036 static bool DefineABIConstant(JSContext* cx, HandleObject ctypesObj,
   2037                              const char* name, ABICode code,
   2038                              HandleObject prototype) {
   2039  RootedObject obj(cx, JS_NewObjectWithGivenProto(cx, &sCABIClass, prototype));
   2040  if (!obj) {
   2041    return false;
   2042  }
   2043  JS_SetReservedSlot(obj, SLOT_ABICODE, Int32Value(code));
   2044 
   2045  if (!JS_FreezeObject(cx, obj)) {
   2046    return false;
   2047  }
   2048 
   2049  return JS_DefineProperty(
   2050      cx, ctypesObj, name, obj,
   2051      JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
   2052 }
   2053 
   2054 // Set up a single type constructor for
   2055 // ctypes.{Pointer,Array,Struct,Function}Type.
   2056 static bool InitTypeConstructor(
   2057    JSContext* cx, HandleObject parent, HandleObject CTypeProto,
   2058    HandleObject CDataProto, const JSFunctionSpec spec,
   2059    const JSFunctionSpec* fns, const JSPropertySpec* props,
   2060    const JSFunctionSpec* instanceFns, const JSPropertySpec* instanceProps,
   2061    MutableHandleObject typeProto, MutableHandleObject dataProto) {
   2062  JSFunction* fun = js::DefineFunctionWithReserved(
   2063      cx, parent, spec.name.string(), spec.call.op, spec.nargs, spec.flags);
   2064  if (!fun) {
   2065    return false;
   2066  }
   2067 
   2068  RootedObject obj(cx, JS_GetFunctionObject(fun));
   2069  if (!obj) {
   2070    return false;
   2071  }
   2072 
   2073  // Set up the .prototype and .prototype.constructor properties.
   2074  typeProto.set(JS_NewObjectWithGivenProto(cx, &sCTypeProtoClass, CTypeProto));
   2075  if (!typeProto) {
   2076    return false;
   2077  }
   2078 
   2079  // Define property before proceeding, for GC safety.
   2080  if (!JS_DefineProperty(cx, obj, "prototype", typeProto,
   2081                         JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
   2082    return false;
   2083 
   2084  if (fns && !JS_DefineFunctions(cx, typeProto, fns)) {
   2085    return false;
   2086  }
   2087 
   2088  if (!JS_DefineProperties(cx, typeProto, props)) {
   2089    return false;
   2090  }
   2091 
   2092  if (!JS_DefineProperty(cx, typeProto, "constructor", obj,
   2093                         JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
   2094    return false;
   2095 
   2096  // Stash ctypes.{Pointer,Array,Struct}Type.prototype on a reserved slot of
   2097  // the type constructor, for faster lookup.
   2098  js::SetFunctionNativeReserved(obj, SLOT_FN_CTORPROTO,
   2099                                ObjectValue(*typeProto));
   2100 
   2101  // Create an object to serve as the common ancestor for all CData objects
   2102  // created from the given type constructor. This has ctypes.CData.prototype
   2103  // as its prototype, such that it inherits the properties and functions
   2104  // common to all CDatas.
   2105  dataProto.set(JS_NewObjectWithGivenProto(cx, &sCDataProtoClass, CDataProto));
   2106  if (!dataProto) {
   2107    return false;
   2108  }
   2109 
   2110  // Define functions and properties on the 'dataProto' object that are common
   2111  // to all CData objects created from this type constructor. (These will
   2112  // become functions and properties on CData objects created from this type.)
   2113  if (instanceFns && !JS_DefineFunctions(cx, dataProto, instanceFns)) {
   2114    return false;
   2115  }
   2116 
   2117  if (instanceProps && !JS_DefineProperties(cx, dataProto, instanceProps)) {
   2118    return false;
   2119  }
   2120 
   2121  // Link the type prototype to the data prototype.
   2122  JS_SetReservedSlot(typeProto, SLOT_OURDATAPROTO, ObjectValue(*dataProto));
   2123 
   2124  if (!JS_FreezeObject(cx, obj) ||
   2125      // !JS_FreezeObject(cx, dataProto) || // XXX fixme - see bug 541212!
   2126      !JS_FreezeObject(cx, typeProto))
   2127    return false;
   2128 
   2129  return true;
   2130 }
   2131 
   2132 static JSObject* InitInt64Class(JSContext* cx, HandleObject parent,
   2133                                const JSClass* clasp, JSNative construct,
   2134                                const JSFunctionSpec* fs,
   2135                                const JSFunctionSpec* static_fs) {
   2136  // Init type class and constructor
   2137  RootedObject prototype(
   2138      cx, JS_InitClass(cx, parent, clasp, nullptr, clasp->name, construct, 0,
   2139                       nullptr, fs, nullptr, static_fs));
   2140  if (!prototype) {
   2141    return nullptr;
   2142  }
   2143 
   2144  if (clasp == &sInt64ProtoClass) {
   2145    if (!DefineToStringTag(cx, prototype, "Int64")) {
   2146      return nullptr;
   2147    }
   2148  } else {
   2149    MOZ_ASSERT(clasp == &sUInt64ProtoClass);
   2150    if (!DefineToStringTag(cx, prototype, "UInt64")) {
   2151      return nullptr;
   2152    }
   2153  }
   2154 
   2155  RootedObject ctor(cx, JS_GetConstructor(cx, prototype));
   2156  if (!ctor) {
   2157    return nullptr;
   2158  }
   2159 
   2160  // Define the 'join' function as an extended native and stash
   2161  // ctypes.{Int64,UInt64}.prototype in a reserved slot of the new function.
   2162  JSNative native = (clasp == &sInt64ProtoClass) ? Int64::Join : UInt64::Join;
   2163  JSFunction* fun = js::DefineFunctionWithReserved(cx, ctor, "join", native, 2,
   2164                                                   CTYPESFN_FLAGS);
   2165  if (!fun) {
   2166    return nullptr;
   2167  }
   2168 
   2169  js::SetFunctionNativeReserved(fun, SLOT_FN_INT64PROTO,
   2170                                ObjectValue(*prototype));
   2171 
   2172  if (!JS_FreezeObject(cx, ctor)) {
   2173    return nullptr;
   2174  }
   2175  if (!JS_FreezeObject(cx, prototype)) {
   2176    return nullptr;
   2177  }
   2178 
   2179  return prototype;
   2180 }
   2181 
   2182 static void AttachProtos(JSObject* proto, HandleObjectVector protos) {
   2183  // For a given 'proto' of [[Class]] "CTypeProto", attach each of the 'protos'
   2184  // to the appropriate CTypeProtoSlot. (SLOT_CTYPES is the last slot
   2185  // of [[Class]] "CTypeProto" that we fill in this automated manner.)
   2186  for (uint32_t i = 0; i <= SLOT_CTYPES; ++i) {
   2187    JS_SetReservedSlot(proto, i, ObjectOrNullValue(protos[i]));
   2188  }
   2189 }
   2190 
   2191 static bool InitTypeClasses(JSContext* cx, HandleObject ctypesObj) {
   2192  // Initialize the ctypes.CType class. This acts as an abstract base class for
   2193  // the various types, and provides the common API functions. It has:
   2194  //   * [[Class]] "Function"
   2195  //   * __proto__ === Function.prototype
   2196  //   * A constructor that throws a TypeError. (You can't construct an
   2197  //     abstract type!)
   2198  //   * 'prototype' property:
   2199  //     * [[Class]] "CTypeProto"
   2200  //     * __proto__ === Function.prototype
   2201  //     * A constructor that throws a TypeError. (You can't construct an
   2202  //       abstract type instance!)
   2203  //     * 'constructor' property === ctypes.CType
   2204  //     * Provides properties and functions common to all CTypes.
   2205  RootedObject CTypeProto(cx, InitCTypeClass(cx, ctypesObj));
   2206  if (!CTypeProto) {
   2207    return false;
   2208  }
   2209 
   2210  // Initialize the ctypes.CData class. This acts as an abstract base class for
   2211  // instances of the various types, and provides the common API functions.
   2212  // It has:
   2213  //   * [[Class]] "Function"
   2214  //   * __proto__ === Function.prototype
   2215  //   * A constructor that throws a TypeError. (You can't construct an
   2216  //     abstract type instance!)
   2217  //   * 'prototype' property:
   2218  //     * [[Class]] "CDataProto"
   2219  //     * 'constructor' property === ctypes.CData
   2220  //     * Provides properties and functions common to all CDatas.
   2221  RootedObject CDataProto(cx, InitCDataClass(cx, ctypesObj, CTypeProto));
   2222  if (!CDataProto) {
   2223    return false;
   2224  }
   2225 
   2226  // Link CTypeProto to CDataProto.
   2227  JS_SetReservedSlot(CTypeProto, SLOT_OURDATAPROTO, ObjectValue(*CDataProto));
   2228 
   2229  // Create and attach the special class constructors: ctypes.PointerType,
   2230  // ctypes.ArrayType, ctypes.StructType, and ctypes.FunctionType.
   2231  // Each of these constructors 'c' has, respectively:
   2232  //   * [[Class]] "Function"
   2233  //   * __proto__ === Function.prototype
   2234  //   * A constructor that creates a user-defined type.
   2235  //   * 'prototype' property:
   2236  //     * [[Class]] "CTypeProto"
   2237  //     * __proto__ === ctypes.CType.prototype
   2238  //     * 'constructor' property === 'c'
   2239  // We also construct an object 'p' to serve, given a type object 't'
   2240  // constructed from one of these type constructors, as
   2241  // 't.prototype.__proto__'. This object has:
   2242  //   * [[Class]] "CDataProto"
   2243  //   * __proto__ === ctypes.CData.prototype
   2244  //   * Properties and functions common to all CDatas.
   2245  // Therefore an instance 't' of ctypes.{Pointer,Array,Struct,Function}Type
   2246  // will have, resp.:
   2247  //   * [[Class]] "CType"
   2248  //   * __proto__ === ctypes.{Pointer,Array,Struct,Function}Type.prototype
   2249  //   * A constructor which creates and returns a CData object, containing
   2250  //     binary data of the given type.
   2251  //   * 'prototype' property:
   2252  //     * [[Class]] "CDataProto"
   2253  //     * __proto__ === 'p', the prototype object from above
   2254  //     * 'constructor' property === 't'
   2255  RootedObjectVector protos(cx);
   2256  if (!protos.resize(CTYPEPROTO_SLOTS)) {
   2257    return false;
   2258  }
   2259  if (!InitTypeConstructor(
   2260          cx, ctypesObj, CTypeProto, CDataProto, sPointerFunction, nullptr,
   2261          sPointerProps, sPointerInstanceFunctions, sPointerInstanceProps,
   2262          protos[SLOT_POINTERPROTO], protos[SLOT_POINTERDATAPROTO]))
   2263    return false;
   2264 
   2265  if (!InitTypeConstructor(
   2266          cx, ctypesObj, CTypeProto, CDataProto, sArrayFunction, nullptr,
   2267          sArrayProps, sArrayInstanceFunctions, sArrayInstanceProps,
   2268          protos[SLOT_ARRAYPROTO], protos[SLOT_ARRAYDATAPROTO]))
   2269    return false;
   2270 
   2271  if (!InitTypeConstructor(
   2272          cx, ctypesObj, CTypeProto, CDataProto, sStructFunction,
   2273          sStructFunctions, sStructProps, sStructInstanceFunctions, nullptr,
   2274          protos[SLOT_STRUCTPROTO], protos[SLOT_STRUCTDATAPROTO]))
   2275    return false;
   2276 
   2277  if (!InitTypeConstructor(cx, ctypesObj, CTypeProto,
   2278                           protos[SLOT_POINTERDATAPROTO], sFunctionFunction,
   2279                           nullptr, sFunctionProps, sFunctionInstanceFunctions,
   2280                           nullptr, protos[SLOT_FUNCTIONPROTO],
   2281                           protos[SLOT_FUNCTIONDATAPROTO]))
   2282    return false;
   2283 
   2284  protos[SLOT_CDATAPROTO].set(CDataProto);
   2285 
   2286  // Create and attach the ctypes.{Int64,UInt64} constructors.
   2287  // Each of these has, respectively:
   2288  //   * [[Class]] "Function"
   2289  //   * __proto__ === Function.prototype
   2290  //   * A constructor that creates a ctypes.{Int64,UInt64} object,
   2291  //     respectively.
   2292  //   * 'prototype' property:
   2293  //     * [[Class]] {"Int64Proto","UInt64Proto"}
   2294  //     * 'constructor' property === ctypes.{Int64,UInt64}
   2295  protos[SLOT_INT64PROTO].set(InitInt64Class(cx, ctypesObj, &sInt64ProtoClass,
   2296                                             Int64::Construct, sInt64Functions,
   2297                                             sInt64StaticFunctions));
   2298  if (!protos[SLOT_INT64PROTO]) {
   2299    return false;
   2300  }
   2301  protos[SLOT_UINT64PROTO].set(
   2302      InitInt64Class(cx, ctypesObj, &sUInt64ProtoClass, UInt64::Construct,
   2303                     sUInt64Functions, sUInt64StaticFunctions));
   2304  if (!protos[SLOT_UINT64PROTO]) {
   2305    return false;
   2306  }
   2307 
   2308  // Finally, store a pointer to the global ctypes object.
   2309  // Note that there is no other reliable manner of locating this object.
   2310  protos[SLOT_CTYPES].set(ctypesObj);
   2311 
   2312  // Attach the prototypes just created to each of ctypes.CType.prototype,
   2313  // and the special type constructors, so we can access them when constructing
   2314  // instances of those types.
   2315  AttachProtos(CTypeProto, protos);
   2316  AttachProtos(protos[SLOT_POINTERPROTO], protos);
   2317  AttachProtos(protos[SLOT_ARRAYPROTO], protos);
   2318  AttachProtos(protos[SLOT_STRUCTPROTO], protos);
   2319  AttachProtos(protos[SLOT_FUNCTIONPROTO], protos);
   2320 
   2321  RootedObject ABIProto(cx, InitABIClass(cx));
   2322  if (!ABIProto) {
   2323    return false;
   2324  }
   2325 
   2326  // Attach objects representing ABI constants.
   2327  if (!DefineABIConstant(cx, ctypesObj, "default_abi", ABI_DEFAULT, ABIProto) ||
   2328      !DefineABIConstant(cx, ctypesObj, "stdcall_abi", ABI_STDCALL, ABIProto) ||
   2329      !DefineABIConstant(cx, ctypesObj, "thiscall_abi", ABI_THISCALL,
   2330                         ABIProto) ||
   2331      !DefineABIConstant(cx, ctypesObj, "winapi_abi", ABI_WINAPI, ABIProto))
   2332    return false;
   2333 
   2334  // Create objects representing the builtin types, and attach them to the
   2335  // ctypes object. Each type object 't' has:
   2336  //   * [[Class]] "CType"
   2337  //   * __proto__ === ctypes.CType.prototype
   2338  //   * A constructor which creates and returns a CData object, containing
   2339  //     binary data of the given type.
   2340  //   * 'prototype' property:
   2341  //     * [[Class]] "CDataProto"
   2342  //     * __proto__ === ctypes.CData.prototype
   2343  //     * 'constructor' property === 't'
   2344 #define DEFINE_TYPE(name, type, ffiType)                                       \
   2345  RootedObject typeObj_##name(cx);                                             \
   2346  {                                                                            \
   2347    RootedValue typeVal(cx, Int32Value(sizeof(type)));                         \
   2348    RootedValue alignVal(cx, Int32Value(ffiType.alignment));                   \
   2349    typeObj_##name =                                                           \
   2350        CType::DefineBuiltin(cx, ctypesObj, #name, CTypeProto, CDataProto,     \
   2351                             #name, TYPE_##name, typeVal, alignVal, &ffiType); \
   2352    if (!typeObj_##name) return false;                                         \
   2353  }
   2354  CTYPES_FOR_EACH_TYPE(DEFINE_TYPE)
   2355 #undef DEFINE_TYPE
   2356 
   2357  // Alias 'ctypes.unsigned' as 'ctypes.unsigned_int', since they represent
   2358  // the same type in C.
   2359  if (!JS_DefineProperty(cx, ctypesObj, "unsigned", typeObj_unsigned_int,
   2360                         JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
   2361    return false;
   2362 
   2363  // Alias 'ctypes.jschar' as 'ctypes.char16_t' to prevent breaking addons
   2364  // that are still using jschar (bug 1064935).
   2365  if (!JS_DefineProperty(cx, ctypesObj, "jschar", typeObj_char16_t,
   2366                         JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
   2367    return false;
   2368 
   2369  // Create objects representing the special types void_t and voidptr_t.
   2370  RootedObject typeObj(
   2371      cx, CType::DefineBuiltin(cx, ctypesObj, "void_t", CTypeProto, CDataProto,
   2372                               "void", TYPE_void_t, JS::UndefinedHandleValue,
   2373                               JS::UndefinedHandleValue, &ffi_type_void));
   2374  if (!typeObj) {
   2375    return false;
   2376  }
   2377 
   2378  typeObj = PointerType::CreateInternal(cx, typeObj);
   2379  if (!typeObj) {
   2380    return false;
   2381  }
   2382  if (!JS_DefineProperty(cx, ctypesObj, "voidptr_t", typeObj,
   2383                         JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
   2384    return false;
   2385 
   2386  return true;
   2387 }
   2388 
   2389 bool IsCTypesGlobal(JSObject* obj) {
   2390  return obj->hasClass(&sCTypesGlobalClass);
   2391 }
   2392 
   2393 bool IsCTypesGlobal(HandleValue v) {
   2394  return v.isObject() && IsCTypesGlobal(&v.toObject());
   2395 }
   2396 
   2397 // Get the JS::CTypesCallbacks struct from the 'ctypes' object 'obj'.
   2398 const JS::CTypesCallbacks* GetCallbacks(JSObject* obj) {
   2399  MOZ_ASSERT(IsCTypesGlobal(obj));
   2400 
   2401  Value result = JS::GetReservedSlot(obj, SLOT_CALLBACKS);
   2402  if (result.isUndefined()) {
   2403    return nullptr;
   2404  }
   2405 
   2406  return static_cast<const JS::CTypesCallbacks*>(result.toPrivate());
   2407 }
   2408 
   2409 // Utility function to access a property of an object as an object
   2410 // returns false and sets the error if the property does not exist
   2411 // or is not an object
   2412 static bool GetObjectProperty(JSContext* cx, HandleObject obj,
   2413                              const char* property,
   2414                              MutableHandleObject result) {
   2415  RootedValue val(cx);
   2416  if (!JS_GetProperty(cx, obj, property, &val)) {
   2417    return false;
   2418  }
   2419 
   2420  if (val.isPrimitive()) {
   2421    JS_ReportErrorASCII(cx, "missing or non-object field");
   2422    return false;
   2423  }
   2424 
   2425  result.set(val.toObjectOrNull());
   2426  return true;
   2427 }
   2428 
   2429 }  // namespace js::ctypes
   2430 
   2431 using namespace js;
   2432 using namespace js::ctypes;
   2433 
   2434 JS_PUBLIC_API bool JS::InitCTypesClass(JSContext* cx,
   2435                                       Handle<JSObject*> global) {
   2436  // attach ctypes property to global object
   2437  RootedObject ctypes(cx, JS_NewObject(cx, &sCTypesGlobalClass));
   2438  if (!ctypes) {
   2439    return false;
   2440  }
   2441 
   2442  if (!JS_DefineProperty(cx, global, "ctypes", ctypes,
   2443                         JSPROP_READONLY | JSPROP_PERMANENT)) {
   2444    return false;
   2445  }
   2446 
   2447  if (!InitTypeClasses(cx, ctypes)) {
   2448    return false;
   2449  }
   2450 
   2451  // attach API functions and properties
   2452  if (!JS_DefineFunctions(cx, ctypes, sModuleFunctions) ||
   2453      !JS_DefineProperties(cx, ctypes, sModuleProps))
   2454    return false;
   2455 
   2456  if (!DefineToStringTag(cx, ctypes, "ctypes")) {
   2457    return false;
   2458  }
   2459 
   2460  // Set up ctypes.CDataFinalizer.prototype.
   2461  RootedObject ctor(cx);
   2462  if (!GetObjectProperty(cx, ctypes, "CDataFinalizer", &ctor)) {
   2463    return false;
   2464  }
   2465 
   2466  RootedObject prototype(cx, JS_NewObject(cx, &sCDataFinalizerProtoClass));
   2467  if (!prototype) {
   2468    return false;
   2469  }
   2470 
   2471  if (!JS_DefineFunctions(cx, prototype, sCDataFinalizerFunctions)) {
   2472    return false;
   2473  }
   2474 
   2475  if (!DefineToStringTag(cx, prototype, "CDataFinalizer")) {
   2476    return false;
   2477  }
   2478 
   2479  if (!JS_DefineProperty(cx, ctor, "prototype", prototype,
   2480                         JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
   2481    return false;
   2482 
   2483  if (!JS_DefineProperty(cx, prototype, "constructor", ctor,
   2484                         JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
   2485    return false;
   2486 
   2487  // Seal the ctypes object, to prevent modification.
   2488  return JS_FreezeObject(cx, ctypes);
   2489 }
   2490 
   2491 JS_PUBLIC_API void JS::SetCTypesCallbacks(JSObject* ctypesObj,
   2492                                          const CTypesCallbacks* callbacks) {
   2493  MOZ_ASSERT(callbacks);
   2494  MOZ_ASSERT(IsCTypesGlobal(ctypesObj));
   2495 
   2496  // Set the callbacks on a reserved slot.
   2497  JS_SetReservedSlot(ctypesObj, SLOT_CALLBACKS,
   2498                     PrivateValue(const_cast<CTypesCallbacks*>(callbacks)));
   2499 }
   2500 
   2501 namespace js {
   2502 
   2503 namespace ctypes {
   2504 
   2505 size_t SizeOfDataIfCDataObject(mozilla::MallocSizeOf mallocSizeOf,
   2506                               JSObject* obj) {
   2507  if (!CData::IsCData(obj)) {
   2508    return 0;
   2509  }
   2510 
   2511  size_t n = 0;
   2512  Value slot = JS::GetReservedSlot(obj, ctypes::SLOT_OWNS);
   2513  if (!slot.isUndefined()) {
   2514    bool owns = slot.toBoolean();
   2515    slot = JS::GetReservedSlot(obj, ctypes::SLOT_DATA);
   2516    if (!slot.isUndefined()) {
   2517      char** buffer = static_cast<char**>(slot.toPrivate());
   2518      n += mallocSizeOf(buffer);
   2519      if (owns) {
   2520        n += mallocSizeOf(*buffer);
   2521      }
   2522    }
   2523  }
   2524  return n;
   2525 }
   2526 
   2527 /*******************************************************************************
   2528 ** Type conversion functions
   2529 *******************************************************************************/
   2530 
   2531 // Enforce some sanity checks on type widths and properties.
   2532 // Where the architecture is 64-bit, make sure it's LP64 or LLP64. (ctypes.int
   2533 // autoconverts to a primitive JS number; to support ILP64 architectures, it
   2534 // would need to autoconvert to an Int64 object instead. Therefore we enforce
   2535 // this invariant here.)
   2536 static_assert(sizeof(bool) == 1 || sizeof(bool) == 4);
   2537 static_assert(sizeof(char) == 1);
   2538 static_assert(sizeof(short) == 2);
   2539 static_assert(sizeof(int) == 4);
   2540 static_assert(sizeof(unsigned) == 4);
   2541 static_assert(sizeof(long) == 4 || sizeof(long) == 8);
   2542 static_assert(sizeof(long long) == 8);
   2543 static_assert(sizeof(size_t) == sizeof(uintptr_t));
   2544 static_assert(sizeof(float) == 4);
   2545 static_assert(sizeof(PRFuncPtr) == sizeof(void*));
   2546 static_assert(numeric_limits<double>::is_signed);
   2547 
   2548 template <typename TargetType, typename FromType,
   2549          bool FromIsIntegral = std::is_integral_v<FromType>>
   2550 struct ConvertImpl;
   2551 
   2552 template <typename TargetType, typename FromType>
   2553 struct ConvertImpl<TargetType, FromType, false> {
   2554  static MOZ_ALWAYS_INLINE TargetType Convert(FromType input) {
   2555    return JS::ToSignedOrUnsignedInteger<TargetType>(input);
   2556  }
   2557 };
   2558 
   2559 template <typename TargetType>
   2560 struct ConvertUnsignedTargetTo {
   2561  static TargetType convert(std::make_unsigned_t<TargetType> input) {
   2562    return std::is_signed_v<TargetType> ? mozilla::WrapToSigned(input) : input;
   2563  }
   2564 };
   2565 
   2566 template <>
   2567 struct ConvertUnsignedTargetTo<char16_t> {
   2568  static char16_t convert(char16_t input) {
   2569    // mozilla::WrapToSigned can't be used on char16_t.
   2570    return input;
   2571  }
   2572 };
   2573 
   2574 template <typename TargetType, typename FromType>
   2575 struct ConvertImpl<TargetType, FromType, true> {
   2576  static MOZ_ALWAYS_INLINE TargetType Convert(FromType input) {
   2577    using UnsignedTargetType = std::make_unsigned_t<TargetType>;
   2578    auto resultUnsigned = static_cast<UnsignedTargetType>(input);
   2579 
   2580    return ConvertUnsignedTargetTo<TargetType>::convert(resultUnsigned);
   2581  }
   2582 };
   2583 
   2584 template <class TargetType, class FromType>
   2585 static MOZ_ALWAYS_INLINE TargetType Convert(FromType d) {
   2586  static_assert(
   2587      std::is_integral_v<FromType> != std::is_floating_point_v<FromType>,
   2588      "should only be converting from floating/integral type");
   2589 
   2590  return ConvertImpl<TargetType, FromType>::Convert(d);
   2591 }
   2592 
   2593 template <class TargetType, class FromType>
   2594 static MOZ_ALWAYS_INLINE bool IsAlwaysExact() {
   2595  // Return 'true' if TargetType can always exactly represent FromType.
   2596  // This means that:
   2597  // 1) TargetType must be the same or more bits wide as FromType. For integers
   2598  //    represented in 'n' bits, unsigned variants will have 'n' digits while
   2599  //    signed will have 'n - 1'. For floating point types, 'digits' is the
   2600  //    mantissa width.
   2601  // 2) If FromType is signed, TargetType must also be signed. (Floating point
   2602  //    types are always signed.)
   2603  // 3) If TargetType is an exact integral type, FromType must be also.
   2604  if (numeric_limits<TargetType>::digits < numeric_limits<FromType>::digits) {
   2605    return false;
   2606  }
   2607 
   2608  if (numeric_limits<FromType>::is_signed &&
   2609      !numeric_limits<TargetType>::is_signed)
   2610    return false;
   2611 
   2612  if (!numeric_limits<FromType>::is_exact &&
   2613      numeric_limits<TargetType>::is_exact)
   2614    return false;
   2615 
   2616  return true;
   2617 }
   2618 
   2619 // Templated helper to determine if FromType 'i' converts losslessly to
   2620 // TargetType 'j'. Default case where both types are the same signedness.
   2621 template <class TargetType, class FromType, bool TargetSigned, bool FromSigned>
   2622 struct IsExactImpl {
   2623  static MOZ_ALWAYS_INLINE bool Test(FromType i, TargetType j) {
   2624    static_assert(numeric_limits<TargetType>::is_exact);
   2625    return FromType(j) == i;
   2626  }
   2627 };
   2628 
   2629 // Specialization where TargetType is unsigned, FromType is signed.
   2630 template <class TargetType, class FromType>
   2631 struct IsExactImpl<TargetType, FromType, false, true> {
   2632  static MOZ_ALWAYS_INLINE bool Test(FromType i, TargetType j) {
   2633    static_assert(numeric_limits<TargetType>::is_exact);
   2634    return i >= 0 && FromType(j) == i;
   2635  }
   2636 };
   2637 
   2638 // Specialization where TargetType is signed, FromType is unsigned.
   2639 template <class TargetType, class FromType>
   2640 struct IsExactImpl<TargetType, FromType, true, false> {
   2641  static MOZ_ALWAYS_INLINE bool Test(FromType i, TargetType j) {
   2642    static_assert(numeric_limits<TargetType>::is_exact);
   2643    return TargetType(i) >= 0 && FromType(j) == i;
   2644  }
   2645 };
   2646 
   2647 // Convert FromType 'i' to TargetType 'result', returning true iff 'result'
   2648 // is an exact representation of 'i'.
   2649 template <class TargetType, class FromType>
   2650 static MOZ_ALWAYS_INLINE bool ConvertExact(FromType i, TargetType* result) {
   2651  static_assert(std::numeric_limits<TargetType>::is_exact,
   2652                "TargetType must be exact to simplify conversion");
   2653 
   2654  *result = Convert<TargetType>(i);
   2655 
   2656  // See if we can avoid a dynamic check.
   2657  if (IsAlwaysExact<TargetType, FromType>()) {
   2658    return true;
   2659  }
   2660 
   2661  // Return 'true' if 'i' is exactly representable in 'TargetType'.
   2662  return IsExactImpl<TargetType, FromType,
   2663                     numeric_limits<TargetType>::is_signed,
   2664                     numeric_limits<FromType>::is_signed>::Test(i, *result);
   2665 }
   2666 
   2667 // Templated helper to determine if Type 'i' is negative. Default case
   2668 // where IntegerType is unsigned.
   2669 template <class Type, bool IsSigned>
   2670 struct IsNegativeImpl {
   2671  static MOZ_ALWAYS_INLINE bool Test(Type i) { return false; }
   2672 };
   2673 
   2674 // Specialization where Type is signed.
   2675 template <class Type>
   2676 struct IsNegativeImpl<Type, true> {
   2677  static MOZ_ALWAYS_INLINE bool Test(Type i) { return i < 0; }
   2678 };
   2679 
   2680 // Determine whether Type 'i' is negative.
   2681 template <class Type>
   2682 static MOZ_ALWAYS_INLINE bool IsNegative(Type i) {
   2683  return IsNegativeImpl<Type, numeric_limits<Type>::is_signed>::Test(i);
   2684 }
   2685 
   2686 // Implicitly convert val to bool, allowing bool, int, and double
   2687 // arguments numerically equal to 0 or 1.
   2688 static bool jsvalToBool(JSContext* cx, HandleValue val, bool* result) {
   2689  if (val.isBoolean()) {
   2690    *result = val.toBoolean();
   2691    return true;
   2692  }
   2693  if (val.isInt32()) {
   2694    int32_t i = val.toInt32();
   2695    *result = i != 0;
   2696    return i == 0 || i == 1;
   2697  }
   2698  if (val.isDouble()) {
   2699    double d = val.toDouble();
   2700    *result = d != 0;
   2701    // Allow -0.
   2702    return d == 1 || d == 0;
   2703  }
   2704  // Don't silently convert null to bool. It's probably a mistake.
   2705  return false;
   2706 }
   2707 
   2708 // Implicitly convert val to IntegerType, allowing bool, int, double,
   2709 // Int64, UInt64, and CData integer types 't' where all values of 't' are
   2710 // representable by IntegerType.
   2711 template <class IntegerType>
   2712 static bool jsvalToInteger(JSContext* cx, HandleValue val,
   2713                           IntegerType* result) {
   2714  static_assert(numeric_limits<IntegerType>::is_exact);
   2715 
   2716  if (val.isInt32()) {
   2717    // Make sure the integer fits in the alotted precision, and has the right
   2718    // sign.
   2719    int32_t i = val.toInt32();
   2720    return ConvertExact(i, result);
   2721  }
   2722  if (val.isDouble()) {
   2723    // Don't silently lose bits here -- check that val really is an
   2724    // integer value, and has the right sign.
   2725    double d = val.toDouble();
   2726    return ConvertExact(d, result);
   2727  }
   2728  if (val.isObject()) {
   2729    RootedObject obj(cx, &val.toObject());
   2730    if (CData::IsCDataMaybeUnwrap(&obj)) {
   2731      JSObject* typeObj = CData::GetCType(obj);
   2732      void* data = CData::GetData(obj);
   2733 
   2734      // Check whether the source type is always representable, with exact
   2735      // precision, by the target type. If it is, convert the value.
   2736      switch (CType::GetTypeCode(typeObj)) {
   2737 #define INTEGER_CASE(name, fromType, ffiType)                  \
   2738  case TYPE_##name:                                            \
   2739    if (!IsAlwaysExact<IntegerType, fromType>()) return false; \
   2740    *result = IntegerType(*static_cast<fromType*>(data));      \
   2741    return true;
   2742        CTYPES_FOR_EACH_INT_TYPE(INTEGER_CASE)
   2743        CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGER_CASE)
   2744 #undef INTEGER_CASE
   2745        case TYPE_void_t:
   2746        case TYPE_bool:
   2747        case TYPE_float:
   2748        case TYPE_double:
   2749        case TYPE_float32_t:
   2750        case TYPE_float64_t:
   2751        case TYPE_char:
   2752        case TYPE_signed_char:
   2753        case TYPE_unsigned_char:
   2754        case TYPE_char16_t:
   2755        case TYPE_pointer:
   2756        case TYPE_function:
   2757        case TYPE_array:
   2758        case TYPE_struct:
   2759          // Not a compatible number type.
   2760          return false;
   2761      }
   2762    }
   2763 
   2764    if (Int64::IsInt64(obj)) {
   2765      // Make sure the integer fits in IntegerType.
   2766      int64_t i = Int64Base::GetInt(obj);
   2767      return ConvertExact(i, result);
   2768    }
   2769 
   2770    if (UInt64::IsUInt64(obj)) {
   2771      // Make sure the integer fits in IntegerType.
   2772      uint64_t i = Int64Base::GetInt(obj);
   2773      return ConvertExact(i, result);
   2774    }
   2775 
   2776    if (CDataFinalizer::IsCDataFinalizer(obj)) {
   2777      RootedValue innerData(cx);
   2778      if (!CDataFinalizer::GetValue(cx, obj, &innerData)) {
   2779        return false;  // Nothing to convert
   2780      }
   2781      return jsvalToInteger(cx, innerData, result);
   2782    }
   2783 
   2784    return false;
   2785  }
   2786  if (val.isBoolean()) {
   2787    // Implicitly promote boolean values to 0 or 1, like C.
   2788    *result = val.toBoolean();
   2789    MOZ_ASSERT(*result == 0 || *result == 1);
   2790    return true;
   2791  }
   2792  // Don't silently convert null to an integer. It's probably a mistake.
   2793  return false;
   2794 }
   2795 
   2796 // Implicitly convert val to FloatType, allowing int, double,
   2797 // Int64, UInt64, and CData numeric types 't' where all values of 't' are
   2798 // representable by FloatType.
   2799 template <class FloatType>
   2800 static bool jsvalToFloat(JSContext* cx, HandleValue val, FloatType* result) {
   2801  static_assert(!numeric_limits<FloatType>::is_exact);
   2802 
   2803  // The following casts may silently throw away some bits, but there's
   2804  // no good way around it. Sternly requiring that the 64-bit double
   2805  // argument be exactly representable as a 32-bit float is
   2806  // unrealistic: it would allow 1/2 to pass but not 1/3.
   2807  if (val.isInt32()) {
   2808    *result = FloatType(val.toInt32());
   2809    return true;
   2810  }
   2811  if (val.isDouble()) {
   2812    *result = FloatType(val.toDouble());
   2813    return true;
   2814  }
   2815  if (val.isObject()) {
   2816    RootedObject obj(cx, &val.toObject());
   2817    if (CData::IsCDataMaybeUnwrap(&obj)) {
   2818      JSObject* typeObj = CData::GetCType(obj);
   2819      void* data = CData::GetData(obj);
   2820 
   2821      // Check whether the source type is always representable, with exact
   2822      // precision, by the target type. If it is, convert the value.
   2823      switch (CType::GetTypeCode(typeObj)) {
   2824 #define NUMERIC_CASE(name, fromType, ffiType)                \
   2825  case TYPE_##name:                                          \
   2826    if (!IsAlwaysExact<FloatType, fromType>()) return false; \
   2827    *result = FloatType(*static_cast<fromType*>(data));      \
   2828    return true;
   2829        CTYPES_FOR_EACH_FLOAT_TYPE(NUMERIC_CASE)
   2830        CTYPES_FOR_EACH_INT_TYPE(NUMERIC_CASE)
   2831        CTYPES_FOR_EACH_WRAPPED_INT_TYPE(NUMERIC_CASE)
   2832 #undef NUMERIC_CASE
   2833        case TYPE_void_t:
   2834        case TYPE_bool:
   2835        case TYPE_char:
   2836        case TYPE_signed_char:
   2837        case TYPE_unsigned_char:
   2838        case TYPE_char16_t:
   2839        case TYPE_pointer:
   2840        case TYPE_function:
   2841        case TYPE_array:
   2842        case TYPE_struct:
   2843          // Not a compatible number type.
   2844          return false;
   2845      }
   2846    }
   2847  }
   2848  // Don't silently convert true to 1.0 or false to 0.0, even though C/C++
   2849  // does it. It's likely to be a mistake.
   2850  return false;
   2851 }
   2852 
   2853 template <class IntegerType, class CharT>
   2854 static bool StringToInteger(JSContext* cx, CharT* cp, size_t length,
   2855                            IntegerType* result, bool* overflow) {
   2856  static_assert(numeric_limits<IntegerType>::is_exact);
   2857 
   2858  const CharT* end = cp + length;
   2859  if (cp == end) {
   2860    return false;
   2861  }
   2862 
   2863  IntegerType sign = 1;
   2864  if (cp[0] == '-') {
   2865    if (!numeric_limits<IntegerType>::is_signed) {
   2866      return false;
   2867    }
   2868 
   2869    sign = -1;
   2870    ++cp;
   2871  }
   2872 
   2873  // Assume base-10, unless the string begins with '0x' or '0X'.
   2874  IntegerType base = 10;
   2875  if (end - cp > 2 && cp[0] == '0' && (cp[1] == 'x' || cp[1] == 'X')) {
   2876    cp += 2;
   2877    base = 16;
   2878  }
   2879 
   2880  // Scan the string left to right and build the number,
   2881  // checking for valid characters 0 - 9, a - f, A - F and overflow.
   2882  IntegerType i = 0;
   2883  while (cp != end) {
   2884    char16_t c = *cp++;
   2885    uint8_t digit;
   2886    if (IsAsciiDigit(c)) {
   2887      digit = c - '0';
   2888    } else if (base == 16 && c >= 'a' && c <= 'f') {
   2889      digit = c - 'a' + 10;
   2890    } else if (base == 16 && c >= 'A' && c <= 'F') {
   2891      digit = c - 'A' + 10;
   2892    } else {
   2893      return false;
   2894    }
   2895 
   2896    IntegerType ii = i;
   2897    i = ii * base + sign * digit;
   2898    if (i / base != ii) {
   2899      *overflow = true;
   2900      return false;
   2901    }
   2902  }
   2903 
   2904  *result = i;
   2905  return true;
   2906 }
   2907 
   2908 template <class IntegerType>
   2909 static bool StringToInteger(JSContext* cx, JSString* string,
   2910                            IntegerType* result, bool* overflow) {
   2911  JSLinearString* linear = string->ensureLinear(cx);
   2912  if (!linear) {
   2913    return false;
   2914  }
   2915 
   2916  AutoCheckCannotGC nogc;
   2917  size_t length = linear->length();
   2918  return string->hasLatin1Chars()
   2919             ? StringToInteger<IntegerType>(cx, linear->latin1Chars(nogc),
   2920                                            length, result, overflow)
   2921             : StringToInteger<IntegerType>(cx, linear->twoByteChars(nogc),
   2922                                            length, result, overflow);
   2923 }
   2924 
   2925 // Implicitly convert val to IntegerType, allowing int, double,
   2926 // Int64, UInt64, and optionally a decimal or hexadecimal string argument.
   2927 // (This is common code shared by jsvalToSize and the Int64/UInt64
   2928 // constructors.)
   2929 template <class IntegerType>
   2930 static bool jsvalToBigInteger(JSContext* cx, HandleValue val, bool allowString,
   2931                              IntegerType* result, bool* overflow) {
   2932  static_assert(numeric_limits<IntegerType>::is_exact);
   2933 
   2934  if (val.isInt32()) {
   2935    // Make sure the integer fits in the alotted precision, and has the right
   2936    // sign.
   2937    int32_t i = val.toInt32();
   2938    return ConvertExact(i, result);
   2939  }
   2940  if (val.isDouble()) {
   2941    // Don't silently lose bits here -- check that val really is an
   2942    // integer value, and has the right sign.
   2943    double d = val.toDouble();
   2944    return ConvertExact(d, result);
   2945  }
   2946  if (allowString && val.isString()) {
   2947    // Allow conversion from base-10 or base-16 strings, provided the result
   2948    // fits in IntegerType. (This allows an Int64 or UInt64 object to be passed
   2949    // to the JS array element operator, which will automatically call
   2950    // toString() on the object for us.)
   2951    return StringToInteger(cx, val.toString(), result, overflow);
   2952  }
   2953  if (val.isObject()) {
   2954    // Allow conversion from an Int64 or UInt64 object directly.
   2955    JSObject* obj = &val.toObject();
   2956 
   2957    if (UInt64::IsUInt64(obj)) {
   2958      // Make sure the integer fits in IntegerType.
   2959      uint64_t i = Int64Base::GetInt(obj);
   2960      return ConvertExact(i, result);
   2961    }
   2962 
   2963    if (Int64::IsInt64(obj)) {
   2964      // Make sure the integer fits in IntegerType.
   2965      int64_t i = Int64Base::GetInt(obj);
   2966      return ConvertExact(i, result);
   2967    }
   2968 
   2969    if (CDataFinalizer::IsCDataFinalizer(obj)) {
   2970      RootedValue innerData(cx);
   2971      if (!CDataFinalizer::GetValue(cx, obj, &innerData)) {
   2972        return false;  // Nothing to convert
   2973      }
   2974      return jsvalToBigInteger(cx, innerData, allowString, result, overflow);
   2975    }
   2976  }
   2977  return false;
   2978 }
   2979 
   2980 // Implicitly convert val to a size value, where the size value is represented
   2981 // by size_t but must also fit in a double.
   2982 static bool jsvalToSize(JSContext* cx, HandleValue val, bool allowString,
   2983                        size_t* result) {
   2984  bool dummy;
   2985  if (!jsvalToBigInteger(cx, val, allowString, result, &dummy)) {
   2986    return false;
   2987  }
   2988 
   2989  // Also check that the result fits in a double.
   2990  return Convert<size_t>(double(*result)) == *result;
   2991 }
   2992 
   2993 // Implicitly convert val to IntegerType, allowing int, double,
   2994 // Int64, UInt64, and optionally a decimal or hexadecimal string argument.
   2995 // (This is common code shared by jsvalToSize and the Int64/UInt64
   2996 // constructors.)
   2997 template <class IntegerType>
   2998 static bool jsidToBigInteger(JSContext* cx, jsid val, bool allowString,
   2999                             IntegerType* result) {
   3000  static_assert(numeric_limits<IntegerType>::is_exact);
   3001 
   3002  if (val.isInt()) {
   3003    // Make sure the integer fits in the alotted precision, and has the right
   3004    // sign.
   3005    int32_t i = val.toInt();
   3006    return ConvertExact(i, result);
   3007  }
   3008  if (allowString && val.isString()) {
   3009    // Allow conversion from base-10 or base-16 strings, provided the result
   3010    // fits in IntegerType. (This allows an Int64 or UInt64 object to be passed
   3011    // to the JS array element operator, which will automatically call
   3012    // toString() on the object for us.)
   3013    bool dummy;
   3014    return StringToInteger(cx, val.toString(), result, &dummy);
   3015  }
   3016  return false;
   3017 }
   3018 
   3019 // Implicitly convert val to a size value, where the size value is represented
   3020 // by size_t but must also fit in a double.
   3021 static bool jsidToSize(JSContext* cx, jsid val, bool allowString,
   3022                       size_t* result) {
   3023  if (!jsidToBigInteger(cx, val, allowString, result)) {
   3024    return false;
   3025  }
   3026 
   3027  // Also check that the result fits in a double.
   3028  return Convert<size_t>(double(*result)) == *result;
   3029 }
   3030 
   3031 // Implicitly convert a size value to a Value, ensuring that the size_t value
   3032 // fits in a double.
   3033 static bool SizeTojsval(JSContext* cx, size_t size, MutableHandleValue result) {
   3034  if (Convert<size_t>(double(size)) != size) {
   3035    return false;
   3036  }
   3037 
   3038  result.setNumber(double(size));
   3039  return true;
   3040 }
   3041 
   3042 // Forcefully convert val to IntegerType when explicitly requested.
   3043 template <class IntegerType>
   3044 static bool jsvalToIntegerExplicit(HandleValue val, IntegerType* result) {
   3045  static_assert(numeric_limits<IntegerType>::is_exact);
   3046 
   3047  if (val.isDouble()) {
   3048    // Convert using ToInt32-style semantics: non-finite numbers become 0, and
   3049    // everything else rounds toward zero then maps into |IntegerType| with
   3050    // wraparound semantics.
   3051    double d = val.toDouble();
   3052    *result = JS::ToSignedOrUnsignedInteger<IntegerType>(d);
   3053    return true;
   3054  }
   3055  if (val.isObject()) {
   3056    // Convert Int64 and UInt64 values by C-style cast.
   3057    JSObject* obj = &val.toObject();
   3058    if (Int64::IsInt64(obj)) {
   3059      int64_t i = Int64Base::GetInt(obj);
   3060      *result = IntegerType(i);
   3061      return true;
   3062    }
   3063    if (UInt64::IsUInt64(obj)) {
   3064      uint64_t i = Int64Base::GetInt(obj);
   3065      *result = IntegerType(i);
   3066      return true;
   3067    }
   3068  }
   3069  return false;
   3070 }
   3071 
   3072 // Forcefully convert val to a pointer value when explicitly requested.
   3073 static bool jsvalToPtrExplicit(JSContext* cx, HandleValue val,
   3074                               uintptr_t* result) {
   3075  if (val.isInt32()) {
   3076    // int32_t always fits in intptr_t. If the integer is negative, cast through
   3077    // an intptr_t intermediate to sign-extend.
   3078    int32_t i = val.toInt32();
   3079    *result = i < 0 ? uintptr_t(intptr_t(i)) : uintptr_t(i);
   3080    return true;
   3081  }
   3082  if (val.isDouble()) {
   3083    double d = val.toDouble();
   3084    if (d < 0) {
   3085      // Cast through an intptr_t intermediate to sign-extend.
   3086      intptr_t i = Convert<intptr_t>(d);
   3087      if (double(i) != d) {
   3088        return false;
   3089      }
   3090 
   3091      *result = uintptr_t(i);
   3092      return true;
   3093    }
   3094 
   3095    // Don't silently lose bits here -- check that val really is an
   3096    // integer value, and has the right sign.
   3097    *result = Convert<uintptr_t>(d);
   3098    return double(*result) == d;
   3099  }
   3100  if (val.isObject()) {
   3101    JSObject* obj = &val.toObject();
   3102    if (Int64::IsInt64(obj)) {
   3103      int64_t i = Int64Base::GetInt(obj);
   3104      intptr_t p = intptr_t(i);
   3105 
   3106      // Make sure the integer fits in the alotted precision.
   3107      if (int64_t(p) != i) {
   3108        return false;
   3109      }
   3110      *result = uintptr_t(p);
   3111      return true;
   3112    }
   3113 
   3114    if (UInt64::IsUInt64(obj)) {
   3115      uint64_t i = Int64Base::GetInt(obj);
   3116 
   3117      // Make sure the integer fits in the alotted precision.
   3118      *result = uintptr_t(i);
   3119      return uint64_t(*result) == i;
   3120    }
   3121  }
   3122  return false;
   3123 }
   3124 
   3125 template <class IntegerType, class CharType, size_t N>
   3126 void IntegerToString(IntegerType i, int radix,
   3127                     StringBuilder<CharType, N>& result) {
   3128  static_assert(numeric_limits<IntegerType>::is_exact);
   3129 
   3130  // The buffer must be big enough for all the bits of IntegerType to fit,
   3131  // in base-2, including '-'.
   3132  CharType buffer[sizeof(IntegerType) * 8 + 1];
   3133  CharType* end = std::end(buffer);
   3134  CharType* cp = end;
   3135 
   3136  // Build the string in reverse. We use multiplication and subtraction
   3137  // instead of modulus because that's much faster.
   3138  const bool isNegative = IsNegative(i);
   3139  size_t sign = isNegative ? -1 : 1;
   3140  do {
   3141    IntegerType ii = i / IntegerType(radix);
   3142    size_t index = sign * size_t(i - ii * IntegerType(radix));
   3143    *--cp = "0123456789abcdefghijklmnopqrstuvwxyz"[index];
   3144    i = ii;
   3145  } while (i != 0);
   3146 
   3147  if (isNegative) {
   3148    *--cp = '-';
   3149  }
   3150 
   3151  MOZ_ASSERT(cp >= buffer);
   3152  if (!result.append(cp, end)) {
   3153    return;
   3154  }
   3155 }
   3156 
   3157 // Convert C binary value 'data' of CType 'typeObj' to a JS primitive, where
   3158 // possible; otherwise, construct and return a CData object. The following
   3159 // semantics apply when constructing a CData object for return:
   3160 // * If 'wantPrimitive' is true, the caller indicates that 'result' must be
   3161 //   a JS primitive, and ConvertToJS will fail if 'result' would be a CData
   3162 //   object. Otherwise:
   3163 // * If a CData object 'parentObj' is supplied, the new CData object is
   3164 //   dependent on the given parent and its buffer refers to a slice of the
   3165 //   parent's buffer.
   3166 // * If 'parentObj' is null, the new CData object may or may not own its
   3167 //   resulting buffer depending on the 'ownResult' argument.
   3168 static bool ConvertToJS(JSContext* cx, HandleObject typeObj,
   3169                        HandleObject parentObj, void* data, bool wantPrimitive,
   3170                        bool ownResult, MutableHandleValue result) {
   3171  MOZ_ASSERT(!parentObj || CData::IsCData(parentObj));
   3172  MOZ_ASSERT(!parentObj || !ownResult);
   3173  MOZ_ASSERT(!wantPrimitive || !ownResult);
   3174 
   3175  TypeCode typeCode = CType::GetTypeCode(typeObj);
   3176 
   3177  switch (typeCode) {
   3178    case TYPE_void_t:
   3179      result.setUndefined();
   3180      break;
   3181    case TYPE_bool:
   3182      result.setBoolean(*static_cast<bool*>(data));
   3183      break;
   3184 #define INT_CASE(name, type, ffiType)       \
   3185  case TYPE_##name: {                       \
   3186    type value = *static_cast<type*>(data); \
   3187    if (sizeof(type) < 4)                   \
   3188      result.setInt32(int32_t(value));      \
   3189    else                                    \
   3190      result.setDouble(double(value));      \
   3191    break;                                  \
   3192  }
   3193      CTYPES_FOR_EACH_INT_TYPE(INT_CASE)
   3194 #undef INT_CASE
   3195 #define WRAPPED_INT_CASE(name, type, ffiType)                               \
   3196  case TYPE_##name: {                                                       \
   3197    /* Return an Int64 or UInt64 object - do not convert to a JS number. */ \
   3198    uint64_t value;                                                         \
   3199    RootedObject proto(cx);                                                 \
   3200    if (!numeric_limits<type>::is_signed) {                                 \
   3201      value = *static_cast<type*>(data);                                    \
   3202      /* Get ctypes.UInt64.prototype from ctypes.CType.prototype. */        \
   3203      proto = CType::GetProtoFromType(cx, typeObj, SLOT_UINT64PROTO);       \
   3204      if (!proto) return false;                                             \
   3205    } else {                                                                \
   3206      value = int64_t(*static_cast<type*>(data));                           \
   3207      /* Get ctypes.Int64.prototype from ctypes.CType.prototype. */         \
   3208      proto = CType::GetProtoFromType(cx, typeObj, SLOT_INT64PROTO);        \
   3209      if (!proto) return false;                                             \
   3210    }                                                                       \
   3211                                                                            \
   3212    JSObject* obj = Int64Base::Construct(cx, proto, value,                  \
   3213                                         !numeric_limits<type>::is_signed); \
   3214    if (!obj) return false;                                                 \
   3215    result.setObject(*obj);                                                 \
   3216    break;                                                                  \
   3217  }
   3218      CTYPES_FOR_EACH_WRAPPED_INT_TYPE(WRAPPED_INT_CASE)
   3219 #undef WRAPPED_INT_CASE
   3220 #define FLOAT_CASE(name, type, ffiType)     \
   3221  case TYPE_##name: {                       \
   3222    type value = *static_cast<type*>(data); \
   3223    result.setDouble(double(value));        \
   3224    break;                                  \
   3225  }
   3226      CTYPES_FOR_EACH_FLOAT_TYPE(FLOAT_CASE)
   3227 #undef FLOAT_CASE
   3228 #define CHAR_CASE(name, type, ffiType)                                      \
   3229  case TYPE_##name:                                                         \
   3230    /* Convert to an integer. We have no idea what character encoding to */ \
   3231    /* use, if any. */                                                      \
   3232    result.setInt32(*static_cast<type*>(data));                             \
   3233    break;
   3234      CTYPES_FOR_EACH_CHAR_TYPE(CHAR_CASE)
   3235 #undef CHAR_CASE
   3236    case TYPE_char16_t: {
   3237      // Convert the char16_t to a 1-character string.
   3238      JSString* str = JS_NewUCStringCopyN(cx, static_cast<char16_t*>(data), 1);
   3239      if (!str) {
   3240        return false;
   3241      }
   3242 
   3243      result.setString(str);
   3244      break;
   3245    }
   3246    case TYPE_pointer:
   3247    case TYPE_array:
   3248    case TYPE_struct: {
   3249      // We're about to create a new CData object to return. If the caller
   3250      // doesn't want this, return early.
   3251      if (wantPrimitive) {
   3252        return NonPrimitiveError(cx, typeObj);
   3253      }
   3254 
   3255      JSObject* obj = CData::Create(cx, typeObj, parentObj, data, ownResult);
   3256      if (!obj) {
   3257        return false;
   3258      }
   3259 
   3260      result.setObject(*obj);
   3261      break;
   3262    }
   3263    case TYPE_function:
   3264      MOZ_CRASH("cannot return a FunctionType");
   3265  }
   3266 
   3267  return true;
   3268 }
   3269 
   3270 // Determine if the contents of a typed array can be converted without
   3271 // ambiguity to a C type. Elements of a Int8Array are converted to
   3272 // ctypes.int8_t, UInt8Array to ctypes.uint8_t, etc.
   3273 bool CanConvertTypedArrayItemTo(JSObject* baseType, JSObject* valObj,
   3274                                JSContext* cx) {
   3275  TypeCode baseTypeCode = CType::GetTypeCode(baseType);
   3276  if (baseTypeCode == TYPE_void_t || baseTypeCode == TYPE_char) {
   3277    return true;
   3278  }
   3279  TypeCode elementTypeCode;
   3280  switch (JS_GetArrayBufferViewType(valObj)) {
   3281    case Scalar::Int8:
   3282      elementTypeCode = TYPE_int8_t;
   3283      break;
   3284    case Scalar::Uint8:
   3285    case Scalar::Uint8Clamped:
   3286      elementTypeCode = TYPE_uint8_t;
   3287      break;
   3288    case Scalar::Int16:
   3289      elementTypeCode = TYPE_int16_t;
   3290      break;
   3291    case Scalar::Uint16:
   3292      elementTypeCode = TYPE_uint16_t;
   3293      break;
   3294    case Scalar::Int32:
   3295      elementTypeCode = TYPE_int32_t;
   3296      break;
   3297    case Scalar::Uint32:
   3298      elementTypeCode = TYPE_uint32_t;
   3299      break;
   3300    case Scalar::Float32:
   3301      elementTypeCode = TYPE_float32_t;
   3302      break;
   3303    case Scalar::Float64:
   3304      elementTypeCode = TYPE_float64_t;
   3305      break;
   3306    default:
   3307      return false;
   3308  }
   3309 
   3310  return elementTypeCode == baseTypeCode;
   3311 }
   3312 
   3313 static CDataFinalizer::Private* GetFinalizerPrivate(JSObject* obj) {
   3314  MOZ_ASSERT(CDataFinalizer::IsCDataFinalizer(obj));
   3315 
   3316  using T = CDataFinalizer::Private;
   3317  return JS::GetMaybePtrFromReservedSlot<T>(obj, SLOT_DATAFINALIZER_PRIVATE);
   3318 }
   3319 
   3320 // Implicitly convert Value 'val' to a C binary representation of CType
   3321 // 'targetType', storing the result in 'buffer'. Adequate space must be
   3322 // provided in 'buffer' by the caller. This function generally does minimal
   3323 // coercion between types. There are two cases in which this function is used:
   3324 // 1) The target buffer is internal to a CData object; we simply write data
   3325 //    into it.
   3326 // 2) We are converting an argument for an ffi call, in which case 'convType'
   3327 //    will be 'ConversionType::Argument'. This allows us to handle a special
   3328 //    case: if necessary, we can autoconvert a JS string primitive to a
   3329 //    pointer-to-character type. In this case, ownership of the allocated string
   3330 //    is handed off to the caller; 'freePointer' will be set to indicate this.
   3331 static bool ImplicitConvert(JSContext* cx, HandleValue val,
   3332                            JSObject* targetType_, void* buffer,
   3333                            ConversionType convType, bool* freePointer,
   3334                            HandleObject funObj = nullptr,
   3335                            unsigned argIndex = 0,
   3336                            HandleObject arrObj = nullptr,
   3337                            unsigned arrIndex = 0) {
   3338  RootedObject targetType(cx, targetType_);
   3339  MOZ_ASSERT(CType::IsSizeDefined(targetType));
   3340 
   3341  // First, check if val is either a CData object or a CDataFinalizer
   3342  // of type targetType.
   3343  JSObject* sourceData = nullptr;
   3344  JSObject* sourceType = nullptr;
   3345  RootedObject valObj(cx, nullptr);
   3346  if (val.isObject()) {
   3347    valObj = &val.toObject();
   3348    if (CData::IsCDataMaybeUnwrap(&valObj)) {
   3349      sourceData = valObj;
   3350      sourceType = CData::GetCType(sourceData);
   3351 
   3352      // If the types are equal, copy the buffer contained within the CData.
   3353      // (Note that the buffers may overlap partially or completely.)
   3354      if (CType::TypesEqual(sourceType, targetType)) {
   3355        size_t size = CType::GetSize(sourceType);
   3356        memmove(buffer, CData::GetData(sourceData), size);
   3357        return true;
   3358      }
   3359    } else if (CDataFinalizer::IsCDataFinalizer(valObj)) {
   3360      sourceData = valObj;
   3361      sourceType = CDataFinalizer::GetCType(cx, sourceData);
   3362 
   3363      CDataFinalizer::Private* p = GetFinalizerPrivate(sourceData);
   3364 
   3365      if (!p) {
   3366        // We have called |dispose| or |forget| already.
   3367        return EmptyFinalizerError(cx, convType, funObj, argIndex);
   3368      }
   3369 
   3370      // If the types are equal, copy the buffer contained within the CData.
   3371      if (CType::TypesEqual(sourceType, targetType)) {
   3372        memmove(buffer, p->cargs, p->cargs_size);
   3373        return true;
   3374      }
   3375    }
   3376  }
   3377 
   3378  TypeCode targetCode = CType::GetTypeCode(targetType);
   3379 
   3380  switch (targetCode) {
   3381    case TYPE_bool: {
   3382      // Do not implicitly lose bits, but allow the values 0, 1, and -0.
   3383      // Programs can convert explicitly, if needed, using `Boolean(v)` or
   3384      // `!!v`.
   3385      bool result;
   3386      if (!jsvalToBool(cx, val, &result)) {
   3387        return ConvError(cx, "boolean", val, convType, funObj, argIndex, arrObj,
   3388                         arrIndex);
   3389      }
   3390      *static_cast<bool*>(buffer) = result;
   3391      break;
   3392    }
   3393 #define CHAR16_CASE(name, type, ffiType)                                     \
   3394  case TYPE_##name: {                                                        \
   3395    /* Convert from a 1-character string, regardless of encoding, */         \
   3396    /* or from an integer, provided the result fits in 'type'. */            \
   3397    type result = 0;                                                         \
   3398    if (val.isString()) {                                                    \
   3399      JSString* str = val.toString();                                        \
   3400      if (str->length() != 1)                                                \
   3401        return ConvError(cx, #name, val, convType, funObj, argIndex, arrObj, \
   3402                         arrIndex);                                          \
   3403      JSLinearString* linear = str->ensureLinear(cx);                        \
   3404      if (!linear) return false;                                             \
   3405      result = linear->latin1OrTwoByteChar(0);                               \
   3406    } else if (!jsvalToInteger(cx, val, &result)) {                          \
   3407      return ConvError(cx, #name, val, convType, funObj, argIndex, arrObj,   \
   3408                       arrIndex);                                            \
   3409    }                                                                        \
   3410    *static_cast<type*>(buffer) = result;                                    \
   3411    break;                                                                   \
   3412  }
   3413      CTYPES_FOR_EACH_CHAR16_TYPE(CHAR16_CASE)
   3414 #undef CHAR16_CASE
   3415 #define INTEGRAL_CASE(name, type, ffiType)                                 \
   3416  case TYPE_##name: {                                                      \
   3417    /* Do not implicitly lose bits. */                                     \
   3418    type result;                                                           \
   3419    if (!jsvalToInteger(cx, val, &result))                                 \
   3420      return ConvError(cx, #name, val, convType, funObj, argIndex, arrObj, \
   3421                       arrIndex);                                          \
   3422    *static_cast<type*>(buffer) = result;                                  \
   3423    break;                                                                 \
   3424  }
   3425      CTYPES_FOR_EACH_INT_TYPE(INTEGRAL_CASE)
   3426      CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGRAL_CASE)
   3427      // It's hard to believe ctypes.char16_t("f") should work yet
   3428      // ctypes.char("f") should not.  Ditto for ctypes.{un,}signed_char.  But
   3429      // this is how ctypes has always worked, so preserve these semantics, and
   3430      // don't switch to an algorithm similar to that in DEFINE_CHAR16_TYPE
   3431      // above, just yet.
   3432      CTYPES_FOR_EACH_CHAR_TYPE(INTEGRAL_CASE)
   3433 #undef INTEGRAL_CASE
   3434 #define FLOAT_CASE(name, type, ffiType)                                    \
   3435  case TYPE_##name: {                                                      \
   3436    type result;                                                           \
   3437    if (!jsvalToFloat(cx, val, &result))                                   \
   3438      return ConvError(cx, #name, val, convType, funObj, argIndex, arrObj, \
   3439                       arrIndex);                                          \
   3440    *static_cast<type*>(buffer) = result;                                  \
   3441    break;                                                                 \
   3442  }
   3443      CTYPES_FOR_EACH_FLOAT_TYPE(FLOAT_CASE)
   3444 #undef FLOAT_CASE
   3445    case TYPE_pointer: {
   3446      if (val.isNull()) {
   3447        // Convert to a null pointer.
   3448        *static_cast<void**>(buffer) = nullptr;
   3449        break;
   3450      }
   3451 
   3452      JS::Rooted<JSObject*> baseType(cx, PointerType::GetBaseType(targetType));
   3453      if (sourceData) {
   3454        // First, determine if the targetType is ctypes.void_t.ptr.
   3455        TypeCode sourceCode = CType::GetTypeCode(sourceType);
   3456        void* sourceBuffer = CData::GetData(sourceData);
   3457        bool voidptrTarget = CType::GetTypeCode(baseType) == TYPE_void_t;
   3458 
   3459        if (sourceCode == TYPE_pointer && voidptrTarget) {
   3460          // Autoconvert if targetType is ctypes.voidptr_t.
   3461          *static_cast<void**>(buffer) = *static_cast<void**>(sourceBuffer);
   3462          break;
   3463        }
   3464        if (sourceCode == TYPE_array) {
   3465          // Autoconvert an array to a ctypes.void_t.ptr or to
   3466          // sourceType.elementType.ptr, just like C.
   3467          JSObject* elementType = ArrayType::GetBaseType(sourceType);
   3468          if (voidptrTarget || CType::TypesEqual(baseType, elementType)) {
   3469            *static_cast<void**>(buffer) = sourceBuffer;
   3470            break;
   3471          }
   3472        }
   3473 
   3474      } else if (convType == ConversionType::Argument && val.isString()) {
   3475        // Convert the string for the ffi call. This requires allocating space
   3476        // which the caller assumes ownership of.
   3477        // TODO: Extend this so we can safely convert strings at other times
   3478        // also.
   3479        JSString* sourceString = val.toString();
   3480        size_t sourceLength = sourceString->length();
   3481        Rooted<JSLinearString*> sourceLinear(cx,
   3482                                             sourceString->ensureLinear(cx));
   3483        if (!sourceLinear) {
   3484          return false;
   3485        }
   3486 
   3487        switch (CType::GetTypeCode(baseType)) {
   3488          case TYPE_char:
   3489          case TYPE_signed_char:
   3490          case TYPE_unsigned_char: {
   3491            // Reject if unpaired surrogate characters are present.
   3492            if (!ReportErrorIfUnpairedSurrogatePresent(cx, sourceLinear)) {
   3493              return false;
   3494            }
   3495 
   3496            // Convert from UTF-16 to UTF-8.
   3497            size_t nbytes = JS::GetDeflatedUTF8StringLength(sourceLinear);
   3498 
   3499            char** charBuffer = static_cast<char**>(buffer);
   3500            *charBuffer = cx->pod_malloc<char>(nbytes + 1);
   3501            if (!*charBuffer) {
   3502              return false;
   3503            }
   3504 
   3505            nbytes = JS::DeflateStringToUTF8Buffer(
   3506                sourceLinear, mozilla::Span(*charBuffer, nbytes));
   3507            (*charBuffer)[nbytes] = '\0';
   3508            *freePointer = true;
   3509            break;
   3510          }
   3511          case TYPE_char16_t: {
   3512            // Copy the char16_t string data. (We could provide direct access to
   3513            // the JSString's buffer, but this approach is safer if the caller
   3514            // happens to modify the string.)
   3515            char16_t** char16Buffer = static_cast<char16_t**>(buffer);
   3516            *char16Buffer = cx->pod_malloc<char16_t>(sourceLength + 1);
   3517            if (!*char16Buffer) {
   3518              return false;
   3519            }
   3520 
   3521            *freePointer = true;
   3522 
   3523            CopyChars(*char16Buffer, *sourceLinear);
   3524            (*char16Buffer)[sourceLength] = '\0';
   3525            break;
   3526          }
   3527          default:
   3528            return ConvError(cx, targetType, val, convType, funObj, argIndex,
   3529                             arrObj, arrIndex);
   3530        }
   3531        break;
   3532      } else if (val.isObject() && JS::IsArrayBufferObject(valObj)) {
   3533        // Immutable ArrayBuffers not yet supported.
   3534        if (JS::IsImmutableArrayBufferMaybeShared(valObj)) {
   3535          return ConvError(cx, targetType, val, convType, funObj, argIndex,
   3536                           arrObj, arrIndex);
   3537        }
   3538 
   3539        // Convert ArrayBuffer to pointer without any copy. This is only valid
   3540        // when converting an argument to a function call, as it is possible for
   3541        // the pointer to be invalidated by anything that runs JS code. (It is
   3542        // invalid to invoke JS code from a ctypes function call.)
   3543        if (convType != ConversionType::Argument) {
   3544          return ConvError(cx, targetType, val, convType, funObj, argIndex,
   3545                           arrObj, arrIndex);
   3546        }
   3547        void* ptr;
   3548        {
   3549          JS::AutoCheckCannotGC nogc;
   3550          bool isShared;
   3551          ptr = JS::GetArrayBufferData(valObj, &isShared, nogc);
   3552          MOZ_ASSERT(!isShared);  // Because ArrayBuffer
   3553        }
   3554        if (!ptr) {
   3555          return ConvError(cx, targetType, val, convType, funObj, argIndex,
   3556                           arrObj, arrIndex);
   3557        }
   3558        *static_cast<void**>(buffer) = ptr;
   3559        break;
   3560      } else if (val.isObject() && JS::IsSharedArrayBufferObject(valObj)) {
   3561        // CTypes has not yet opted in to allowing shared memory pointers
   3562        // to escape.  Exporting a pointer to the shared buffer without
   3563        // indicating sharedness would expose client code to races.
   3564        return ConvError(cx, targetType, val, convType, funObj, argIndex,
   3565                         arrObj, arrIndex);
   3566      } else if (val.isObject() && JS_IsArrayBufferViewObject(valObj)) {
   3567        // Immutable ArrayBuffers not yet supported.
   3568        if (JS::IsImmutableArrayBufferView(valObj)) {
   3569          return ConvError(cx, targetType, val, convType, funObj, argIndex,
   3570                           arrObj, arrIndex);
   3571        }
   3572 
   3573        // Same as ArrayBuffer, above, though note that this will take the
   3574        // offset of the view into account.
   3575        if (!CanConvertTypedArrayItemTo(baseType, valObj, cx)) {
   3576          return ConvError(cx, targetType, val, convType, funObj, argIndex,
   3577                           arrObj, arrIndex);
   3578        }
   3579        if (convType != ConversionType::Argument) {
   3580          return ConvError(cx, targetType, val, convType, funObj, argIndex,
   3581                           arrObj, arrIndex);
   3582        }
   3583        void* ptr;
   3584        {
   3585          JS::AutoCheckCannotGC nogc;
   3586          bool isShared;
   3587          ptr = JS_GetArrayBufferViewData(valObj, &isShared, nogc);
   3588          if (isShared) {
   3589            // Opt out of shared memory, for now.  Exporting a
   3590            // pointer to the shared buffer without indicating
   3591            // sharedness would expose client code to races.
   3592            ptr = nullptr;
   3593          }
   3594        }
   3595        if (!ptr) {
   3596          return ConvError(cx, targetType, val, convType, funObj, argIndex,
   3597                           arrObj, arrIndex);
   3598        }
   3599        *static_cast<void**>(buffer) = ptr;
   3600        break;
   3601      }
   3602      return ConvError(cx, targetType, val, convType, funObj, argIndex, arrObj,
   3603                       arrIndex);
   3604    }
   3605    case TYPE_array: {
   3606      MOZ_ASSERT(!funObj);
   3607 
   3608      RootedObject baseType(cx, ArrayType::GetBaseType(targetType));
   3609      size_t targetLength = ArrayType::GetLength(targetType);
   3610 
   3611      if (val.isString()) {
   3612        JSString* sourceString = val.toString();
   3613        size_t sourceLength = sourceString->length();
   3614        Rooted<JSLinearString*> sourceLinear(cx,
   3615                                             sourceString->ensureLinear(cx));
   3616        if (!sourceLinear) {
   3617          return false;
   3618        }
   3619 
   3620        switch (CType::GetTypeCode(baseType)) {
   3621          case TYPE_char:
   3622          case TYPE_signed_char:
   3623          case TYPE_unsigned_char: {
   3624            // Reject if unpaired surrogate characters are present.
   3625            if (!ReportErrorIfUnpairedSurrogatePresent(cx, sourceLinear)) {
   3626              return false;
   3627            }
   3628 
   3629            // Convert from UTF-16 or Latin1 to UTF-8.
   3630            size_t nbytes = JS::GetDeflatedUTF8StringLength(sourceLinear);
   3631 
   3632            if (targetLength < nbytes) {
   3633              MOZ_ASSERT(!funObj);
   3634              return ArrayLengthOverflow(cx, targetLength, targetType, nbytes,
   3635                                         val, convType);
   3636            }
   3637 
   3638            char* charBuffer = static_cast<char*>(buffer);
   3639            nbytes = JS::DeflateStringToUTF8Buffer(
   3640                sourceLinear, mozilla::Span(charBuffer, nbytes));
   3641 
   3642            if (targetLength > nbytes) {
   3643              charBuffer[nbytes] = '\0';
   3644            }
   3645 
   3646            break;
   3647          }
   3648          case TYPE_char16_t: {
   3649            // Copy the string data, char16_t for char16_t, including the
   3650            // terminator if there's space.
   3651            if (targetLength < sourceLength) {
   3652              MOZ_ASSERT(!funObj);
   3653              return ArrayLengthOverflow(cx, targetLength, targetType,
   3654                                         sourceLength, val, convType);
   3655            }
   3656 
   3657            char16_t* dest = static_cast<char16_t*>(buffer);
   3658            CopyChars(dest, *sourceLinear);
   3659 
   3660            if (targetLength > sourceLength) {
   3661              dest[sourceLength] = '\0';
   3662            }
   3663 
   3664            break;
   3665          }
   3666          default:
   3667            return ConvError(cx, targetType, val, convType, funObj, argIndex,
   3668                             arrObj, arrIndex);
   3669        }
   3670      } else {
   3671        ESClass cls;
   3672        if (!GetClassOfValue(cx, val, &cls)) {
   3673          return false;
   3674        }
   3675 
   3676        if (cls == ESClass::Array) {
   3677          // Convert each element of the array by calling ImplicitConvert.
   3678          uint32_t sourceLength;
   3679          if (!JS::GetArrayLength(cx, valObj, &sourceLength) ||
   3680              targetLength != size_t(sourceLength)) {
   3681            MOZ_ASSERT(!funObj);
   3682            return ArrayLengthMismatch(cx, targetLength, targetType,
   3683                                       size_t(sourceLength), val, convType);
   3684          }
   3685 
   3686          // Convert into an intermediate, in case of failure.
   3687          size_t elementSize = CType::GetSize(baseType);
   3688          size_t arraySize = elementSize * targetLength;
   3689          auto intermediate = cx->make_pod_array<char>(arraySize);
   3690          if (!intermediate) {
   3691            return false;
   3692          }
   3693 
   3694          RootedValue item(cx);
   3695          for (uint32_t i = 0; i < sourceLength; ++i) {
   3696            if (!JS_GetElement(cx, valObj, i, &item)) {
   3697              return false;
   3698            }
   3699 
   3700            char* data = intermediate.get() + elementSize * i;
   3701            if (!ImplicitConvert(cx, item, baseType, data, convType, nullptr,
   3702                                 funObj, argIndex, targetType, i))
   3703              return false;
   3704          }
   3705 
   3706          memcpy(buffer, intermediate.get(), arraySize);
   3707        } else if (cls == ESClass::ArrayBuffer ||
   3708                   cls == ESClass::SharedArrayBuffer) {
   3709          // Check that array is consistent with type, then
   3710          // copy the array.
   3711          const bool bufferShared = cls == ESClass::SharedArrayBuffer;
   3712          size_t sourceLength = bufferShared
   3713                                    ? JS::GetSharedArrayBufferByteLength(valObj)
   3714                                    : JS::GetArrayBufferByteLength(valObj);
   3715          size_t elementSize = CType::GetSize(baseType);
   3716          size_t arraySize = elementSize * targetLength;
   3717          if (arraySize != sourceLength) {
   3718            MOZ_ASSERT(!funObj);
   3719            return ArrayLengthMismatch(cx, arraySize, targetType, sourceLength,
   3720                                       val, convType);
   3721          }
   3722          SharedMem<void*> target = SharedMem<void*>::unshared(buffer);
   3723          JS::AutoCheckCannotGC nogc;
   3724          bool isShared;
   3725          SharedMem<void*> src =
   3726              (bufferShared
   3727                   ? SharedMem<void*>::shared(
   3728                         JS::GetSharedArrayBufferData(valObj, &isShared, nogc))
   3729                   : SharedMem<void*>::unshared(
   3730                         JS::GetArrayBufferData(valObj, &isShared, nogc)));
   3731          MOZ_ASSERT(isShared == bufferShared);
   3732          jit::AtomicOperations::memcpySafeWhenRacy(target, src, sourceLength);
   3733          break;
   3734        } else if (JS_IsTypedArrayObject(valObj)) {
   3735          // Check that array is consistent with type, then
   3736          // copy the array.  It is OK to copy from shared to unshared
   3737          // or vice versa.
   3738          if (!CanConvertTypedArrayItemTo(baseType, valObj, cx)) {
   3739            return ConvError(cx, targetType, val, convType, funObj, argIndex,
   3740                             arrObj, arrIndex);
   3741          }
   3742 
   3743          size_t sourceLength = JS_GetTypedArrayByteLength(valObj);
   3744          size_t elementSize = CType::GetSize(baseType);
   3745          size_t arraySize = elementSize * targetLength;
   3746          if (arraySize != sourceLength) {
   3747            MOZ_ASSERT(!funObj);
   3748            return ArrayLengthMismatch(cx, arraySize, targetType, sourceLength,
   3749                                       val, convType);
   3750          }
   3751          SharedMem<void*> target = SharedMem<void*>::unshared(buffer);
   3752          JS::AutoCheckCannotGC nogc;
   3753          bool isShared;
   3754          SharedMem<void*> src = SharedMem<void*>::shared(
   3755              JS_GetArrayBufferViewData(valObj, &isShared, nogc));
   3756          jit::AtomicOperations::memcpySafeWhenRacy(target, src, sourceLength);
   3757          break;
   3758        } else {
   3759          // Don't implicitly convert to string. Users can implicitly convert
   3760          // with `String(x)` or `""+x`.
   3761          return ConvError(cx, targetType, val, convType, funObj, argIndex,
   3762                           arrObj, arrIndex);
   3763        }
   3764      }
   3765      break;
   3766    }
   3767    case TYPE_struct: {
   3768      if (val.isObject() && !sourceData) {
   3769        // Enumerate the properties of the object; if they match the struct
   3770        // specification, convert the fields.
   3771        Rooted<IdVector> props(cx, IdVector(cx));
   3772        if (!JS_Enumerate(cx, valObj, &props)) {
   3773          return false;
   3774        }
   3775 
   3776        // Convert into an intermediate, in case of failure.
   3777        size_t structSize = CType::GetSize(targetType);
   3778        auto intermediate = cx->make_pod_array<char>(structSize);
   3779        if (!intermediate) {
   3780          return false;
   3781        }
   3782 
   3783        const FieldInfoHash* fields = StructType::GetFieldInfo(targetType);
   3784        if (props.length() != fields->count()) {
   3785          return FieldCountMismatch(cx, fields->count(), targetType,
   3786                                    props.length(), val, convType, funObj,
   3787                                    argIndex);
   3788        }
   3789 
   3790        RootedId id(cx);
   3791        for (size_t i = 0; i < props.length(); ++i) {
   3792          id = props[i];
   3793 
   3794          if (!id.isString()) {
   3795            return PropNameNonStringError(cx, id, val, convType, funObj,
   3796                                          argIndex);
   3797          }
   3798 
   3799          JSLinearString* name = id.toLinearString();
   3800          const FieldInfo* field =
   3801              StructType::LookupField(cx, targetType, name);
   3802          if (!field) {
   3803            return false;
   3804          }
   3805 
   3806          RootedValue prop(cx);
   3807          if (!JS_GetPropertyById(cx, valObj, id, &prop)) {
   3808            return false;
   3809          }
   3810 
   3811          // Convert the field via ImplicitConvert().
   3812          char* fieldData = intermediate.get() + field->mOffset;
   3813          if (!ImplicitConvert(cx, prop, field->mType, fieldData, convType,
   3814                               nullptr, funObj, argIndex, targetType, i))
   3815            return false;
   3816        }
   3817 
   3818        memcpy(buffer, intermediate.get(), structSize);
   3819        break;
   3820      }
   3821 
   3822      return ConvError(cx, targetType, val, convType, funObj, argIndex, arrObj,
   3823                       arrIndex);
   3824    }
   3825    case TYPE_void_t:
   3826    case TYPE_function:
   3827      MOZ_CRASH("invalid type");
   3828  }
   3829 
   3830  return true;
   3831 }
   3832 
   3833 // Convert Value 'val' to a C binary representation of CType 'targetType',
   3834 // storing the result in 'buffer'. This function is more forceful than
   3835 // ImplicitConvert.
   3836 static bool ExplicitConvert(JSContext* cx, HandleValue val,
   3837                            HandleObject targetType, void* buffer,
   3838                            ConversionType convType) {
   3839  // If ImplicitConvert succeeds, use that result.
   3840  if (ImplicitConvert(cx, val, targetType, buffer, convType, nullptr)) {
   3841    return true;
   3842  }
   3843 
   3844  // If ImplicitConvert failed, and there is no pending exception, then assume
   3845  // hard failure (out of memory, or some other similarly serious condition).
   3846  // We store any pending exception in case we need to re-throw it.
   3847  RootedValue ex(cx);
   3848  if (!JS_GetPendingException(cx, &ex)) {
   3849    return false;
   3850  }
   3851 
   3852  // Otherwise, assume soft failure. Clear the pending exception so that we
   3853  // can throw a different one as required.
   3854  JS_ClearPendingException(cx);
   3855 
   3856  TypeCode type = CType::GetTypeCode(targetType);
   3857 
   3858  switch (type) {
   3859    case TYPE_bool: {
   3860      *static_cast<bool*>(buffer) = ToBoolean(val);
   3861      break;
   3862    }
   3863 #define INTEGRAL_CASE(name, type, ffiType)                            \
   3864  case TYPE_##name: {                                                 \
   3865    /* Convert numeric values with a C-style cast, and */             \
   3866    /* allow conversion from a base-10 or base-16 string. */          \
   3867    type result;                                                      \
   3868    bool overflow = false;                                            \
   3869    if (!jsvalToIntegerExplicit(val, &result) &&                      \
   3870        (!val.isString() ||                                           \
   3871         !StringToInteger(cx, val.toString(), &result, &overflow))) { \
   3872      if (overflow) {                                                 \
   3873        return TypeOverflow(cx, #name, val);                          \
   3874      }                                                               \
   3875      return ConvError(cx, #name, val, convType);                     \
   3876    }                                                                 \
   3877    *static_cast<type*>(buffer) = result;                             \
   3878    break;                                                            \
   3879  }
   3880      CTYPES_FOR_EACH_INT_TYPE(INTEGRAL_CASE)
   3881      CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGRAL_CASE)
   3882      CTYPES_FOR_EACH_CHAR_TYPE(INTEGRAL_CASE)
   3883      CTYPES_FOR_EACH_CHAR16_TYPE(INTEGRAL_CASE)
   3884 #undef INTEGRAL_CASE
   3885    case TYPE_pointer: {
   3886      // Convert a number, Int64 object, or UInt64 object to a pointer.
   3887      uintptr_t result;
   3888      if (!jsvalToPtrExplicit(cx, val, &result)) {
   3889        return ConvError(cx, targetType, val, convType);
   3890      }
   3891      *static_cast<uintptr_t*>(buffer) = result;
   3892      break;
   3893    }
   3894    case TYPE_float32_t:
   3895    case TYPE_float64_t:
   3896    case TYPE_float:
   3897    case TYPE_double:
   3898    case TYPE_array:
   3899    case TYPE_struct:
   3900      // ImplicitConvert is sufficient. Re-throw the exception it generated.
   3901      JS_SetPendingException(cx, ex);
   3902      return false;
   3903    case TYPE_void_t:
   3904    case TYPE_function:
   3905      MOZ_CRASH("invalid type");
   3906  }
   3907  return true;
   3908 }
   3909 
   3910 // Given a CType 'typeObj', generate a string describing the C type declaration
   3911 // corresponding to 'typeObj'. For instance, the CType constructed from
   3912 // 'ctypes.int32_t.ptr.array(4).ptr.ptr' will result in the type string
   3913 // 'int32_t*(**)[4]'.
   3914 static JSString* BuildTypeName(JSContext* cx, JSObject* typeObj_) {
   3915  AutoString result;
   3916  RootedObject typeObj(cx, typeObj_);
   3917 
   3918  // Walk the hierarchy of types, outermost to innermost, building up the type
   3919  // string. This consists of the base type, which goes on the left.
   3920  // Derived type modifiers (* and []) build from the inside outward, with
   3921  // pointers on the left and arrays on the right. An excellent description
   3922  // of the rules for building C type declarations can be found at:
   3923  // http://unixwiz.net/techtips/reading-cdecl.html
   3924  TypeCode prevGrouping = CType::GetTypeCode(typeObj), currentGrouping;
   3925  while (true) {
   3926    currentGrouping = CType::GetTypeCode(typeObj);
   3927    switch (currentGrouping) {
   3928      case TYPE_pointer: {
   3929        // Pointer types go on the left.
   3930        PrependString(cx, result, "*");
   3931 
   3932        typeObj = PointerType::GetBaseType(typeObj);
   3933        prevGrouping = currentGrouping;
   3934        continue;
   3935      }
   3936      case TYPE_array: {
   3937        if (prevGrouping == TYPE_pointer) {
   3938          // Outer type is pointer, inner type is array. Grouping is required.
   3939          PrependString(cx, result, "(");
   3940          AppendString(cx, result, ")");
   3941        }
   3942 
   3943        // Array types go on the right.
   3944        AppendString(cx, result, "[");
   3945        size_t length;
   3946        if (ArrayType::GetSafeLength(typeObj, &length)) {
   3947          IntegerToString(length, 10, result);
   3948        }
   3949 
   3950        AppendString(cx, result, "]");
   3951 
   3952        typeObj = ArrayType::GetBaseType(typeObj);
   3953        prevGrouping = currentGrouping;
   3954        continue;
   3955      }
   3956      case TYPE_function: {
   3957        FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
   3958 
   3959        // Add in the calling convention, if it's not cdecl.
   3960        // There's no trailing or leading space needed here, as none of the
   3961        // modifiers can produce a string beginning with an identifier ---
   3962        // except for TYPE_function itself, which is fine because functions
   3963        // can't return functions.
   3964        ABICode abi = GetABICode(fninfo->mABI);
   3965        if (abi == ABI_STDCALL) {
   3966          PrependString(cx, result, "__stdcall");
   3967        } else if (abi == ABI_THISCALL) {
   3968          PrependString(cx, result, "__thiscall");
   3969        } else if (abi == ABI_WINAPI) {
   3970          PrependString(cx, result, "WINAPI");
   3971        }
   3972 
   3973        // Function application binds more tightly than dereferencing, so
   3974        // wrap pointer types in parens. Functions can't return functions
   3975        // (only pointers to them), and arrays can't hold functions
   3976        // (similarly), so we don't need to address those cases.
   3977        if (prevGrouping == TYPE_pointer) {
   3978          PrependString(cx, result, "(");
   3979          AppendString(cx, result, ")");
   3980        }
   3981 
   3982        // Argument list goes on the right.
   3983        AppendString(cx, result, "(");
   3984        for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
   3985          RootedObject argType(cx, fninfo->mArgTypes[i]);
   3986          JSString* argName = CType::GetName(cx, argType);
   3987          AppendString(cx, result, argName);
   3988          if (i != fninfo->mArgTypes.length() - 1 || fninfo->mIsVariadic)
   3989            AppendString(cx, result, ", ");
   3990        }
   3991        if (fninfo->mIsVariadic) {
   3992          AppendString(cx, result, "...");
   3993        }
   3994        AppendString(cx, result, ")");
   3995 
   3996        // Set 'typeObj' to the return type, and let the loop process it.
   3997        // 'prevGrouping' doesn't matter here, because functions cannot return
   3998        // arrays -- thus the parenthetical rules don't get tickled.
   3999        typeObj = fninfo->mReturnType;
   4000        continue;
   4001      }
   4002      default:
   4003        // Either a basic or struct type. Use the type's name as the base type.
   4004        break;
   4005    }
   4006    break;
   4007  }
   4008 
   4009  // If prepending the base type name directly would splice two
   4010  // identifiers, insert a space.
   4011  if (IsAsciiAlpha(result[0]) || result[0] == '_') {
   4012    PrependString(cx, result, " ");
   4013  }
   4014 
   4015  // Stick the base type and derived type parts together.
   4016  JSString* baseName = CType::GetName(cx, typeObj);
   4017  PrependString(cx, result, baseName);
   4018  if (!result) {
   4019    return nullptr;
   4020  }
   4021  return NewUCString(cx, result.finish());
   4022 }
   4023 
   4024 // Given a CType 'typeObj', generate a string 'result' such that 'eval(result)'
   4025 // would construct the same CType. If 'makeShort' is true, assume that any
   4026 // StructType 't' is bound to an in-scope variable of name 't.name', and use
   4027 // that variable in place of generating a string to construct the type 't'.
   4028 // (This means the type comparison function CType::TypesEqual will return true
   4029 // when comparing the input and output of AppendTypeSource, since struct
   4030 // equality is determined by strict JSObject pointer equality.)
   4031 static void BuildTypeSource(JSContext* cx, JSObject* typeObj_, bool makeShort,
   4032                            AutoString& result) {
   4033  RootedObject typeObj(cx, typeObj_);
   4034 
   4035  // Walk the types, building up the toSource() string.
   4036  switch (CType::GetTypeCode(typeObj)) {
   4037    case TYPE_void_t:
   4038 #define CASE_FOR_TYPE(name, type, ffiType) case TYPE_##name:
   4039      CTYPES_FOR_EACH_TYPE(CASE_FOR_TYPE)
   4040 #undef CASE_FOR_TYPE
   4041      {
   4042        AppendString(cx, result, "ctypes.");
   4043        JSString* nameStr = CType::GetName(cx, typeObj);
   4044        AppendString(cx, result, nameStr);
   4045        break;
   4046      }
   4047    case TYPE_pointer: {
   4048      RootedObject baseType(cx, PointerType::GetBaseType(typeObj));
   4049 
   4050      // Specialcase ctypes.voidptr_t.
   4051      if (CType::GetTypeCode(baseType) == TYPE_void_t) {
   4052        AppendString(cx, result, "ctypes.voidptr_t");
   4053        break;
   4054      }
   4055 
   4056      // Recursively build the source string, and append '.ptr'.
   4057      BuildTypeSource(cx, baseType, makeShort, result);
   4058      AppendString(cx, result, ".ptr");
   4059      break;
   4060    }
   4061    case TYPE_function: {
   4062      FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
   4063 
   4064      AppendString(cx, result, "ctypes.FunctionType(");
   4065 
   4066      switch (GetABICode(fninfo->mABI)) {
   4067        case ABI_DEFAULT:
   4068          AppendString(cx, result, "ctypes.default_abi, ");
   4069          break;
   4070        case ABI_STDCALL:
   4071          AppendString(cx, result, "ctypes.stdcall_abi, ");
   4072          break;
   4073        case ABI_THISCALL:
   4074          AppendString(cx, result, "ctypes.thiscall_abi, ");
   4075          break;
   4076        case ABI_WINAPI:
   4077          AppendString(cx, result, "ctypes.winapi_abi, ");
   4078          break;
   4079        case INVALID_ABI:
   4080          MOZ_CRASH("invalid abi");
   4081      }
   4082 
   4083      // Recursively build the source string describing the function return and
   4084      // argument types.
   4085      BuildTypeSource(cx, fninfo->mReturnType, true, result);
   4086 
   4087      if (fninfo->mArgTypes.length() > 0) {
   4088        AppendString(cx, result, ", [");
   4089        for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
   4090          BuildTypeSource(cx, fninfo->mArgTypes[i], true, result);
   4091          if (i != fninfo->mArgTypes.length() - 1 || fninfo->mIsVariadic)
   4092            AppendString(cx, result, ", ");
   4093        }
   4094        if (fninfo->mIsVariadic) {
   4095          AppendString(cx, result, "\"...\"");
   4096        }
   4097        AppendString(cx, result, "]");
   4098      }
   4099 
   4100      AppendString(cx, result, ")");
   4101      break;
   4102    }
   4103    case TYPE_array: {
   4104      // Recursively build the source string, and append '.array(n)',
   4105      // where n is the array length, or the empty string if the array length
   4106      // is undefined.
   4107      JSObject* baseType = ArrayType::GetBaseType(typeObj);
   4108      BuildTypeSource(cx, baseType, makeShort, result);
   4109      AppendString(cx, result, ".array(");
   4110 
   4111      size_t length;
   4112      if (ArrayType::GetSafeLength(typeObj, &length)) {
   4113        IntegerToString(length, 10, result);
   4114      }
   4115 
   4116      AppendString(cx, result, ")");
   4117      break;
   4118    }
   4119    case TYPE_struct: {
   4120      JSString* name = CType::GetName(cx, typeObj);
   4121 
   4122      if (makeShort) {
   4123        // Shorten the type declaration by assuming that StructType 't' is bound
   4124        // to an in-scope variable of name 't.name'.
   4125        AppendString(cx, result, name);
   4126        break;
   4127      }
   4128 
   4129      // Write the full struct declaration.
   4130      AppendString(cx, result, "ctypes.StructType(\"");
   4131      AppendString(cx, result, name);
   4132      AppendString(cx, result, "\"");
   4133 
   4134      // If it's an opaque struct, we're done.
   4135      if (!CType::IsSizeDefined(typeObj)) {
   4136        AppendString(cx, result, ")");
   4137        break;
   4138      }
   4139 
   4140      AppendString(cx, result, ", [");
   4141 
   4142      const FieldInfoHash* fields = StructType::GetFieldInfo(typeObj);
   4143      size_t length = fields->count();
   4144      Vector<const FieldInfoHash::Entry*, 64, SystemAllocPolicy> fieldsArray;
   4145      if (!fieldsArray.resize(length)) {
   4146        break;
   4147      }
   4148 
   4149      for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
   4150        fieldsArray[r.front().value().mIndex] = &r.front();
   4151      }
   4152 
   4153      for (size_t i = 0; i < length; ++i) {
   4154        const FieldInfoHash::Entry* entry = fieldsArray[i];
   4155        AppendString(cx, result, "{ \"");
   4156        AppendString(cx, result, entry->key());
   4157        AppendString(cx, result, "\": ");
   4158        BuildTypeSource(cx, entry->value().mType, true, result);
   4159        AppendString(cx, result, " }");
   4160        if (i != length - 1) {
   4161          AppendString(cx, result, ", ");
   4162        }
   4163      }
   4164 
   4165      AppendString(cx, result, "])");
   4166      break;
   4167    }
   4168  }
   4169 }
   4170 
   4171 // Given a CData object of CType 'typeObj' with binary value 'data', generate a
   4172 // string 'result' such that 'eval(result)' would construct a CData object with
   4173 // the same CType and containing the same binary value. This assumes that any
   4174 // StructType 't' is bound to an in-scope variable of name 't.name'. (This means
   4175 // the type comparison function CType::TypesEqual will return true when
   4176 // comparing the types, since struct equality is determined by strict JSObject
   4177 // pointer equality.) Further, if 'isImplicit' is true, ensure that the
   4178 // resulting string can ImplicitConvert successfully if passed to another data
   4179 // constructor. (This is important when called recursively, since fields of
   4180 // structs and arrays are converted with ImplicitConvert.)
   4181 [[nodiscard]] static bool BuildDataSource(JSContext* cx, HandleObject typeObj,
   4182                                          void* data, bool isImplicit,
   4183                                          AutoString& result) {
   4184  TypeCode type = CType::GetTypeCode(typeObj);
   4185  switch (type) {
   4186    case TYPE_bool:
   4187      if (*static_cast<bool*>(data)) {
   4188        AppendString(cx, result, "true");
   4189      } else {
   4190        AppendString(cx, result, "false");
   4191      }
   4192      break;
   4193 #define INTEGRAL_CASE(name, type, ffiType)                  \
   4194  case TYPE_##name:                                         \
   4195    /* Serialize as a primitive decimal integer. */         \
   4196    IntegerToString(*static_cast<type*>(data), 10, result); \
   4197    break;
   4198      CTYPES_FOR_EACH_INT_TYPE(INTEGRAL_CASE)
   4199 #undef INTEGRAL_CASE
   4200 #define WRAPPED_INT_CASE(name, type, ffiType)               \
   4201  case TYPE_##name:                                         \
   4202    /* Serialize as a wrapped decimal integer. */           \
   4203    if (!numeric_limits<type>::is_signed)                   \
   4204      AppendString(cx, result, "ctypes.UInt64(\"");         \
   4205    else                                                    \
   4206      AppendString(cx, result, "ctypes.Int64(\"");          \
   4207                                                            \
   4208    IntegerToString(*static_cast<type*>(data), 10, result); \
   4209    AppendString(cx, result, "\")");                        \
   4210    break;
   4211      CTYPES_FOR_EACH_WRAPPED_INT_TYPE(WRAPPED_INT_CASE)
   4212 #undef WRAPPED_INT_CASE
   4213 #define FLOAT_CASE(name, type, ffiType)                 \
   4214  case TYPE_##name: {                                   \
   4215    /* Serialize as a primitive double. */              \
   4216    double fp = *static_cast<type*>(data);              \
   4217    ToCStringBuf cbuf;                                  \
   4218    size_t strLength;                                   \
   4219    char* str = NumberToCString(&cbuf, fp, &strLength); \
   4220    MOZ_ASSERT(str);                                    \
   4221    if (!result.append(str, strLength)) {               \
   4222      JS_ReportOutOfMemory(cx);                         \
   4223      return false;                                     \
   4224    }                                                   \
   4225    break;                                              \
   4226  }
   4227      CTYPES_FOR_EACH_FLOAT_TYPE(FLOAT_CASE)
   4228 #undef FLOAT_CASE
   4229 #define CHAR_CASE(name, type, ffiType)                      \
   4230  case TYPE_##name:                                         \
   4231    /* Serialize as an integer. */                          \
   4232    IntegerToString(*static_cast<type*>(data), 10, result); \
   4233    break;
   4234      CTYPES_FOR_EACH_CHAR_TYPE(CHAR_CASE)
   4235 #undef CHAR_CASE
   4236    case TYPE_char16_t: {
   4237      // Serialize as a 1-character JS string.
   4238      JSString* str = JS_NewUCStringCopyN(cx, static_cast<char16_t*>(data), 1);
   4239      if (!str) {
   4240        return false;
   4241      }
   4242 
   4243      // Escape characters, and quote as necessary.
   4244      RootedValue valStr(cx, StringValue(str));
   4245      JSString* src = JS_ValueToSource(cx, valStr);
   4246      if (!src) {
   4247        return false;
   4248      }
   4249 
   4250      AppendString(cx, result, src);
   4251      break;
   4252    }
   4253    case TYPE_pointer:
   4254    case TYPE_function: {
   4255      if (isImplicit) {
   4256        // The result must be able to ImplicitConvert successfully.
   4257        // Wrap in a type constructor, then serialize for ExplicitConvert.
   4258        BuildTypeSource(cx, typeObj, true, result);
   4259        AppendString(cx, result, "(");
   4260      }
   4261 
   4262      // Serialize the pointer value as a wrapped hexadecimal integer.
   4263      uintptr_t ptr = *static_cast<uintptr_t*>(data);
   4264      AppendString(cx, result, "ctypes.UInt64(\"0x");
   4265      IntegerToString(ptr, 16, result);
   4266      AppendString(cx, result, "\")");
   4267 
   4268      if (isImplicit) {
   4269        AppendString(cx, result, ")");
   4270      }
   4271 
   4272      break;
   4273    }
   4274    case TYPE_array: {
   4275      // Serialize each element of the array recursively. Each element must
   4276      // be able to ImplicitConvert successfully.
   4277      RootedObject baseType(cx, ArrayType::GetBaseType(typeObj));
   4278      AppendString(cx, result, "[");
   4279 
   4280      size_t length = ArrayType::GetLength(typeObj);
   4281      size_t elementSize = CType::GetSize(baseType);
   4282      for (size_t i = 0; i < length; ++i) {
   4283        char* element = static_cast<char*>(data) + elementSize * i;
   4284        if (!BuildDataSource(cx, baseType, element, true, result)) {
   4285          return false;
   4286        }
   4287 
   4288        if (i + 1 < length) {
   4289          AppendString(cx, result, ", ");
   4290        }
   4291      }
   4292      AppendString(cx, result, "]");
   4293      break;
   4294    }
   4295    case TYPE_struct: {
   4296      if (isImplicit) {
   4297        // The result must be able to ImplicitConvert successfully.
   4298        // Serialize the data as an object with properties, rather than
   4299        // a sequence of arguments to the StructType constructor.
   4300        AppendString(cx, result, "{");
   4301      }
   4302 
   4303      // Serialize each field of the struct recursively. Each field must
   4304      // be able to ImplicitConvert successfully.
   4305      const FieldInfoHash* fields = StructType::GetFieldInfo(typeObj);
   4306      size_t length = fields->count();
   4307      Vector<const FieldInfoHash::Entry*, 64, SystemAllocPolicy> fieldsArray;
   4308      if (!fieldsArray.resize(length)) {
   4309        return false;
   4310      }
   4311 
   4312      for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
   4313        fieldsArray[r.front().value().mIndex] = &r.front();
   4314      }
   4315 
   4316      for (size_t i = 0; i < length; ++i) {
   4317        const FieldInfoHash::Entry* entry = fieldsArray[i];
   4318 
   4319        if (isImplicit) {
   4320          AppendString(cx, result, "\"");
   4321          AppendString(cx, result, entry->key());
   4322          AppendString(cx, result, "\": ");
   4323        }
   4324 
   4325        char* fieldData = static_cast<char*>(data) + entry->value().mOffset;
   4326        RootedObject entryType(cx, entry->value().mType);
   4327        if (!BuildDataSource(cx, entryType, fieldData, true, result)) {
   4328          return false;
   4329        }
   4330 
   4331        if (i + 1 != length) {
   4332          AppendString(cx, result, ", ");
   4333        }
   4334      }
   4335 
   4336      if (isImplicit) {
   4337        AppendString(cx, result, "}");
   4338      }
   4339 
   4340      break;
   4341    }
   4342    case TYPE_void_t:
   4343      MOZ_CRASH("invalid type");
   4344  }
   4345 
   4346  return true;
   4347 }
   4348 
   4349 /*******************************************************************************
   4350 ** JSAPI callback function implementations
   4351 *******************************************************************************/
   4352 
   4353 bool ConstructAbstract(JSContext* cx, unsigned argc, Value* vp) {
   4354  // Calling an abstract base class constructor is disallowed.
   4355  return CannotConstructError(cx, "abstract type");
   4356 }
   4357 
   4358 /*******************************************************************************
   4359 ** CType implementation
   4360 *******************************************************************************/
   4361 
   4362 bool CType::ConstructData(JSContext* cx, unsigned argc, Value* vp) {
   4363  CallArgs args = CallArgsFromVp(argc, vp);
   4364  // get the callee object...
   4365  RootedObject obj(cx, &args.callee());
   4366  if (!CType::IsCType(obj)) {
   4367    return IncompatibleCallee(cx, "CType constructor", obj);
   4368  }
   4369 
   4370  // How we construct the CData object depends on what type we represent.
   4371  // An instance 'd' of a CData object of type 't' has:
   4372  //   * [[Class]] "CData"
   4373  //   * __proto__ === t.prototype
   4374  switch (GetTypeCode(obj)) {
   4375    case TYPE_void_t:
   4376      return CannotConstructError(cx, "void_t");
   4377    case TYPE_function:
   4378      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   4379                                CTYPESMSG_FUNCTION_CONSTRUCT);
   4380      return false;
   4381    case TYPE_pointer:
   4382      return PointerType::ConstructData(cx, obj, args);
   4383    case TYPE_array:
   4384      return ArrayType::ConstructData(cx, obj, args);
   4385    case TYPE_struct:
   4386      return StructType::ConstructData(cx, obj, args);
   4387    default:
   4388      return ConstructBasic(cx, obj, args);
   4389  }
   4390 }
   4391 
   4392 bool CType::ConstructBasic(JSContext* cx, HandleObject obj,
   4393                           const CallArgs& args) {
   4394  if (args.length() > 1) {
   4395    return ArgumentLengthError(cx, "CType constructor", "at most one", "");
   4396  }
   4397 
   4398  // construct a CData object
   4399  RootedObject result(cx, CData::Create(cx, obj, nullptr, nullptr, true));
   4400  if (!result) {
   4401    return false;
   4402  }
   4403 
   4404  if (args.length() == 1) {
   4405    if (!ExplicitConvert(cx, args[0], obj, CData::GetData(result),
   4406                         ConversionType::Construct))
   4407      return false;
   4408  }
   4409 
   4410  args.rval().setObject(*result);
   4411  return true;
   4412 }
   4413 
   4414 JSObject* CType::Create(JSContext* cx, HandleObject typeProto,
   4415                        HandleObject dataProto, TypeCode type, JSString* name_,
   4416                        HandleValue size, HandleValue align,
   4417                        ffi_type* ffiType) {
   4418  RootedString name(cx, name_);
   4419 
   4420  // Create a CType object with the properties and slots common to all CTypes.
   4421  // Each type object 't' has:
   4422  //   * [[Class]] "CType"
   4423  //   * __proto__ === 'typeProto'; one of ctypes.{CType,PointerType,ArrayType,
   4424  //     StructType}.prototype
   4425  //   * A constructor which creates and returns a CData object, containing
   4426  //     binary data of the given type.
   4427  //   * 'prototype' property:
   4428  //     * [[Class]] "CDataProto"
   4429  //     * __proto__ === 'dataProto'; an object containing properties and
   4430  //       functions common to all CData objects of types derived from
   4431  //       'typeProto'. (For instance, this could be ctypes.CData.prototype
   4432  //       for simple types, or something representing structs for StructTypes.)
   4433  //     * 'constructor' property === 't'
   4434  //     * Additional properties specified by 'ps', as appropriate for the
   4435  //       specific type instance 't'.
   4436  RootedObject typeObj(cx,
   4437                       JS_NewObjectWithGivenProto(cx, &sCTypeClass, typeProto));
   4438  if (!typeObj) {
   4439    return nullptr;
   4440  }
   4441 
   4442  // Set up the reserved slots.
   4443  JS_SetReservedSlot(typeObj, SLOT_TYPECODE, Int32Value(type));
   4444  if (ffiType) {
   4445    JS_SetReservedSlot(typeObj, SLOT_FFITYPE, PrivateValue(ffiType));
   4446    if (type == TYPE_struct || type == TYPE_array) {
   4447      AddCellMemory(typeObj, sizeof(ffi_type), MemoryUse::CTypeFFIType);
   4448    }
   4449  }
   4450  if (name) {
   4451    JS_SetReservedSlot(typeObj, SLOT_NAME, StringValue(name));
   4452  }
   4453  JS_SetReservedSlot(typeObj, SLOT_SIZE, size);
   4454  JS_SetReservedSlot(typeObj, SLOT_ALIGN, align);
   4455 
   4456  if (dataProto) {
   4457    // Set up the 'prototype' and 'prototype.constructor' properties.
   4458    RootedObject prototype(
   4459        cx, JS_NewObjectWithGivenProto(cx, &sCDataProtoClass, dataProto));
   4460    if (!prototype) {
   4461      return nullptr;
   4462    }
   4463 
   4464    if (!JS_DefineProperty(cx, prototype, "constructor", typeObj,
   4465                           JSPROP_READONLY | JSPROP_PERMANENT))
   4466      return nullptr;
   4467 
   4468    // Set the 'prototype' object.
   4469    // if (!JS_FreezeObject(cx, prototype)) // XXX fixme - see bug 541212!
   4470    //  return nullptr;
   4471    JS_SetReservedSlot(typeObj, SLOT_PROTO, ObjectValue(*prototype));
   4472  }
   4473 
   4474  if (!JS_FreezeObject(cx, typeObj)) {
   4475    return nullptr;
   4476  }
   4477 
   4478  // Assert a sanity check on size and alignment: size % alignment should always
   4479  // be zero.
   4480  MOZ_ASSERT_IF(IsSizeDefined(typeObj),
   4481                GetSize(typeObj) % GetAlignment(typeObj) == 0);
   4482 
   4483  return typeObj;
   4484 }
   4485 
   4486 JSObject* CType::DefineBuiltin(JSContext* cx, HandleObject ctypesObj,
   4487                               const char* propName, JSObject* typeProto_,
   4488                               JSObject* dataProto_, const char* name,
   4489                               TypeCode type, HandleValue size,
   4490                               HandleValue align, ffi_type* ffiType) {
   4491  RootedObject typeProto(cx, typeProto_);
   4492  RootedObject dataProto(cx, dataProto_);
   4493 
   4494  RootedString nameStr(cx, JS_NewStringCopyZ(cx, name));
   4495  if (!nameStr) {
   4496    return nullptr;
   4497  }
   4498 
   4499  // Create a new CType object with the common properties and slots.
   4500  RootedObject typeObj(cx, Create(cx, typeProto, dataProto, type, nameStr, size,
   4501                                  align, ffiType));
   4502  if (!typeObj) {
   4503    return nullptr;
   4504  }
   4505 
   4506  // Define the CType as a 'propName' property on 'ctypesObj'.
   4507  if (!JS_DefineProperty(cx, ctypesObj, propName, typeObj,
   4508                         JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
   4509    return nullptr;
   4510 
   4511  return typeObj;
   4512 }
   4513 
   4514 static void FinalizeFFIType(JS::GCContext* gcx, JSObject* obj,
   4515                            const Value& slot, size_t elementCount) {
   4516  ffi_type* ffiType = static_cast<ffi_type*>(slot.toPrivate());
   4517  size_t size = elementCount * sizeof(ffi_type*);
   4518  gcx->free_(obj, ffiType->elements, size, MemoryUse::CTypeFFITypeElements);
   4519  gcx->delete_(obj, ffiType, MemoryUse::CTypeFFIType);
   4520 }
   4521 
   4522 void CType::Finalize(JS::GCContext* gcx, JSObject* obj) {
   4523  // Make sure our TypeCode slot is legit. If it's not, bail.
   4524  Value slot = JS::GetReservedSlot(obj, SLOT_TYPECODE);
   4525  if (slot.isUndefined()) {
   4526    return;
   4527  }
   4528 
   4529  // The contents of our slots depends on what kind of type we are.
   4530  switch (TypeCode(slot.toInt32())) {
   4531    case TYPE_function: {
   4532      // Free the FunctionInfo.
   4533      slot = JS::GetReservedSlot(obj, SLOT_FNINFO);
   4534      if (!slot.isUndefined()) {
   4535        auto fninfo = static_cast<FunctionInfo*>(slot.toPrivate());
   4536        gcx->delete_(obj, fninfo, MemoryUse::CTypeFunctionInfo);
   4537      }
   4538      break;
   4539    }
   4540 
   4541    case TYPE_struct: {
   4542      size_t fieldCount = 0;
   4543 
   4544      // Free the FieldInfoHash table.
   4545      slot = JS::GetReservedSlot(obj, SLOT_FIELDINFO);
   4546      if (!slot.isUndefined()) {
   4547        auto info = static_cast<FieldInfoHash*>(slot.toPrivate());
   4548        fieldCount = info->count();
   4549        gcx->delete_(obj, info, MemoryUse::CTypeFieldInfo);
   4550      }
   4551 
   4552      // Free the ffi_type info.
   4553      Value slot = JS::GetReservedSlot(obj, SLOT_FFITYPE);
   4554      if (!slot.isUndefined()) {
   4555        size_t elementCount = fieldCount != 0 ? fieldCount + 1 : 2;
   4556        FinalizeFFIType(gcx, obj, slot, elementCount);
   4557      }
   4558 
   4559      // Free the ffi_type info.
   4560      break;
   4561    }
   4562 
   4563    case TYPE_array: {
   4564      // Free the ffi_type info.
   4565      Value slot = JS::GetReservedSlot(obj, SLOT_FFITYPE);
   4566      if (!slot.isUndefined()) {
   4567        size_t elementCount = ArrayType::GetLength(obj);
   4568        FinalizeFFIType(gcx, obj, slot, elementCount);
   4569      }
   4570      break;
   4571    }
   4572 
   4573    default:
   4574      // Nothing to do here.
   4575      break;
   4576  }
   4577 }
   4578 
   4579 void CType::Trace(JSTracer* trc, JSObject* obj) {
   4580  // Make sure our TypeCode slot is legit. If it's not, bail.
   4581  Value slot = obj->as<NativeObject>().getReservedSlot(SLOT_TYPECODE);
   4582  if (slot.isUndefined()) {
   4583    return;
   4584  }
   4585 
   4586  // The contents of our slots depends on what kind of type we are.
   4587  switch (TypeCode(slot.toInt32())) {
   4588    case TYPE_struct: {
   4589      slot = obj->as<NativeObject>().getReservedSlot(SLOT_FIELDINFO);
   4590      if (slot.isUndefined()) {
   4591        return;
   4592      }
   4593 
   4594      FieldInfoHash* fields = static_cast<FieldInfoHash*>(slot.toPrivate());
   4595      fields->trace(trc);
   4596      break;
   4597    }
   4598    case TYPE_function: {
   4599      // Check if we have a FunctionInfo.
   4600      slot = obj->as<NativeObject>().getReservedSlot(SLOT_FNINFO);
   4601      if (slot.isUndefined()) {
   4602        return;
   4603      }
   4604 
   4605      FunctionInfo* fninfo = static_cast<FunctionInfo*>(slot.toPrivate());
   4606      MOZ_ASSERT(fninfo);
   4607 
   4608      // Identify our objects to the tracer.
   4609      TraceEdge(trc, &fninfo->mABI, "abi");
   4610      TraceEdge(trc, &fninfo->mReturnType, "returnType");
   4611      fninfo->mArgTypes.trace(trc);
   4612 
   4613      break;
   4614    }
   4615    default:
   4616      // Nothing to do here.
   4617      break;
   4618  }
   4619 }
   4620 
   4621 bool CType::IsCType(JSObject* obj) { return obj->hasClass(&sCTypeClass); }
   4622 
   4623 bool CType::IsCTypeProto(JSObject* obj) {
   4624  return obj->hasClass(&sCTypeProtoClass);
   4625 }
   4626 
   4627 TypeCode CType::GetTypeCode(JSObject* typeObj) {
   4628  MOZ_ASSERT(IsCType(typeObj));
   4629 
   4630  Value result = JS::GetReservedSlot(typeObj, SLOT_TYPECODE);
   4631  return TypeCode(result.toInt32());
   4632 }
   4633 
   4634 bool CType::TypesEqual(JSObject* t1, JSObject* t2) {
   4635  MOZ_ASSERT(IsCType(t1) && IsCType(t2));
   4636 
   4637  // Fast path: check for object equality.
   4638  if (t1 == t2) {
   4639    return true;
   4640  }
   4641 
   4642  // First, perform shallow comparison.
   4643  TypeCode c1 = GetTypeCode(t1);
   4644  TypeCode c2 = GetTypeCode(t2);
   4645  if (c1 != c2) {
   4646    return false;
   4647  }
   4648 
   4649  // Determine whether the types require shallow or deep comparison.
   4650  switch (c1) {
   4651    case TYPE_pointer: {
   4652      // Compare base types.
   4653      JSObject* b1 = PointerType::GetBaseType(t1);
   4654      JSObject* b2 = PointerType::GetBaseType(t2);
   4655      return TypesEqual(b1, b2);
   4656    }
   4657    case TYPE_function: {
   4658      FunctionInfo* f1 = FunctionType::GetFunctionInfo(t1);
   4659      FunctionInfo* f2 = FunctionType::GetFunctionInfo(t2);
   4660 
   4661      // Compare abi, return type, and argument types.
   4662      if (f1->mABI != f2->mABI) {
   4663        return false;
   4664      }
   4665 
   4666      if (!TypesEqual(f1->mReturnType, f2->mReturnType)) {
   4667        return false;
   4668      }
   4669 
   4670      if (f1->mArgTypes.length() != f2->mArgTypes.length()) {
   4671        return false;
   4672      }
   4673 
   4674      if (f1->mIsVariadic != f2->mIsVariadic) {
   4675        return false;
   4676      }
   4677 
   4678      for (size_t i = 0; i < f1->mArgTypes.length(); ++i) {
   4679        if (!TypesEqual(f1->mArgTypes[i], f2->mArgTypes[i])) {
   4680          return false;
   4681        }
   4682      }
   4683 
   4684      return true;
   4685    }
   4686    case TYPE_array: {
   4687      // Compare length, then base types.
   4688      // An undefined length array matches other undefined length arrays.
   4689      size_t s1 = 0, s2 = 0;
   4690      bool d1 = ArrayType::GetSafeLength(t1, &s1);
   4691      bool d2 = ArrayType::GetSafeLength(t2, &s2);
   4692      if (d1 != d2 || (d1 && s1 != s2)) {
   4693        return false;
   4694      }
   4695 
   4696      JSObject* b1 = ArrayType::GetBaseType(t1);
   4697      JSObject* b2 = ArrayType::GetBaseType(t2);
   4698      return TypesEqual(b1, b2);
   4699    }
   4700    case TYPE_struct:
   4701      // Require exact type object equality.
   4702      return false;
   4703    default:
   4704      // Shallow comparison is sufficient.
   4705      return true;
   4706  }
   4707 }
   4708 
   4709 bool CType::GetSafeSize(JSObject* obj, size_t* result) {
   4710  MOZ_ASSERT(CType::IsCType(obj));
   4711 
   4712  Value size = JS::GetReservedSlot(obj, SLOT_SIZE);
   4713 
   4714  // The "size" property can be an int, a double, or JS::UndefinedValue()
   4715  // (for arrays of undefined length), and must always fit in a size_t.
   4716  if (size.isInt32()) {
   4717    *result = size.toInt32();
   4718    return true;
   4719  }
   4720  if (size.isDouble()) {
   4721    *result = Convert<size_t>(size.toDouble());
   4722    return true;
   4723  }
   4724 
   4725  MOZ_ASSERT(size.isUndefined());
   4726  return false;
   4727 }
   4728 
   4729 size_t CType::GetSize(JSObject* obj) {
   4730  MOZ_ASSERT(CType::IsCType(obj));
   4731 
   4732  Value size = JS::GetReservedSlot(obj, SLOT_SIZE);
   4733 
   4734  MOZ_ASSERT(!size.isUndefined());
   4735 
   4736  // The "size" property can be an int, a double, or JS::UndefinedValue()
   4737  // (for arrays of undefined length), and must always fit in a size_t.
   4738  // For callers who know it can never be JS::UndefinedValue(), return a size_t
   4739  // directly.
   4740  if (size.isInt32()) {
   4741    return size.toInt32();
   4742  }
   4743  return Convert<size_t>(size.toDouble());
   4744 }
   4745 
   4746 bool CType::IsSizeDefined(JSObject* obj) {
   4747  MOZ_ASSERT(CType::IsCType(obj));
   4748 
   4749  Value size = JS::GetReservedSlot(obj, SLOT_SIZE);
   4750 
   4751  // The "size" property can be an int, a double, or JS::UndefinedValue()
   4752  // (for arrays of undefined length), and must always fit in a size_t.
   4753  MOZ_ASSERT(size.isInt32() || size.isDouble() || size.isUndefined());
   4754  return !size.isUndefined();
   4755 }
   4756 
   4757 size_t CType::GetAlignment(JSObject* obj) {
   4758  MOZ_ASSERT(CType::IsCType(obj));
   4759 
   4760  Value slot = JS::GetReservedSlot(obj, SLOT_ALIGN);
   4761  return static_cast<size_t>(slot.toInt32());
   4762 }
   4763 
   4764 ffi_type* CType::GetFFIType(JSContext* cx, JSObject* obj) {
   4765  MOZ_ASSERT(CType::IsCType(obj));
   4766 
   4767  Value slot = JS::GetReservedSlot(obj, SLOT_FFITYPE);
   4768 
   4769  if (!slot.isUndefined()) {
   4770    return static_cast<ffi_type*>(slot.toPrivate());
   4771  }
   4772 
   4773  UniquePtrFFIType result;
   4774  switch (CType::GetTypeCode(obj)) {
   4775    case TYPE_array:
   4776      result = ArrayType::BuildFFIType(cx, obj);
   4777      break;
   4778 
   4779    case TYPE_struct:
   4780      result = StructType::BuildFFIType(cx, obj);
   4781      break;
   4782 
   4783    default:
   4784      MOZ_CRASH("simple types must have an ffi_type");
   4785  }
   4786 
   4787  if (!result) {
   4788    return nullptr;
   4789  }
   4790  JS_InitReservedSlot(obj, SLOT_FFITYPE, result.get(),
   4791                      JS::MemoryUse::CTypeFFIType);
   4792  return result.release();
   4793 }
   4794 
   4795 JSString* CType::GetName(JSContext* cx, HandleObject obj) {
   4796  MOZ_ASSERT(CType::IsCType(obj));
   4797 
   4798  Value string = JS::GetReservedSlot(obj, SLOT_NAME);
   4799  if (!string.isUndefined()) {
   4800    return string.toString();
   4801  }
   4802 
   4803  // Build the type name lazily.
   4804  JSString* name = BuildTypeName(cx, obj);
   4805  if (!name) {
   4806    return nullptr;
   4807  }
   4808  JS_SetReservedSlot(obj, SLOT_NAME, StringValue(name));
   4809  return name;
   4810 }
   4811 
   4812 JSObject* CType::GetProtoFromCtor(JSObject* obj, CTypeProtoSlot slot) {
   4813  // Get ctypes.{Pointer,Array,Struct}Type.prototype from a reserved slot
   4814  // on the type constructor.
   4815  Value protoslot = js::GetFunctionNativeReserved(obj, SLOT_FN_CTORPROTO);
   4816  JSObject* proto = &protoslot.toObject();
   4817  MOZ_ASSERT(proto);
   4818  MOZ_ASSERT(CType::IsCTypeProto(proto));
   4819 
   4820  // Get the desired prototype.
   4821  Value result = JS::GetReservedSlot(proto, slot);
   4822  return &result.toObject();
   4823 }
   4824 
   4825 JSObject* CType::GetProtoFromType(JSContext* cx, JSObject* objArg,
   4826                                  CTypeProtoSlot slot) {
   4827  MOZ_ASSERT(IsCType(objArg));
   4828  RootedObject obj(cx, objArg);
   4829 
   4830  // Get the prototype of the type object.
   4831  RootedObject proto(cx);
   4832  if (!JS_GetPrototype(cx, obj, &proto)) {
   4833    return nullptr;
   4834  }
   4835  MOZ_ASSERT(proto);
   4836  MOZ_ASSERT(CType::IsCTypeProto(proto));
   4837 
   4838  // Get the requested ctypes.{Pointer,Array,Struct,Function}Type.prototype.
   4839  Value result = JS::GetReservedSlot(proto, slot);
   4840  MOZ_ASSERT(result.isObject());
   4841  return &result.toObject();
   4842 }
   4843 
   4844 bool CType::IsCTypeOrProto(HandleValue v) {
   4845  if (!v.isObject()) {
   4846    return false;
   4847  }
   4848  JSObject* obj = &v.toObject();
   4849  return CType::IsCType(obj) || CType::IsCTypeProto(obj);
   4850 }
   4851 
   4852 bool CType::PrototypeGetter(JSContext* cx, const JS::CallArgs& args) {
   4853  RootedObject obj(cx, &args.thisv().toObject());
   4854  unsigned slot = CType::IsCTypeProto(obj) ? (unsigned)SLOT_OURDATAPROTO
   4855                                           : (unsigned)SLOT_PROTO;
   4856  args.rval().set(JS::GetReservedSlot(obj, slot));
   4857  MOZ_ASSERT(args.rval().isObject() || args.rval().isUndefined());
   4858  return true;
   4859 }
   4860 
   4861 bool CType::IsCType(HandleValue v) {
   4862  return v.isObject() && CType::IsCType(&v.toObject());
   4863 }
   4864 
   4865 bool CType::NameGetter(JSContext* cx, const JS::CallArgs& args) {
   4866  RootedObject obj(cx, &args.thisv().toObject());
   4867  JSString* name = CType::GetName(cx, obj);
   4868  if (!name) {
   4869    return false;
   4870  }
   4871 
   4872  args.rval().setString(name);
   4873  return true;
   4874 }
   4875 
   4876 bool CType::SizeGetter(JSContext* cx, const JS::CallArgs& args) {
   4877  RootedObject obj(cx, &args.thisv().toObject());
   4878  args.rval().set(JS::GetReservedSlot(obj, SLOT_SIZE));
   4879  MOZ_ASSERT(args.rval().isNumber() || args.rval().isUndefined());
   4880  return true;
   4881 }
   4882 
   4883 bool CType::PtrGetter(JSContext* cx, const JS::CallArgs& args) {
   4884  RootedObject obj(cx, &args.thisv().toObject());
   4885  JSObject* pointerType = PointerType::CreateInternal(cx, obj);
   4886  if (!pointerType) {
   4887    return false;
   4888  }
   4889 
   4890  args.rval().setObject(*pointerType);
   4891  return true;
   4892 }
   4893 
   4894 bool CType::CreateArray(JSContext* cx, unsigned argc, Value* vp) {
   4895  CallArgs args = CallArgsFromVp(argc, vp);
   4896  RootedObject baseType(cx, GetThisObject(cx, args, "CType.prototype.array"));
   4897  if (!baseType) {
   4898    return false;
   4899  }
   4900  if (!CType::IsCType(baseType)) {
   4901    return IncompatibleThisProto(cx, "CType.prototype.array", args.thisv());
   4902  }
   4903 
   4904  // Construct and return a new ArrayType object.
   4905  if (args.length() > 1) {
   4906    return ArgumentLengthError(cx, "CType.prototype.array", "at most one", "");
   4907  }
   4908 
   4909  // Convert the length argument to a size_t.
   4910  size_t length = 0;
   4911  if (args.length() == 1 && !jsvalToSize(cx, args[0], false, &length)) {
   4912    return ArgumentTypeMismatch(cx, "", "CType.prototype.array",
   4913                                "a nonnegative integer");
   4914  }
   4915 
   4916  JSObject* result =
   4917      ArrayType::CreateInternal(cx, baseType, length, args.length() == 1);
   4918  if (!result) {
   4919    return false;
   4920  }
   4921 
   4922  args.rval().setObject(*result);
   4923  return true;
   4924 }
   4925 
   4926 bool CType::ToString(JSContext* cx, unsigned argc, Value* vp) {
   4927  CallArgs args = CallArgsFromVp(argc, vp);
   4928  RootedObject obj(cx, GetThisObject(cx, args, "CType.prototype.toString"));
   4929  if (!obj) {
   4930    return false;
   4931  }
   4932  if (!CType::IsCType(obj) && !CType::IsCTypeProto(obj)) {
   4933    return IncompatibleThisProto(cx, "CType.prototype.toString",
   4934                                 InformalValueTypeName(args.thisv()));
   4935  }
   4936 
   4937  // Create the appropriate string depending on whether we're sCTypeClass or
   4938  // sCTypeProtoClass.
   4939  JSString* result;
   4940  if (CType::IsCType(obj)) {
   4941    AutoString type;
   4942    AppendString(cx, type, "type ");
   4943    AppendString(cx, type, GetName(cx, obj));
   4944    if (!type) {
   4945      return false;
   4946    }
   4947    result = NewUCString(cx, type.finish());
   4948  } else {
   4949    result = JS_NewStringCopyZ(cx, "[CType proto object]");
   4950  }
   4951  if (!result) {
   4952    return false;
   4953  }
   4954 
   4955  args.rval().setString(result);
   4956  return true;
   4957 }
   4958 
   4959 bool CType::ToSource(JSContext* cx, unsigned argc, Value* vp) {
   4960  CallArgs args = CallArgsFromVp(argc, vp);
   4961  JSObject* obj = GetThisObject(cx, args, "CType.prototype.toSource");
   4962  if (!obj) {
   4963    return false;
   4964  }
   4965  if (!CType::IsCType(obj) && !CType::IsCTypeProto(obj)) {
   4966    return IncompatibleThisProto(cx, "CType.prototype.toSource",
   4967                                 InformalValueTypeName(args.thisv()));
   4968  }
   4969 
   4970  // Create the appropriate string depending on whether we're sCTypeClass or
   4971  // sCTypeProtoClass.
   4972  JSString* result;
   4973  if (CType::IsCType(obj)) {
   4974    AutoString source;
   4975    BuildTypeSource(cx, obj, false, source);
   4976    if (!source) {
   4977      return false;
   4978    }
   4979    result = NewUCString(cx, source.finish());
   4980  } else {
   4981    result = JS_NewStringCopyZ(cx, "[CType proto object]");
   4982  }
   4983  if (!result) {
   4984    return false;
   4985  }
   4986 
   4987  args.rval().setString(result);
   4988  return true;
   4989 }
   4990 
   4991 static JSObject* CType::GetGlobalCTypes(JSContext* cx, JSObject* objArg) {
   4992  MOZ_ASSERT(CType::IsCType(objArg));
   4993 
   4994  RootedObject obj(cx, objArg);
   4995  RootedObject objTypeProto(cx);
   4996  if (!JS_GetPrototype(cx, obj, &objTypeProto)) {
   4997    return nullptr;
   4998  }
   4999  MOZ_ASSERT(objTypeProto);
   5000  MOZ_ASSERT(CType::IsCTypeProto(objTypeProto));
   5001 
   5002  Value valCTypes = JS::GetReservedSlot(objTypeProto, SLOT_CTYPES);
   5003  MOZ_ASSERT(valCTypes.isObject());
   5004  return &valCTypes.toObject();
   5005 }
   5006 
   5007 /*******************************************************************************
   5008 ** ABI implementation
   5009 *******************************************************************************/
   5010 
   5011 bool ABI::IsABI(JSObject* obj) { return obj->hasClass(&sCABIClass); }
   5012 
   5013 bool ABI::ToSource(JSContext* cx, unsigned argc, Value* vp) {
   5014  CallArgs args = CallArgsFromVp(argc, vp);
   5015  if (args.length() != 0) {
   5016    return ArgumentLengthError(cx, "ABI.prototype.toSource", "no", "s");
   5017  }
   5018 
   5019  JSObject* obj = GetThisObject(cx, args, "ABI.prototype.toSource");
   5020  if (!obj) {
   5021    return false;
   5022  }
   5023  if (!ABI::IsABI(obj)) {
   5024    return IncompatibleThisProto(cx, "ABI.prototype.toSource",
   5025                                 InformalValueTypeName(args.thisv()));
   5026  }
   5027 
   5028  JSString* result;
   5029  switch (GetABICode(obj)) {
   5030    case ABI_DEFAULT:
   5031      result = JS_NewStringCopyZ(cx, "ctypes.default_abi");
   5032      break;
   5033    case ABI_STDCALL:
   5034      result = JS_NewStringCopyZ(cx, "ctypes.stdcall_abi");
   5035      break;
   5036    case ABI_THISCALL:
   5037      result = JS_NewStringCopyZ(cx, "ctypes.thiscall_abi");
   5038      break;
   5039    case ABI_WINAPI:
   5040      result = JS_NewStringCopyZ(cx, "ctypes.winapi_abi");
   5041      break;
   5042    default:
   5043      JS_ReportErrorASCII(cx, "not a valid ABICode");
   5044      return false;
   5045  }
   5046  if (!result) {
   5047    return false;
   5048  }
   5049 
   5050  args.rval().setString(result);
   5051  return true;
   5052 }
   5053 
   5054 /*******************************************************************************
   5055 ** PointerType implementation
   5056 *******************************************************************************/
   5057 
   5058 bool PointerType::Create(JSContext* cx, unsigned argc, Value* vp) {
   5059  CallArgs args = CallArgsFromVp(argc, vp);
   5060  // Construct and return a new PointerType object.
   5061  if (args.length() != 1) {
   5062    return ArgumentLengthError(cx, "PointerType", "one", "");
   5063  }
   5064 
   5065  Value arg = args[0];
   5066  RootedObject obj(cx);
   5067  if (arg.isPrimitive() || !CType::IsCType(obj = &arg.toObject())) {
   5068    return ArgumentTypeMismatch(cx, "", "PointerType", "a CType");
   5069  }
   5070 
   5071  JSObject* result = CreateInternal(cx, obj);
   5072  if (!result) {
   5073    return false;
   5074  }
   5075 
   5076  args.rval().setObject(*result);
   5077  return true;
   5078 }
   5079 
   5080 JSObject* PointerType::CreateInternal(JSContext* cx, HandleObject baseType) {
   5081  // check if we have a cached PointerType on our base CType.
   5082  Value slot = JS::GetReservedSlot(baseType, SLOT_PTR);
   5083  if (!slot.isUndefined()) {
   5084    return &slot.toObject();
   5085  }
   5086 
   5087  // Get ctypes.PointerType.prototype and the common prototype for CData objects
   5088  // of this type, or ctypes.FunctionType.prototype for function pointers.
   5089  CTypeProtoSlot slotId = CType::GetTypeCode(baseType) == TYPE_function
   5090                              ? SLOT_FUNCTIONDATAPROTO
   5091                              : SLOT_POINTERDATAPROTO;
   5092  RootedObject dataProto(cx, CType::GetProtoFromType(cx, baseType, slotId));
   5093  if (!dataProto) {
   5094    return nullptr;
   5095  }
   5096  RootedObject typeProto(
   5097      cx, CType::GetProtoFromType(cx, baseType, SLOT_POINTERPROTO));
   5098  if (!typeProto) {
   5099    return nullptr;
   5100  }
   5101 
   5102  // Create a new CType object with the common properties and slots.
   5103  RootedValue sizeVal(cx, Int32Value(sizeof(void*)));
   5104  RootedValue alignVal(cx, Int32Value(ffi_type_pointer.alignment));
   5105  JSObject* typeObj =
   5106      CType::Create(cx, typeProto, dataProto, TYPE_pointer, nullptr, sizeVal,
   5107                    alignVal, &ffi_type_pointer);
   5108  if (!typeObj) {
   5109    return nullptr;
   5110  }
   5111 
   5112  // Set the target type. (This will be 'null' for an opaque pointer type.)
   5113  JS_SetReservedSlot(typeObj, SLOT_TARGET_T, ObjectValue(*baseType));
   5114 
   5115  // Finally, cache our newly-created PointerType on our pointed-to CType.
   5116  JS_SetReservedSlot(baseType, SLOT_PTR, ObjectValue(*typeObj));
   5117 
   5118  return typeObj;
   5119 }
   5120 
   5121 bool PointerType::ConstructData(JSContext* cx, HandleObject obj,
   5122                                const CallArgs& args) {
   5123  if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_pointer) {
   5124    return IncompatibleCallee(cx, "PointerType constructor", obj);
   5125  }
   5126 
   5127  if (args.length() > 3) {
   5128    return ArgumentLengthError(cx, "PointerType constructor", "0, 1, 2, or 3",
   5129                               "s");
   5130  }
   5131 
   5132  RootedObject result(cx, CData::Create(cx, obj, nullptr, nullptr, true));
   5133  if (!result) {
   5134    return false;
   5135  }
   5136 
   5137  // Set return value early, must not observe *vp after
   5138  args.rval().setObject(*result);
   5139 
   5140  // There are 3 things that we might be creating here:
   5141  // 1 - A null pointer (no arguments)
   5142  // 2 - An initialized pointer (1 argument)
   5143  // 3 - A closure (1-3 arguments)
   5144  //
   5145  // The API doesn't give us a perfect way to distinguish 2 and 3, but the
   5146  // heuristics we use should be fine.
   5147 
   5148  //
   5149  // Case 1 - Null pointer
   5150  //
   5151  if (args.length() == 0) {
   5152    return true;
   5153  }
   5154 
   5155  // Analyze the arguments a bit to decide what to do next.
   5156  RootedObject baseObj(cx, PointerType::GetBaseType(obj));
   5157  bool looksLikeClosure = CType::GetTypeCode(baseObj) == TYPE_function &&
   5158                          args[0].isObject() &&
   5159                          JS::IsCallable(&args[0].toObject());
   5160 
   5161  //
   5162  // Case 2 - Initialized pointer
   5163  //
   5164  if (!looksLikeClosure) {
   5165    if (args.length() != 1) {
   5166      return ArgumentLengthError(cx, "FunctionType constructor", "one", "");
   5167    }
   5168    return ExplicitConvert(cx, args[0], obj, CData::GetData(result),
   5169                           ConversionType::Construct);
   5170  }
   5171 
   5172  //
   5173  // Case 3 - Closure
   5174  //
   5175 
   5176  // The second argument is an optional 'this' parameter with which to invoke
   5177  // the given js function. Callers may leave this blank, or pass null if they
   5178  // wish to pass the third argument.
   5179  RootedObject thisObj(cx, nullptr);
   5180  if (args.length() >= 2) {
   5181    if (args[1].isNull()) {
   5182      thisObj = nullptr;
   5183    } else if (args[1].isObject()) {
   5184      thisObj = &args[1].toObject();
   5185    } else if (!JS_ValueToObject(cx, args[1], &thisObj)) {
   5186      return false;
   5187    }
   5188  }
   5189 
   5190  // The third argument is an optional error sentinel that js-ctypes will return
   5191  // if an exception is raised while executing the closure. The type must match
   5192  // the return type of the callback.
   5193  RootedValue errVal(cx);
   5194  if (args.length() == 3) {
   5195    errVal = args[2];
   5196  }
   5197 
   5198  RootedObject fnObj(cx, &args[0].toObject());
   5199  return FunctionType::ConstructData(cx, baseObj, result, fnObj, thisObj,
   5200                                     errVal);
   5201 }
   5202 
   5203 JSObject* PointerType::GetBaseType(JSObject* obj) {
   5204  MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_pointer);
   5205 
   5206  Value type = JS::GetReservedSlot(obj, SLOT_TARGET_T);
   5207  MOZ_ASSERT(!type.isNull());
   5208  return &type.toObject();
   5209 }
   5210 
   5211 bool PointerType::IsPointerType(HandleValue v) {
   5212  if (!v.isObject()) {
   5213    return false;
   5214  }
   5215  JSObject* obj = &v.toObject();
   5216  return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_pointer;
   5217 }
   5218 
   5219 bool PointerType::IsPointer(HandleValue v) {
   5220  if (!v.isObject()) {
   5221    return false;
   5222  }
   5223  JSObject* obj = MaybeUnwrapArrayWrapper(&v.toObject());
   5224  return CData::IsCData(obj) &&
   5225         CType::GetTypeCode(CData::GetCType(obj)) == TYPE_pointer;
   5226 }
   5227 
   5228 bool PointerType::TargetTypeGetter(JSContext* cx, const JS::CallArgs& args) {
   5229  RootedObject obj(cx, &args.thisv().toObject());
   5230  args.rval().set(JS::GetReservedSlot(obj, SLOT_TARGET_T));
   5231  MOZ_ASSERT(args.rval().isObject());
   5232  return true;
   5233 }
   5234 
   5235 bool PointerType::IsNull(JSContext* cx, unsigned argc, Value* vp) {
   5236  CallArgs args = CallArgsFromVp(argc, vp);
   5237  RootedObject obj(cx, GetThisObject(cx, args, "PointerType.prototype.isNull"));
   5238  if (!obj) {
   5239    return false;
   5240  }
   5241  if (!CData::IsCDataMaybeUnwrap(&obj)) {
   5242    return IncompatibleThisProto(cx, "PointerType.prototype.isNull",
   5243                                 args.thisv());
   5244  }
   5245 
   5246  // Get pointer type and base type.
   5247  JSObject* typeObj = CData::GetCType(obj);
   5248  if (CType::GetTypeCode(typeObj) != TYPE_pointer) {
   5249    return IncompatibleThisType(cx, "PointerType.prototype.isNull",
   5250                                "non-PointerType CData", args.thisv());
   5251  }
   5252 
   5253  void* data = *static_cast<void**>(CData::GetData(obj));
   5254  args.rval().setBoolean(data == nullptr);
   5255  return true;
   5256 }
   5257 
   5258 bool PointerType::OffsetBy(JSContext* cx, const CallArgs& args, int offset,
   5259                           const char* name) {
   5260  RootedObject obj(cx, GetThisObject(cx, args, name));
   5261  if (!obj) {
   5262    return false;
   5263  }
   5264  if (!CData::IsCDataMaybeUnwrap(&obj)) {
   5265    return IncompatibleThisProto(cx, name, args.thisv());
   5266  }
   5267 
   5268  RootedObject typeObj(cx, CData::GetCType(obj));
   5269  if (CType::GetTypeCode(typeObj) != TYPE_pointer) {
   5270    return IncompatibleThisType(cx, name, "non-PointerType CData",
   5271                                args.thisv());
   5272  }
   5273 
   5274  RootedObject baseType(cx, PointerType::GetBaseType(typeObj));
   5275  if (!CType::IsSizeDefined(baseType)) {
   5276    return UndefinedSizePointerError(cx, "modify", obj);
   5277  }
   5278 
   5279  size_t elementSize = CType::GetSize(baseType);
   5280  char* data = static_cast<char*>(*static_cast<void**>(CData::GetData(obj)));
   5281  void* address = data + offset * ptrdiff_t(elementSize);
   5282 
   5283  // Create a PointerType CData object containing the new address.
   5284  JSObject* result = CData::Create(cx, typeObj, nullptr, &address, true);
   5285  if (!result) {
   5286    return false;
   5287  }
   5288 
   5289  args.rval().setObject(*result);
   5290  return true;
   5291 }
   5292 
   5293 bool PointerType::Increment(JSContext* cx, unsigned argc, Value* vp) {
   5294  CallArgs args = CallArgsFromVp(argc, vp);
   5295  return OffsetBy(cx, args, 1, "PointerType.prototype.increment");
   5296 }
   5297 
   5298 bool PointerType::Decrement(JSContext* cx, unsigned argc, Value* vp) {
   5299  CallArgs args = CallArgsFromVp(argc, vp);
   5300  return OffsetBy(cx, args, -1, "PointerType.prototype.decrement");
   5301 }
   5302 
   5303 bool PointerType::ContentsGetter(JSContext* cx, const JS::CallArgs& args) {
   5304  RootedObject obj(cx, &args.thisv().toObject());
   5305  RootedObject baseType(cx, GetBaseType(CData::GetCType(obj)));
   5306  if (!CType::IsSizeDefined(baseType)) {
   5307    return UndefinedSizePointerError(cx, "get contents of", obj);
   5308  }
   5309 
   5310  void* data = *static_cast<void**>(CData::GetData(obj));
   5311  if (data == nullptr) {
   5312    return NullPointerError(cx, "read contents of", obj);
   5313  }
   5314 
   5315  RootedValue result(cx);
   5316  if (!ConvertToJS(cx, baseType, nullptr, data, false, false, &result)) {
   5317    return false;
   5318  }
   5319 
   5320  args.rval().set(result);
   5321  return true;
   5322 }
   5323 
   5324 bool PointerType::ContentsSetter(JSContext* cx, const JS::CallArgs& args) {
   5325  RootedObject obj(cx, &args.thisv().toObject());
   5326  RootedObject baseType(cx, GetBaseType(CData::GetCType(obj)));
   5327  if (!CType::IsSizeDefined(baseType)) {
   5328    return UndefinedSizePointerError(cx, "set contents of", obj);
   5329  }
   5330 
   5331  void* data = *static_cast<void**>(CData::GetData(obj));
   5332  if (data == nullptr) {
   5333    return NullPointerError(cx, "write contents to", obj);
   5334  }
   5335 
   5336  args.rval().setUndefined();
   5337  return ImplicitConvert(cx, args.get(0), baseType, data,
   5338                         ConversionType::Setter, nullptr);
   5339 }
   5340 
   5341 /*******************************************************************************
   5342 ** ArrayType implementation
   5343 *******************************************************************************/
   5344 
   5345 bool ArrayType::Create(JSContext* cx, unsigned argc, Value* vp) {
   5346  CallArgs args = CallArgsFromVp(argc, vp);
   5347  // Construct and return a new ArrayType object.
   5348  if (args.length() < 1 || args.length() > 2) {
   5349    return ArgumentLengthError(cx, "ArrayType", "one or two", "s");
   5350  }
   5351 
   5352  if (args[0].isPrimitive() || !CType::IsCType(&args[0].toObject())) {
   5353    return ArgumentTypeMismatch(cx, "first ", "ArrayType", "a CType");
   5354  }
   5355 
   5356  // Convert the length argument to a size_t.
   5357  size_t length = 0;
   5358  if (args.length() == 2 && !jsvalToSize(cx, args[1], false, &length)) {
   5359    return ArgumentTypeMismatch(cx, "second ", "ArrayType",
   5360                                "a nonnegative integer");
   5361  }
   5362 
   5363  RootedObject baseType(cx, &args[0].toObject());
   5364  JSObject* result = CreateInternal(cx, baseType, length, args.length() == 2);
   5365  if (!result) {
   5366    return false;
   5367  }
   5368 
   5369  args.rval().setObject(*result);
   5370  return true;
   5371 }
   5372 
   5373 JSObject* ArrayType::CreateInternal(JSContext* cx, HandleObject baseType,
   5374                                    size_t length, bool lengthDefined) {
   5375  // Get ctypes.ArrayType.prototype and the common prototype for CData objects
   5376  // of this type, from ctypes.CType.prototype.
   5377  RootedObject typeProto(
   5378      cx, CType::GetProtoFromType(cx, baseType, SLOT_ARRAYPROTO));
   5379  if (!typeProto) {
   5380    return nullptr;
   5381  }
   5382  RootedObject dataProto(
   5383      cx, CType::GetProtoFromType(cx, baseType, SLOT_ARRAYDATAPROTO));
   5384  if (!dataProto) {
   5385    return nullptr;
   5386  }
   5387 
   5388  // Determine the size of the array from the base type, if possible.
   5389  // The size of the base type must be defined.
   5390  // If our length is undefined, both our size and length will be undefined.
   5391  size_t baseSize;
   5392  if (!CType::GetSafeSize(baseType, &baseSize)) {
   5393    JS_ReportErrorASCII(cx, "base size must be defined");
   5394    return nullptr;
   5395  }
   5396 
   5397  RootedValue sizeVal(cx);
   5398  RootedValue lengthVal(cx);
   5399  if (lengthDefined) {
   5400    // Check for overflow, and convert to an int or double as required.
   5401    size_t size = length * baseSize;
   5402    if (length > 0 && size / length != baseSize) {
   5403      SizeOverflow(cx, "array size", "size_t");
   5404      return nullptr;
   5405    }
   5406    if (!SizeTojsval(cx, size, &sizeVal)) {
   5407      SizeOverflow(cx, "array size", "JavaScript number");
   5408      return nullptr;
   5409    }
   5410    if (!SizeTojsval(cx, length, &lengthVal)) {
   5411      SizeOverflow(cx, "array length", "JavaScript number");
   5412      return nullptr;
   5413    }
   5414  }
   5415 
   5416  RootedValue alignVal(cx, Int32Value(CType::GetAlignment(baseType)));
   5417 
   5418  // Create a new CType object with the common properties and slots.
   5419  JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_array,
   5420                                    nullptr, sizeVal, alignVal, nullptr);
   5421  if (!typeObj) {
   5422    return nullptr;
   5423  }
   5424 
   5425  // Set the element type.
   5426  JS_SetReservedSlot(typeObj, SLOT_ELEMENT_T, ObjectValue(*baseType));
   5427 
   5428  // Set the length.
   5429  JS_SetReservedSlot(typeObj, SLOT_LENGTH, lengthVal);
   5430 
   5431  return typeObj;
   5432 }
   5433 
   5434 bool ArrayType::ConstructData(JSContext* cx, HandleObject obj_,
   5435                              const CallArgs& args) {
   5436  RootedObject obj(cx, obj_);  // Make a mutable version
   5437 
   5438  if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_array) {
   5439    return IncompatibleCallee(cx, "ArrayType constructor", obj);
   5440  }
   5441 
   5442  // Decide whether we have an object to initialize from. We'll override this
   5443  // if we get a length argument instead.
   5444  bool convertObject = args.length() == 1;
   5445 
   5446  // Check if we're an array of undefined length. If we are, allow construction
   5447  // with a length argument, or with an actual JS array.
   5448  if (CType::IsSizeDefined(obj)) {
   5449    if (args.length() > 1) {
   5450      return ArgumentLengthError(cx, "size defined ArrayType constructor",
   5451                                 "at most one", "");
   5452    }
   5453 
   5454  } else {
   5455    if (args.length() != 1) {
   5456      return ArgumentLengthError(cx, "size undefined ArrayType constructor",
   5457                                 "one", "");
   5458    }
   5459 
   5460    RootedObject baseType(cx, GetBaseType(obj));
   5461 
   5462    size_t length;
   5463    if (jsvalToSize(cx, args[0], false, &length)) {
   5464      // Have a length, rather than an object to initialize from.
   5465      convertObject = false;
   5466 
   5467    } else if (args[0].isObject()) {
   5468      // We were given an object with a .length property.
   5469      // This could be a JS array, or a CData array.
   5470      RootedObject arg(cx, &args[0].toObject());
   5471      RootedValue lengthVal(cx);
   5472      if (!JS_GetProperty(cx, arg, "length", &lengthVal) ||
   5473          !jsvalToSize(cx, lengthVal, false, &length)) {
   5474        return ArgumentTypeMismatch(cx, "",
   5475                                    "size undefined ArrayType constructor",
   5476                                    "an array object or integer");
   5477      }
   5478 
   5479    } else if (args[0].isString()) {
   5480      // We were given a string. Size the array to the appropriate length,
   5481      // including space for the terminator.
   5482      JSString* sourceString = args[0].toString();
   5483      size_t sourceLength = sourceString->length();
   5484      Rooted<JSLinearString*> sourceLinear(cx, sourceString->ensureLinear(cx));
   5485      if (!sourceLinear) {
   5486        return false;
   5487      }
   5488 
   5489      switch (CType::GetTypeCode(baseType)) {
   5490        case TYPE_char:
   5491        case TYPE_signed_char:
   5492        case TYPE_unsigned_char: {
   5493          // Reject if unpaired surrogate characters are present.
   5494          if (!ReportErrorIfUnpairedSurrogatePresent(cx, sourceLinear)) {
   5495            return false;
   5496          }
   5497 
   5498          // Determine the UTF-8 length.
   5499          length = JS::GetDeflatedUTF8StringLength(sourceLinear);
   5500 
   5501          ++length;
   5502          break;
   5503        }
   5504        case TYPE_char16_t:
   5505          length = sourceLength + 1;
   5506          break;
   5507        default:
   5508          return ConvError(cx, obj, args[0], ConversionType::Construct);
   5509      }
   5510 
   5511    } else {
   5512      return ArgumentTypeMismatch(cx, "",
   5513                                  "size undefined ArrayType constructor",
   5514                                  "an array object or integer");
   5515    }
   5516 
   5517    // Construct a new ArrayType of defined length, for the new CData object.
   5518    obj = CreateInternal(cx, baseType, length, true);
   5519    if (!obj) {
   5520      return false;
   5521    }
   5522  }
   5523 
   5524  JSObject* result = CData::Create(cx, obj, nullptr, nullptr, true);
   5525  if (!result) {
   5526    return false;
   5527  }
   5528 
   5529  args.rval().setObject(*result);
   5530 
   5531  if (convertObject) {
   5532    if (!ExplicitConvert(cx, args[0], obj, CData::GetData(result),
   5533                         ConversionType::Construct))
   5534      return false;
   5535  }
   5536 
   5537  return true;
   5538 }
   5539 
   5540 JSObject* ArrayType::GetBaseType(JSObject* obj) {
   5541  MOZ_ASSERT(CType::IsCType(obj));
   5542  MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_array);
   5543 
   5544  Value type = JS::GetReservedSlot(obj, SLOT_ELEMENT_T);
   5545  MOZ_ASSERT(!type.isNull());
   5546  return &type.toObject();
   5547 }
   5548 
   5549 bool ArrayType::GetSafeLength(JSObject* obj, size_t* result) {
   5550  MOZ_ASSERT(CType::IsCType(obj));
   5551  MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_array);
   5552 
   5553  Value length = JS::GetReservedSlot(obj, SLOT_LENGTH);
   5554 
   5555  // The "length" property can be an int, a double, or JS::UndefinedValue()
   5556  // (for arrays of undefined length), and must always fit in a size_t.
   5557  if (length.isInt32()) {
   5558    *result = length.toInt32();
   5559    return true;
   5560  }
   5561  if (length.isDouble()) {
   5562    *result = Convert<size_t>(length.toDouble());
   5563    return true;
   5564  }
   5565 
   5566  MOZ_ASSERT(length.isUndefined());
   5567  return false;
   5568 }
   5569 
   5570 size_t ArrayType::GetLength(JSObject* obj) {
   5571  MOZ_ASSERT(CType::IsCType(obj));
   5572  MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_array);
   5573 
   5574  Value length = JS::GetReservedSlot(obj, SLOT_LENGTH);
   5575 
   5576  MOZ_ASSERT(!length.isUndefined());
   5577 
   5578  // The "length" property can be an int, a double, or JS::UndefinedValue()
   5579  // (for arrays of undefined length), and must always fit in a size_t.
   5580  // For callers who know it can never be JS::UndefinedValue(), return a size_t
   5581  // directly.
   5582  if (length.isInt32()) {
   5583    return length.toInt32();
   5584  }
   5585  return Convert<size_t>(length.toDouble());
   5586 }
   5587 
   5588 UniquePtrFFIType ArrayType::BuildFFIType(JSContext* cx, JSObject* obj) {
   5589  MOZ_ASSERT(CType::IsCType(obj));
   5590  MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_array);
   5591  MOZ_ASSERT(CType::IsSizeDefined(obj));
   5592 
   5593  JSObject* baseType = ArrayType::GetBaseType(obj);
   5594  ffi_type* ffiBaseType = CType::GetFFIType(cx, baseType);
   5595  if (!ffiBaseType) {
   5596    return nullptr;
   5597  }
   5598 
   5599  size_t length = ArrayType::GetLength(obj);
   5600 
   5601  // Create an ffi_type to represent the array. This is necessary for the case
   5602  // where the array is part of a struct. Since libffi has no intrinsic
   5603  // support for array types, we approximate it by creating a struct type
   5604  // with elements of type 'baseType' and with appropriate size and alignment
   5605  // values. It would be nice to not do all the work of setting up 'elements',
   5606  // but some libffi platforms currently require that it be meaningful. I'm
   5607  // looking at you, x86_64.
   5608  auto ffiType = cx->make_unique<ffi_type>();
   5609  if (!ffiType) {
   5610    return nullptr;
   5611  }
   5612 
   5613  ffiType->type = FFI_TYPE_STRUCT;
   5614  ffiType->size = CType::GetSize(obj);
   5615  ffiType->alignment = CType::GetAlignment(obj);
   5616  ffiType->elements = cx->pod_malloc<ffi_type*>(length + 1);
   5617  if (!ffiType->elements) {
   5618    return nullptr;
   5619  }
   5620 
   5621  for (size_t i = 0; i < length; ++i) {
   5622    ffiType->elements[i] = ffiBaseType;
   5623  }
   5624  ffiType->elements[length] = nullptr;
   5625 
   5626  return ffiType;
   5627 }
   5628 
   5629 bool ArrayType::IsArrayType(HandleValue v) {
   5630  if (!v.isObject()) {
   5631    return false;
   5632  }
   5633  JSObject* obj = &v.toObject();
   5634  return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_array;
   5635 }
   5636 
   5637 bool ArrayType::IsArrayOrArrayType(HandleValue v) {
   5638  if (!v.isObject()) {
   5639    return false;
   5640  }
   5641  JSObject* obj = MaybeUnwrapArrayWrapper(&v.toObject());
   5642 
   5643  // Allow both CTypes and CDatas of the ArrayType persuasion by extracting the
   5644  // CType if we're dealing with a CData.
   5645  if (CData::IsCData(obj)) {
   5646    obj = CData::GetCType(obj);
   5647  }
   5648  return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_array;
   5649 }
   5650 
   5651 bool ArrayType::ElementTypeGetter(JSContext* cx, const JS::CallArgs& args) {
   5652  RootedObject obj(cx, &args.thisv().toObject());
   5653  args.rval().set(JS::GetReservedSlot(obj, SLOT_ELEMENT_T));
   5654  MOZ_ASSERT(args.rval().isObject());
   5655  return true;
   5656 }
   5657 
   5658 bool ArrayType::LengthGetter(JSContext* cx, const JS::CallArgs& args) {
   5659  RootedObject obj(cx, &args.thisv().toObject());
   5660 
   5661  // This getter exists for both CTypes and CDatas of the ArrayType persuasion.
   5662  // If we're dealing with a CData, get the CType from it.
   5663  if (CData::IsCDataMaybeUnwrap(&obj)) {
   5664    obj = CData::GetCType(obj);
   5665  }
   5666 
   5667  args.rval().set(JS::GetReservedSlot(obj, SLOT_LENGTH));
   5668  MOZ_ASSERT(args.rval().isNumber() || args.rval().isUndefined());
   5669  return true;
   5670 }
   5671 
   5672 bool ArrayType::Getter(JSContext* cx, HandleObject obj, HandleId idval,
   5673                       MutableHandleValue vp, bool* handled) {
   5674  *handled = false;
   5675 
   5676  // This should never happen, but we'll check to be safe.
   5677  if (!CData::IsCData(obj)) {
   5678    RootedValue objVal(cx, ObjectValue(*obj));
   5679    return IncompatibleThisProto(cx, "ArrayType property getter", objVal);
   5680  }
   5681 
   5682  // Bail early if we're not an ArrayType. (This setter is present for all
   5683  // CData, regardless of CType.)
   5684  JSObject* typeObj = CData::GetCType(obj);
   5685  if (CType::GetTypeCode(typeObj) != TYPE_array) {
   5686    return true;
   5687  }
   5688 
   5689  // Convert the index to a size_t and bounds-check it.
   5690  size_t index;
   5691  size_t length = GetLength(typeObj);
   5692  bool ok = jsidToSize(cx, idval, true, &index);
   5693  int32_t dummy;
   5694  if (!ok && idval.isSymbol()) {
   5695    return true;
   5696  }
   5697  bool dummy2;
   5698  if (!ok && idval.isString() &&
   5699      !StringToInteger(cx, idval.toString(), &dummy, &dummy2)) {
   5700    // String either isn't a number, or doesn't fit in size_t.
   5701    // Chances are it's a regular property lookup, so return.
   5702    return true;
   5703  }
   5704  if (!ok) {
   5705    return InvalidIndexError(cx, idval);
   5706  }
   5707  if (index >= length) {
   5708    return InvalidIndexRangeError(cx, index, length);
   5709  }
   5710 
   5711  *handled = true;
   5712 
   5713  RootedObject baseType(cx, GetBaseType(typeObj));
   5714  size_t elementSize = CType::GetSize(baseType);
   5715  char* data = static_cast<char*>(CData::GetData(obj)) + elementSize * index;
   5716  return ConvertToJS(cx, baseType, obj, data, false, false, vp);
   5717 }
   5718 
   5719 bool ArrayType::Setter(JSContext* cx, HandleObject obj, HandleId idval,
   5720                       HandleValue vp, ObjectOpResult& result, bool* handled) {
   5721  *handled = false;
   5722 
   5723  // This should never happen, but we'll check to be safe.
   5724  if (!CData::IsCData(obj)) {
   5725    RootedValue objVal(cx, ObjectValue(*obj));
   5726    return IncompatibleThisProto(cx, "ArrayType property setter", objVal);
   5727  }
   5728 
   5729  // Bail early if we're not an ArrayType. (This setter is present for all
   5730  // CData, regardless of CType.)
   5731  RootedObject typeObj(cx, CData::GetCType(obj));
   5732  if (CType::GetTypeCode(typeObj) != TYPE_array) {
   5733    return result.succeed();
   5734  }
   5735 
   5736  // Convert the index to a size_t and bounds-check it.
   5737  size_t index;
   5738  size_t length = GetLength(typeObj);
   5739  bool ok = jsidToSize(cx, idval, true, &index);
   5740  int32_t dummy;
   5741  if (!ok && idval.isSymbol()) {
   5742    return true;
   5743  }
   5744  bool dummy2;
   5745  if (!ok && idval.isString() &&
   5746      !StringToInteger(cx, idval.toString(), &dummy, &dummy2)) {
   5747    // String either isn't a number, or doesn't fit in size_t.
   5748    // Chances are it's a regular property lookup, so return.
   5749    return result.succeed();
   5750  }
   5751  if (!ok) {
   5752    return InvalidIndexError(cx, idval);
   5753  }
   5754  if (index >= length) {
   5755    return InvalidIndexRangeError(cx, index, length);
   5756  }
   5757 
   5758  *handled = true;
   5759 
   5760  RootedObject baseType(cx, GetBaseType(typeObj));
   5761  size_t elementSize = CType::GetSize(baseType);
   5762  char* data = static_cast<char*>(CData::GetData(obj)) + elementSize * index;
   5763  if (!ImplicitConvert(cx, vp, baseType, data, ConversionType::Setter, nullptr,
   5764                       nullptr, 0, typeObj, index))
   5765    return false;
   5766  return result.succeed();
   5767 }
   5768 
   5769 bool ArrayType::AddressOfElement(JSContext* cx, unsigned argc, Value* vp) {
   5770  CallArgs args = CallArgsFromVp(argc, vp);
   5771  RootedObject obj(
   5772      cx, GetThisObject(cx, args, "ArrayType.prototype.addressOfElement"));
   5773  if (!obj) {
   5774    return false;
   5775  }
   5776  if (!CData::IsCDataMaybeUnwrap(&obj)) {
   5777    return IncompatibleThisProto(cx, "ArrayType.prototype.addressOfElement",
   5778                                 args.thisv());
   5779  }
   5780 
   5781  RootedObject typeObj(cx, CData::GetCType(obj));
   5782  if (CType::GetTypeCode(typeObj) != TYPE_array) {
   5783    return IncompatibleThisType(cx, "ArrayType.prototype.addressOfElement",
   5784                                "non-ArrayType CData", args.thisv());
   5785  }
   5786 
   5787  if (args.length() != 1) {
   5788    return ArgumentLengthError(cx, "ArrayType.prototype.addressOfElement",
   5789                               "one", "");
   5790  }
   5791 
   5792  RootedObject baseType(cx, GetBaseType(typeObj));
   5793  RootedObject pointerType(cx, PointerType::CreateInternal(cx, baseType));
   5794  if (!pointerType) {
   5795    return false;
   5796  }
   5797 
   5798  // Create a PointerType CData object containing null.
   5799  RootedObject result(cx,
   5800                      CData::Create(cx, pointerType, nullptr, nullptr, true));
   5801  if (!result) {
   5802    return false;
   5803  }
   5804 
   5805  args.rval().setObject(*result);
   5806 
   5807  // Convert the index to a size_t and bounds-check it.
   5808  size_t index;
   5809  size_t length = GetLength(typeObj);
   5810  if (!jsvalToSize(cx, args[0], false, &index)) {
   5811    return InvalidIndexError(cx, args[0]);
   5812  }
   5813  if (index >= length) {
   5814    return InvalidIndexRangeError(cx, index, length);
   5815  }
   5816 
   5817  // Manually set the pointer inside the object, so we skip the conversion step.
   5818  void** data = static_cast<void**>(CData::GetData(result));
   5819  size_t elementSize = CType::GetSize(baseType);
   5820  *data = static_cast<char*>(CData::GetData(obj)) + elementSize * index;
   5821  return true;
   5822 }
   5823 
   5824 /*******************************************************************************
   5825 ** StructType implementation
   5826 *******************************************************************************/
   5827 
   5828 // For a struct field descriptor 'val' of the form { name : type }, extract
   5829 // 'name' and 'type'.
   5830 static JSLinearString* ExtractStructField(JSContext* cx, HandleValue val,
   5831                                          MutableHandleObject typeObj) {
   5832  if (val.isPrimitive()) {
   5833    FieldDescriptorNameTypeError(cx, val);
   5834    return nullptr;
   5835  }
   5836 
   5837  RootedObject obj(cx, &val.toObject());
   5838  Rooted<IdVector> props(cx, IdVector(cx));
   5839  if (!JS_Enumerate(cx, obj, &props)) {
   5840    return nullptr;
   5841  }
   5842 
   5843  // make sure we have one, and only one, property
   5844  if (props.length() != 1) {
   5845    FieldDescriptorCountError(cx, val, props.length());
   5846    return nullptr;
   5847  }
   5848 
   5849  RootedId nameid(cx, props[0]);
   5850  if (!nameid.isString()) {
   5851    FieldDescriptorNameError(cx, nameid);
   5852    return nullptr;
   5853  }
   5854 
   5855  RootedValue propVal(cx);
   5856  if (!JS_GetPropertyById(cx, obj, nameid, &propVal)) {
   5857    return nullptr;
   5858  }
   5859 
   5860  if (propVal.isPrimitive() || !CType::IsCType(&propVal.toObject())) {
   5861    FieldDescriptorTypeError(cx, propVal, nameid);
   5862    return nullptr;
   5863  }
   5864 
   5865  // Undefined size or zero size struct members are illegal.
   5866  // (Zero-size arrays are legal as struct members in C++, but libffi will
   5867  // choke on a zero-size struct, so we disallow them.)
   5868  typeObj.set(&propVal.toObject());
   5869  size_t size;
   5870  if (!CType::GetSafeSize(typeObj, &size) || size == 0) {
   5871    FieldDescriptorSizeError(cx, typeObj, nameid);
   5872    return nullptr;
   5873  }
   5874 
   5875  return nameid.toLinearString();
   5876 }
   5877 
   5878 // For a struct field with 'name' and 'type', add an element of the form
   5879 // { name : type }.
   5880 static bool AddFieldToArray(JSContext* cx, MutableHandleValue element,
   5881                            JSLinearString* name_, JSObject* typeObj_) {
   5882  RootedObject typeObj(cx, typeObj_);
   5883  Rooted<JSLinearString*> name(cx, name_);
   5884  RootedObject fieldObj(cx, JS_NewPlainObject(cx));
   5885  if (!fieldObj) {
   5886    return false;
   5887  }
   5888 
   5889  element.setObject(*fieldObj);
   5890 
   5891  AutoStableStringChars nameChars(cx);
   5892  if (!nameChars.initTwoByte(cx, name)) {
   5893    return false;
   5894  }
   5895 
   5896  if (!JS_DefineUCProperty(
   5897          cx, fieldObj, nameChars.twoByteChars(), name->length(), typeObj,
   5898          JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
   5899    return false;
   5900 
   5901  return JS_FreezeObject(cx, fieldObj);
   5902 }
   5903 
   5904 bool StructType::Create(JSContext* cx, unsigned argc, Value* vp) {
   5905  CallArgs args = CallArgsFromVp(argc, vp);
   5906 
   5907  // Construct and return a new StructType object.
   5908  if (args.length() < 1 || args.length() > 2) {
   5909    return ArgumentLengthError(cx, "StructType", "one or two", "s");
   5910  }
   5911 
   5912  Value name = args[0];
   5913  if (!name.isString()) {
   5914    return ArgumentTypeMismatch(cx, "first ", "StructType", "a string");
   5915  }
   5916 
   5917  // Get ctypes.StructType.prototype from the ctypes.StructType constructor.
   5918  RootedObject typeProto(
   5919      cx, CType::GetProtoFromCtor(&args.callee(), SLOT_STRUCTPROTO));
   5920 
   5921  // Create a simple StructType with no defined fields. The result will be
   5922  // non-instantiable as CData, will have no 'prototype' property, and will
   5923  // have undefined size and alignment and no ffi_type.
   5924  RootedObject result(
   5925      cx, CType::Create(cx, typeProto, nullptr, TYPE_struct, name.toString(),
   5926                        JS::UndefinedHandleValue, JS::UndefinedHandleValue,
   5927                        nullptr));
   5928  if (!result) {
   5929    return false;
   5930  }
   5931 
   5932  if (args.length() == 2) {
   5933    RootedObject arr(cx, args[1].isObject() ? &args[1].toObject() : nullptr);
   5934    bool isArray;
   5935    if (!arr) {
   5936      isArray = false;
   5937    } else {
   5938      if (!JS::IsArrayObject(cx, arr, &isArray)) {
   5939        return false;
   5940      }
   5941    }
   5942    if (!isArray) {
   5943      return ArgumentTypeMismatch(cx, "second ", "StructType", "an array");
   5944    }
   5945 
   5946    // Define the struct fields.
   5947    if (!DefineInternal(cx, result, arr)) {
   5948      return false;
   5949    }
   5950  }
   5951 
   5952  args.rval().setObject(*result);
   5953  return true;
   5954 }
   5955 
   5956 bool StructType::DefineInternal(JSContext* cx, JSObject* typeObj_,
   5957                                JSObject* fieldsObj_) {
   5958  RootedObject typeObj(cx, typeObj_);
   5959  RootedObject fieldsObj(cx, fieldsObj_);
   5960 
   5961  uint32_t len;
   5962  MOZ_ALWAYS_TRUE(JS::GetArrayLength(cx, fieldsObj, &len));
   5963 
   5964  // Get the common prototype for CData objects of this type from
   5965  // ctypes.CType.prototype.
   5966  RootedObject dataProto(
   5967      cx, CType::GetProtoFromType(cx, typeObj, SLOT_STRUCTDATAPROTO));
   5968  if (!dataProto) {
   5969    return false;
   5970  }
   5971 
   5972  // Set up the 'prototype' and 'prototype.constructor' properties.
   5973  // The prototype will reflect the struct fields as properties on CData objects
   5974  // created from this type.
   5975  RootedObject prototype(
   5976      cx, JS_NewObjectWithGivenProto(cx, &sCDataProtoClass, dataProto));
   5977  if (!prototype) {
   5978    return false;
   5979  }
   5980 
   5981  if (!JS_DefineProperty(cx, prototype, "constructor", typeObj,
   5982                         JSPROP_READONLY | JSPROP_PERMANENT))
   5983    return false;
   5984 
   5985  // Create a FieldInfoHash to stash on the type object.
   5986  Rooted<FieldInfoHash> fields(cx, FieldInfoHash(cx->zone(), len));
   5987 
   5988  // Process the field types.
   5989  size_t structSize, structAlign;
   5990  if (len != 0) {
   5991    structSize = 0;
   5992    structAlign = 0;
   5993 
   5994    for (uint32_t i = 0; i < len; ++i) {
   5995      RootedValue item(cx);
   5996      if (!JS_GetElement(cx, fieldsObj, i, &item)) {
   5997        return false;
   5998      }
   5999 
   6000      RootedObject fieldType(cx, nullptr);
   6001      Rooted<JSLinearString*> name(cx,
   6002                                   ExtractStructField(cx, item, &fieldType));
   6003      if (!name) {
   6004        return false;
   6005      }
   6006 
   6007      // Make sure each field name is unique
   6008      FieldInfoHash::AddPtr entryPtr = fields.lookupForAdd(name);
   6009      if (entryPtr) {
   6010        return DuplicateFieldError(cx, name);
   6011      }
   6012 
   6013      // Add the field to the StructType's 'prototype' property.
   6014      AutoStableStringChars nameChars(cx);
   6015      if (!nameChars.initTwoByte(cx, name)) {
   6016        return false;
   6017      }
   6018 
   6019      RootedFunction getter(
   6020          cx,
   6021          NewFunctionWithReserved(cx, StructType::FieldGetter, 0, 0, nullptr));
   6022      if (!getter) {
   6023        return false;
   6024      }
   6025      SetFunctionNativeReserved(getter, StructType::SLOT_FIELDNAME,
   6026                                StringValue(JS_FORGET_STRING_LINEARNESS(name)));
   6027      RootedObject getterObj(cx, JS_GetFunctionObject(getter));
   6028 
   6029      RootedFunction setter(
   6030          cx,
   6031          NewFunctionWithReserved(cx, StructType::FieldSetter, 1, 0, nullptr));
   6032      if (!setter) {
   6033        return false;
   6034      }
   6035      SetFunctionNativeReserved(setter, StructType::SLOT_FIELDNAME,
   6036                                StringValue(JS_FORGET_STRING_LINEARNESS(name)));
   6037      RootedObject setterObj(cx, JS_GetFunctionObject(setter));
   6038 
   6039      if (!JS_DefineUCProperty(cx, prototype, nameChars.twoByteChars(),
   6040                               name->length(), getterObj, setterObj,
   6041                               JSPROP_ENUMERATE | JSPROP_PERMANENT)) {
   6042        return false;
   6043      }
   6044 
   6045      size_t fieldSize = CType::GetSize(fieldType);
   6046      size_t fieldAlign = CType::GetAlignment(fieldType);
   6047      size_t fieldOffset = Align(structSize, fieldAlign);
   6048      // Check for overflow. Since we hold invariant that fieldSize % fieldAlign
   6049      // be zero, we can safely check fieldOffset + fieldSize without first
   6050      // checking fieldOffset for overflow.
   6051      if (fieldOffset + fieldSize < structSize) {
   6052        SizeOverflow(cx, "struct size", "size_t");
   6053        return false;
   6054      }
   6055 
   6056      // Add field name to the hash
   6057      FieldInfo info;
   6058      info.mType = fieldType;
   6059      info.mIndex = i;
   6060      info.mOffset = fieldOffset;
   6061      if (!fields.add(entryPtr, name, info)) {
   6062        JS_ReportOutOfMemory(cx);
   6063        return false;
   6064      }
   6065 
   6066      structSize = fieldOffset + fieldSize;
   6067 
   6068      if (fieldAlign > structAlign) {
   6069        structAlign = fieldAlign;
   6070      }
   6071    }
   6072 
   6073    // Pad the struct tail according to struct alignment.
   6074    size_t structTail = Align(structSize, structAlign);
   6075    if (structTail < structSize) {
   6076      SizeOverflow(cx, "struct size", "size_t");
   6077      return false;
   6078    }
   6079    structSize = structTail;
   6080 
   6081  } else {
   6082    // Empty structs are illegal in C, but are legal and have a size of
   6083    // 1 byte in C++. We're going to allow them, and trick libffi into
   6084    // believing this by adding a char member. The resulting struct will have
   6085    // no getters or setters, and will be initialized to zero.
   6086    structSize = 1;
   6087    structAlign = 1;
   6088  }
   6089 
   6090  RootedValue sizeVal(cx);
   6091  if (!SizeTojsval(cx, structSize, &sizeVal)) {
   6092    SizeOverflow(cx, "struct size", "double");
   6093    return false;
   6094  }
   6095 
   6096  // Move the field hash to the heap and store it in the typeObj.
   6097  FieldInfoHash* heapHash = cx->new_<FieldInfoHash>(std::move(fields.get()));
   6098  if (!heapHash) {
   6099    JS_ReportOutOfMemory(cx);
   6100    return false;
   6101  }
   6102  JS_InitReservedSlot(typeObj, SLOT_FIELDINFO, heapHash,
   6103                      JS::MemoryUse::CTypeFieldInfo);
   6104  JS_SetReservedSlot(typeObj, SLOT_SIZE, sizeVal);
   6105  JS_SetReservedSlot(typeObj, SLOT_ALIGN, Int32Value(structAlign));
   6106  // if (!JS_FreezeObject(cx, prototype)0 // XXX fixme - see bug 541212!
   6107  //  return false;
   6108  JS_SetReservedSlot(typeObj, SLOT_PROTO, ObjectValue(*prototype));
   6109  return true;
   6110 }
   6111 
   6112 UniquePtrFFIType StructType::BuildFFIType(JSContext* cx, JSObject* obj) {
   6113  MOZ_ASSERT(CType::IsCType(obj));
   6114  MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_struct);
   6115  MOZ_ASSERT(CType::IsSizeDefined(obj));
   6116 
   6117  const FieldInfoHash* fields = GetFieldInfo(obj);
   6118  size_t len = fields->count();
   6119 
   6120  size_t structSize = CType::GetSize(obj);
   6121  size_t structAlign = CType::GetAlignment(obj);
   6122 
   6123  auto ffiType = cx->make_unique<ffi_type>();
   6124  if (!ffiType) {
   6125    return nullptr;
   6126  }
   6127  ffiType->type = FFI_TYPE_STRUCT;
   6128 
   6129  size_t count = len != 0 ? len + 1 : 2;
   6130  auto elements = cx->make_pod_array<ffi_type*>(count);
   6131  if (!elements) {
   6132    return nullptr;
   6133  }
   6134 
   6135  if (len != 0) {
   6136    elements[len] = nullptr;
   6137 
   6138    for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
   6139      const FieldInfoHash::Entry& entry = r.front();
   6140      ffi_type* fieldType = CType::GetFFIType(cx, entry.value().mType);
   6141      if (!fieldType) {
   6142        return nullptr;
   6143      }
   6144      elements[entry.value().mIndex] = fieldType;
   6145    }
   6146  } else {
   6147    // Represent an empty struct as having a size of 1 byte, just like C++.
   6148    MOZ_ASSERT(structSize == 1);
   6149    MOZ_ASSERT(structAlign == 1);
   6150    elements[0] = &ffi_type_uint8;
   6151    elements[1] = nullptr;
   6152  }
   6153 
   6154  ffiType->elements = elements.release();
   6155  AddCellMemory(obj, count * sizeof(ffi_type*),
   6156                MemoryUse::CTypeFFITypeElements);
   6157 
   6158 #ifdef DEBUG
   6159  // Perform a sanity check: the result of our struct size and alignment
   6160  // calculations should match libffi's. We force it to do this calculation
   6161  // by calling ffi_prep_cif.
   6162  ffi_cif cif;
   6163  ffiType->size = 0;
   6164  ffiType->alignment = 0;
   6165  ffi_status status =
   6166      ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 0, ffiType.get(), nullptr);
   6167  MOZ_ASSERT(status == FFI_OK);
   6168  MOZ_ASSERT(structSize == ffiType->size);
   6169  MOZ_ASSERT(structAlign == ffiType->alignment);
   6170 #else
   6171  // Fill in the ffi_type's size and align fields. This makes libffi treat the
   6172  // type as initialized; it will not recompute the values. (We assume
   6173  // everything agrees; if it doesn't, we really want to know about it, which
   6174  // is the purpose of the above debug-only check.)
   6175  ffiType->size = structSize;
   6176  ffiType->alignment = structAlign;
   6177 #endif
   6178 
   6179  return ffiType;
   6180 }
   6181 
   6182 bool StructType::Define(JSContext* cx, unsigned argc, Value* vp) {
   6183  CallArgs args = CallArgsFromVp(argc, vp);
   6184  RootedObject obj(cx, GetThisObject(cx, args, "StructType.prototype.define"));
   6185  if (!obj) {
   6186    return false;
   6187  }
   6188  if (!CType::IsCType(obj)) {
   6189    return IncompatibleThisProto(cx, "StructType.prototype.define",
   6190                                 args.thisv());
   6191  }
   6192  if (CType::GetTypeCode(obj) != TYPE_struct) {
   6193    return IncompatibleThisType(cx, "StructType.prototype.define",
   6194                                "non-StructType", args.thisv());
   6195  }
   6196 
   6197  if (CType::IsSizeDefined(obj)) {
   6198    JS_ReportErrorASCII(cx, "StructType has already been defined");
   6199    return false;
   6200  }
   6201 
   6202  if (args.length() != 1) {
   6203    return ArgumentLengthError(cx, "StructType.prototype.define", "one", "");
   6204  }
   6205 
   6206  HandleValue arg = args[0];
   6207  if (arg.isPrimitive()) {
   6208    return ArgumentTypeMismatch(cx, "", "StructType.prototype.define",
   6209                                "an array");
   6210  }
   6211 
   6212  bool isArray;
   6213  if (!arg.isObject()) {
   6214    isArray = false;
   6215  } else {
   6216    if (!JS::IsArrayObject(cx, arg, &isArray)) {
   6217      return false;
   6218    }
   6219  }
   6220 
   6221  if (!isArray) {
   6222    return ArgumentTypeMismatch(cx, "", "StructType.prototype.define",
   6223                                "an array");
   6224  }
   6225 
   6226  RootedObject arr(cx, &arg.toObject());
   6227  return DefineInternal(cx, obj, arr);
   6228 }
   6229 
   6230 bool StructType::ConstructData(JSContext* cx, HandleObject obj,
   6231                               const CallArgs& args) {
   6232  if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_struct) {
   6233    return IncompatibleCallee(cx, "StructType constructor", obj);
   6234  }
   6235 
   6236  if (!CType::IsSizeDefined(obj)) {
   6237    JS_ReportErrorASCII(cx, "cannot construct an opaque StructType");
   6238    return false;
   6239  }
   6240 
   6241  JSObject* result = CData::Create(cx, obj, nullptr, nullptr, true);
   6242  if (!result) {
   6243    return false;
   6244  }
   6245 
   6246  args.rval().setObject(*result);
   6247 
   6248  if (args.length() == 0) {
   6249    return true;
   6250  }
   6251 
   6252  char* buffer = static_cast<char*>(CData::GetData(result));
   6253  const FieldInfoHash* fields = GetFieldInfo(obj);
   6254 
   6255  if (args.length() == 1) {
   6256    // There are two possible interpretations of the argument:
   6257    // 1) It may be an object '{ ... }' with properties representing the
   6258    //    struct fields intended to ExplicitConvert wholesale to our StructType.
   6259    // 2) If the struct contains one field, the arg may be intended to
   6260    //    ImplicitConvert directly to that arg's CType.
   6261    // Thankfully, the conditions for these two possibilities to succeed
   6262    // are mutually exclusive, so we can pick the right one.
   6263 
   6264    // Try option 1) first.
   6265    if (ExplicitConvert(cx, args[0], obj, buffer, ConversionType::Construct)) {
   6266      return true;
   6267    }
   6268 
   6269    if (fields->count() != 1) {
   6270      return false;
   6271    }
   6272 
   6273    // If ExplicitConvert failed, and there is no pending exception, then assume
   6274    // hard failure (out of memory, or some other similarly serious condition).
   6275    if (!JS_IsExceptionPending(cx)) {
   6276      return false;
   6277    }
   6278 
   6279    // Otherwise, assume soft failure, and clear the pending exception so that
   6280    // we can throw a different one as required.
   6281    JS_ClearPendingException(cx);
   6282 
   6283    // Fall through to try option 2).
   6284  }
   6285 
   6286  // We have a type constructor of the form 'ctypes.StructType(a, b, c, ...)'.
   6287  // ImplicitConvert each field.
   6288  if (args.length() == fields->count()) {
   6289    for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
   6290      const FieldInfo& field = r.front().value();
   6291      MOZ_ASSERT(field.mIndex < fields->count()); /* Quantified invariant */
   6292      if (!ImplicitConvert(cx, args[field.mIndex], field.mType,
   6293                           buffer + field.mOffset, ConversionType::Construct,
   6294                           nullptr, nullptr, 0, obj, field.mIndex))
   6295        return false;
   6296    }
   6297 
   6298    return true;
   6299  }
   6300 
   6301  size_t count = fields->count();
   6302  if (count >= 2) {
   6303    char fieldLengthStr[32];
   6304    SprintfLiteral(fieldLengthStr, "0, 1, or %zu", count);
   6305    return ArgumentLengthError(cx, "StructType constructor", fieldLengthStr,
   6306                               "s");
   6307  }
   6308  return ArgumentLengthError(cx, "StructType constructor", "at most one", "");
   6309 }
   6310 
   6311 const FieldInfoHash* StructType::GetFieldInfo(JSObject* obj) {
   6312  MOZ_ASSERT(CType::IsCType(obj));
   6313  MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_struct);
   6314 
   6315  Value slot = JS::GetReservedSlot(obj, SLOT_FIELDINFO);
   6316  MOZ_ASSERT(!slot.isUndefined() && slot.toPrivate());
   6317 
   6318  return static_cast<const FieldInfoHash*>(slot.toPrivate());
   6319 }
   6320 
   6321 const FieldInfo* StructType::LookupField(JSContext* cx, JSObject* obj,
   6322                                         JSLinearString* name) {
   6323  MOZ_ASSERT(CType::IsCType(obj));
   6324  MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_struct);
   6325 
   6326  FieldInfoHash::Ptr ptr = GetFieldInfo(obj)->lookup(name);
   6327  if (ptr) {
   6328    return &ptr->value();
   6329  }
   6330 
   6331  FieldMissingError(cx, obj, name);
   6332  return nullptr;
   6333 }
   6334 
   6335 JSObject* StructType::BuildFieldsArray(JSContext* cx, JSObject* obj) {
   6336  MOZ_ASSERT(CType::IsCType(obj));
   6337  MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_struct);
   6338  MOZ_ASSERT(CType::IsSizeDefined(obj));
   6339 
   6340  const FieldInfoHash* fields = GetFieldInfo(obj);
   6341  size_t len = fields->count();
   6342 
   6343  // Prepare a new array for the 'fields' property of the StructType.
   6344  JS::RootedValueVector fieldsVec(cx);
   6345  if (!fieldsVec.resize(len)) {
   6346    return nullptr;
   6347  }
   6348 
   6349  for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
   6350    const FieldInfoHash::Entry& entry = r.front();
   6351    // Add the field descriptor to the array.
   6352    if (!AddFieldToArray(cx, fieldsVec[entry.value().mIndex], entry.key(),
   6353                         entry.value().mType))
   6354      return nullptr;
   6355  }
   6356 
   6357  RootedObject fieldsProp(cx, JS::NewArrayObject(cx, fieldsVec));
   6358  if (!fieldsProp) {
   6359    return nullptr;
   6360  }
   6361 
   6362  // Seal the fields array.
   6363  if (!JS_FreezeObject(cx, fieldsProp)) {
   6364    return nullptr;
   6365  }
   6366 
   6367  return fieldsProp;
   6368 }
   6369 
   6370 /* static */
   6371 bool StructType::IsStruct(HandleValue v) {
   6372  if (!v.isObject()) {
   6373    return false;
   6374  }
   6375  JSObject* obj = &v.toObject();
   6376  return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_struct;
   6377 }
   6378 
   6379 bool StructType::FieldsArrayGetter(JSContext* cx, const JS::CallArgs& args) {
   6380  RootedObject obj(cx, &args.thisv().toObject());
   6381 
   6382  args.rval().set(JS::GetReservedSlot(obj, SLOT_FIELDS));
   6383 
   6384  if (!CType::IsSizeDefined(obj)) {
   6385    MOZ_ASSERT(args.rval().isUndefined());
   6386    return true;
   6387  }
   6388 
   6389  if (args.rval().isUndefined()) {
   6390    // Build the 'fields' array lazily.
   6391    JSObject* fields = BuildFieldsArray(cx, obj);
   6392    if (!fields) {
   6393      return false;
   6394    }
   6395    JS_SetReservedSlot(obj, SLOT_FIELDS, ObjectValue(*fields));
   6396 
   6397    args.rval().setObject(*fields);
   6398  }
   6399 
   6400  MOZ_ASSERT(args.rval().isObject());
   6401  return true;
   6402 }
   6403 
   6404 bool StructType::FieldGetter(JSContext* cx, unsigned argc, Value* vp) {
   6405  CallArgs args = CallArgsFromVp(argc, vp);
   6406 
   6407  if (!args.thisv().isObject()) {
   6408    return IncompatibleThisProto(cx, "StructType property getter",
   6409                                 args.thisv());
   6410  }
   6411 
   6412  RootedObject obj(cx, &args.thisv().toObject());
   6413  if (!CData::IsCDataMaybeUnwrap(&obj)) {
   6414    return IncompatibleThisProto(cx, "StructType property getter",
   6415                                 args.thisv());
   6416  }
   6417 
   6418  JSObject* typeObj = CData::GetCType(obj);
   6419  if (CType::GetTypeCode(typeObj) != TYPE_struct) {
   6420    return IncompatibleThisType(cx, "StructType property getter",
   6421                                "non-StructType CData", args.thisv());
   6422  }
   6423 
   6424  RootedValue nameVal(
   6425      cx, GetFunctionNativeReserved(&args.callee(), SLOT_FIELDNAME));
   6426  Rooted<JSLinearString*> name(cx,
   6427                               JS_EnsureLinearString(cx, nameVal.toString()));
   6428  if (!name) {
   6429    return false;
   6430  }
   6431 
   6432  const FieldInfo* field = LookupField(cx, typeObj, name);
   6433  if (!field) {
   6434    return false;
   6435  }
   6436 
   6437  char* data = static_cast<char*>(CData::GetData(obj)) + field->mOffset;
   6438  RootedObject fieldType(cx, field->mType);
   6439  return ConvertToJS(cx, fieldType, obj, data, false, false, args.rval());
   6440 }
   6441 
   6442 bool StructType::FieldSetter(JSContext* cx, unsigned argc, Value* vp) {
   6443  CallArgs args = CallArgsFromVp(argc, vp);
   6444 
   6445  if (!args.thisv().isObject()) {
   6446    return IncompatibleThisProto(cx, "StructType property setter",
   6447                                 args.thisv());
   6448  }
   6449 
   6450  RootedObject obj(cx, &args.thisv().toObject());
   6451  if (!CData::IsCDataMaybeUnwrap(&obj)) {
   6452    return IncompatibleThisProto(cx, "StructType property setter",
   6453                                 args.thisv());
   6454  }
   6455 
   6456  RootedObject typeObj(cx, CData::GetCType(obj));
   6457  if (CType::GetTypeCode(typeObj) != TYPE_struct) {
   6458    return IncompatibleThisType(cx, "StructType property setter",
   6459                                "non-StructType CData", args.thisv());
   6460  }
   6461 
   6462  RootedValue nameVal(
   6463      cx, GetFunctionNativeReserved(&args.callee(), SLOT_FIELDNAME));
   6464  Rooted<JSLinearString*> name(cx,
   6465                               JS_EnsureLinearString(cx, nameVal.toString()));
   6466  if (!name) {
   6467    return false;
   6468  }
   6469 
   6470  const FieldInfo* field = LookupField(cx, typeObj, name);
   6471  if (!field) {
   6472    return false;
   6473  }
   6474 
   6475  args.rval().setUndefined();
   6476 
   6477  char* data = static_cast<char*>(CData::GetData(obj)) + field->mOffset;
   6478  return ImplicitConvert(cx, args.get(0), field->mType, data,
   6479                         ConversionType::Setter, nullptr, nullptr, 0, typeObj,
   6480                         field->mIndex);
   6481 }
   6482 
   6483 bool StructType::AddressOfField(JSContext* cx, unsigned argc, Value* vp) {
   6484  CallArgs args = CallArgsFromVp(argc, vp);
   6485  RootedObject obj(
   6486      cx, GetThisObject(cx, args, "StructType.prototype.addressOfField"));
   6487  if (!obj) {
   6488    return false;
   6489  }
   6490 
   6491  if (!CData::IsCDataMaybeUnwrap(&obj)) {
   6492    return IncompatibleThisProto(cx, "StructType.prototype.addressOfField",
   6493                                 args.thisv());
   6494  }
   6495 
   6496  JSObject* typeObj = CData::GetCType(obj);
   6497  if (CType::GetTypeCode(typeObj) != TYPE_struct) {
   6498    return IncompatibleThisType(cx, "StructType.prototype.addressOfField",
   6499                                "non-StructType CData", args.thisv());
   6500  }
   6501 
   6502  if (args.length() != 1) {
   6503    return ArgumentLengthError(cx, "StructType.prototype.addressOfField", "one",
   6504                               "");
   6505  }
   6506 
   6507  if (!args[0].isString()) {
   6508    return ArgumentTypeMismatch(cx, "", "StructType.prototype.addressOfField",
   6509                                "a string");
   6510  }
   6511 
   6512  JSLinearString* str = JS_EnsureLinearString(cx, args[0].toString());
   6513  if (!str) {
   6514    return false;
   6515  }
   6516 
   6517  const FieldInfo* field = LookupField(cx, typeObj, str);
   6518  if (!field) {
   6519    return false;
   6520  }
   6521 
   6522  RootedObject baseType(cx, field->mType);
   6523  RootedObject pointerType(cx, PointerType::CreateInternal(cx, baseType));
   6524  if (!pointerType) {
   6525    return false;
   6526  }
   6527 
   6528  // Create a PointerType CData object containing null.
   6529  JSObject* result = CData::Create(cx, pointerType, nullptr, nullptr, true);
   6530  if (!result) {
   6531    return false;
   6532  }
   6533 
   6534  args.rval().setObject(*result);
   6535 
   6536  // Manually set the pointer inside the object, so we skip the conversion step.
   6537  void** data = static_cast<void**>(CData::GetData(result));
   6538  *data = static_cast<char*>(CData::GetData(obj)) + field->mOffset;
   6539  return true;
   6540 }
   6541 
   6542 /*******************************************************************************
   6543 ** FunctionType implementation
   6544 *******************************************************************************/
   6545 
   6546 // Helper class for handling allocation of function arguments.
   6547 struct AutoValue {
   6548  AutoValue() : mData(nullptr) {}
   6549 
   6550  ~AutoValue() { js_free(mData); }
   6551 
   6552  bool SizeToType(JSContext* cx, JSObject* type) {
   6553    // Allocate a minimum of sizeof(ffi_arg) to handle small integers.
   6554    size_t size = Align(CType::GetSize(type), sizeof(ffi_arg));
   6555    mData = js_calloc(size);
   6556    return mData != nullptr;
   6557  }
   6558 
   6559  void* mData;
   6560 };
   6561 
   6562 static bool GetABI(JSContext* cx, HandleValue abiType, ffi_abi* result) {
   6563  if (abiType.isPrimitive()) {
   6564    return false;
   6565  }
   6566 
   6567  ABICode abi = GetABICode(abiType.toObjectOrNull());
   6568 
   6569  // determine the ABI from the subset of those available on the
   6570  // given platform. ABI_DEFAULT specifies the default
   6571  // C calling convention (cdecl) on each platform.
   6572  switch (abi) {
   6573    case ABI_DEFAULT:
   6574      *result = FFI_DEFAULT_ABI;
   6575      return true;
   6576    case ABI_THISCALL:
   6577 #if defined(_WIN64)
   6578 #  if defined(_M_X64)
   6579      *result = FFI_WIN64;
   6580 #  elif defined(_M_ARM64)
   6581      *result = FFI_SYSV;
   6582 #  else
   6583 #    error unknown 64-bit Windows platform
   6584 #  endif
   6585      return true;
   6586 #elif defined(_WIN32)
   6587      *result = FFI_THISCALL;
   6588      return true;
   6589 #else
   6590      break;
   6591 #endif
   6592    case ABI_STDCALL:
   6593    case ABI_WINAPI:
   6594 #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
   6595      *result = FFI_STDCALL;
   6596      return true;
   6597 #elif (defined(_WIN64))
   6598      // We'd like the same code to work across Win32 and Win64, so stdcall_api
   6599      // and winapi_abi become aliases to the lone Win64 ABI.
   6600 #  if defined(_M_X64)
   6601      *result = FFI_WIN64;
   6602 #  elif defined(_M_ARM64)
   6603      *result = FFI_SYSV;
   6604 #  else
   6605 #    error unknown 64-bit Windows platform
   6606 #  endif
   6607      return true;
   6608 #endif
   6609    case INVALID_ABI:
   6610      break;
   6611  }
   6612  return false;
   6613 }
   6614 
   6615 static JSObject* PrepareType(JSContext* cx, uint32_t index, HandleValue type) {
   6616  if (type.isPrimitive() || !CType::IsCType(type.toObjectOrNull())) {
   6617    FunctionArgumentTypeError(cx, index, type, "is not a ctypes type");
   6618    return nullptr;
   6619  }
   6620 
   6621  JSObject* result = type.toObjectOrNull();
   6622  TypeCode typeCode = CType::GetTypeCode(result);
   6623 
   6624  if (typeCode == TYPE_array) {
   6625    // convert array argument types to pointers, just like C.
   6626    // ImplicitConvert will do the same, when passing an array as data.
   6627    RootedObject baseType(cx, ArrayType::GetBaseType(result));
   6628    result = PointerType::CreateInternal(cx, baseType);
   6629    if (!result) {
   6630      return nullptr;
   6631    }
   6632 
   6633  } else if (typeCode == TYPE_void_t || typeCode == TYPE_function) {
   6634    // disallow void or function argument types
   6635    FunctionArgumentTypeError(cx, index, type, "cannot be void or function");
   6636    return nullptr;
   6637  }
   6638 
   6639  if (!CType::IsSizeDefined(result)) {
   6640    FunctionArgumentTypeError(cx, index, type, "must have defined size");
   6641    return nullptr;
   6642  }
   6643 
   6644  // libffi cannot pass types of zero size by value.
   6645  MOZ_ASSERT(CType::GetSize(result) != 0);
   6646 
   6647  return result;
   6648 }
   6649 
   6650 static JSObject* PrepareReturnType(JSContext* cx, HandleValue type) {
   6651  if (type.isPrimitive() || !CType::IsCType(type.toObjectOrNull())) {
   6652    FunctionReturnTypeError(cx, type, "is not a ctypes type");
   6653    return nullptr;
   6654  }
   6655 
   6656  JSObject* result = type.toObjectOrNull();
   6657  TypeCode typeCode = CType::GetTypeCode(result);
   6658 
   6659  // Arrays and functions can never be return types.
   6660  if (typeCode == TYPE_array || typeCode == TYPE_function) {
   6661    FunctionReturnTypeError(cx, type, "cannot be an array or function");
   6662    return nullptr;
   6663  }
   6664 
   6665  if (typeCode != TYPE_void_t && !CType::IsSizeDefined(result)) {
   6666    FunctionReturnTypeError(cx, type, "must have defined size");
   6667    return nullptr;
   6668  }
   6669 
   6670  // libffi cannot pass types of zero size by value.
   6671  MOZ_ASSERT(typeCode == TYPE_void_t || CType::GetSize(result) != 0);
   6672 
   6673  return result;
   6674 }
   6675 
   6676 static MOZ_ALWAYS_INLINE bool IsEllipsis(JSContext* cx, HandleValue v,
   6677                                         bool* isEllipsis) {
   6678  *isEllipsis = false;
   6679  if (!v.isString()) {
   6680    return true;
   6681  }
   6682  JSString* str = v.toString();
   6683  if (str->length() != 3) {
   6684    return true;
   6685  }
   6686  JSLinearString* linear = str->ensureLinear(cx);
   6687  if (!linear) {
   6688    return false;
   6689  }
   6690  char16_t dot = '.';
   6691  *isEllipsis = (linear->latin1OrTwoByteChar(0) == dot &&
   6692                 linear->latin1OrTwoByteChar(1) == dot &&
   6693                 linear->latin1OrTwoByteChar(2) == dot);
   6694  return true;
   6695 }
   6696 
   6697 static bool PrepareCIF(JSContext* cx, FunctionInfo* fninfo) {
   6698  ffi_abi abi;
   6699  RootedValue abiType(cx, ObjectOrNullValue(fninfo->mABI));
   6700  if (!GetABI(cx, abiType, &abi)) {
   6701    JS_ReportErrorASCII(cx, "Invalid ABI specification");
   6702    return false;
   6703  }
   6704 
   6705  ffi_type* rtype = CType::GetFFIType(cx, fninfo->mReturnType);
   6706  if (!rtype) {
   6707    return false;
   6708  }
   6709 
   6710  ffi_status status;
   6711  if (fninfo->mIsVariadic) {
   6712    status = ffi_prep_cif_var(&fninfo->mCIF, abi, fninfo->mArgTypes.length(),
   6713                              fninfo->mFFITypes.length(), rtype,
   6714                              fninfo->mFFITypes.begin());
   6715  } else {
   6716    status = ffi_prep_cif(&fninfo->mCIF, abi, fninfo->mFFITypes.length(), rtype,
   6717                          fninfo->mFFITypes.begin());
   6718  }
   6719 
   6720  switch (status) {
   6721    case FFI_OK:
   6722      return true;
   6723    case FFI_BAD_ABI:
   6724      JS_ReportErrorASCII(cx, "Invalid ABI specification");
   6725      return false;
   6726    case FFI_BAD_TYPEDEF:
   6727      JS_ReportErrorASCII(cx, "Invalid type specification");
   6728      return false;
   6729    default:
   6730      JS_ReportErrorASCII(cx, "Unknown libffi error");
   6731      return false;
   6732  }
   6733 }
   6734 
   6735 void FunctionType::BuildSymbolName(JSContext* cx, JSString* name,
   6736                                   JSObject* typeObj, AutoCString& result) {
   6737  FunctionInfo* fninfo = GetFunctionInfo(typeObj);
   6738 
   6739  switch (GetABICode(fninfo->mABI)) {
   6740    case ABI_DEFAULT:
   6741    case ABI_THISCALL:
   6742    case ABI_WINAPI:
   6743      // For cdecl or WINAPI functions, no mangling is necessary.
   6744      AppendString(cx, result, name);
   6745      break;
   6746 
   6747    case ABI_STDCALL: {
   6748 #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
   6749      // On WIN32, stdcall functions look like:
   6750      //   _foo@40
   6751      // where 'foo' is the function name, and '40' is the aligned size of the
   6752      // arguments.
   6753      AppendString(cx, result, "_");
   6754      AppendString(cx, result, name);
   6755      AppendString(cx, result, "@");
   6756 
   6757      // Compute the suffix by aligning each argument to sizeof(ffi_arg).
   6758      size_t size = 0;
   6759      for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
   6760        JSObject* argType = fninfo->mArgTypes[i];
   6761        size += Align(CType::GetSize(argType), sizeof(ffi_arg));
   6762      }
   6763 
   6764      IntegerToString(size, 10, result);
   6765 #elif defined(_WIN64)
   6766      // On Win64, stdcall is an alias to the default ABI for compatibility, so
   6767      // no mangling is done.
   6768      AppendString(cx, result, name);
   6769 #endif
   6770      break;
   6771    }
   6772 
   6773    case INVALID_ABI:
   6774      MOZ_CRASH("invalid abi");
   6775  }
   6776 }
   6777 
   6778 static bool CreateFunctionInfo(JSContext* cx, HandleObject typeObj,
   6779                               HandleValue abiType, HandleObject returnType,
   6780                               const HandleValueArray& args) {
   6781  FunctionInfo* fninfo(cx->new_<FunctionInfo>(cx->zone()));
   6782  if (!fninfo) {
   6783    return false;
   6784  }
   6785 
   6786  // Stash the FunctionInfo in a reserved slot.
   6787  JS_InitReservedSlot(typeObj, SLOT_FNINFO, fninfo,
   6788                      JS::MemoryUse::CTypeFunctionInfo);
   6789 
   6790  ffi_abi abi;
   6791  if (!GetABI(cx, abiType, &abi)) {
   6792    JS_ReportErrorASCII(cx, "Invalid ABI specification");
   6793    return false;
   6794  }
   6795  fninfo->mABI = abiType.toObjectOrNull();
   6796 
   6797  fninfo->mReturnType = returnType;
   6798 
   6799  // prepare the argument types
   6800  if (!fninfo->mArgTypes.reserve(args.length()) ||
   6801      !fninfo->mFFITypes.reserve(args.length())) {
   6802    JS_ReportOutOfMemory(cx);
   6803    return false;
   6804  }
   6805 
   6806  fninfo->mIsVariadic = false;
   6807 
   6808  for (uint32_t i = 0; i < args.length(); ++i) {
   6809    bool isEllipsis;
   6810    if (!IsEllipsis(cx, args[i], &isEllipsis)) {
   6811      return false;
   6812    }
   6813    if (isEllipsis) {
   6814      fninfo->mIsVariadic = true;
   6815      if (i < 1) {
   6816        JS_ReportErrorASCII(cx,
   6817                            "\"...\" may not be the first and only parameter "
   6818                            "type of a variadic function declaration");
   6819        return false;
   6820      }
   6821      if (i < args.length() - 1) {
   6822        JS_ReportErrorASCII(cx,
   6823                            "\"...\" must be the last parameter type of a "
   6824                            "variadic function declaration");
   6825        return false;
   6826      }
   6827      if (GetABICode(fninfo->mABI) != ABI_DEFAULT) {
   6828        JS_ReportErrorASCII(cx,
   6829                            "Variadic functions must use the __cdecl calling "
   6830                            "convention");
   6831        return false;
   6832      }
   6833      break;
   6834    }
   6835 
   6836    JSObject* argType = PrepareType(cx, i, args[i]);
   6837    if (!argType) {
   6838      return false;
   6839    }
   6840 
   6841    ffi_type* ffiType = CType::GetFFIType(cx, argType);
   6842    if (!ffiType) {
   6843      return false;
   6844    }
   6845 
   6846    fninfo->mArgTypes.infallibleAppend(argType);
   6847    fninfo->mFFITypes.infallibleAppend(ffiType);
   6848  }
   6849 
   6850  if (fninfo->mIsVariadic) {
   6851    // wait to PrepareCIF until function is called
   6852    return true;
   6853  }
   6854 
   6855  if (!PrepareCIF(cx, fninfo)) {
   6856    return false;
   6857  }
   6858 
   6859  return true;
   6860 }
   6861 
   6862 bool FunctionType::Create(JSContext* cx, unsigned argc, Value* vp) {
   6863  // Construct and return a new FunctionType object.
   6864  CallArgs args = CallArgsFromVp(argc, vp);
   6865  if (args.length() < 2 || args.length() > 3) {
   6866    return ArgumentLengthError(cx, "FunctionType", "two or three", "s");
   6867  }
   6868 
   6869  JS::RootedValueVector argTypes(cx);
   6870  RootedObject arrayObj(cx, nullptr);
   6871 
   6872  if (args.length() == 3) {
   6873    // Prepare an array of Values for the arguments.
   6874    bool isArray;
   6875    if (!args[2].isObject()) {
   6876      isArray = false;
   6877    } else {
   6878      if (!JS::IsArrayObject(cx, args[2], &isArray)) {
   6879        return false;
   6880      }
   6881    }
   6882 
   6883    if (!isArray) {
   6884      return ArgumentTypeMismatch(cx, "third ", "FunctionType", "an array");
   6885    }
   6886 
   6887    arrayObj = &args[2].toObject();
   6888 
   6889    uint32_t len;
   6890    MOZ_ALWAYS_TRUE(JS::GetArrayLength(cx, arrayObj, &len));
   6891 
   6892    if (!argTypes.resize(len)) {
   6893      JS_ReportOutOfMemory(cx);
   6894      return false;
   6895    }
   6896  }
   6897 
   6898  // Pull out the argument types from the array, if any.
   6899  MOZ_ASSERT_IF(argTypes.length(), arrayObj);
   6900  for (uint32_t i = 0; i < argTypes.length(); ++i) {
   6901    if (!JS_GetElement(cx, arrayObj, i, argTypes[i])) {
   6902      return false;
   6903    }
   6904  }
   6905 
   6906  JSObject* result = CreateInternal(cx, args[0], args[1], argTypes);
   6907  if (!result) {
   6908    return false;
   6909  }
   6910 
   6911  args.rval().setObject(*result);
   6912  return true;
   6913 }
   6914 
   6915 JSObject* FunctionType::CreateInternal(JSContext* cx, HandleValue abi,
   6916                                       HandleValue rtype,
   6917                                       const HandleValueArray& args) {
   6918  // Prepare the result type
   6919  RootedObject returnType(cx, PrepareReturnType(cx, rtype));
   6920  if (!returnType) {
   6921    return nullptr;
   6922  }
   6923 
   6924  // Get ctypes.FunctionType.prototype and the common prototype for CData
   6925  // objects of this type, from ctypes.CType.prototype.
   6926  RootedObject typeProto(
   6927      cx, CType::GetProtoFromType(cx, returnType, SLOT_FUNCTIONPROTO));
   6928  if (!typeProto) {
   6929    return nullptr;
   6930  }
   6931  RootedObject dataProto(
   6932      cx, CType::GetProtoFromType(cx, returnType, SLOT_FUNCTIONDATAPROTO));
   6933  if (!dataProto) {
   6934    return nullptr;
   6935  }
   6936 
   6937  // Create a new CType object with the common properties and slots.
   6938  RootedObject typeObj(
   6939      cx, CType::Create(cx, typeProto, dataProto, TYPE_function, nullptr,
   6940                        JS::UndefinedHandleValue, JS::UndefinedHandleValue,
   6941                        nullptr));
   6942  if (!typeObj) {
   6943    return nullptr;
   6944  }
   6945 
   6946  // Determine and check the types, and prepare the function CIF.
   6947  if (!CreateFunctionInfo(cx, typeObj, abi, returnType, args)) {
   6948    return nullptr;
   6949  }
   6950 
   6951  return typeObj;
   6952 }
   6953 
   6954 // Construct a function pointer to a JS function (see CClosure::Create()).
   6955 // Regular function pointers are constructed directly in
   6956 // PointerType::ConstructData().
   6957 bool FunctionType::ConstructData(JSContext* cx, HandleObject typeObj,
   6958                                 HandleObject dataObj, HandleObject fnObj,
   6959                                 HandleObject thisObj, HandleValue errVal) {
   6960  MOZ_ASSERT(CType::GetTypeCode(typeObj) == TYPE_function);
   6961 
   6962  PRFuncPtr* data = static_cast<PRFuncPtr*>(CData::GetData(dataObj));
   6963 
   6964  FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
   6965  if (fninfo->mIsVariadic) {
   6966    JS_ReportErrorASCII(cx, "Can't declare a variadic callback function");
   6967    return false;
   6968  }
   6969  if (GetABICode(fninfo->mABI) == ABI_WINAPI) {
   6970    JS_ReportErrorASCII(cx,
   6971                        "Can't declare a ctypes.winapi_abi callback function, "
   6972                        "use ctypes.stdcall_abi instead");
   6973    return false;
   6974  }
   6975 
   6976  RootedObject closureObj(
   6977      cx, CClosure::Create(cx, typeObj, fnObj, thisObj, errVal, data));
   6978  if (!closureObj) {
   6979    return false;
   6980  }
   6981 
   6982  // Set the closure object as the referent of the new CData object.
   6983  JS_SetReservedSlot(dataObj, SLOT_REFERENT, ObjectValue(*closureObj));
   6984 
   6985  // Seal the CData object, to prevent modification of the function pointer.
   6986  // This permanently associates this object with the closure, and avoids
   6987  // having to do things like reset SLOT_REFERENT when someone tries to
   6988  // change the pointer value.
   6989  // XXX This will need to change when bug 541212 is fixed -- CData::ValueSetter
   6990  // could be called on a frozen object.
   6991  return JS_FreezeObject(cx, dataObj);
   6992 }
   6993 
   6994 typedef Vector<AutoValue, 16, SystemAllocPolicy> AutoValueAutoArray;
   6995 
   6996 static bool ConvertArgument(JSContext* cx, HandleObject funObj,
   6997                            unsigned argIndex, HandleValue arg, JSObject* type,
   6998                            AutoValue* value, AutoValueAutoArray* strings) {
   6999  if (!value->SizeToType(cx, type)) {
   7000    JS_ReportAllocationOverflow(cx);
   7001    return false;
   7002  }
   7003 
   7004  bool freePointer = false;
   7005  if (!ImplicitConvert(cx, arg, type, value->mData, ConversionType::Argument,
   7006                       &freePointer, funObj, argIndex))
   7007    return false;
   7008 
   7009  if (freePointer) {
   7010    // ImplicitConvert converted a string for us, which we have to free.
   7011    // Keep track of it.
   7012    if (!strings->growBy(1)) {
   7013      JS_ReportOutOfMemory(cx);
   7014      return false;
   7015    }
   7016    strings->back().mData = *static_cast<char**>(value->mData);
   7017  }
   7018 
   7019  return true;
   7020 }
   7021 
   7022 bool FunctionType::Call(JSContext* cx, unsigned argc, Value* vp) {
   7023  CallArgs args = CallArgsFromVp(argc, vp);
   7024  // get the callee object...
   7025  RootedObject obj(cx, &args.callee());
   7026  if (!CData::IsCDataMaybeUnwrap(&obj)) {
   7027    return IncompatibleThisProto(cx, "FunctionType.prototype.call",
   7028                                 args.calleev());
   7029  }
   7030 
   7031  RootedObject typeObj(cx, CData::GetCType(obj));
   7032  if (CType::GetTypeCode(typeObj) != TYPE_pointer) {
   7033    return IncompatibleThisType(cx, "FunctionType.prototype.call",
   7034                                "non-PointerType CData", args.calleev());
   7035  }
   7036 
   7037  typeObj = PointerType::GetBaseType(typeObj);
   7038  if (CType::GetTypeCode(typeObj) != TYPE_function) {
   7039    return IncompatibleThisType(cx, "FunctionType.prototype.call",
   7040                                "non-FunctionType pointer", args.calleev());
   7041  }
   7042 
   7043  FunctionInfo* fninfo = GetFunctionInfo(typeObj);
   7044  uint32_t argcFixed = fninfo->mArgTypes.length();
   7045 
   7046  if ((!fninfo->mIsVariadic && args.length() != argcFixed) ||
   7047      (fninfo->mIsVariadic && args.length() < argcFixed)) {
   7048    return FunctionArgumentLengthMismatch(cx, argcFixed, args.length(), obj,
   7049                                          typeObj, fninfo->mIsVariadic);
   7050  }
   7051 
   7052  // Check if we have a Library object. If we do, make sure it's open.
   7053  Value slot = JS::GetReservedSlot(obj, SLOT_REFERENT);
   7054  if (!slot.isUndefined() && Library::IsLibrary(&slot.toObject())) {
   7055    PRLibrary* library = Library::GetLibrary(&slot.toObject());
   7056    if (!library) {
   7057      JS_ReportErrorASCII(cx, "library is not open");
   7058      return false;
   7059    }
   7060  }
   7061 
   7062  // prepare the values for each argument
   7063  AutoValueAutoArray values;
   7064  AutoValueAutoArray strings;
   7065  if (!values.resize(args.length())) {
   7066    JS_ReportOutOfMemory(cx);
   7067    return false;
   7068  }
   7069 
   7070  for (unsigned i = 0; i < argcFixed; ++i) {
   7071    if (!ConvertArgument(cx, obj, i, args[i], fninfo->mArgTypes[i], &values[i],
   7072                         &strings)) {
   7073      return false;
   7074    }
   7075  }
   7076 
   7077  if (fninfo->mIsVariadic) {
   7078    if (!fninfo->mFFITypes.resize(args.length())) {
   7079      JS_ReportOutOfMemory(cx);
   7080      return false;
   7081    }
   7082 
   7083    RootedObject obj(cx);   // Could reuse obj instead of declaring a second
   7084    RootedObject type(cx);  // RootedObject, but readability would suffer.
   7085    RootedValue arg(cx);
   7086 
   7087    for (uint32_t i = argcFixed; i < args.length(); ++i) {
   7088      obj = args[i].isObject() ? &args[i].toObject() : nullptr;
   7089      if (!obj || !CData::IsCDataMaybeUnwrap(&obj)) {
   7090        // Since we know nothing about the CTypes of the ... arguments,
   7091        // they absolutely must be CData objects already.
   7092        return VariadicArgumentTypeError(cx, i, args[i]);
   7093      }
   7094      type = CData::GetCType(obj);
   7095      if (!type) {
   7096        // These functions report their own errors.
   7097        return false;
   7098      }
   7099      RootedValue typeVal(cx, ObjectValue(*type));
   7100      type = PrepareType(cx, i, typeVal);
   7101      if (!type) {
   7102        return false;
   7103      }
   7104      // Relying on ImplicitConvert only for the limited purpose of
   7105      // converting one CType to another (e.g., T[] to T*).
   7106      arg = ObjectValue(*obj);
   7107      if (!ConvertArgument(cx, obj, i, arg, type, &values[i], &strings)) {
   7108        return false;
   7109      }
   7110      fninfo->mFFITypes[i] = CType::GetFFIType(cx, type);
   7111      if (!fninfo->mFFITypes[i]) {
   7112        return false;
   7113      }
   7114    }
   7115    if (!PrepareCIF(cx, fninfo)) {
   7116      return false;
   7117    }
   7118  }
   7119 
   7120  // initialize a pointer to an appropriate location, for storing the result
   7121  AutoValue returnValue;
   7122  TypeCode typeCode = CType::GetTypeCode(fninfo->mReturnType);
   7123  if (typeCode != TYPE_void_t &&
   7124      !returnValue.SizeToType(cx, fninfo->mReturnType)) {
   7125    JS_ReportAllocationOverflow(cx);
   7126    return false;
   7127  }
   7128 
   7129  // Let the runtime callback know that we are about to call into C.
   7130  AutoCTypesActivityCallback autoCallback(cx, CTypesActivityType::BeginCall,
   7131                                          CTypesActivityType::EndCall);
   7132 
   7133  uintptr_t fn = *reinterpret_cast<uintptr_t*>(CData::GetData(obj));
   7134 
   7135 #if defined(XP_WIN)
   7136  int32_t lastErrorStatus;  // The status as defined by |GetLastError|
   7137  int32_t savedLastError = GetLastError();
   7138  SetLastError(0);
   7139 #endif              // defined(XP_WIN)
   7140  int errnoStatus;  // The status as defined by |errno|
   7141  int savedErrno = errno;
   7142  errno = 0;
   7143 
   7144  ffi_call(&fninfo->mCIF, FFI_FN(fn), returnValue.mData,
   7145           reinterpret_cast<void**>(values.begin()));
   7146 
   7147  // Save error value.
   7148  // We need to save it before leaving the scope of |suspend| as destructing
   7149  // |suspend| has the side-effect of clearing |GetLastError|
   7150  // (see bug 684017).
   7151 
   7152  errnoStatus = errno;
   7153 #if defined(XP_WIN)
   7154  lastErrorStatus = GetLastError();
   7155  SetLastError(savedLastError);
   7156 #endif  // defined(XP_WIN)
   7157 
   7158  errno = savedErrno;
   7159 
   7160  // We're no longer calling into C.
   7161  autoCallback.DoEndCallback();
   7162 
   7163  // Store the error value for later consultation with |ctypes.getStatus|
   7164  JSObject* objCTypes = CType::GetGlobalCTypes(cx, typeObj);
   7165  if (!objCTypes) {
   7166    return false;
   7167  }
   7168 
   7169  JS_SetReservedSlot(objCTypes, SLOT_ERRNO, Int32Value(errnoStatus));
   7170 #if defined(XP_WIN)
   7171  JS_SetReservedSlot(objCTypes, SLOT_LASTERROR, Int32Value(lastErrorStatus));
   7172 #endif  // defined(XP_WIN)
   7173 
   7174  // Small integer types get returned as a word-sized ffi_arg. Coerce it back
   7175  // into the correct size for ConvertToJS.
   7176  switch (typeCode) {
   7177 #define INTEGRAL_CASE(name, type, ffiType)                              \
   7178  case TYPE_##name:                                                     \
   7179    if (sizeof(type) < sizeof(ffi_arg)) {                               \
   7180      ffi_arg data = *static_cast<ffi_arg*>(returnValue.mData);         \
   7181      *static_cast<type*>(returnValue.mData) = static_cast<type>(data); \
   7182    }                                                                   \
   7183    break;
   7184    CTYPES_FOR_EACH_INT_TYPE(INTEGRAL_CASE)
   7185    CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGRAL_CASE)
   7186    CTYPES_FOR_EACH_BOOL_TYPE(INTEGRAL_CASE)
   7187    CTYPES_FOR_EACH_CHAR_TYPE(INTEGRAL_CASE)
   7188    CTYPES_FOR_EACH_CHAR16_TYPE(INTEGRAL_CASE)
   7189 #undef INTEGRAL_CASE
   7190    default:
   7191      break;
   7192  }
   7193 
   7194  // prepare a JS object from the result
   7195  RootedObject returnType(cx, fninfo->mReturnType);
   7196  return ConvertToJS(cx, returnType, nullptr, returnValue.mData, false, true,
   7197                     args.rval());
   7198 }
   7199 
   7200 FunctionInfo* FunctionType::GetFunctionInfo(JSObject* obj) {
   7201  MOZ_ASSERT(CType::IsCType(obj));
   7202  MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_function);
   7203 
   7204  Value slot = JS::GetReservedSlot(obj, SLOT_FNINFO);
   7205  MOZ_ASSERT(!slot.isUndefined() && slot.toPrivate());
   7206 
   7207  return static_cast<FunctionInfo*>(slot.toPrivate());
   7208 }
   7209 
   7210 bool FunctionType::IsFunctionType(HandleValue v) {
   7211  if (!v.isObject()) {
   7212    return false;
   7213  }
   7214  JSObject* obj = &v.toObject();
   7215  return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_function;
   7216 }
   7217 
   7218 bool FunctionType::ArgTypesGetter(JSContext* cx, const JS::CallArgs& args) {
   7219  JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
   7220 
   7221  args.rval().set(JS::GetReservedSlot(obj, SLOT_ARGS_T));
   7222  if (!args.rval().isUndefined()) {
   7223    return true;
   7224  }
   7225 
   7226  FunctionInfo* fninfo = GetFunctionInfo(obj);
   7227  size_t len = fninfo->mArgTypes.length();
   7228 
   7229  // Prepare a new array.
   7230  JS::Rooted<JSObject*> argTypes(cx);
   7231  {
   7232    JS::RootedValueVector vec(cx);
   7233    if (!vec.resize(len)) {
   7234      return false;
   7235    }
   7236 
   7237    for (size_t i = 0; i < len; ++i) {
   7238      vec[i].setObject(*fninfo->mArgTypes[i]);
   7239    }
   7240 
   7241    argTypes = JS::NewArrayObject(cx, vec);
   7242    if (!argTypes) {
   7243      return false;
   7244    }
   7245  }
   7246 
   7247  // Seal and cache it.
   7248  if (!JS_FreezeObject(cx, argTypes)) {
   7249    return false;
   7250  }
   7251  JS_SetReservedSlot(obj, SLOT_ARGS_T, JS::ObjectValue(*argTypes));
   7252 
   7253  args.rval().setObject(*argTypes);
   7254  return true;
   7255 }
   7256 
   7257 bool FunctionType::ReturnTypeGetter(JSContext* cx, const JS::CallArgs& args) {
   7258  // Get the returnType object from the FunctionInfo.
   7259  args.rval().setObject(
   7260      *GetFunctionInfo(&args.thisv().toObject())->mReturnType);
   7261  return true;
   7262 }
   7263 
   7264 bool FunctionType::ABIGetter(JSContext* cx, const JS::CallArgs& args) {
   7265  // Get the abi object from the FunctionInfo.
   7266  args.rval().setObject(*GetFunctionInfo(&args.thisv().toObject())->mABI);
   7267  return true;
   7268 }
   7269 
   7270 bool FunctionType::IsVariadicGetter(JSContext* cx, const JS::CallArgs& args) {
   7271  args.rval().setBoolean(
   7272      GetFunctionInfo(&args.thisv().toObject())->mIsVariadic);
   7273  return true;
   7274 }
   7275 
   7276 /*******************************************************************************
   7277 ** CClosure implementation
   7278 *******************************************************************************/
   7279 
   7280 JSObject* CClosure::Create(JSContext* cx, HandleObject typeObj,
   7281                           HandleObject fnObj, HandleObject thisObj,
   7282                           HandleValue errVal, PRFuncPtr* fnptr) {
   7283  MOZ_ASSERT(fnObj);
   7284 
   7285  RootedObject result(cx, JS_NewObject(cx, &sCClosureClass));
   7286  if (!result) {
   7287    return nullptr;
   7288  }
   7289 
   7290  // Get the FunctionInfo from the FunctionType.
   7291  FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
   7292  MOZ_ASSERT(!fninfo->mIsVariadic);
   7293  MOZ_ASSERT(GetABICode(fninfo->mABI) != ABI_WINAPI);
   7294 
   7295  // Get the prototype of the FunctionType object, of class CTypeProto,
   7296  // which stores our JSContext for use with the closure.
   7297  RootedObject proto(cx);
   7298  if (!JS_GetPrototype(cx, typeObj, &proto)) {
   7299    return nullptr;
   7300  }
   7301  MOZ_ASSERT(proto);
   7302  MOZ_ASSERT(CType::IsCTypeProto(proto));
   7303 
   7304  // Prepare the error sentinel value. It's important to do this now, because
   7305  // we might be unable to convert the value to the proper type. If so, we want
   7306  // the caller to know about it _now_, rather than some uncertain time in the
   7307  // future when the error sentinel is actually needed.
   7308  UniquePtr<uint8_t[], JS::FreePolicy> errResult;
   7309  if (!errVal.isUndefined()) {
   7310    // Make sure the callback returns something.
   7311    if (CType::GetTypeCode(fninfo->mReturnType) == TYPE_void_t) {
   7312      JS_ReportErrorASCII(cx, "A void callback can't pass an error sentinel");
   7313      return nullptr;
   7314    }
   7315 
   7316    // With the exception of void, the FunctionType constructor ensures that
   7317    // the return type has a defined size.
   7318    MOZ_ASSERT(CType::IsSizeDefined(fninfo->mReturnType));
   7319 
   7320    // Allocate a buffer for the return value.
   7321    size_t rvSize = CType::GetSize(fninfo->mReturnType);
   7322    errResult = cx->make_pod_array<uint8_t>(rvSize);
   7323    if (!errResult) {
   7324      return nullptr;
   7325    }
   7326 
   7327    // Do the value conversion. This might fail, in which case we throw.
   7328    if (!ImplicitConvert(cx, errVal, fninfo->mReturnType, errResult.get(),
   7329                         ConversionType::Return, nullptr, typeObj))
   7330      return nullptr;
   7331  }
   7332 
   7333  ClosureInfo* cinfo = cx->new_<ClosureInfo>(cx);
   7334  if (!cinfo) {
   7335    JS_ReportOutOfMemory(cx);
   7336    return nullptr;
   7337  }
   7338 
   7339  // Copy the important bits of context into cinfo.
   7340  cinfo->errResult = errResult.release();
   7341  cinfo->closureObj = result;
   7342  cinfo->typeObj = typeObj;
   7343  cinfo->thisObj = thisObj;
   7344  cinfo->jsfnObj = fnObj;
   7345 
   7346  // Stash the ClosureInfo struct on our new object.
   7347  JS_InitReservedSlot(result, SLOT_CLOSUREINFO, cinfo,
   7348                      JS::MemoryUse::CClosureInfo);
   7349 
   7350  // Create an ffi_closure object and initialize it.
   7351  void* code;
   7352  cinfo->closure =
   7353      static_cast<ffi_closure*>(ffi_closure_alloc(sizeof(ffi_closure), &code));
   7354  if (!cinfo->closure || !code) {
   7355    JS_ReportErrorASCII(cx, "couldn't create closure - libffi error");
   7356    return nullptr;
   7357  }
   7358 
   7359  ffi_status status = ffi_prep_closure_loc(cinfo->closure, &fninfo->mCIF,
   7360                                           CClosure::ClosureStub, cinfo, code);
   7361  if (status != FFI_OK) {
   7362    JS_ReportErrorASCII(cx, "couldn't create closure - libffi error");
   7363    return nullptr;
   7364  }
   7365 
   7366  // Casting between void* and a function pointer is forbidden in C and C++.
   7367  // Do it via an integral type.
   7368  *fnptr = reinterpret_cast<PRFuncPtr>(reinterpret_cast<uintptr_t>(code));
   7369  return result;
   7370 }
   7371 
   7372 void CClosure::Trace(JSTracer* trc, JSObject* obj) {
   7373  // Make sure our ClosureInfo slot is legit. If it's not, bail.
   7374  Value slot = JS::GetReservedSlot(obj, SLOT_CLOSUREINFO);
   7375  if (slot.isUndefined()) {
   7376    return;
   7377  }
   7378 
   7379  ClosureInfo* cinfo = static_cast<ClosureInfo*>(slot.toPrivate());
   7380 
   7381  TraceEdge(trc, &cinfo->closureObj, "closureObj");
   7382  TraceEdge(trc, &cinfo->typeObj, "typeObj");
   7383  TraceEdge(trc, &cinfo->jsfnObj, "jsfnObj");
   7384  TraceNullableEdge(trc, &cinfo->thisObj, "thisObj");
   7385 }
   7386 
   7387 void CClosure::Finalize(JS::GCContext* gcx, JSObject* obj) {
   7388  // Make sure our ClosureInfo slot is legit. If it's not, bail.
   7389  Value slot = JS::GetReservedSlot(obj, SLOT_CLOSUREINFO);
   7390  if (slot.isUndefined()) {
   7391    return;
   7392  }
   7393 
   7394  ClosureInfo* cinfo = static_cast<ClosureInfo*>(slot.toPrivate());
   7395  gcx->delete_(obj, cinfo, MemoryUse::CClosureInfo);
   7396 }
   7397 
   7398 void CClosure::ClosureStub(ffi_cif* cif, void* result, void** args,
   7399                           void* userData) {
   7400  MOZ_ASSERT(cif);
   7401  MOZ_ASSERT(result);
   7402  MOZ_ASSERT(args);
   7403  MOZ_ASSERT(userData);
   7404 
   7405  // Retrieve the essentials from our closure object.
   7406  ArgClosure argClosure(cif, result, args, static_cast<ClosureInfo*>(userData));
   7407  JSContext* cx = argClosure.cinfo->cx;
   7408 
   7409  js::AssertSameCompartment(cx, argClosure.cinfo->jsfnObj);
   7410 
   7411  RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
   7412  MOZ_ASSERT(global);
   7413 
   7414  js::PrepareScriptEnvironmentAndInvoke(cx, global, argClosure);
   7415 }
   7416 
   7417 bool CClosure::ArgClosure::operator()(JSContext* cx) {
   7418  // Let the runtime callback know that we are about to call into JS again. The
   7419  // end callback will fire automatically when we exit this function.
   7420  AutoCTypesActivityCallback autoCallback(cx, CTypesActivityType::BeginCallback,
   7421                                          CTypesActivityType::EndCallback);
   7422 
   7423  RootedObject typeObj(cx, cinfo->typeObj);
   7424  RootedObject thisObj(cx, cinfo->thisObj);
   7425  RootedValue jsfnVal(cx, ObjectValue(*cinfo->jsfnObj));
   7426  AssertSameCompartment(cx, cinfo->jsfnObj);
   7427 
   7428  JS_AbortIfWrongThread(cx);
   7429 
   7430  // Assert that our CIFs agree.
   7431  FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
   7432  MOZ_ASSERT(cif == &fninfo->mCIF);
   7433 
   7434  TypeCode typeCode = CType::GetTypeCode(fninfo->mReturnType);
   7435 
   7436  // Initialize the result to zero, in case something fails. Small integer types
   7437  // are promoted to a word-sized ffi_arg, so we must be careful to zero the
   7438  // whole word.
   7439  size_t rvSize = 0;
   7440  if (cif->rtype != &ffi_type_void) {
   7441    rvSize = cif->rtype->size;
   7442    switch (typeCode) {
   7443 #define INTEGRAL_CASE(name, type, ffiType) case TYPE_##name:
   7444      CTYPES_FOR_EACH_INT_TYPE(INTEGRAL_CASE)
   7445      CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGRAL_CASE)
   7446      CTYPES_FOR_EACH_BOOL_TYPE(INTEGRAL_CASE)
   7447      CTYPES_FOR_EACH_CHAR_TYPE(INTEGRAL_CASE)
   7448      CTYPES_FOR_EACH_CHAR16_TYPE(INTEGRAL_CASE)
   7449 #undef INTEGRAL_CASE
   7450      rvSize = Align(rvSize, sizeof(ffi_arg));
   7451      break;
   7452      default:
   7453        break;
   7454    }
   7455    memset(result, 0, rvSize);
   7456  }
   7457 
   7458  // Set up an array for converted arguments.
   7459  JS::RootedValueVector argv(cx);
   7460  if (!argv.resize(cif->nargs)) {
   7461    JS_ReportOutOfMemory(cx);
   7462    return false;
   7463  }
   7464 
   7465  for (uint32_t i = 0; i < cif->nargs; ++i) {
   7466    // Convert each argument, and have any CData objects created depend on
   7467    // the existing buffers.
   7468    RootedObject argType(cx, fninfo->mArgTypes[i]);
   7469    if (!ConvertToJS(cx, argType, nullptr, args[i], false, false, argv[i])) {
   7470      return false;
   7471    }
   7472  }
   7473 
   7474  // Call the JS function. 'thisObj' may be nullptr, in which case the JS
   7475  // engine will find an appropriate object to use.
   7476  RootedValue rval(cx);
   7477  bool success = JS_CallFunctionValue(cx, thisObj, jsfnVal, argv, &rval);
   7478 
   7479  // Convert the result. Note that we pass 'ConversionType::Return', such that
   7480  // ImplicitConvert will *not* autoconvert a JS string into a pointer-to-char
   7481  // type, which would require an allocation that we can't track. The JS
   7482  // function must perform this conversion itself and return a PointerType
   7483  // CData; thusly, the burden of freeing the data is left to the user.
   7484  if (success && cif->rtype != &ffi_type_void) {
   7485    success = ImplicitConvert(cx, rval, fninfo->mReturnType, result,
   7486                              ConversionType::Return, nullptr, typeObj);
   7487  }
   7488 
   7489  if (!success) {
   7490    // Something failed. The callee may have thrown, or it may not have
   7491    // returned a value that ImplicitConvert() was happy with. Depending on how
   7492    // prudent the consumer has been, we may or may not have a recovery plan.
   7493    //
   7494    // Note that PrepareScriptEnvironmentAndInvoke should take care of reporting
   7495    // the exception.
   7496 
   7497    if (cinfo->errResult) {
   7498      // Good case: we have a sentinel that we can return. Copy it in place of
   7499      // the actual return value, and then proceed.
   7500 
   7501      // The buffer we're returning might be larger than the size of the return
   7502      // type, due to libffi alignment issues (see above). But it should never
   7503      // be smaller.
   7504      size_t copySize = CType::GetSize(fninfo->mReturnType);
   7505      MOZ_ASSERT(copySize <= rvSize);
   7506      memcpy(result, cinfo->errResult, copySize);
   7507 
   7508      // We still want to return false here, so that
   7509      // PrepareScriptEnvironmentAndInvoke will report the exception.
   7510    } else {
   7511      // Bad case: not much we can do here. The rv is already zeroed out, so we
   7512      // just return and hope for the best.
   7513    }
   7514    return false;
   7515  }
   7516 
   7517  // Small integer types must be returned as a word-sized ffi_arg. Coerce it
   7518  // back into the size libffi expects.
   7519  switch (typeCode) {
   7520 #define INTEGRAL_CASE(name, type, ffiType)        \
   7521  case TYPE_##name:                               \
   7522    if (sizeof(type) < sizeof(ffi_arg)) {         \
   7523      ffi_arg data = *static_cast<type*>(result); \
   7524      *static_cast<ffi_arg*>(result) = data;      \
   7525    }                                             \
   7526    break;
   7527    CTYPES_FOR_EACH_INT_TYPE(INTEGRAL_CASE)
   7528    CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGRAL_CASE)
   7529    CTYPES_FOR_EACH_BOOL_TYPE(INTEGRAL_CASE)
   7530    CTYPES_FOR_EACH_CHAR_TYPE(INTEGRAL_CASE)
   7531    CTYPES_FOR_EACH_CHAR16_TYPE(INTEGRAL_CASE)
   7532 #undef INTEGRAL_CASE
   7533    default:
   7534      break;
   7535  }
   7536 
   7537  return true;
   7538 }
   7539 
   7540 /*******************************************************************************
   7541 ** CData implementation
   7542 *******************************************************************************/
   7543 
   7544 // Create a new CData object of type 'typeObj' containing binary data supplied
   7545 // in 'source', optionally with a referent object 'refObj'.
   7546 //
   7547 // * 'typeObj' must be a CType of defined (but possibly zero) size.
   7548 //
   7549 // * If an object 'refObj' is supplied, the new CData object stores the
   7550 //   referent object in a reserved slot for GC safety, such that 'refObj' will
   7551 //   be held alive by the resulting CData object. 'refObj' may or may not be
   7552 //   a CData object; merely an object we want to keep alive.
   7553 //   * If 'refObj' is a CData object, 'ownResult' must be false.
   7554 //   * Otherwise, 'refObj' is a Library or CClosure object, and 'ownResult'
   7555 //     may be true or false.
   7556 // * Otherwise 'refObj' is nullptr. In this case, 'ownResult' may be true or
   7557 //   false.
   7558 //
   7559 // * If 'ownResult' is true, the CData object will allocate an appropriately
   7560 //   sized buffer, and free it upon finalization. If 'source' data is
   7561 //   supplied, the data will be copied from 'source' into the buffer;
   7562 //   otherwise, the entirety of the new buffer will be initialized to zero.
   7563 // * If 'ownResult' is false, the new CData's buffer refers to a slice of
   7564 //   another buffer kept alive by 'refObj'. 'source' data must be provided,
   7565 //   and the new CData's buffer will refer to 'source'.
   7566 JSObject* CData::Create(JSContext* cx, HandleObject typeObj,
   7567                        HandleObject refObj, void* source, bool ownResult) {
   7568  MOZ_ASSERT(typeObj);
   7569  MOZ_ASSERT(CType::IsCType(typeObj));
   7570  MOZ_ASSERT(CType::IsSizeDefined(typeObj));
   7571  MOZ_ASSERT(ownResult || source);
   7572  MOZ_ASSERT_IF(refObj && CData::IsCData(refObj), !ownResult);
   7573 
   7574  // Get the 'prototype' property from the type.
   7575  Value slot = JS::GetReservedSlot(typeObj, SLOT_PROTO);
   7576  MOZ_ASSERT(slot.isObject());
   7577 
   7578  RootedObject proto(cx, &slot.toObject());
   7579 
   7580  RootedObject dataObj(cx, JS_NewObjectWithGivenProto(cx, &sCDataClass, proto));
   7581  if (!dataObj) {
   7582    return nullptr;
   7583  }
   7584 
   7585  // set the CData's associated type
   7586  JS_SetReservedSlot(dataObj, SLOT_CTYPE, ObjectValue(*typeObj));
   7587 
   7588  // Stash the referent object, if any, for GC safety.
   7589  if (refObj) {
   7590    JS_SetReservedSlot(dataObj, SLOT_REFERENT, ObjectValue(*refObj));
   7591  }
   7592 
   7593  // Set our ownership flag.
   7594  JS_SetReservedSlot(dataObj, SLOT_OWNS, BooleanValue(ownResult));
   7595 
   7596  // attach the buffer. since it might not be 2-byte aligned, we need to
   7597  // allocate an aligned space for it and store it there. :(
   7598  UniquePtr<char*, JS::FreePolicy> buffer(cx->new_<char*>());
   7599  if (!buffer) {
   7600    return nullptr;
   7601  }
   7602 
   7603  char* data;
   7604  if (!ownResult) {
   7605    data = static_cast<char*>(source);
   7606  } else {
   7607    // Initialize our own buffer.
   7608    size_t size = CType::GetSize(typeObj);
   7609    data = cx->pod_malloc<char>(size);
   7610    if (!data) {
   7611      return nullptr;
   7612    }
   7613 
   7614    if (!source) {
   7615      memset(data, 0, size);
   7616    } else {
   7617      memcpy(data, source, size);
   7618    }
   7619 
   7620    AddCellMemory(dataObj, size, MemoryUse::CDataBuffer);
   7621  }
   7622 
   7623  *buffer.get() = data;
   7624  JS_InitReservedSlot(dataObj, SLOT_DATA, buffer.release(),
   7625                      JS::MemoryUse::CDataBufferPtr);
   7626 
   7627  // If this is an array, wrap it in a proxy so we can intercept element
   7628  // gets/sets.
   7629 
   7630  if (CType::GetTypeCode(typeObj) != TYPE_array) {
   7631    return dataObj;
   7632  }
   7633 
   7634  RootedValue priv(cx, ObjectValue(*dataObj));
   7635  ProxyOptions options;
   7636  options.setLazyProto(true);
   7637  return NewProxyObject(cx, &CDataArrayProxyHandler::singleton, priv, nullptr,
   7638                        options);
   7639 }
   7640 
   7641 void CData::Finalize(JS::GCContext* gcx, JSObject* obj) {
   7642  // Delete our buffer, and the data it contains if we own it.
   7643  Value slot = JS::GetReservedSlot(obj, SLOT_OWNS);
   7644  if (slot.isUndefined()) {
   7645    return;
   7646  }
   7647 
   7648  bool owns = slot.toBoolean();
   7649 
   7650  slot = JS::GetReservedSlot(obj, SLOT_DATA);
   7651  if (slot.isUndefined()) {
   7652    return;
   7653  }
   7654  char** buffer = static_cast<char**>(slot.toPrivate());
   7655 
   7656  if (owns) {
   7657    JSObject* typeObj = &JS::GetReservedSlot(obj, SLOT_CTYPE).toObject();
   7658    size_t size = CType::GetSize(typeObj);
   7659    gcx->free_(obj, *buffer, size, MemoryUse::CDataBuffer);
   7660  }
   7661  gcx->delete_(obj, buffer, MemoryUse::CDataBufferPtr);
   7662 }
   7663 
   7664 JSObject* CData::GetCType(JSObject* dataObj) {
   7665  dataObj = MaybeUnwrapArrayWrapper(dataObj);
   7666  MOZ_ASSERT(CData::IsCData(dataObj));
   7667 
   7668  Value slot = JS::GetReservedSlot(dataObj, SLOT_CTYPE);
   7669  JSObject* typeObj = slot.toObjectOrNull();
   7670  MOZ_ASSERT(CType::IsCType(typeObj));
   7671  return typeObj;
   7672 }
   7673 
   7674 void* CData::GetData(JSObject* dataObj) {
   7675  dataObj = MaybeUnwrapArrayWrapper(dataObj);
   7676  MOZ_ASSERT(CData::IsCData(dataObj));
   7677 
   7678  Value slot = JS::GetReservedSlot(dataObj, SLOT_DATA);
   7679 
   7680  void** buffer = static_cast<void**>(slot.toPrivate());
   7681  MOZ_ASSERT(buffer);
   7682  MOZ_ASSERT(*buffer);
   7683  return *buffer;
   7684 }
   7685 
   7686 bool CData::IsCData(JSObject* obj) {
   7687  // Assert we don't have an array wrapper.
   7688  MOZ_ASSERT(MaybeUnwrapArrayWrapper(obj) == obj);
   7689 
   7690  return obj->hasClass(&sCDataClass);
   7691 }
   7692 
   7693 bool CData::IsCDataMaybeUnwrap(MutableHandleObject obj) {
   7694  obj.set(MaybeUnwrapArrayWrapper(obj));
   7695  return IsCData(obj);
   7696 }
   7697 
   7698 bool CData::IsCData(HandleValue v) {
   7699  return v.isObject() && CData::IsCData(MaybeUnwrapArrayWrapper(&v.toObject()));
   7700 }
   7701 
   7702 bool CData::IsCDataProto(JSObject* obj) {
   7703  return obj->hasClass(&sCDataProtoClass);
   7704 }
   7705 
   7706 bool CData::ValueGetter(JSContext* cx, const JS::CallArgs& args) {
   7707  RootedObject obj(cx, &args.thisv().toObject());
   7708 
   7709  // Convert the value to a primitive; do not create a new CData object.
   7710  RootedObject ctype(cx, GetCType(obj));
   7711  return ConvertToJS(cx, ctype, nullptr, GetData(obj), true, false,
   7712                     args.rval());
   7713 }
   7714 
   7715 bool CData::ValueSetter(JSContext* cx, const JS::CallArgs& args) {
   7716  RootedObject obj(cx, &args.thisv().toObject());
   7717  args.rval().setUndefined();
   7718  return ImplicitConvert(cx, args.get(0), GetCType(obj), GetData(obj),
   7719                         ConversionType::Setter, nullptr);
   7720 }
   7721 
   7722 bool CData::Address(JSContext* cx, unsigned argc, Value* vp) {
   7723  CallArgs args = CallArgsFromVp(argc, vp);
   7724  if (args.length() != 0) {
   7725    return ArgumentLengthError(cx, "CData.prototype.address", "no", "s");
   7726  }
   7727 
   7728  RootedObject obj(cx, GetThisObject(cx, args, "CData.prototype.address"));
   7729  if (!obj) {
   7730    return false;
   7731  }
   7732  if (!IsCDataMaybeUnwrap(&obj)) {
   7733    return IncompatibleThisProto(cx, "CData.prototype.address", args.thisv());
   7734  }
   7735 
   7736  RootedObject typeObj(cx, CData::GetCType(obj));
   7737  RootedObject pointerType(cx, PointerType::CreateInternal(cx, typeObj));
   7738  if (!pointerType) {
   7739    return false;
   7740  }
   7741 
   7742  // Create a PointerType CData object containing null.
   7743  JSObject* result = CData::Create(cx, pointerType, nullptr, nullptr, true);
   7744  if (!result) {
   7745    return false;
   7746  }
   7747 
   7748  args.rval().setObject(*result);
   7749 
   7750  // Manually set the pointer inside the object, so we skip the conversion step.
   7751  void** data = static_cast<void**>(GetData(result));
   7752  *data = GetData(obj);
   7753  return true;
   7754 }
   7755 
   7756 bool CData::Cast(JSContext* cx, unsigned argc, Value* vp) {
   7757  CallArgs args = CallArgsFromVp(argc, vp);
   7758  if (args.length() != 2) {
   7759    return ArgumentLengthError(cx, "ctypes.cast", "two", "s");
   7760  }
   7761 
   7762  RootedObject sourceData(cx);
   7763  if (args[0].isObject()) {
   7764    sourceData = &args[0].toObject();
   7765  }
   7766 
   7767  if (!sourceData || !CData::IsCDataMaybeUnwrap(&sourceData)) {
   7768    return ArgumentTypeMismatch(cx, "first ", "ctypes.cast", "a CData");
   7769  }
   7770  RootedObject sourceType(cx, CData::GetCType(sourceData));
   7771 
   7772  if (args[1].isPrimitive() || !CType::IsCType(&args[1].toObject())) {
   7773    return ArgumentTypeMismatch(cx, "second ", "ctypes.cast", "a CType");
   7774  }
   7775 
   7776  RootedObject targetType(cx, &args[1].toObject());
   7777  size_t targetSize;
   7778  if (!CType::GetSafeSize(targetType, &targetSize)) {
   7779    return UndefinedSizeCastError(cx, targetType);
   7780  }
   7781  if (targetSize > CType::GetSize(sourceType)) {
   7782    return SizeMismatchCastError(cx, sourceType, targetType,
   7783                                 CType::GetSize(sourceType), targetSize);
   7784  }
   7785 
   7786  // Construct a new CData object with a type of 'targetType' and a referent
   7787  // of 'sourceData'.
   7788  void* data = CData::GetData(sourceData);
   7789  JSObject* result = CData::Create(cx, targetType, sourceData, data, false);
   7790  if (!result) {
   7791    return false;
   7792  }
   7793 
   7794  args.rval().setObject(*result);
   7795  return true;
   7796 }
   7797 
   7798 bool CData::GetRuntime(JSContext* cx, unsigned argc, Value* vp) {
   7799  CallArgs args = CallArgsFromVp(argc, vp);
   7800  if (args.length() != 1) {
   7801    return ArgumentLengthError(cx, "ctypes.getRuntime", "one", "");
   7802  }
   7803 
   7804  if (args[0].isPrimitive() || !CType::IsCType(&args[0].toObject())) {
   7805    return ArgumentTypeMismatch(cx, "", "ctypes.getRuntime", "a CType");
   7806  }
   7807 
   7808  RootedObject targetType(cx, &args[0].toObject());
   7809  size_t targetSize;
   7810  if (!CType::GetSafeSize(targetType, &targetSize) ||
   7811      targetSize != sizeof(void*)) {
   7812    JS_ReportErrorASCII(cx, "target CType has non-pointer size");
   7813    return false;
   7814  }
   7815 
   7816  void* data = static_cast<void*>(cx->runtime());
   7817  JSObject* result = CData::Create(cx, targetType, nullptr, &data, true);
   7818  if (!result) {
   7819    return false;
   7820  }
   7821 
   7822  args.rval().setObject(*result);
   7823  return true;
   7824 }
   7825 
   7826 // Unwrap the `this` object to a CData object, or extract a CData object from a
   7827 // CDataFinalizer.
   7828 static bool GetThisDataObject(JSContext* cx, const CallArgs& args,
   7829                              const char* funName, MutableHandleObject obj) {
   7830  obj.set(GetThisObject(cx, args, funName));
   7831  if (!obj) {
   7832    return IncompatibleThisProto(cx, funName, args.thisv());
   7833  }
   7834  if (!CData::IsCDataMaybeUnwrap(obj)) {
   7835    if (!CDataFinalizer::IsCDataFinalizer(obj)) {
   7836      return IncompatibleThisProto(cx, funName, args.thisv());
   7837    }
   7838 
   7839    CDataFinalizer::Private* p = GetFinalizerPrivate(obj);
   7840    if (!p) {
   7841      return EmptyFinalizerCallError(cx, funName);
   7842    }
   7843 
   7844    RootedValue dataVal(cx);
   7845    if (!CDataFinalizer::GetValue(cx, obj, &dataVal)) {
   7846      return IncompatibleThisProto(cx, funName, args.thisv());
   7847    }
   7848 
   7849    if (dataVal.isPrimitive()) {
   7850      return IncompatibleThisProto(cx, funName, args.thisv());
   7851    }
   7852 
   7853    obj.set(dataVal.toObjectOrNull());
   7854    if (!obj || !CData::IsCDataMaybeUnwrap(obj)) {
   7855      return IncompatibleThisProto(cx, funName, args.thisv());
   7856    }
   7857  }
   7858 
   7859  return true;
   7860 }
   7861 
   7862 typedef JS::TwoByteCharsZ (*InflateUTF8Method)(JSContext*, const JS::UTF8Chars&,
   7863                                               size_t*, arena_id_t);
   7864 
   7865 static bool ReadStringCommon(JSContext* cx, InflateUTF8Method inflateUTF8,
   7866                             unsigned argc, Value* vp, const char* funName,
   7867                             arena_id_t destArenaId) {
   7868  CallArgs args = CallArgsFromVp(argc, vp);
   7869  if (args.length() != 0) {
   7870    return ArgumentLengthError(cx, funName, "no", "s");
   7871  }
   7872 
   7873  RootedObject obj(cx);
   7874  if (!GetThisDataObject(cx, args, funName, &obj)) {
   7875    return false;
   7876  }
   7877 
   7878  // Make sure we are a pointer to, or an array of, an 8-bit or 16-bit
   7879  // character or integer type.
   7880  JSObject* baseType;
   7881  JSObject* typeObj = CData::GetCType(obj);
   7882  TypeCode typeCode = CType::GetTypeCode(typeObj);
   7883  void* data;
   7884  size_t maxLength = -1;
   7885  switch (typeCode) {
   7886    case TYPE_pointer:
   7887      baseType = PointerType::GetBaseType(typeObj);
   7888      data = *static_cast<void**>(CData::GetData(obj));
   7889      if (data == nullptr) {
   7890        return NullPointerError(cx, "read contents of", obj);
   7891      }
   7892      break;
   7893    case TYPE_array:
   7894      baseType = ArrayType::GetBaseType(typeObj);
   7895      data = CData::GetData(obj);
   7896      maxLength = ArrayType::GetLength(typeObj);
   7897      break;
   7898    default:
   7899      return TypeError(cx, "PointerType or ArrayType", args.thisv());
   7900  }
   7901 
   7902  // Convert the string buffer, taking care to determine the correct string
   7903  // length in the case of arrays (which may contain embedded nulls).
   7904  JSString* result;
   7905  switch (CType::GetTypeCode(baseType)) {
   7906    case TYPE_int8_t:
   7907    case TYPE_uint8_t:
   7908    case TYPE_char:
   7909    case TYPE_signed_char:
   7910    case TYPE_unsigned_char: {
   7911      char* bytes = static_cast<char*>(data);
   7912      size_t length = js_strnlen(bytes, maxLength);
   7913 
   7914      // Determine the length.
   7915      UniqueTwoByteChars dst(
   7916          inflateUTF8(cx, JS::UTF8Chars(bytes, length), &length, destArenaId)
   7917              .get());
   7918      if (!dst) {
   7919        return false;
   7920      }
   7921 
   7922      result = JS_NewUCString(cx, std::move(dst), length);
   7923      if (!result) {
   7924        return false;
   7925      }
   7926 
   7927      break;
   7928    }
   7929    case TYPE_int16_t:
   7930    case TYPE_uint16_t:
   7931    case TYPE_short:
   7932    case TYPE_unsigned_short:
   7933    case TYPE_char16_t: {
   7934      char16_t* chars = static_cast<char16_t*>(data);
   7935      size_t length = js_strnlen(chars, maxLength);
   7936      result = JS_NewUCStringCopyN(cx, chars, length);
   7937      break;
   7938    }
   7939    default:
   7940      return NonStringBaseError(cx, args.thisv());
   7941  }
   7942 
   7943  if (!result) {
   7944    return false;
   7945  }
   7946 
   7947  args.rval().setString(result);
   7948  return true;
   7949 }
   7950 
   7951 bool CData::ReadString(JSContext* cx, unsigned argc, Value* vp) {
   7952  return ReadStringCommon(cx, JS::UTF8CharsToNewTwoByteCharsZ, argc, vp,
   7953                          "CData.prototype.readString", js::StringBufferArena);
   7954 }
   7955 
   7956 bool CDataFinalizer::Methods::ReadString(JSContext* cx, unsigned argc,
   7957                                         Value* vp) {
   7958  return ReadStringCommon(cx, JS::UTF8CharsToNewTwoByteCharsZ, argc, vp,
   7959                          "CDataFinalizer.prototype.readString",
   7960                          js::StringBufferArena);
   7961 }
   7962 
   7963 bool CData::ReadStringReplaceMalformed(JSContext* cx, unsigned argc,
   7964                                       Value* vp) {
   7965  return ReadStringCommon(cx, JS::LossyUTF8CharsToNewTwoByteCharsZ, argc, vp,
   7966                          "CData.prototype.readStringReplaceMalformed",
   7967                          js::StringBufferArena);
   7968 }
   7969 
   7970 using TypedArrayConstructor = JSObject* (*)(JSContext*, size_t);
   7971 
   7972 template <typename Type>
   7973 TypedArrayConstructor GetTypedArrayConstructorImpl() {
   7974  if (std::is_floating_point_v<Type>) {
   7975    switch (sizeof(Type)) {
   7976      case 4:
   7977        return JS_NewFloat32Array;
   7978      case 8:
   7979        return JS_NewFloat64Array;
   7980      default:
   7981        return nullptr;
   7982    }
   7983  }
   7984 
   7985  constexpr bool isSigned = std::is_signed_v<Type>;
   7986  switch (sizeof(Type)) {
   7987    case 1:
   7988      return isSigned ? JS_NewInt8Array : JS_NewUint8Array;
   7989    case 2:
   7990      return isSigned ? JS_NewInt16Array : JS_NewUint16Array;
   7991    case 4:
   7992      return isSigned ? JS_NewInt32Array : JS_NewUint32Array;
   7993    default:
   7994      return nullptr;
   7995  }
   7996 }
   7997 
   7998 static TypedArrayConstructor GetTypedArrayConstructor(TypeCode baseType) {
   7999  switch (baseType) {
   8000 #define MACRO(name, ctype, _) \
   8001  case TYPE_##name:           \
   8002    return GetTypedArrayConstructorImpl<ctype>();
   8003    CTYPES_FOR_EACH_TYPE(MACRO)
   8004 #undef MACRO
   8005    default:
   8006      return nullptr;
   8007  }
   8008 }
   8009 
   8010 static bool ReadTypedArrayCommon(JSContext* cx, unsigned argc, Value* vp,
   8011                                 const char* funName) {
   8012  CallArgs args = CallArgsFromVp(argc, vp);
   8013  if (args.length() != 0) {
   8014    return ArgumentLengthError(cx, funName, "no", "s");
   8015  }
   8016 
   8017  RootedObject obj(cx);
   8018  if (!GetThisDataObject(cx, args, funName, &obj)) {
   8019    return false;
   8020  }
   8021 
   8022  // Make sure we are a pointer to, or an array of, a type that is compatible
   8023  // with a typed array base type.
   8024  JSObject* baseType;
   8025  JSObject* typeObj = CData::GetCType(obj);
   8026  TypeCode typeCode = CType::GetTypeCode(typeObj);
   8027  void* data;
   8028  mozilla::Maybe<size_t> length;
   8029  switch (typeCode) {
   8030    case TYPE_pointer:
   8031      baseType = PointerType::GetBaseType(typeObj);
   8032      data = *static_cast<void**>(CData::GetData(obj));
   8033      if (data == nullptr) {
   8034        return NullPointerError(cx, "read contents of", obj);
   8035      }
   8036      break;
   8037    case TYPE_array:
   8038      baseType = ArrayType::GetBaseType(typeObj);
   8039      data = CData::GetData(obj);
   8040      length.emplace(ArrayType::GetLength(typeObj));
   8041      break;
   8042    default:
   8043      return TypeError(cx, "PointerType or ArrayType", args.thisv());
   8044  }
   8045 
   8046  TypeCode baseTypeCode = CType::GetTypeCode(baseType);
   8047 
   8048  // For string inputs only, use strlen to determine the length.
   8049  switch (baseTypeCode) {
   8050    case TYPE_char:
   8051    case TYPE_signed_char:
   8052    case TYPE_unsigned_char:
   8053      if (!length) {
   8054        length.emplace(js_strnlen(static_cast<char*>(data), INT32_MAX));
   8055      }
   8056      break;
   8057 
   8058    case TYPE_char16_t:
   8059      if (!length) {
   8060        length.emplace(js_strlen(static_cast<char16_t*>(data)));
   8061      }
   8062      break;
   8063 
   8064    default:
   8065      break;
   8066  }
   8067 
   8068  if (!length) {
   8069    return NonStringBaseError(cx, args.thisv());
   8070  }
   8071 
   8072  auto makeTypedArray = GetTypedArrayConstructor(baseTypeCode);
   8073  if (!makeTypedArray) {
   8074    return NonTypedArrayBaseError(cx, args.thisv());
   8075  }
   8076 
   8077  CheckedInt<size_t> size = *length;
   8078  size *= CType::GetSize(baseType);
   8079  if (!size.isValid() || size.value() > ArrayBufferObject::ByteLengthLimit) {
   8080    return SizeOverflow(cx, "data", "typed array");
   8081  }
   8082 
   8083  JSObject* result = makeTypedArray(cx, *length);
   8084  if (!result) {
   8085    return false;
   8086  }
   8087 
   8088  AutoCheckCannotGC nogc(cx);
   8089  bool isShared;
   8090  void* buffer = JS_GetArrayBufferViewData(&result->as<ArrayBufferViewObject>(),
   8091                                           &isShared, nogc);
   8092  MOZ_ASSERT(!isShared);
   8093  memcpy(buffer, data, size.value());
   8094 
   8095  args.rval().setObject(*result);
   8096  return true;
   8097 }
   8098 
   8099 bool CData::ReadTypedArray(JSContext* cx, unsigned argc, Value* vp) {
   8100  return ReadTypedArrayCommon(cx, argc, vp, "CData.prototype.readTypedArray");
   8101 }
   8102 
   8103 bool CDataFinalizer::Methods::ReadTypedArray(JSContext* cx, unsigned argc,
   8104                                             Value* vp) {
   8105  return ReadTypedArrayCommon(cx, argc, vp,
   8106                              "CDataFinalizer.prototype.readTypedArray");
   8107 }
   8108 
   8109 JSString* CData::GetSourceString(JSContext* cx, HandleObject typeObj,
   8110                                 void* data) {
   8111  // Walk the types, building up the toSource() string.
   8112  // First, we build up the type expression:
   8113  // 't.ptr' for pointers;
   8114  // 't.array([n])' for arrays;
   8115  // 'n' for structs, where n = t.name, the struct's name. (We assume this is
   8116  // bound to a variable in the current scope.)
   8117  AutoString source;
   8118  BuildTypeSource(cx, typeObj, true, source);
   8119  AppendString(cx, source, "(");
   8120  if (!BuildDataSource(cx, typeObj, data, false, source)) {
   8121    source.handle(false);
   8122  }
   8123  AppendString(cx, source, ")");
   8124  if (!source) {
   8125    return nullptr;
   8126  }
   8127 
   8128  return NewUCString(cx, source.finish());
   8129 }
   8130 
   8131 bool CData::ToSource(JSContext* cx, unsigned argc, Value* vp) {
   8132  CallArgs args = CallArgsFromVp(argc, vp);
   8133  if (args.length() != 0) {
   8134    return ArgumentLengthError(cx, "CData.prototype.toSource", "no", "s");
   8135  }
   8136 
   8137  RootedObject obj(cx, GetThisObject(cx, args, "CData.prototype.toSource"));
   8138  if (!obj) {
   8139    return false;
   8140  }
   8141  if (!CData::IsCDataMaybeUnwrap(&obj) && !CData::IsCDataProto(obj)) {
   8142    return IncompatibleThisProto(cx, "CData.prototype.toSource",
   8143                                 InformalValueTypeName(args.thisv()));
   8144  }
   8145 
   8146  JSString* result;
   8147  if (CData::IsCData(obj)) {
   8148    RootedObject typeObj(cx, CData::GetCType(obj));
   8149    void* data = CData::GetData(obj);
   8150 
   8151    result = CData::GetSourceString(cx, typeObj, data);
   8152  } else {
   8153    result = JS_NewStringCopyZ(cx, "[CData proto object]");
   8154  }
   8155 
   8156  if (!result) {
   8157    return false;
   8158  }
   8159 
   8160  args.rval().setString(result);
   8161  return true;
   8162 }
   8163 
   8164 bool CData::ErrnoGetter(JSContext* cx, const JS::CallArgs& args) {
   8165  args.rval().set(JS::GetReservedSlot(&args.thisv().toObject(), SLOT_ERRNO));
   8166  return true;
   8167 }
   8168 
   8169 #if defined(XP_WIN)
   8170 bool CData::LastErrorGetter(JSContext* cx, const JS::CallArgs& args) {
   8171  args.rval().set(
   8172      JS::GetReservedSlot(&args.thisv().toObject(), SLOT_LASTERROR));
   8173  return true;
   8174 }
   8175 #endif  // defined(XP_WIN)
   8176 
   8177 bool CDataFinalizer::Methods::ToSource(JSContext* cx, unsigned argc,
   8178                                       Value* vp) {
   8179  CallArgs args = CallArgsFromVp(argc, vp);
   8180  RootedObject objThis(
   8181      cx, GetThisObject(cx, args, "CDataFinalizer.prototype.toSource"));
   8182  if (!objThis) {
   8183    return false;
   8184  }
   8185  if (!CDataFinalizer::IsCDataFinalizer(objThis)) {
   8186    return IncompatibleThisProto(cx, "CDataFinalizer.prototype.toSource",
   8187                                 InformalValueTypeName(args.thisv()));
   8188  }
   8189 
   8190  CDataFinalizer::Private* p = GetFinalizerPrivate(objThis);
   8191 
   8192  JSString* strMessage;
   8193  if (!p) {
   8194    strMessage = JS_NewStringCopyZ(cx, "ctypes.CDataFinalizer()");
   8195  } else {
   8196    RootedObject objType(cx, CDataFinalizer::GetCType(cx, objThis));
   8197    if (!objType) {
   8198      JS_ReportErrorASCII(cx, "CDataFinalizer has no type");
   8199      return false;
   8200    }
   8201 
   8202    AutoString source;
   8203    AppendString(cx, source, "ctypes.CDataFinalizer(");
   8204    JSString* srcValue = CData::GetSourceString(cx, objType, p->cargs);
   8205    if (!srcValue) {
   8206      return false;
   8207    }
   8208    AppendString(cx, source, srcValue);
   8209    AppendString(cx, source, ", ");
   8210    Value valCodePtrType =
   8211        JS::GetReservedSlot(objThis, SLOT_DATAFINALIZER_CODETYPE);
   8212    if (valCodePtrType.isPrimitive()) {
   8213      return false;
   8214    }
   8215 
   8216    RootedObject typeObj(cx, valCodePtrType.toObjectOrNull());
   8217    JSString* srcDispose = CData::GetSourceString(cx, typeObj, &(p->code));
   8218    if (!srcDispose) {
   8219      return false;
   8220    }
   8221 
   8222    AppendString(cx, source, srcDispose);
   8223    AppendString(cx, source, ")");
   8224    if (!source) {
   8225      return false;
   8226    }
   8227    strMessage = NewUCString(cx, source.finish());
   8228  }
   8229 
   8230  if (!strMessage) {
   8231    // This is a memory issue, no error message
   8232    return false;
   8233  }
   8234 
   8235  args.rval().setString(strMessage);
   8236  return true;
   8237 }
   8238 
   8239 bool CDataFinalizer::Methods::ToString(JSContext* cx, unsigned argc,
   8240                                       Value* vp) {
   8241  CallArgs args = CallArgsFromVp(argc, vp);
   8242  JSObject* objThis =
   8243      GetThisObject(cx, args, "CDataFinalizer.prototype.toString");
   8244  if (!objThis) {
   8245    return false;
   8246  }
   8247  if (!CDataFinalizer::IsCDataFinalizer(objThis)) {
   8248    return IncompatibleThisProto(cx, "CDataFinalizer.prototype.toString",
   8249                                 InformalValueTypeName(args.thisv()));
   8250  }
   8251 
   8252  JSString* strMessage;
   8253  RootedValue value(cx);
   8254  if (!GetFinalizerPrivate(objThis)) {
   8255    // Pre-check whether CDataFinalizer::GetValue can fail
   8256    // to avoid reporting an error when not appropriate.
   8257    strMessage = JS_NewStringCopyZ(cx, "[CDataFinalizer - empty]");
   8258    if (!strMessage) {
   8259      return false;
   8260    }
   8261  } else if (!CDataFinalizer::GetValue(cx, objThis, &value)) {
   8262    MOZ_CRASH("Could not convert an empty CDataFinalizer");
   8263  } else {
   8264    strMessage = ToString(cx, value);
   8265    if (!strMessage) {
   8266      return false;
   8267    }
   8268  }
   8269  args.rval().setString(strMessage);
   8270  return true;
   8271 }
   8272 
   8273 bool CDataFinalizer::IsCDataFinalizer(JSObject* obj) {
   8274  return obj->hasClass(&sCDataFinalizerClass);
   8275 }
   8276 
   8277 JSObject* CDataFinalizer::GetCType(JSContext* cx, JSObject* obj) {
   8278  MOZ_ASSERT(IsCDataFinalizer(obj));
   8279 
   8280  Value valData = JS::GetReservedSlot(obj, SLOT_DATAFINALIZER_VALTYPE);
   8281  if (valData.isUndefined()) {
   8282    return nullptr;
   8283  }
   8284 
   8285  return valData.toObjectOrNull();
   8286 }
   8287 
   8288 bool CDataFinalizer::GetValue(JSContext* cx, JSObject* obj,
   8289                              MutableHandleValue aResult) {
   8290  MOZ_ASSERT(IsCDataFinalizer(obj));
   8291 
   8292  CDataFinalizer::Private* p = GetFinalizerPrivate(obj);
   8293 
   8294  if (!p) {
   8295    // We have called |dispose| or |forget| already.
   8296    JS_ReportErrorASCII(
   8297        cx, "Attempting to get the value of an empty CDataFinalizer");
   8298    return false;
   8299  }
   8300 
   8301  RootedObject ctype(cx, GetCType(cx, obj));
   8302  return ConvertToJS(cx, ctype, /*parent*/ nullptr, p->cargs, false, true,
   8303                     aResult);
   8304 }
   8305 
   8306 /*
   8307 * Attach a C function as a finalizer to a JS object.
   8308 *
   8309 * Pseudo-JS signature:
   8310 * function(CData<T>, CData<T -> U>): CDataFinalizer<T>
   8311 *          value,    finalizer
   8312 *
   8313 * This function attaches strong references to the following values:
   8314 * - the CType of |value|
   8315 *
   8316 * Note: This function takes advantage of the fact that non-variadic
   8317 * CData functions are initialized during creation.
   8318 */
   8319 bool CDataFinalizer::Construct(JSContext* cx, unsigned argc, Value* vp) {
   8320  CallArgs args = CallArgsFromVp(argc, vp);
   8321  RootedObject objSelf(cx, &args.callee());
   8322  RootedObject objProto(cx);
   8323  if (!GetObjectProperty(cx, objSelf, "prototype", &objProto)) {
   8324    JS_ReportErrorASCII(cx, "CDataFinalizer.prototype does not exist");
   8325    return false;
   8326  }
   8327 
   8328  // Get arguments
   8329  if (args.length() ==
   8330      0) {  // Special case: the empty (already finalized) object
   8331    JSObject* objResult =
   8332        JS_NewObjectWithGivenProto(cx, &sCDataFinalizerClass, objProto);
   8333    args.rval().setObject(*objResult);
   8334    return true;
   8335  }
   8336 
   8337  if (args.length() != 2) {
   8338    return ArgumentLengthError(cx, "CDataFinalizer constructor", "two", "s");
   8339  }
   8340 
   8341  JS::HandleValue valCodePtr = args[1];
   8342  if (!valCodePtr.isObject()) {
   8343    return TypeError(cx, "_a CData object_ of a function pointer type",
   8344                     valCodePtr);
   8345  }
   8346  RootedObject objCodePtr(cx, &valCodePtr.toObject());
   8347 
   8348  // Note: Using a custom argument formatter here would be awkward (requires
   8349  // a destructor just to uninstall the formatter).
   8350 
   8351  // 2. Extract argument type of |objCodePtr|
   8352  if (!CData::IsCDataMaybeUnwrap(&objCodePtr)) {
   8353    return TypeError(cx, "a _CData_ object of a function pointer type",
   8354                     valCodePtr);
   8355  }
   8356  RootedObject objCodePtrType(cx, CData::GetCType(objCodePtr));
   8357  RootedValue valCodePtrType(cx, ObjectValue(*objCodePtrType));
   8358  MOZ_ASSERT(objCodePtrType);
   8359 
   8360  TypeCode typCodePtr = CType::GetTypeCode(objCodePtrType);
   8361  if (typCodePtr != TYPE_pointer) {
   8362    return TypeError(cx, "a CData object of a function _pointer_ type",
   8363                     valCodePtr);
   8364  }
   8365 
   8366  JSObject* objCodeType = PointerType::GetBaseType(objCodePtrType);
   8367  MOZ_ASSERT(objCodeType);
   8368 
   8369  TypeCode typCode = CType::GetTypeCode(objCodeType);
   8370  if (typCode != TYPE_function) {
   8371    return TypeError(cx, "a CData object of a _function_ pointer type",
   8372                     valCodePtr);
   8373  }
   8374  uintptr_t code = *reinterpret_cast<uintptr_t*>(CData::GetData(objCodePtr));
   8375  if (!code) {
   8376    return TypeError(cx, "a CData object of a _non-NULL_ function pointer type",
   8377                     valCodePtr);
   8378  }
   8379 
   8380  FunctionInfo* funInfoFinalizer = FunctionType::GetFunctionInfo(objCodeType);
   8381  MOZ_ASSERT(funInfoFinalizer);
   8382 
   8383  if ((funInfoFinalizer->mArgTypes.length() != 1) ||
   8384      (funInfoFinalizer->mIsVariadic)) {
   8385    RootedValue valCodeType(cx, ObjectValue(*objCodeType));
   8386    return TypeError(cx, "a function accepting exactly one argument",
   8387                     valCodeType);
   8388  }
   8389  RootedObject objArgType(cx, funInfoFinalizer->mArgTypes[0]);
   8390  RootedObject returnType(cx, funInfoFinalizer->mReturnType);
   8391 
   8392  // Invariant: At this stage, we know that funInfoFinalizer->mIsVariadic
   8393  // is |false|. Therefore, funInfoFinalizer->mCIF has already been initialized.
   8394 
   8395  bool freePointer = false;
   8396 
   8397  // 3. Perform dynamic cast of |args[0]| into |objType|, store it in |cargs|
   8398 
   8399  size_t sizeArg;
   8400  RootedValue valData(cx, args[0]);
   8401  if (!CType::GetSafeSize(objArgType, &sizeArg)) {
   8402    RootedValue valCodeType(cx, ObjectValue(*objCodeType));
   8403    return TypeError(cx, "a function with one known size argument",
   8404                     valCodeType);
   8405  }
   8406 
   8407  UniquePtr<void, JS::FreePolicy> cargs(malloc(sizeArg));
   8408 
   8409  if (!ImplicitConvert(cx, valData, objArgType, cargs.get(),
   8410                       ConversionType::Finalizer, &freePointer, objCodePtrType,
   8411                       0)) {
   8412    return false;
   8413  }
   8414  if (freePointer) {
   8415    // Note: We could handle that case, if necessary.
   8416    JS_ReportErrorASCII(
   8417        cx,
   8418        "Internal Error during CDataFinalizer. Object cannot be represented");
   8419    return false;
   8420  }
   8421 
   8422  // 4. Prepare buffer for holding return value
   8423 
   8424  UniquePtr<void, JS::FreePolicy> rvalue;
   8425  if (CType::GetTypeCode(returnType) != TYPE_void_t) {
   8426    rvalue.reset(malloc(Align(CType::GetSize(returnType), sizeof(ffi_arg))));
   8427  }  // Otherwise, simply do not allocate
   8428 
   8429  // 5. Create |objResult|
   8430 
   8431  JSObject* objResult =
   8432      JS_NewObjectWithGivenProto(cx, &sCDataFinalizerClass, objProto);
   8433  if (!objResult) {
   8434    return false;
   8435  }
   8436 
   8437  // If our argument is a CData, it holds a type.
   8438  // This is the type that we should capture, not that
   8439  // of the function, which may be less precise.
   8440  JSObject* objBestArgType = objArgType;
   8441  if (valData.isObject()) {
   8442    RootedObject objData(cx, &valData.toObject());
   8443    if (CData::IsCDataMaybeUnwrap(&objData)) {
   8444      objBestArgType = CData::GetCType(objData);
   8445      size_t sizeBestArg;
   8446      if (!CType::GetSafeSize(objBestArgType, &sizeBestArg)) {
   8447        MOZ_CRASH("object with unknown size");
   8448      }
   8449      if (sizeBestArg != sizeArg) {
   8450        return FinalizerSizeError(cx, objCodePtrType, valData);
   8451      }
   8452    }
   8453  }
   8454 
   8455  // Used by GetCType
   8456  JS_SetReservedSlot(objResult, SLOT_DATAFINALIZER_VALTYPE,
   8457                     ObjectOrNullValue(objBestArgType));
   8458 
   8459  // Used by ToSource
   8460  JS_SetReservedSlot(objResult, SLOT_DATAFINALIZER_CODETYPE,
   8461                     ObjectValue(*objCodePtrType));
   8462 
   8463  RootedValue abiType(cx, ObjectOrNullValue(funInfoFinalizer->mABI));
   8464  ffi_abi abi;
   8465  if (!GetABI(cx, abiType, &abi)) {
   8466    JS_ReportErrorASCII(cx,
   8467                        "Internal Error: "
   8468                        "Invalid ABI specification in CDataFinalizer");
   8469    return false;
   8470  }
   8471 
   8472  ffi_type* rtype = CType::GetFFIType(cx, funInfoFinalizer->mReturnType);
   8473  if (!rtype) {
   8474    JS_ReportErrorASCII(cx,
   8475                        "Internal Error: "
   8476                        "Could not access ffi type of CDataFinalizer");
   8477    return false;
   8478  }
   8479 
   8480  // 7. Store C information as private
   8481  UniquePtr<CDataFinalizer::Private, JS::FreePolicy> p(
   8482      (CDataFinalizer::Private*)malloc(sizeof(CDataFinalizer::Private)));
   8483 
   8484  memmove(&p->CIF, &funInfoFinalizer->mCIF, sizeof(ffi_cif));
   8485 
   8486  p->cargs = cargs.release();
   8487  p->rvalue = rvalue.release();
   8488  p->cargs_size = sizeArg;
   8489  p->code = code;
   8490 
   8491  JS::SetReservedSlot(objResult, SLOT_DATAFINALIZER_PRIVATE,
   8492                      JS::PrivateValue(p.release()));
   8493  args.rval().setObject(*objResult);
   8494  return true;
   8495 }
   8496 
   8497 /*
   8498 * Actually call the finalizer. Does not perform any cleanup on the object.
   8499 *
   8500 * Preconditions: |this| must be a |CDataFinalizer|, |p| must be non-null.
   8501 * The function fails if |this| has gone through |Forget|/|Dispose|
   8502 * or |Finalize|.
   8503 *
   8504 * This function does not alter the value of |errno|/|GetLastError|.
   8505 *
   8506 * If argument |errnoStatus| is non-nullptr, it receives the value of |errno|
   8507 * immediately after the call. Under Windows, if argument |lastErrorStatus|
   8508 * is non-nullptr, it receives the value of |GetLastError| immediately after
   8509 * the call. On other platforms, |lastErrorStatus| is ignored.
   8510 */
   8511 void CDataFinalizer::CallFinalizer(CDataFinalizer::Private* p, int* errnoStatus,
   8512                                   int32_t* lastErrorStatus) {
   8513  int savedErrno = errno;
   8514  errno = 0;
   8515 #if defined(XP_WIN)
   8516  int32_t savedLastError = GetLastError();
   8517  SetLastError(0);
   8518 #endif  // defined(XP_WIN)
   8519 
   8520  void* args[1] = {p->cargs};
   8521  ffi_call(&p->CIF, FFI_FN(p->code), p->rvalue, args);
   8522 
   8523  if (errnoStatus) {
   8524    *errnoStatus = errno;
   8525  }
   8526  errno = savedErrno;
   8527 #if defined(XP_WIN)
   8528  if (lastErrorStatus) {
   8529    *lastErrorStatus = GetLastError();
   8530  }
   8531  SetLastError(savedLastError);
   8532 #endif  // defined(XP_WIN)
   8533 }
   8534 
   8535 /*
   8536 * Forget the value.
   8537 *
   8538 * Preconditions: |this| must be a |CDataFinalizer|.
   8539 * The function fails if |this| has gone through |Forget|/|Dispose|
   8540 * or |Finalize|.
   8541 *
   8542 * Does not call the finalizer. Cleans up the Private memory and releases all
   8543 * strong references.
   8544 */
   8545 bool CDataFinalizer::Methods::Forget(JSContext* cx, unsigned argc, Value* vp) {
   8546  CallArgs args = CallArgsFromVp(argc, vp);
   8547  if (args.length() != 0) {
   8548    return ArgumentLengthError(cx, "CDataFinalizer.prototype.forget", "no",
   8549                               "s");
   8550  }
   8551 
   8552  RootedObject obj(cx,
   8553                   GetThisObject(cx, args, "CDataFinalizer.prototype.forget"));
   8554  if (!obj) {
   8555    return false;
   8556  }
   8557  if (!CDataFinalizer::IsCDataFinalizer(obj)) {
   8558    return IncompatibleThisProto(cx, "CDataFinalizer.prototype.forget",
   8559                                 args.thisv());
   8560  }
   8561 
   8562  CDataFinalizer::Private* p = GetFinalizerPrivate(obj);
   8563 
   8564  if (!p) {
   8565    return EmptyFinalizerCallError(cx, "CDataFinalizer.prototype.forget");
   8566  }
   8567 
   8568  RootedValue valJSData(cx);
   8569  RootedObject ctype(cx, GetCType(cx, obj));
   8570  if (!ConvertToJS(cx, ctype, nullptr, p->cargs, false, true, &valJSData)) {
   8571    JS_ReportErrorASCII(cx, "CDataFinalizer value cannot be represented");
   8572    return false;
   8573  }
   8574 
   8575  CDataFinalizer::Cleanup(p, obj);
   8576 
   8577  args.rval().set(valJSData);
   8578  return true;
   8579 }
   8580 
   8581 /*
   8582 * Clean up the value.
   8583 *
   8584 * Preconditions: |this| must be a |CDataFinalizer|.
   8585 * The function fails if |this| has gone through |Forget|/|Dispose|
   8586 * or |Finalize|.
   8587 *
   8588 * Calls the finalizer, cleans up the Private memory and releases all
   8589 * strong references.
   8590 */
   8591 bool CDataFinalizer::Methods::Dispose(JSContext* cx, unsigned argc, Value* vp) {
   8592  CallArgs args = CallArgsFromVp(argc, vp);
   8593  if (args.length() != 0) {
   8594    return ArgumentLengthError(cx, "CDataFinalizer.prototype.dispose", "no",
   8595                               "s");
   8596  }
   8597 
   8598  RootedObject obj(cx,
   8599                   GetThisObject(cx, args, "CDataFinalizer.prototype.dispose"));
   8600  if (!obj) {
   8601    return false;
   8602  }
   8603  if (!CDataFinalizer::IsCDataFinalizer(obj)) {
   8604    return IncompatibleThisProto(cx, "CDataFinalizer.prototype.dispose",
   8605                                 args.thisv());
   8606  }
   8607 
   8608  CDataFinalizer::Private* p = GetFinalizerPrivate(obj);
   8609 
   8610  if (!p) {
   8611    return EmptyFinalizerCallError(cx, "CDataFinalizer.prototype.dispose");
   8612  }
   8613 
   8614  Value valType = JS::GetReservedSlot(obj, SLOT_DATAFINALIZER_VALTYPE);
   8615  MOZ_ASSERT(valType.isObject());
   8616 
   8617  RootedObject objCTypes(cx, CType::GetGlobalCTypes(cx, &valType.toObject()));
   8618  if (!objCTypes) {
   8619    return false;
   8620  }
   8621 
   8622  Value valCodePtrType = JS::GetReservedSlot(obj, SLOT_DATAFINALIZER_CODETYPE);
   8623  MOZ_ASSERT(valCodePtrType.isObject());
   8624  JSObject* objCodePtrType = &valCodePtrType.toObject();
   8625 
   8626  JSObject* objCodeType = PointerType::GetBaseType(objCodePtrType);
   8627  MOZ_ASSERT(objCodeType);
   8628  MOZ_ASSERT(CType::GetTypeCode(objCodeType) == TYPE_function);
   8629 
   8630  RootedObject resultType(
   8631      cx, FunctionType::GetFunctionInfo(objCodeType)->mReturnType);
   8632  RootedValue result(cx);
   8633 
   8634  int errnoStatus;
   8635 #if defined(XP_WIN)
   8636  int32_t lastErrorStatus;
   8637  CDataFinalizer::CallFinalizer(p, &errnoStatus, &lastErrorStatus);
   8638 #else
   8639  CDataFinalizer::CallFinalizer(p, &errnoStatus, nullptr);
   8640 #endif  // defined(XP_WIN)
   8641 
   8642  JS_SetReservedSlot(objCTypes, SLOT_ERRNO, Int32Value(errnoStatus));
   8643 #if defined(XP_WIN)
   8644  JS_SetReservedSlot(objCTypes, SLOT_LASTERROR, Int32Value(lastErrorStatus));
   8645 #endif  // defined(XP_WIN)
   8646 
   8647  if (ConvertToJS(cx, resultType, nullptr, p->rvalue, false, true, &result)) {
   8648    CDataFinalizer::Cleanup(p, obj);
   8649    args.rval().set(result);
   8650    return true;
   8651  }
   8652  CDataFinalizer::Cleanup(p, obj);
   8653  return false;
   8654 }
   8655 
   8656 /*
   8657 * Perform finalization.
   8658 *
   8659 * Preconditions: |this| must be the result of |CDataFinalizer|.
   8660 * It may have gone through |Forget|/|Dispose|.
   8661 *
   8662 * If |this| has not gone through |Forget|/|Dispose|, calls the
   8663 * finalizer, cleans up the Private memory and releases all
   8664 * strong references.
   8665 */
   8666 void CDataFinalizer::Finalize(JS::GCContext* gcx, JSObject* obj) {
   8667  CDataFinalizer::Private* p = GetFinalizerPrivate(obj);
   8668 
   8669  if (!p) {
   8670    return;
   8671  }
   8672 
   8673  CDataFinalizer::CallFinalizer(p, nullptr, nullptr);
   8674  CDataFinalizer::Cleanup(p, nullptr);
   8675 }
   8676 
   8677 /*
   8678 * Perform cleanup of a CDataFinalizer
   8679 *
   8680 * Release strong references, cleanup |Private|.
   8681 *
   8682 * Argument |p| contains the private information of the CDataFinalizer. If
   8683 * nullptr, this function does nothing.
   8684 * Argument |obj| should contain |nullptr| during finalization (or in any
   8685 * context in which the object itself should not be cleaned up), or a
   8686 * CDataFinalizer object otherwise.
   8687 */
   8688 void CDataFinalizer::Cleanup(CDataFinalizer::Private* p, JSObject* obj) {
   8689  if (!p) {
   8690    return;  // We have already cleaned up
   8691  }
   8692 
   8693  free(p->cargs);
   8694  free(p->rvalue);
   8695  free(p);
   8696 
   8697  if (!obj) {
   8698    return;  // No slots to clean up
   8699  }
   8700 
   8701  MOZ_ASSERT(CDataFinalizer::IsCDataFinalizer(obj));
   8702 
   8703  static_assert(CDATAFINALIZER_SLOTS == 3, "Code below must clear all slots");
   8704 
   8705  JS::SetReservedSlot(obj, SLOT_DATAFINALIZER_PRIVATE, JS::UndefinedValue());
   8706  JS::SetReservedSlot(obj, SLOT_DATAFINALIZER_VALTYPE, JS::NullValue());
   8707  JS::SetReservedSlot(obj, SLOT_DATAFINALIZER_CODETYPE, JS::NullValue());
   8708 }
   8709 
   8710 /*******************************************************************************
   8711 ** Int64 and UInt64 implementation
   8712 *******************************************************************************/
   8713 
   8714 JSObject* Int64Base::Construct(JSContext* cx, HandleObject proto, uint64_t data,
   8715                               bool isUnsigned) {
   8716  const JSClass* clasp = isUnsigned ? &sUInt64Class : &sInt64Class;
   8717  RootedObject result(cx, JS_NewObjectWithGivenProto(cx, clasp, proto));
   8718  if (!result) {
   8719    return nullptr;
   8720  }
   8721 
   8722  // attach the Int64's data
   8723  uint64_t* buffer = cx->new_<uint64_t>(data);
   8724  if (!buffer) {
   8725    return nullptr;
   8726  }
   8727 
   8728  JS_InitReservedSlot(result, SLOT_INT64, buffer, JS::MemoryUse::CTypesInt64);
   8729 
   8730  if (!JS_FreezeObject(cx, result)) {
   8731    return nullptr;
   8732  }
   8733 
   8734  return result;
   8735 }
   8736 
   8737 void Int64Base::Finalize(JS::GCContext* gcx, JSObject* obj) {
   8738  Value slot = JS::GetReservedSlot(obj, SLOT_INT64);
   8739  if (slot.isUndefined()) {
   8740    return;
   8741  }
   8742 
   8743  uint64_t* buffer = static_cast<uint64_t*>(slot.toPrivate());
   8744  gcx->delete_(obj, buffer, MemoryUse::CTypesInt64);
   8745 }
   8746 
   8747 uint64_t Int64Base::GetInt(JSObject* obj) {
   8748  MOZ_ASSERT(Int64::IsInt64(obj) || UInt64::IsUInt64(obj));
   8749 
   8750  Value slot = JS::GetReservedSlot(obj, SLOT_INT64);
   8751  return *static_cast<uint64_t*>(slot.toPrivate());
   8752 }
   8753 
   8754 bool Int64Base::ToString(JSContext* cx, JSObject* obj, const CallArgs& args,
   8755                         bool isUnsigned) {
   8756  if (args.length() > 1) {
   8757    if (isUnsigned) {
   8758      return ArgumentLengthError(cx, "UInt64.prototype.toString", "at most one",
   8759                                 "");
   8760    }
   8761    return ArgumentLengthError(cx, "Int64.prototype.toString", "at most one",
   8762                               "");
   8763  }
   8764 
   8765  int radix = 10;
   8766  if (args.length() == 1) {
   8767    Value arg = args[0];
   8768    if (arg.isInt32()) {
   8769      radix = arg.toInt32();
   8770    }
   8771    if (!arg.isInt32() || radix < 2 || radix > 36) {
   8772      if (isUnsigned) {
   8773        return ArgumentRangeMismatch(
   8774            cx, "UInt64.prototype.toString",
   8775            "an integer at least 2 and no greater than 36");
   8776      }
   8777      return ArgumentRangeMismatch(
   8778          cx, "Int64.prototype.toString",
   8779          "an integer at least 2 and no greater than 36");
   8780    }
   8781  }
   8782 
   8783  AutoString intString;
   8784  if (isUnsigned) {
   8785    IntegerToString(GetInt(obj), radix, intString);
   8786  } else {
   8787    IntegerToString(static_cast<int64_t>(GetInt(obj)), radix, intString);
   8788  }
   8789 
   8790  if (!intString) {
   8791    return false;
   8792  }
   8793  JSString* result = NewUCString(cx, intString.finish());
   8794  if (!result) {
   8795    return false;
   8796  }
   8797 
   8798  args.rval().setString(result);
   8799  return true;
   8800 }
   8801 
   8802 bool Int64Base::ToSource(JSContext* cx, JSObject* obj, const CallArgs& args,
   8803                         bool isUnsigned) {
   8804  if (args.length() != 0) {
   8805    if (isUnsigned) {
   8806      return ArgumentLengthError(cx, "UInt64.prototype.toSource", "no", "s");
   8807    }
   8808    return ArgumentLengthError(cx, "Int64.prototype.toSource", "no", "s");
   8809  }
   8810 
   8811  // Return a decimal string suitable for constructing the number.
   8812  AutoString source;
   8813  if (isUnsigned) {
   8814    AppendString(cx, source, "ctypes.UInt64(\"");
   8815    IntegerToString(GetInt(obj), 10, source);
   8816  } else {
   8817    AppendString(cx, source, "ctypes.Int64(\"");
   8818    IntegerToString(static_cast<int64_t>(GetInt(obj)), 10, source);
   8819  }
   8820  AppendString(cx, source, "\")");
   8821  if (!source) {
   8822    return false;
   8823  }
   8824 
   8825  JSString* result = NewUCString(cx, source.finish());
   8826  if (!result) {
   8827    return false;
   8828  }
   8829 
   8830  args.rval().setString(result);
   8831  return true;
   8832 }
   8833 
   8834 bool Int64::Construct(JSContext* cx, unsigned argc, Value* vp) {
   8835  CallArgs args = CallArgsFromVp(argc, vp);
   8836 
   8837  // Construct and return a new Int64 object.
   8838  if (args.length() != 1) {
   8839    return ArgumentLengthError(cx, "Int64 constructor", "one", "");
   8840  }
   8841 
   8842  int64_t i = 0;
   8843  bool overflow = false;
   8844  if (!jsvalToBigInteger(cx, args[0], true, &i, &overflow)) {
   8845    if (overflow) {
   8846      return TypeOverflow(cx, "int64", args[0]);
   8847    }
   8848    return ArgumentConvError(cx, args[0], "Int64", 0);
   8849  }
   8850 
   8851  // Get ctypes.Int64.prototype from the 'prototype' property of the ctor.
   8852  RootedValue slot(cx);
   8853  RootedObject callee(cx, &args.callee());
   8854  MOZ_ALWAYS_TRUE(JS_GetProperty(cx, callee, "prototype", &slot));
   8855  RootedObject proto(cx, slot.toObjectOrNull());
   8856  MOZ_ASSERT(proto->hasClass(&sInt64ProtoClass));
   8857 
   8858  JSObject* result = Int64Base::Construct(cx, proto, i, false);
   8859  if (!result) {
   8860    return false;
   8861  }
   8862 
   8863  args.rval().setObject(*result);
   8864  return true;
   8865 }
   8866 
   8867 bool Int64::IsInt64(JSObject* obj) { return obj->hasClass(&sInt64Class); }
   8868 
   8869 bool Int64::ToString(JSContext* cx, unsigned argc, Value* vp) {
   8870  CallArgs args = CallArgsFromVp(argc, vp);
   8871  RootedObject obj(cx, GetThisObject(cx, args, "Int64.prototype.toString"));
   8872  if (!obj) {
   8873    return false;
   8874  }
   8875  if (!Int64::IsInt64(obj)) {
   8876    if (!CData::IsCDataMaybeUnwrap(&obj)) {
   8877      return IncompatibleThisProto(cx, "Int64.prototype.toString",
   8878                                   InformalValueTypeName(args.thisv()));
   8879    }
   8880    return IncompatibleThisType(cx, "Int64.prototype.toString",
   8881                                "non-Int64 CData");
   8882  }
   8883 
   8884  return Int64Base::ToString(cx, obj, args, false);
   8885 }
   8886 
   8887 bool Int64::ToSource(JSContext* cx, unsigned argc, Value* vp) {
   8888  CallArgs args = CallArgsFromVp(argc, vp);
   8889  RootedObject obj(cx, GetThisObject(cx, args, "Int64.prototype.toSource"));
   8890  if (!obj) {
   8891    return false;
   8892  }
   8893  if (!Int64::IsInt64(obj)) {
   8894    if (!CData::IsCDataMaybeUnwrap(&obj)) {
   8895      return IncompatibleThisProto(cx, "Int64.prototype.toSource",
   8896                                   InformalValueTypeName(args.thisv()));
   8897    }
   8898    return IncompatibleThisType(cx, "Int64.prototype.toSource",
   8899                                "non-Int64 CData");
   8900  }
   8901 
   8902  return Int64Base::ToSource(cx, obj, args, false);
   8903 }
   8904 
   8905 bool Int64::Compare(JSContext* cx, unsigned argc, Value* vp) {
   8906  CallArgs args = CallArgsFromVp(argc, vp);
   8907  if (args.length() != 2) {
   8908    return ArgumentLengthError(cx, "Int64.compare", "two", "s");
   8909  }
   8910  if (args[0].isPrimitive() || !Int64::IsInt64(&args[0].toObject())) {
   8911    return ArgumentTypeMismatch(cx, "first ", "Int64.compare", "a Int64");
   8912  }
   8913  if (args[1].isPrimitive() || !Int64::IsInt64(&args[1].toObject())) {
   8914    return ArgumentTypeMismatch(cx, "second ", "Int64.compare", "a Int64");
   8915  }
   8916 
   8917  JSObject* obj1 = &args[0].toObject();
   8918  JSObject* obj2 = &args[1].toObject();
   8919 
   8920  int64_t i1 = Int64Base::GetInt(obj1);
   8921  int64_t i2 = Int64Base::GetInt(obj2);
   8922 
   8923  if (i1 == i2) {
   8924    args.rval().setInt32(0);
   8925  } else if (i1 < i2) {
   8926    args.rval().setInt32(-1);
   8927  } else {
   8928    args.rval().setInt32(1);
   8929  }
   8930 
   8931  return true;
   8932 }
   8933 
   8934 #define LO_MASK ((uint64_t(1) << 32) - 1)
   8935 #define INT64_LO(i) ((i) & LO_MASK)
   8936 #define INT64_HI(i) ((i) >> 32)
   8937 
   8938 bool Int64::Lo(JSContext* cx, unsigned argc, Value* vp) {
   8939  CallArgs args = CallArgsFromVp(argc, vp);
   8940  if (args.length() != 1) {
   8941    return ArgumentLengthError(cx, "Int64.lo", "one", "");
   8942  }
   8943  if (args[0].isPrimitive() || !Int64::IsInt64(&args[0].toObject())) {
   8944    return ArgumentTypeMismatch(cx, "", "Int64.lo", "a Int64");
   8945  }
   8946 
   8947  JSObject* obj = &args[0].toObject();
   8948  int64_t u = Int64Base::GetInt(obj);
   8949  double d = uint32_t(INT64_LO(u));
   8950 
   8951  args.rval().setNumber(d);
   8952  return true;
   8953 }
   8954 
   8955 bool Int64::Hi(JSContext* cx, unsigned argc, Value* vp) {
   8956  CallArgs args = CallArgsFromVp(argc, vp);
   8957  if (args.length() != 1) {
   8958    return ArgumentLengthError(cx, "Int64.hi", "one", "");
   8959  }
   8960  if (args[0].isPrimitive() || !Int64::IsInt64(&args[0].toObject())) {
   8961    return ArgumentTypeMismatch(cx, "", "Int64.hi", "a Int64");
   8962  }
   8963 
   8964  JSObject* obj = &args[0].toObject();
   8965  int64_t u = Int64Base::GetInt(obj);
   8966  double d = int32_t(INT64_HI(u));
   8967 
   8968  args.rval().setDouble(d);
   8969  return true;
   8970 }
   8971 
   8972 bool Int64::Join(JSContext* cx, unsigned argc, Value* vp) {
   8973  CallArgs args = CallArgsFromVp(argc, vp);
   8974  if (args.length() != 2) {
   8975    return ArgumentLengthError(cx, "Int64.join", "two", "s");
   8976  }
   8977 
   8978  int32_t hi;
   8979  uint32_t lo;
   8980  if (!jsvalToInteger(cx, args[0], &hi)) {
   8981    return ArgumentConvError(cx, args[0], "Int64.join", 0);
   8982  }
   8983  if (!jsvalToInteger(cx, args[1], &lo)) {
   8984    return ArgumentConvError(cx, args[1], "Int64.join", 1);
   8985  }
   8986 
   8987  int64_t i = mozilla::WrapToSigned((uint64_t(hi) << 32) + lo);
   8988 
   8989  // Get Int64.prototype from the function's reserved slot.
   8990  JSObject* callee = &args.callee();
   8991 
   8992  Value slot = js::GetFunctionNativeReserved(callee, SLOT_FN_INT64PROTO);
   8993  RootedObject proto(cx, &slot.toObject());
   8994  MOZ_ASSERT(proto->hasClass(&sInt64ProtoClass));
   8995 
   8996  JSObject* result = Int64Base::Construct(cx, proto, i, false);
   8997  if (!result) {
   8998    return false;
   8999  }
   9000 
   9001  args.rval().setObject(*result);
   9002  return true;
   9003 }
   9004 
   9005 bool UInt64::Construct(JSContext* cx, unsigned argc, Value* vp) {
   9006  CallArgs args = CallArgsFromVp(argc, vp);
   9007 
   9008  // Construct and return a new UInt64 object.
   9009  if (args.length() != 1) {
   9010    return ArgumentLengthError(cx, "UInt64 constructor", "one", "");
   9011  }
   9012 
   9013  uint64_t u = 0;
   9014  bool overflow = false;
   9015  if (!jsvalToBigInteger(cx, args[0], true, &u, &overflow)) {
   9016    if (overflow) {
   9017      return TypeOverflow(cx, "uint64", args[0]);
   9018    }
   9019    return ArgumentConvError(cx, args[0], "UInt64", 0);
   9020  }
   9021 
   9022  // Get ctypes.UInt64.prototype from the 'prototype' property of the ctor.
   9023  RootedValue slot(cx);
   9024  RootedObject callee(cx, &args.callee());
   9025  MOZ_ALWAYS_TRUE(JS_GetProperty(cx, callee, "prototype", &slot));
   9026  RootedObject proto(cx, &slot.toObject());
   9027  MOZ_ASSERT(proto->hasClass(&sUInt64ProtoClass));
   9028 
   9029  JSObject* result = Int64Base::Construct(cx, proto, u, true);
   9030  if (!result) {
   9031    return false;
   9032  }
   9033 
   9034  args.rval().setObject(*result);
   9035  return true;
   9036 }
   9037 
   9038 bool UInt64::IsUInt64(JSObject* obj) { return obj->hasClass(&sUInt64Class); }
   9039 
   9040 bool UInt64::ToString(JSContext* cx, unsigned argc, Value* vp) {
   9041  CallArgs args = CallArgsFromVp(argc, vp);
   9042  RootedObject obj(cx, GetThisObject(cx, args, "UInt64.prototype.toString"));
   9043  if (!obj) {
   9044    return false;
   9045  }
   9046  if (!UInt64::IsUInt64(obj)) {
   9047    if (!CData::IsCDataMaybeUnwrap(&obj)) {
   9048      return IncompatibleThisProto(cx, "UInt64.prototype.toString",
   9049                                   InformalValueTypeName(args.thisv()));
   9050    }
   9051    return IncompatibleThisType(cx, "UInt64.prototype.toString",
   9052                                "non-UInt64 CData");
   9053  }
   9054 
   9055  return Int64Base::ToString(cx, obj, args, true);
   9056 }
   9057 
   9058 bool UInt64::ToSource(JSContext* cx, unsigned argc, Value* vp) {
   9059  CallArgs args = CallArgsFromVp(argc, vp);
   9060  RootedObject obj(cx, GetThisObject(cx, args, "UInt64.prototype.toSource"));
   9061  if (!obj) {
   9062    return false;
   9063  }
   9064  if (!UInt64::IsUInt64(obj)) {
   9065    if (!CData::IsCDataMaybeUnwrap(&obj)) {
   9066      return IncompatibleThisProto(cx, "UInt64.prototype.toSource",
   9067                                   InformalValueTypeName(args.thisv()));
   9068    }
   9069    return IncompatibleThisType(cx, "UInt64.prototype.toSource",
   9070                                "non-UInt64 CData");
   9071  }
   9072 
   9073  return Int64Base::ToSource(cx, obj, args, true);
   9074 }
   9075 
   9076 bool UInt64::Compare(JSContext* cx, unsigned argc, Value* vp) {
   9077  CallArgs args = CallArgsFromVp(argc, vp);
   9078  if (args.length() != 2) {
   9079    return ArgumentLengthError(cx, "UInt64.compare", "two", "s");
   9080  }
   9081  if (args[0].isPrimitive() || !UInt64::IsUInt64(&args[0].toObject())) {
   9082    return ArgumentTypeMismatch(cx, "first ", "UInt64.compare", "a UInt64");
   9083  }
   9084  if (args[1].isPrimitive() || !UInt64::IsUInt64(&args[1].toObject())) {
   9085    return ArgumentTypeMismatch(cx, "second ", "UInt64.compare", "a UInt64");
   9086  }
   9087 
   9088  JSObject* obj1 = &args[0].toObject();
   9089  JSObject* obj2 = &args[1].toObject();
   9090 
   9091  uint64_t u1 = Int64Base::GetInt(obj1);
   9092  uint64_t u2 = Int64Base::GetInt(obj2);
   9093 
   9094  if (u1 == u2) {
   9095    args.rval().setInt32(0);
   9096  } else if (u1 < u2) {
   9097    args.rval().setInt32(-1);
   9098  } else {
   9099    args.rval().setInt32(1);
   9100  }
   9101 
   9102  return true;
   9103 }
   9104 
   9105 bool UInt64::Lo(JSContext* cx, unsigned argc, Value* vp) {
   9106  CallArgs args = CallArgsFromVp(argc, vp);
   9107  if (args.length() != 1) {
   9108    return ArgumentLengthError(cx, "UInt64.lo", "one", "");
   9109  }
   9110  if (args[0].isPrimitive() || !UInt64::IsUInt64(&args[0].toObject())) {
   9111    return ArgumentTypeMismatch(cx, "", "UInt64.lo", "a UInt64");
   9112  }
   9113 
   9114  JSObject* obj = &args[0].toObject();
   9115  uint64_t u = Int64Base::GetInt(obj);
   9116  double d = uint32_t(INT64_LO(u));
   9117 
   9118  args.rval().setDouble(d);
   9119  return true;
   9120 }
   9121 
   9122 bool UInt64::Hi(JSContext* cx, unsigned argc, Value* vp) {
   9123  CallArgs args = CallArgsFromVp(argc, vp);
   9124  if (args.length() != 1) {
   9125    return ArgumentLengthError(cx, "UInt64.hi", "one", "");
   9126  }
   9127  if (args[0].isPrimitive() || !UInt64::IsUInt64(&args[0].toObject())) {
   9128    return ArgumentTypeMismatch(cx, "", "UInt64.hi", "a UInt64");
   9129  }
   9130 
   9131  JSObject* obj = &args[0].toObject();
   9132  uint64_t u = Int64Base::GetInt(obj);
   9133  double d = uint32_t(INT64_HI(u));
   9134 
   9135  args.rval().setDouble(d);
   9136  return true;
   9137 }
   9138 
   9139 bool UInt64::Join(JSContext* cx, unsigned argc, Value* vp) {
   9140  CallArgs args = CallArgsFromVp(argc, vp);
   9141  if (args.length() != 2) {
   9142    return ArgumentLengthError(cx, "UInt64.join", "two", "s");
   9143  }
   9144 
   9145  uint32_t hi;
   9146  uint32_t lo;
   9147  if (!jsvalToInteger(cx, args[0], &hi)) {
   9148    return ArgumentConvError(cx, args[0], "UInt64.join", 0);
   9149  }
   9150  if (!jsvalToInteger(cx, args[1], &lo)) {
   9151    return ArgumentConvError(cx, args[1], "UInt64.join", 1);
   9152  }
   9153 
   9154  uint64_t u = (uint64_t(hi) << 32) + uint64_t(lo);
   9155 
   9156  // Get UInt64.prototype from the function's reserved slot.
   9157  JSObject* callee = &args.callee();
   9158 
   9159  Value slot = js::GetFunctionNativeReserved(callee, SLOT_FN_INT64PROTO);
   9160  RootedObject proto(cx, &slot.toObject());
   9161  MOZ_ASSERT(proto->hasClass(&sUInt64ProtoClass));
   9162 
   9163  JSObject* result = Int64Base::Construct(cx, proto, u, true);
   9164  if (!result) {
   9165    return false;
   9166  }
   9167 
   9168  args.rval().setObject(*result);
   9169  return true;
   9170 }
   9171 
   9172 }  // namespace ctypes
   9173 }  // namespace js