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