CTypes.h (19096B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- 2 */ 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 #ifndef ctypes_CTypes_h 8 #define ctypes_CTypes_h 9 10 #include "mozilla/Sprintf.h" 11 #include "mozilla/Vector.h" 12 13 #include "ffi.h" 14 #include "prlink.h" 15 16 #include "ctypes/typedefs.h" 17 #include "gc/ZoneAllocator.h" 18 #include "js/AllocPolicy.h" 19 #include "js/GCHashTable.h" 20 #include "js/UniquePtr.h" 21 #include "js/Vector.h" 22 #include "vm/JSObject.h" 23 #include "vm/StringType.h" 24 25 namespace JS { 26 struct CTypesCallbacks; 27 } // namespace JS 28 29 namespace js { 30 namespace ctypes { 31 32 /******************************************************************************* 33 ** Utility classes 34 *******************************************************************************/ 35 36 // CTypes builds a number of strings. StringBuilder allows repeated appending 37 // with a single error check at the end. Only the Vector methods required for 38 // building the string are exposed. 39 40 template <class CharT, size_t N> 41 class StringBuilder { 42 Vector<CharT, N, SystemAllocPolicy> v; 43 44 // Have any (OOM) errors been encountered while constructing this string? 45 bool errored{false}; 46 47 #ifdef DEBUG 48 // Have we finished building this string? 49 bool finished{false}; 50 51 // Did we check for errors? 52 mutable bool checked{false}; 53 #endif 54 55 public: 56 explicit operator bool() const { 57 #ifdef DEBUG 58 checked = true; 59 #endif 60 return !errored; 61 } 62 63 // Handle the result of modifying the string, by remembering the persistent 64 // errored status. 65 bool handle(bool result) { 66 MOZ_ASSERT(!finished); 67 if (!result) { 68 errored = true; 69 } 70 return result; 71 } 72 73 bool resize(size_t n) { return handle(v.resize(n)); } 74 75 CharT& operator[](size_t index) { return v[index]; } 76 const CharT& operator[](size_t index) const { return v[index]; } 77 size_t length() const { return v.length(); } 78 79 template <typename U> 80 [[nodiscard]] bool append(U&& u) { 81 return handle(v.append(u)); 82 } 83 84 template <typename U> 85 [[nodiscard]] bool append(const U* begin, const U* end) { 86 return handle(v.append(begin, end)); 87 } 88 89 template <typename U> 90 [[nodiscard]] bool append(const U* begin, size_t len) { 91 return handle(v.append(begin, len)); 92 } 93 94 CharT* begin() { 95 MOZ_ASSERT(!finished); 96 return v.begin(); 97 } 98 99 // finish() produces the results of the string building, and is required as 100 // the last thing before the string contents are used. The StringBuilder must 101 // be checked for errors before calling this, however. 102 Vector<CharT, N, SystemAllocPolicy>&& finish() { 103 MOZ_ASSERT(!errored); 104 MOZ_ASSERT(!finished); 105 MOZ_ASSERT(checked); 106 #ifdef DEBUG 107 finished = true; 108 #endif 109 return std::move(v); 110 } 111 }; 112 113 // Note that these strings do not have any inline storage, because we use move 114 // constructors to pass the data around and inline storage would necessitate 115 // copying. 116 typedef StringBuilder<char16_t, 0> AutoString; 117 typedef StringBuilder<char, 0> AutoCString; 118 119 typedef Vector<char16_t, 0, SystemAllocPolicy> AutoStringChars; 120 typedef Vector<char, 0, SystemAllocPolicy> AutoCStringChars; 121 122 // Convenience functions to append, insert, and compare Strings. 123 template <class T, size_t N, size_t ArrayLength> 124 void AppendString(JSContext* cx, StringBuilder<T, N>& v, 125 const char (&array)[ArrayLength]) { 126 // Don't include the trailing '\0'. 127 size_t alen = ArrayLength - 1; 128 size_t vlen = v.length(); 129 if (!v.resize(vlen + alen)) { 130 return; 131 } 132 133 for (size_t i = 0; i < alen; ++i) { 134 v[i + vlen] = array[i]; 135 } 136 } 137 138 template <class T, size_t N> 139 void AppendChars(StringBuilder<T, N>& v, const char c, size_t count) { 140 size_t vlen = v.length(); 141 if (!v.resize(vlen + count)) { 142 return; 143 } 144 145 for (size_t i = 0; i < count; ++i) { 146 v[i + vlen] = c; 147 } 148 } 149 150 template <class T, size_t N> 151 void AppendUInt(StringBuilder<T, N>& v, unsigned n) { 152 char array[16]; 153 size_t alen = SprintfLiteral(array, "%u", n); 154 size_t vlen = v.length(); 155 if (!v.resize(vlen + alen)) { 156 return; 157 } 158 159 for (size_t i = 0; i < alen; ++i) { 160 v[i + vlen] = array[i]; 161 } 162 } 163 164 template <class T, size_t N, size_t M, class AP> 165 void AppendString(JSContext* cx, StringBuilder<T, N>& v, 166 mozilla::Vector<T, M, AP>& w) { 167 if (!v.append(w.begin(), w.length())) { 168 return; 169 } 170 } 171 172 template <size_t N> 173 void AppendString(JSContext* cx, StringBuilder<char16_t, N>& v, JSString* str) { 174 MOZ_ASSERT(str); 175 JSLinearString* linear = str->ensureLinear(cx); 176 if (!linear) { 177 return; 178 } 179 JS::AutoCheckCannotGC nogc; 180 if (linear->hasLatin1Chars()) { 181 if (!v.append(linear->latin1Chars(nogc), linear->length())) { 182 return; 183 } 184 } else { 185 if (!v.append(linear->twoByteChars(nogc), linear->length())) { 186 return; 187 } 188 } 189 } 190 191 template <size_t N> 192 void AppendString(JSContext* cx, StringBuilder<char, N>& v, JSString* str) { 193 MOZ_ASSERT(str); 194 size_t vlen = v.length(); 195 size_t alen = str->length(); 196 if (!v.resize(vlen + alen)) { 197 return; 198 } 199 200 JSLinearString* linear = str->ensureLinear(cx); 201 if (!linear) { 202 return; 203 } 204 205 JS::AutoCheckCannotGC nogc; 206 if (linear->hasLatin1Chars()) { 207 const Latin1Char* chars = linear->latin1Chars(nogc); 208 for (size_t i = 0; i < alen; ++i) { 209 v[i + vlen] = char(chars[i]); 210 } 211 } else { 212 const char16_t* chars = linear->twoByteChars(nogc); 213 for (size_t i = 0; i < alen; ++i) { 214 v[i + vlen] = char(chars[i]); 215 } 216 } 217 } 218 219 template <class T, size_t N, size_t ArrayLength> 220 void PrependString(JSContext* cx, StringBuilder<T, N>& v, 221 const char (&array)[ArrayLength]) { 222 // Don't include the trailing '\0'. 223 size_t alen = ArrayLength - 1; 224 size_t vlen = v.length(); 225 if (!v.resize(vlen + alen)) { 226 return; 227 } 228 229 // Move vector data forward. This is safe since we've already resized. 230 memmove(v.begin() + alen, v.begin(), vlen * sizeof(T)); 231 232 // Copy data to insert. 233 for (size_t i = 0; i < alen; ++i) { 234 v[i] = array[i]; 235 } 236 } 237 238 template <size_t N> 239 void PrependString(JSContext* cx, StringBuilder<char16_t, N>& v, 240 JSString* str) { 241 MOZ_ASSERT(str); 242 size_t vlen = v.length(); 243 size_t alen = str->length(); 244 if (!v.resize(vlen + alen)) { 245 return; 246 } 247 248 JSLinearString* linear = str->ensureLinear(cx); 249 if (!linear) { 250 return; 251 } 252 253 // Move vector data forward. This is safe since we've already resized. 254 memmove(v.begin() + alen, v.begin(), vlen * sizeof(char16_t)); 255 256 // Copy data to insert. 257 CopyChars(v.begin(), *linear); 258 } 259 260 [[nodiscard]] bool ReportErrorIfUnpairedSurrogatePresent(JSContext* cx, 261 JSLinearString* str); 262 263 [[nodiscard]] JSObject* GetThisObject(JSContext* cx, const CallArgs& args, 264 const char* msg); 265 266 /******************************************************************************* 267 ** Function and struct API definitions 268 *******************************************************************************/ 269 270 // for JS error reporting 271 enum ErrorNum { 272 #define MSG_DEF(name, count, exception, format) name, 273 #include "ctypes/ctypes.msg" 274 #undef MSG_DEF 275 CTYPESERR_LIMIT 276 }; 277 278 /** 279 * ABI constants that specify the calling convention to use. 280 * ctypes.default_abi corresponds to the cdecl convention, and in almost all 281 * cases is the correct choice. ctypes.stdcall_abi is provided for calling 282 * stdcall functions on Win32, and implies stdcall symbol name decoration; 283 * ctypes.winapi_abi is just stdcall but without decoration. 284 */ 285 enum ABICode { 286 ABI_DEFAULT, 287 ABI_STDCALL, 288 ABI_THISCALL, 289 ABI_WINAPI, 290 INVALID_ABI 291 }; 292 293 enum TypeCode { 294 TYPE_void_t, 295 #define DEFINE_TYPE(name, type, ffiType) TYPE_##name, 296 CTYPES_FOR_EACH_TYPE(DEFINE_TYPE) 297 #undef DEFINE_TYPE 298 TYPE_pointer, 299 TYPE_function, 300 TYPE_array, 301 TYPE_struct 302 }; 303 304 // Descriptor of one field in a StructType. The name of the field is stored 305 // as the key to the hash entry. 306 struct FieldInfo { 307 HeapPtr<JSObject*> mType; // CType of the field 308 size_t mIndex; // index of the field in the struct (first is 0) 309 size_t mOffset; // offset of the field in the struct, in bytes 310 311 void trace(JSTracer* trc) { TraceEdge(trc, &mType, "fieldType"); } 312 }; 313 314 struct UnbarrieredFieldInfo { 315 JSObject* mType; // CType of the field 316 size_t mIndex; // index of the field in the struct (first is 0) 317 size_t mOffset; // offset of the field in the struct, in bytes 318 }; 319 static_assert(sizeof(UnbarrieredFieldInfo) == sizeof(FieldInfo), 320 "UnbarrieredFieldInfo should be the same as FieldInfo but with " 321 "unbarriered mType"); 322 323 // Hash policy for FieldInfos. 324 struct FieldHashPolicy { 325 using Key = JSLinearString*; 326 using Lookup = Key; 327 328 static HashNumber hash(const Lookup& l) { return js::HashStringChars(l); } 329 330 static bool match(const Key& k, const Lookup& l) { 331 return js::EqualStrings(k, l); 332 } 333 }; 334 335 using FieldInfoHash = GCHashMap<js::HeapPtr<JSLinearString*>, FieldInfo, 336 FieldHashPolicy, CellAllocPolicy>; 337 338 // Descriptor of ABI, return type, argument types, and variadicity for a 339 // FunctionType. 340 struct FunctionInfo { 341 explicit FunctionInfo(JS::Zone* zone) : mArgTypes(zone), mFFITypes(zone) {} 342 343 // Initialized in NewFunctionInfo when !mIsVariadic, but only later, in 344 // FunctionType::Call, when mIsVariadic. Not always consistent with 345 // mFFITypes, due to lazy initialization when mIsVariadic. 346 ffi_cif mCIF; 347 348 // Calling convention of the function. Convert to ffi_abi using GetABI 349 // and ObjectValue. Stored as a JSObject* for ease of tracing. 350 HeapPtr<JSObject*> mABI; 351 352 // The CType of the value returned by the function. 353 HeapPtr<JSObject*> mReturnType; 354 355 // A fixed array of known parameter types, excluding any variadic 356 // parameters (if mIsVariadic). 357 GCVector<HeapPtr<JSObject*>, 0, CellAllocPolicy> mArgTypes; 358 359 // A variable array of ffi_type*s corresponding to both known parameter 360 // types and dynamic (variadic) parameter types. Longer than mArgTypes 361 // only if mIsVariadic. 362 Vector<ffi_type*, 0, CellAllocPolicy> mFFITypes; 363 364 // Flag indicating whether the function behaves like a C function with 365 // ... as the final formal parameter. 366 bool mIsVariadic; 367 }; 368 369 // Parameters necessary for invoking a JS function from a C closure. 370 struct ClosureInfo { 371 JSContext* cx; 372 HeapPtr<JSObject*> closureObj; // CClosure object 373 HeapPtr<JSObject*> typeObj; // FunctionType describing the C function 374 HeapPtr<JSObject*> thisObj; // 'this' object to use for the JS function call 375 HeapPtr<JSObject*> jsfnObj; // JS function 376 void* errResult; // Result that will be returned if the closure throws 377 ffi_closure* closure; // The C closure itself 378 379 // Anything conditionally freed in the destructor should be initialized to 380 // nullptr here. 381 explicit ClosureInfo(JSContext* context) 382 : cx(context), errResult(nullptr), closure(nullptr) {} 383 384 ~ClosureInfo() { 385 if (closure) { 386 ffi_closure_free(closure); 387 } 388 js_free(errResult); 389 } 390 }; 391 392 bool IsCTypesGlobal(HandleValue v); 393 bool IsCTypesGlobal(JSObject* obj); 394 395 const JS::CTypesCallbacks* GetCallbacks(JSObject* obj); 396 397 /******************************************************************************* 398 ** JSClass reserved slot definitions 399 *******************************************************************************/ 400 401 enum CTypesGlobalSlot { 402 SLOT_CALLBACKS = 0, // pointer to JS::CTypesCallbacks struct 403 SLOT_ERRNO = 1, // Value for latest |errno| 404 SLOT_LASTERROR = 405 2, // Value for latest |GetLastError|, used only with Windows 406 CTYPESGLOBAL_SLOTS 407 }; 408 409 enum CABISlot { 410 SLOT_ABICODE = 0, // ABICode of the CABI object 411 CABI_SLOTS 412 }; 413 414 enum CTypeProtoSlot { 415 SLOT_POINTERPROTO = 0, // ctypes.PointerType.prototype object 416 SLOT_ARRAYPROTO = 1, // ctypes.ArrayType.prototype object 417 SLOT_STRUCTPROTO = 2, // ctypes.StructType.prototype object 418 SLOT_FUNCTIONPROTO = 3, // ctypes.FunctionType.prototype object 419 SLOT_CDATAPROTO = 4, // ctypes.CData.prototype object 420 SLOT_POINTERDATAPROTO = 421 5, // common ancestor of all CData objects of PointerType 422 SLOT_ARRAYDATAPROTO = 6, // common ancestor of all CData objects of ArrayType 423 SLOT_STRUCTDATAPROTO = 424 7, // common ancestor of all CData objects of StructType 425 SLOT_FUNCTIONDATAPROTO = 426 8, // common ancestor of all CData objects of FunctionType 427 SLOT_INT64PROTO = 9, // ctypes.Int64.prototype object 428 SLOT_UINT64PROTO = 10, // ctypes.UInt64.prototype object 429 SLOT_CTYPES = 11, // ctypes object 430 SLOT_OURDATAPROTO = 12, // the data prototype corresponding to this object 431 CTYPEPROTO_SLOTS 432 }; 433 434 enum CTypeSlot { 435 SLOT_PROTO = 0, // 'prototype' property of the CType object 436 SLOT_TYPECODE = 1, // TypeCode of the CType object 437 SLOT_FFITYPE = 2, // ffi_type representing the type 438 SLOT_NAME = 3, // name of the type 439 SLOT_SIZE = 4, // size of the type, in bytes 440 SLOT_ALIGN = 5, // alignment of the type, in bytes 441 SLOT_PTR = 6, // cached PointerType object for type.ptr 442 // Note that some of the slots below can overlap, since they're for 443 // mutually exclusive types. 444 SLOT_TARGET_T = 7, // (PointerTypes only) 'targetType' property 445 SLOT_ELEMENT_T = 7, // (ArrayTypes only) 'elementType' property 446 SLOT_LENGTH = 8, // (ArrayTypes only) 'length' property 447 SLOT_FIELDS = 7, // (StructTypes only) 'fields' property 448 SLOT_FIELDINFO = 8, // (StructTypes only) FieldInfoHash table 449 SLOT_FNINFO = 7, // (FunctionTypes only) FunctionInfo struct 450 SLOT_ARGS_T = 8, // (FunctionTypes only) 'argTypes' property (cached) 451 CTYPE_SLOTS 452 }; 453 454 enum CDataSlot { 455 SLOT_CTYPE = 0, // CType object representing the underlying type 456 SLOT_REFERENT = 1, // JSObject this object must keep alive, if any 457 SLOT_DATA = 2, // pointer to a buffer containing the binary data 458 SLOT_OWNS = 3, // TrueValue() if this CData owns its own buffer 459 SLOT_FUNNAME = 4, // JSString representing the function name 460 CDATA_SLOTS 461 }; 462 463 enum CClosureSlot { 464 SLOT_CLOSUREINFO = 0, // ClosureInfo struct 465 CCLOSURE_SLOTS 466 }; 467 468 enum CDataFinalizerSlot { 469 // PrivateValue storing CDataFinalizer::Private* pointer or UndefinedValue. 470 SLOT_DATAFINALIZER_PRIVATE = 0, 471 // The type of the value (a CType JSObject). 472 // We hold it to permit ImplicitConvert and ToSource. 473 SLOT_DATAFINALIZER_VALTYPE = 1, 474 // The type of the function used at finalization (a CType JSObject). 475 // We hold it to permit |ToSource|. 476 SLOT_DATAFINALIZER_CODETYPE = 2, 477 CDATAFINALIZER_SLOTS 478 }; 479 480 enum TypeCtorSlot { 481 SLOT_FN_CTORPROTO = 0 // ctypes.{Pointer,Array,Struct}Type.prototype 482 // JSFunction objects always get exactly two slots. 483 }; 484 485 enum Int64Slot { 486 SLOT_INT64 = 0, // pointer to a 64-bit buffer containing the integer 487 INT64_SLOTS 488 }; 489 490 enum Int64FunctionSlot { 491 SLOT_FN_INT64PROTO = 0 // ctypes.{Int64,UInt64}.prototype object 492 // JSFunction objects always get exactly two slots. 493 }; 494 495 /******************************************************************************* 496 ** Object API definitions 497 *******************************************************************************/ 498 499 namespace CType { 500 JSObject* Create(JSContext* cx, HandleObject typeProto, HandleObject dataProto, 501 TypeCode type, JSString* name, HandleValue size, 502 HandleValue align, ffi_type* ffiType); 503 504 JSObject* DefineBuiltin(JSContext* cx, HandleObject ctypesObj, 505 const char* propName, JSObject* typeProto, 506 JSObject* dataProto, const char* name, TypeCode type, 507 HandleValue size, HandleValue align, ffi_type* ffiType); 508 509 bool IsCType(JSObject* obj); 510 bool IsCTypeProto(JSObject* obj); 511 TypeCode GetTypeCode(JSObject* typeObj); 512 bool TypesEqual(JSObject* t1, JSObject* t2); 513 size_t GetSize(JSObject* obj); 514 [[nodiscard]] bool GetSafeSize(JSObject* obj, size_t* result); 515 bool IsSizeDefined(JSObject* obj); 516 size_t GetAlignment(JSObject* obj); 517 ffi_type* GetFFIType(JSContext* cx, JSObject* obj); 518 JSString* GetName(JSContext* cx, HandleObject obj); 519 JSObject* GetProtoFromCtor(JSObject* obj, CTypeProtoSlot slot); 520 JSObject* GetProtoFromType(JSContext* cx, JSObject* obj, CTypeProtoSlot slot); 521 const JS::CTypesCallbacks* GetCallbacksFromType(JSObject* obj); 522 } // namespace CType 523 524 namespace PointerType { 525 JSObject* CreateInternal(JSContext* cx, HandleObject baseType); 526 527 JSObject* GetBaseType(JSObject* obj); 528 } // namespace PointerType 529 530 using UniquePtrFFIType = UniquePtr<ffi_type>; 531 532 namespace ArrayType { 533 JSObject* CreateInternal(JSContext* cx, HandleObject baseType, size_t length, 534 bool lengthDefined); 535 536 JSObject* GetBaseType(JSObject* obj); 537 size_t GetLength(JSObject* obj); 538 [[nodiscard]] bool GetSafeLength(JSObject* obj, size_t* result); 539 UniquePtrFFIType BuildFFIType(JSContext* cx, JSObject* obj); 540 } // namespace ArrayType 541 542 namespace StructType { 543 [[nodiscard]] bool DefineInternal(JSContext* cx, JSObject* typeObj, 544 JSObject* fieldsObj); 545 546 const FieldInfoHash* GetFieldInfo(JSObject* obj); 547 const FieldInfo* LookupField(JSContext* cx, JSObject* obj, 548 JSLinearString* name); 549 JSObject* BuildFieldsArray(JSContext* cx, JSObject* obj); 550 UniquePtrFFIType BuildFFIType(JSContext* cx, JSObject* obj); 551 } // namespace StructType 552 553 namespace FunctionType { 554 JSObject* CreateInternal(JSContext* cx, HandleValue abi, HandleValue rtype, 555 const HandleValueArray& args); 556 557 JSObject* ConstructWithObject(JSContext* cx, JSObject* typeObj, 558 JSObject* refObj, PRFuncPtr fnptr, 559 JSObject* result); 560 561 FunctionInfo* GetFunctionInfo(JSObject* obj); 562 void BuildSymbolName(JSContext* cx, JSString* name, JSObject* typeObj, 563 AutoCString& result); 564 } // namespace FunctionType 565 566 namespace CClosure { 567 JSObject* Create(JSContext* cx, HandleObject typeObj, HandleObject fnObj, 568 HandleObject thisObj, HandleValue errVal, PRFuncPtr* fnptr); 569 } // namespace CClosure 570 571 namespace CData { 572 JSObject* Create(JSContext* cx, HandleObject typeObj, HandleObject refObj, 573 void* data, bool ownResult); 574 575 JSObject* GetCType(JSObject* dataObj); 576 void* GetData(JSObject* dataObj); 577 bool IsCData(JSObject* obj); 578 bool IsCDataMaybeUnwrap(MutableHandleObject obj); 579 bool IsCData(HandleValue v); 580 bool IsCDataProto(JSObject* obj); 581 582 // Attached by JSAPI as the function 'ctypes.cast' 583 [[nodiscard]] bool Cast(JSContext* cx, unsigned argc, Value* vp); 584 // Attached by JSAPI as the function 'ctypes.getRuntime' 585 [[nodiscard]] bool GetRuntime(JSContext* cx, unsigned argc, Value* vp); 586 } // namespace CData 587 588 namespace Int64 { 589 bool IsInt64(JSObject* obj); 590 } // namespace Int64 591 592 namespace UInt64 { 593 bool IsUInt64(JSObject* obj); 594 } // namespace UInt64 595 596 } // namespace ctypes 597 } // namespace js 598 599 #endif /* ctypes_CTypes_h */