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