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