1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
7 #ifndef ctypes_CTypes_h
8 #define ctypes_CTypes_h
10 #include "mozilla/Sprintf.h"
11 #include "mozilla/Vector.h"
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"
26 struct CTypesCallbacks
;
32 /*******************************************************************************
34 *******************************************************************************/
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.
40 template <class CharT
, size_t N
>
42 Vector
<CharT
, N
, SystemAllocPolicy
> v
;
44 // Have any (OOM) errors been encountered while constructing this string?
48 // Have we finished building this string?
51 // Did we check for errors?
52 mutable bool checked
{false};
56 explicit operator bool() const {
63 // Handle the result of modifying the string, by remembering the persistent
65 bool handle(bool result
) {
66 MOZ_ASSERT(!finished
);
73 bool resize(size_t n
) { return handle(v
.resize(n
)); }
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(); }
80 [[nodiscard
]] bool append(U
&& u
) {
81 return handle(v
.append(u
));
85 [[nodiscard
]] bool append(const U
* begin
, const U
* end
) {
86 return handle(v
.append(begin
, end
));
90 [[nodiscard
]] bool append(const U
* begin
, size_t len
) {
91 return handle(v
.append(begin
, len
));
95 MOZ_ASSERT(!finished
);
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
);
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
116 typedef StringBuilder
<char16_t
, 0> AutoString
;
117 typedef StringBuilder
<char, 0> AutoCString
;
119 typedef Vector
<char16_t
, 0, SystemAllocPolicy
> AutoStringChars
;
120 typedef Vector
<char, 0, SystemAllocPolicy
> AutoCStringChars
;
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
)) {
133 for (size_t i
= 0; i
< alen
; ++i
) {
134 v
[i
+ vlen
] = array
[i
];
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
)) {
145 for (size_t i
= 0; i
< count
; ++i
) {
150 template <class T
, size_t N
>
151 void AppendUInt(StringBuilder
<T
, N
>& v
, unsigned n
) {
153 size_t alen
= SprintfLiteral(array
, "%u", n
);
154 size_t vlen
= v
.length();
155 if (!v
.resize(vlen
+ alen
)) {
159 for (size_t i
= 0; i
< alen
; ++i
) {
160 v
[i
+ vlen
] = array
[i
];
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())) {
173 void AppendString(JSContext
* cx
, StringBuilder
<char16_t
, N
>& v
, JSString
* str
) {
175 JSLinearString
* linear
= str
->ensureLinear(cx
);
179 JS::AutoCheckCannotGC nogc
;
180 if (linear
->hasLatin1Chars()) {
181 if (!v
.append(linear
->latin1Chars(nogc
), linear
->length())) {
185 if (!v
.append(linear
->twoByteChars(nogc
), linear
->length())) {
192 void AppendString(JSContext
* cx
, StringBuilder
<char, N
>& v
, JSString
* str
) {
194 size_t vlen
= v
.length();
195 size_t alen
= str
->length();
196 if (!v
.resize(vlen
+ alen
)) {
200 JSLinearString
* linear
= str
->ensureLinear(cx
);
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
]);
212 const char16_t
* chars
= linear
->twoByteChars(nogc
);
213 for (size_t i
= 0; i
< alen
; ++i
) {
214 v
[i
+ vlen
] = char(chars
[i
]);
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
)) {
229 // Move vector data forward. This is safe since we've already resized.
230 memmove(v
.begin() + alen
, v
.begin(), vlen
* sizeof(T
));
232 // Copy data to insert.
233 for (size_t i
= 0; i
< alen
; ++i
) {
239 void PrependString(JSContext
* cx
, StringBuilder
<char16_t
, N
>& v
,
242 size_t vlen
= v
.length();
243 size_t alen
= str
->length();
244 if (!v
.resize(vlen
+ alen
)) {
248 JSLinearString
* linear
= str
->ensureLinear(cx
);
253 // Move vector data forward. This is safe since we've already resized.
254 memmove(v
.begin() + alen
, v
.begin(), vlen
* sizeof(char16_t
));
256 // Copy data to insert.
257 CopyChars(v
.begin(), *linear
);
260 [[nodiscard
]] bool ReportErrorIfUnpairedSurrogatePresent(JSContext
* cx
,
261 JSLinearString
* str
);
263 [[nodiscard
]] JSObject
* GetThisObject(JSContext
* cx
, const CallArgs
& args
,
266 /*******************************************************************************
267 ** Function and struct API definitions
268 *******************************************************************************/
270 // for JS error reporting
272 #define MSG_DEF(name, count, exception, format) name,
273 #include "ctypes/ctypes.msg"
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.
295 #define DEFINE_TYPE(name, type, ffiType) TYPE_##name,
296 CTYPES_FOR_EACH_TYPE(DEFINE_TYPE
)
304 // Descriptor of one field in a StructType. The name of the field is stored
305 // as the key to the hash entry.
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
311 void trace(JSTracer
* trc
) { TraceEdge(trc
, &mType
, "fieldType"); }
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
319 static_assert(sizeof(UnbarrieredFieldInfo
) == sizeof(FieldInfo
),
320 "UnbarrieredFieldInfo should be the same as FieldInfo but with "
321 "unbarriered mType");
323 // Hash policy for FieldInfos.
324 struct FieldHashPolicy
{
325 using Key
= JSLinearString
*;
328 static HashNumber
hash(const Lookup
& l
) { return js::HashStringChars(l
); }
330 static bool match(const Key
& k
, const Lookup
& l
) {
331 return js::EqualStrings(k
, l
);
335 using FieldInfoHash
= GCHashMap
<js::HeapPtr
<JSLinearString
*>, FieldInfo
,
336 FieldHashPolicy
, CellAllocPolicy
>;
338 // Descriptor of ABI, return type, argument types, and variadicity for a
340 struct FunctionInfo
{
341 explicit FunctionInfo(JS::Zone
* zone
) : mArgTypes(zone
), mFFITypes(zone
) {}
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.
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
;
352 // The CType of the value returned by the function.
353 HeapPtr
<JSObject
*> mReturnType
;
355 // A fixed array of known parameter types, excluding any variadic
356 // parameters (if mIsVariadic).
357 GCVector
<HeapPtr
<JSObject
*>, 0, CellAllocPolicy
> mArgTypes
;
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
;
364 // Flag indicating whether the function behaves like a C function with
365 // ... as the final formal parameter.
369 // Parameters necessary for invoking a JS function from a C closure.
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
379 // Anything conditionally freed in the destructor should be initialized to
381 explicit ClosureInfo(JSContext
* context
)
382 : cx(context
), errResult(nullptr), closure(nullptr) {}
386 ffi_closure_free(closure
);
392 bool IsCTypesGlobal(HandleValue v
);
393 bool IsCTypesGlobal(JSObject
* obj
);
395 const JS::CTypesCallbacks
* GetCallbacks(JSObject
* obj
);
397 /*******************************************************************************
398 ** JSClass reserved slot definitions
399 *******************************************************************************/
401 enum CTypesGlobalSlot
{
402 SLOT_CALLBACKS
= 0, // pointer to JS::CTypesCallbacks struct
403 SLOT_ERRNO
= 1, // Value for latest |errno|
405 2, // Value for latest |GetLastError|, used only with Windows
410 SLOT_ABICODE
= 0, // ABICode of the CABI object
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
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)
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
464 SLOT_CLOSUREINFO
= 0, // ClosureInfo struct
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,
481 SLOT_FN_CTORPROTO
= 0 // ctypes.{Pointer,Array,Struct}Type.prototype
482 // JSFunction objects always get exactly two slots.
486 SLOT_INT64
= 0, // pointer to a 64-bit buffer containing the integer
490 enum Int64FunctionSlot
{
491 SLOT_FN_INT64PROTO
= 0 // ctypes.{Int64,UInt64}.prototype object
492 // JSFunction objects always get exactly two slots.
495 /*******************************************************************************
496 ** Object API definitions
497 *******************************************************************************/
500 JSObject
* Create(JSContext
* cx
, HandleObject typeProto
, HandleObject dataProto
,
501 TypeCode type
, JSString
* name
, HandleValue size
,
502 HandleValue align
, ffi_type
* ffiType
);
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
);
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
);
524 namespace PointerType
{
525 JSObject
* CreateInternal(JSContext
* cx
, HandleObject baseType
);
527 JSObject
* GetBaseType(JSObject
* obj
);
528 } // namespace PointerType
530 using UniquePtrFFIType
= UniquePtr
<ffi_type
>;
532 namespace ArrayType
{
533 JSObject
* CreateInternal(JSContext
* cx
, HandleObject baseType
, size_t length
,
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
542 namespace StructType
{
543 [[nodiscard
]] bool DefineInternal(JSContext
* cx
, JSObject
* typeObj
,
544 JSObject
* fieldsObj
);
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
553 namespace FunctionType
{
554 JSObject
* CreateInternal(JSContext
* cx
, HandleValue abi
, HandleValue rtype
,
555 const HandleValueArray
& args
);
557 JSObject
* ConstructWithObject(JSContext
* cx
, JSObject
* typeObj
,
558 JSObject
* refObj
, PRFuncPtr fnptr
,
561 FunctionInfo
* GetFunctionInfo(JSObject
* obj
);
562 void BuildSymbolName(JSContext
* cx
, JSString
* name
, JSObject
* typeObj
,
563 AutoCString
& result
);
564 } // namespace FunctionType
567 JSObject
* Create(JSContext
* cx
, HandleObject typeObj
, HandleObject fnObj
,
568 HandleObject thisObj
, HandleValue errVal
, PRFuncPtr
* fnptr
);
569 } // namespace CClosure
572 JSObject
* Create(JSContext
* cx
, HandleObject typeObj
, HandleObject refObj
,
573 void* data
, bool ownResult
);
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
);
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
);
589 bool IsInt64(JSObject
* obj
);
593 bool IsUInt64(JSObject
* obj
);
594 } // namespace UInt64
596 } // namespace ctypes
599 #endif /* ctypes_CTypes_h */