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