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
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/
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
15 * The Original Code is js-ctypes.
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.
23 * Dan Witte <dwitte@mozilla.com>
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.
37 * ***** END LICENSE BLOCK ***** */
45 #if defined(XP_WIN) || defined(XP_OS2)
54 #include <sys/types.h>
62 /*******************************************************************************
64 *******************************************************************************/
66 class ScopedContextThread
69 ScopedContextThread(JSContext* cx) : mCx(cx) { JS_SetContextThread(cx); }
70 ~ScopedContextThread() { JS_ClearContextThread(mCx); }
75 /*******************************************************************************
76 ** JSAPI function prototypes
77 *******************************************************************************/
79 static JSBool ConstructAbstract(JSContext* cx, uintN argc, jsval* vp);
82 static JSBool ConstructData(JSContext* cx, uintN argc, jsval* vp);
83 static JSBool ConstructBasic(JSContext* cx, JSObject* obj, uintN argc, jsval* vp);
85 static void Trace(JSTracer* trc, JSObject* obj);
86 static void Finalize(JSContext* cx, JSObject* obj);
87 static void FinalizeProtoClass(JSContext* cx, JSObject* obj);
89 static JSBool PrototypeGetter(JSContext* cx, JSObject* obj, jsid idval,
91 static JSBool NameGetter(JSContext* cx, JSObject* obj, jsid idval,
93 static JSBool SizeGetter(JSContext* cx, JSObject* obj, jsid idval,
95 static JSBool PtrGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp);
96 static JSBool CreateArray(JSContext* cx, uintN argc, jsval* vp);
97 static JSBool ToString(JSContext* cx, uintN argc, jsval* vp);
98 static JSBool ToSource(JSContext* cx, uintN argc, jsval* vp);
99 static JSBool HasInstance(JSContext* cx, JSObject* obj, const jsval* v, JSBool* bp);
102 namespace PointerType {
103 static JSBool Create(JSContext* cx, uintN argc, jsval* vp);
104 static JSBool ConstructData(JSContext* cx, JSObject* obj, uintN argc, jsval* vp);
106 static JSBool TargetTypeGetter(JSContext* cx, JSObject* obj, jsid idval,
108 static JSBool ContentsGetter(JSContext* cx, JSObject* obj, jsid idval,
110 static JSBool ContentsSetter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict,
112 static JSBool IsNull(JSContext* cx, uintN argc, jsval* vp);
115 namespace ArrayType {
116 static JSBool Create(JSContext* cx, uintN argc, jsval* vp);
117 static JSBool ConstructData(JSContext* cx, JSObject* obj, uintN argc, jsval* vp);
119 static JSBool ElementTypeGetter(JSContext* cx, JSObject* obj, jsid idval,
121 static JSBool LengthGetter(JSContext* cx, JSObject* obj, jsid idval,
123 static JSBool Getter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp);
124 static JSBool Setter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict, jsval* vp);
125 static JSBool AddressOfElement(JSContext* cx, uintN argc, jsval* vp);
128 namespace StructType {
129 static JSBool Create(JSContext* cx, uintN argc, jsval* vp);
130 static JSBool ConstructData(JSContext* cx, JSObject* obj, uintN argc, jsval* vp);
132 static JSBool FieldsArrayGetter(JSContext* cx, JSObject* obj, jsid idval,
134 static JSBool FieldGetter(JSContext* cx, JSObject* obj, jsid idval,
136 static JSBool FieldSetter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict,
138 static JSBool AddressOfField(JSContext* cx, uintN argc, jsval* vp);
139 static JSBool Define(JSContext* cx, uintN argc, jsval* vp);
142 namespace FunctionType {
143 static JSBool Create(JSContext* cx, uintN argc, jsval* vp);
144 static JSBool ConstructData(JSContext* cx, JSObject* typeObj,
145 JSObject* dataObj, JSObject* fnObj, JSObject* thisObj);
147 static JSBool Call(JSContext* cx, uintN argc, jsval* vp);
149 static JSBool ArgTypesGetter(JSContext* cx, JSObject* obj, jsid idval,
151 static JSBool ReturnTypeGetter(JSContext* cx, JSObject* obj, jsid idval,
153 static JSBool ABIGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp);
154 static JSBool IsVariadicGetter(JSContext* cx, JSObject* obj, jsid idval,
159 static void Trace(JSTracer* trc, JSObject* obj);
160 static void Finalize(JSContext* cx, JSObject* obj);
163 static void ClosureStub(ffi_cif* cif, void* result, void** args,
168 static void Finalize(JSContext* cx, JSObject* obj);
170 static JSBool ValueGetter(JSContext* cx, JSObject* obj, jsid idval,
172 static JSBool ValueSetter(JSContext* cx, JSObject* obj, jsid idval,
173 JSBool strict, jsval* vp);
174 static JSBool Address(JSContext* cx, uintN argc, jsval* vp);
175 static JSBool ReadString(JSContext* cx, uintN argc, jsval* vp);
176 static JSBool ToSource(JSContext* cx, uintN argc, jsval* vp);
179 // Int64Base provides functions common to Int64 and UInt64.
180 namespace Int64Base {
181 JSObject* Construct(JSContext* cx, JSObject* proto, JSUint64 data,
184 JSUint64 GetInt(JSContext* cx, JSObject* obj);
186 JSBool ToString(JSContext* cx, JSObject* obj, uintN argc, jsval* vp,
189 JSBool ToSource(JSContext* cx, JSObject* obj, uintN argc, jsval* vp,
192 static void Finalize(JSContext* cx, JSObject* obj);
196 static JSBool Construct(JSContext* cx, uintN argc, jsval* vp);
198 static JSBool ToString(JSContext* cx, uintN argc, jsval* vp);
199 static JSBool ToSource(JSContext* cx, uintN argc, jsval* vp);
201 static JSBool Compare(JSContext* cx, uintN argc, jsval* vp);
202 static JSBool Lo(JSContext* cx, uintN argc, jsval* vp);
203 static JSBool Hi(JSContext* cx, uintN argc, jsval* vp);
204 static JSBool Join(JSContext* cx, uintN argc, jsval* vp);
208 static JSBool Construct(JSContext* cx, uintN argc, jsval* vp);
210 static JSBool ToString(JSContext* cx, uintN argc, jsval* vp);
211 static JSBool ToSource(JSContext* cx, uintN argc, jsval* vp);
213 static JSBool Compare(JSContext* cx, uintN argc, jsval* vp);
214 static JSBool Lo(JSContext* cx, uintN argc, jsval* vp);
215 static JSBool Hi(JSContext* cx, uintN argc, jsval* vp);
216 static JSBool Join(JSContext* cx, uintN argc, jsval* vp);
219 /*******************************************************************************
220 ** JSClass definitions and initialization functions
221 *******************************************************************************/
223 // Class representing the 'ctypes' object itself. This exists to contain the
224 // JSCTypesCallbacks set of function pointers.
225 static JSClass sCTypesGlobalClass = {
227 JSCLASS_HAS_RESERVED_SLOTS(CTYPESGLOBAL_SLOTS),
228 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
229 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
230 JSCLASS_NO_OPTIONAL_MEMBERS
233 static JSClass sCABIClass = {
235 JSCLASS_HAS_RESERVED_SLOTS(CABI_SLOTS),
236 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
237 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
238 JSCLASS_NO_OPTIONAL_MEMBERS
241 // Class representing ctypes.{C,Pointer,Array,Struct,Function}Type.prototype.
242 // This exists to give said prototypes a class of "CType", and to provide
243 // reserved slots for stashing various other prototype objects.
244 static JSClass sCTypeProtoClass = {
246 JSCLASS_HAS_RESERVED_SLOTS(CTYPEPROTO_SLOTS),
247 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
248 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CType::FinalizeProtoClass,
249 NULL, NULL, ConstructAbstract, ConstructAbstract, NULL, NULL, NULL, NULL
252 // Class representing ctypes.CData.prototype and the 'prototype' properties
253 // of CTypes. This exists to give said prototypes a class of "CData".
254 static JSClass sCDataProtoClass = {
257 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
258 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
259 JSCLASS_NO_OPTIONAL_MEMBERS
262 static JSClass sCTypeClass = {
264 JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS) | JSCLASS_MARK_IS_TRACE,
265 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
266 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CType::Finalize,
267 NULL, NULL, CType::ConstructData, CType::ConstructData, NULL,
268 CType::HasInstance, JS_CLASS_TRACE(CType::Trace), NULL
271 static JSClass sCDataClass = {
273 JSCLASS_HAS_RESERVED_SLOTS(CDATA_SLOTS),
274 JS_PropertyStub, JS_PropertyStub, ArrayType::Getter, ArrayType::Setter,
275 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CData::Finalize,
276 NULL, NULL, FunctionType::Call, FunctionType::Call, NULL, NULL, NULL, NULL
279 static JSClass sCClosureClass = {
281 JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS) | JSCLASS_MARK_IS_TRACE,
282 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
283 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CClosure::Finalize,
284 NULL, NULL, NULL, NULL, NULL, NULL, JS_CLASS_TRACE(CClosure::Trace), NULL
287 #define CTYPESFN_FLAGS \
288 (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
290 #define CTYPESCTOR_FLAGS \
291 (CTYPESFN_FLAGS | JSFUN_CONSTRUCTOR)
293 #define CTYPESPROP_FLAGS \
294 (JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
296 #define CDATAFN_FLAGS \
297 (JSPROP_READONLY | JSPROP_PERMANENT)
299 static JSPropertySpec sCTypeProps[] = {
300 { "name", 0, CTYPESPROP_FLAGS, CType::NameGetter, NULL },
301 { "size", 0, CTYPESPROP_FLAGS, CType::SizeGetter, NULL },
302 { "ptr", 0, CTYPESPROP_FLAGS, CType::PtrGetter, NULL },
303 { "prototype", 0, CTYPESPROP_FLAGS, CType::PrototypeGetter, NULL },
304 { 0, 0, 0, NULL, NULL }
307 static JSFunctionSpec sCTypeFunctions[] = {
308 JS_FN("array", CType::CreateArray, 0, CTYPESFN_FLAGS),
309 JS_FN("toString", CType::ToString, 0, CTYPESFN_FLAGS),
310 JS_FN("toSource", CType::ToSource, 0, CTYPESFN_FLAGS),
314 static JSPropertySpec sCDataProps[] = {
315 { "value", 0, JSPROP_SHARED | JSPROP_PERMANENT,
316 CData::ValueGetter, CData::ValueSetter },
317 { 0, 0, 0, NULL, NULL }
320 static JSFunctionSpec sCDataFunctions[] = {
321 JS_FN("address", CData::Address, 0, CDATAFN_FLAGS),
322 JS_FN("readString", CData::ReadString, 0, CDATAFN_FLAGS),
323 JS_FN("toSource", CData::ToSource, 0, CDATAFN_FLAGS),
324 JS_FN("toString", CData::ToSource, 0, CDATAFN_FLAGS),
328 static JSFunctionSpec sPointerFunction =
329 JS_FN("PointerType", PointerType::Create, 1, CTYPESCTOR_FLAGS);
331 static JSPropertySpec sPointerProps[] = {
332 { "targetType", 0, CTYPESPROP_FLAGS, PointerType::TargetTypeGetter, NULL },
333 { 0, 0, 0, NULL, NULL }
336 static JSFunctionSpec sPointerInstanceFunctions[] = {
337 JS_FN("isNull", PointerType::IsNull, 0, CTYPESFN_FLAGS),
341 static JSPropertySpec sPointerInstanceProps[] = {
342 { "contents", 0, JSPROP_SHARED | JSPROP_PERMANENT,
343 PointerType::ContentsGetter, PointerType::ContentsSetter },
344 { 0, 0, 0, NULL, NULL }
347 static JSFunctionSpec sArrayFunction =
348 JS_FN("ArrayType", ArrayType::Create, 1, CTYPESCTOR_FLAGS);
350 static JSPropertySpec sArrayProps[] = {
351 { "elementType", 0, CTYPESPROP_FLAGS, ArrayType::ElementTypeGetter, NULL },
352 { "length", 0, CTYPESPROP_FLAGS, ArrayType::LengthGetter, NULL },
353 { 0, 0, 0, NULL, NULL }
356 static JSFunctionSpec sArrayInstanceFunctions[] = {
357 JS_FN("addressOfElement", ArrayType::AddressOfElement, 1, CDATAFN_FLAGS),
361 static JSPropertySpec sArrayInstanceProps[] = {
362 { "length", 0, JSPROP_SHARED | JSPROP_READONLY | JSPROP_PERMANENT,
363 ArrayType::LengthGetter, NULL },
364 { 0, 0, 0, NULL, NULL }
367 static JSFunctionSpec sStructFunction =
368 JS_FN("StructType", StructType::Create, 2, CTYPESCTOR_FLAGS);
370 static JSPropertySpec sStructProps[] = {
371 { "fields", 0, CTYPESPROP_FLAGS, StructType::FieldsArrayGetter, NULL },
372 { 0, 0, 0, NULL, NULL }
375 static JSFunctionSpec sStructFunctions[] = {
376 JS_FN("define", StructType::Define, 1, CDATAFN_FLAGS),
380 static JSFunctionSpec sStructInstanceFunctions[] = {
381 JS_FN("addressOfField", StructType::AddressOfField, 1, CDATAFN_FLAGS),
385 static JSFunctionSpec sFunctionFunction =
386 JS_FN("FunctionType", FunctionType::Create, 2, CTYPESCTOR_FLAGS);
388 static JSPropertySpec sFunctionProps[] = {
389 { "argTypes", 0, CTYPESPROP_FLAGS, FunctionType::ArgTypesGetter, NULL },
390 { "returnType", 0, CTYPESPROP_FLAGS, FunctionType::ReturnTypeGetter, NULL },
391 { "abi", 0, CTYPESPROP_FLAGS, FunctionType::ABIGetter, NULL },
392 { "isVariadic", 0, CTYPESPROP_FLAGS, FunctionType::IsVariadicGetter, NULL },
393 { 0, 0, 0, NULL, NULL }
396 static JSClass sInt64ProtoClass = {
399 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
400 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
401 JSCLASS_NO_OPTIONAL_MEMBERS
404 static JSClass sUInt64ProtoClass = {
407 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
408 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
409 JSCLASS_NO_OPTIONAL_MEMBERS
412 static JSClass sInt64Class = {
414 JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS),
415 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
416 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Int64Base::Finalize,
417 JSCLASS_NO_OPTIONAL_MEMBERS
420 static JSClass sUInt64Class = {
422 JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS),
423 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
424 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Int64Base::Finalize,
425 JSCLASS_NO_OPTIONAL_MEMBERS
428 static JSFunctionSpec sInt64StaticFunctions[] = {
429 JS_FN("compare", Int64::Compare, 2, CTYPESFN_FLAGS),
430 JS_FN("lo", Int64::Lo, 1, CTYPESFN_FLAGS),
431 JS_FN("hi", Int64::Hi, 1, CTYPESFN_FLAGS),
432 JS_FN("join", Int64::Join, 2, CTYPESFN_FLAGS),
436 static JSFunctionSpec sUInt64StaticFunctions[] = {
437 JS_FN("compare", UInt64::Compare, 2, CTYPESFN_FLAGS),
438 JS_FN("lo", UInt64::Lo, 1, CTYPESFN_FLAGS),
439 JS_FN("hi", UInt64::Hi, 1, CTYPESFN_FLAGS),
440 JS_FN("join", UInt64::Join, 2, CTYPESFN_FLAGS),
444 static JSFunctionSpec sInt64Functions[] = {
445 JS_FN("toString", Int64::ToString, 0, CTYPESFN_FLAGS),
446 JS_FN("toSource", Int64::ToSource, 0, CTYPESFN_FLAGS),
450 static JSFunctionSpec sUInt64Functions[] = {
451 JS_FN("toString", UInt64::ToString, 0, CTYPESFN_FLAGS),
452 JS_FN("toSource", UInt64::ToSource, 0, CTYPESFN_FLAGS),
456 static JSFunctionSpec sModuleFunctions[] = {
457 JS_FN("open", Library::Open, 1, CTYPESFN_FLAGS),
458 JS_FN("cast", CData::Cast, 2, CTYPESFN_FLAGS),
459 JS_FN("libraryName", Library::Name, 1, CTYPESFN_FLAGS),
463 static inline bool FloatIsFinite(jsdouble f) {
465 return _finite(f) != 0;
471 JS_ALWAYS_INLINE JSString*
472 NewUCString(JSContext* cx, const AutoString& from)
474 return JS_NewUCStringCopyN(cx, from.begin(), from.length());
477 JS_ALWAYS_INLINE size_t
478 Align(size_t val, size_t align)
480 return ((val - 1) | (align - 1)) + 1;
484 GetABICode(JSContext* cx, JSObject* obj)
486 // make sure we have an object representing a CABI class,
487 // and extract the enumerated class type from the reserved slot.
488 if (JS_GET_CLASS(cx, obj) != &sCABIClass)
492 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_ABICODE, &result));
494 return ABICode(JSVAL_TO_INT(result));
497 JSErrorFormatString ErrorFormatString[CTYPESERR_LIMIT] = {
498 #define MSG_DEF(name, number, count, exception, format) \
499 { format, count, exception } ,
500 #include "ctypes.msg"
504 const JSErrorFormatString*
505 GetErrorMessage(void* userRef, const char* locale, const uintN errorNumber)
507 if (0 < errorNumber && errorNumber < CTYPESERR_LIMIT)
508 return &ErrorFormatString[errorNumber];
513 TypeError(JSContext* cx, const char* expected, jsval actual)
515 JSString* str = JS_ValueToSource(cx, actual);
516 JSAutoByteString bytes;
520 src = bytes.encode(cx, str);
524 JS_ClearPendingException(cx);
525 src = "<<error converting value to string>>";
527 JS_ReportErrorNumber(cx, GetErrorMessage, NULL,
528 CTYPESMSG_TYPE_ERROR, expected, src);
533 InitCTypeClass(JSContext* cx, JSObject* parent)
535 JSFunction* fun = JS_DefineFunction(cx, parent, "CType", ConstructAbstract, 0,
540 JSObject* ctor = JS_GetFunctionObject(fun);
541 JSObject* fnproto = JS_GetPrototype(cx, ctor);
545 // Set up ctypes.CType.prototype.
546 JSObject* prototype = JS_NewObject(cx, &sCTypeProtoClass, fnproto, parent);
550 if (!JS_DefineProperty(cx, ctor, "prototype", OBJECT_TO_JSVAL(prototype),
551 NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
554 if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(ctor),
555 NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
558 // Define properties and functions common to all CTypes.
559 if (!JS_DefineProperties(cx, prototype, sCTypeProps) ||
560 !JS_DefineFunctions(cx, prototype, sCTypeFunctions))
563 if (!JS_FreezeObject(cx, ctor) || !JS_FreezeObject(cx, prototype))
570 InitCDataClass(JSContext* cx, JSObject* parent, JSObject* CTypeProto)
572 JSFunction* fun = JS_DefineFunction(cx, parent, "CData", ConstructAbstract, 0,
577 JSObject* ctor = JS_GetFunctionObject(fun);
580 // Set up ctypes.CData.__proto__ === ctypes.CType.prototype.
581 // (Note that 'ctypes.CData instanceof Function' is still true, thanks to the
583 if (!JS_SetPrototype(cx, ctor, CTypeProto))
586 // Set up ctypes.CData.prototype.
587 JSObject* prototype = JS_NewObject(cx, &sCDataProtoClass, NULL, parent);
591 if (!JS_DefineProperty(cx, ctor, "prototype", OBJECT_TO_JSVAL(prototype),
592 NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
595 if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(ctor),
596 NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
599 // Define properties and functions common to all CDatas.
600 if (!JS_DefineProperties(cx, prototype, sCDataProps) ||
601 !JS_DefineFunctions(cx, prototype, sCDataFunctions))
604 if (//!JS_FreezeObject(cx, prototype) || // XXX fixme - see bug 541212!
605 !JS_FreezeObject(cx, ctor))
612 DefineABIConstant(JSContext* cx,
617 JSObject* obj = JS_DefineObject(cx, parent, name, &sCABIClass, NULL,
618 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
621 if (!JS_SetReservedSlot(cx, obj, SLOT_ABICODE, INT_TO_JSVAL(code)))
623 return JS_FreezeObject(cx, obj);
626 // Set up a single type constructor for
627 // ctypes.{Pointer,Array,Struct,Function}Type.
629 InitTypeConstructor(JSContext* cx,
631 JSObject* CTypeProto,
632 JSObject* CDataProto,
635 JSPropertySpec* props,
636 JSFunctionSpec* instanceFns,
637 JSPropertySpec* instanceProps,
638 JSObject*& typeProto,
639 JSObject*& dataProto)
641 JSFunction* fun = JS_DefineFunction(cx, parent, spec.name, spec.call,
642 spec.nargs, spec.flags);
646 JSObject* obj = JS_GetFunctionObject(fun);
650 // Set up the .prototype and .prototype.constructor properties.
651 typeProto = JS_NewObject(cx, &sCTypeProtoClass, CTypeProto, parent);
655 // Define property before proceeding, for GC safety.
656 if (!JS_DefineProperty(cx, obj, "prototype", OBJECT_TO_JSVAL(typeProto),
657 NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
660 if (fns && !JS_DefineFunctions(cx, typeProto, fns))
663 if (!JS_DefineProperties(cx, typeProto, props))
666 if (!JS_DefineProperty(cx, typeProto, "constructor", OBJECT_TO_JSVAL(obj),
667 NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
670 // Stash ctypes.{Pointer,Array,Struct}Type.prototype on a reserved slot of
671 // the type constructor, for faster lookup.
672 if (!JS_SetReservedSlot(cx, obj, SLOT_FN_CTORPROTO, OBJECT_TO_JSVAL(typeProto)))
675 // Create an object to serve as the common ancestor for all CData objects
676 // created from the given type constructor. This has ctypes.CData.prototype
677 // as its prototype, such that it inherits the properties and functions
678 // common to all CDatas.
679 dataProto = JS_NewObject(cx, &sCDataProtoClass, CDataProto, parent);
682 js::AutoObjectRooter protoroot(cx, dataProto);
684 // Define functions and properties on the 'dataProto' object that are common
685 // to all CData objects created from this type constructor. (These will
686 // become functions and properties on CData objects created from this type.)
687 if (instanceFns && !JS_DefineFunctions(cx, dataProto, instanceFns))
690 if (instanceProps && !JS_DefineProperties(cx, dataProto, instanceProps))
693 if (!JS_FreezeObject(cx, obj) ||
694 //!JS_FreezeObject(cx, dataProto) || // XXX fixme - see bug 541212!
695 !JS_FreezeObject(cx, typeProto))
702 InitInt64Class(JSContext* cx,
707 JSFunctionSpec* static_fs)
709 // Init type class and constructor
710 JSObject* prototype = JS_InitClass(cx, parent, NULL, clasp, construct,
711 0, NULL, fs, NULL, static_fs);
715 JSObject* ctor = JS_GetConstructor(cx, prototype);
718 if (!JS_FreezeObject(cx, ctor))
721 // Stash ctypes.{Int64,UInt64}.prototype on a reserved slot of the 'join'
724 ASSERT_OK(JS_GetProperty(cx, ctor, "join", &join));
725 if (!JS_SetReservedSlot(cx, JSVAL_TO_OBJECT(join), SLOT_FN_INT64PROTO,
726 OBJECT_TO_JSVAL(prototype)))
729 if (!JS_FreezeObject(cx, prototype))
736 AttachProtos(JSContext* cx, JSObject* proto, JSObject** protos)
738 // For a given 'proto' of [[Class]] "CTypeProto", attach each of the 'protos'
739 // to the appropriate CTypeProtoSlot. (SLOT_UINT64PROTO is the last slot
740 // of [[Class]] "CTypeProto".)
741 for (JSUint32 i = 0; i <= SLOT_UINT64PROTO; ++i) {
742 if (!JS_SetReservedSlot(cx, proto, i, OBJECT_TO_JSVAL(protos[i])))
750 InitTypeClasses(JSContext* cx, JSObject* parent)
752 // Initialize the ctypes.CType class. This acts as an abstract base class for
753 // the various types, and provides the common API functions. It has:
754 // * [[Class]] "Function"
755 // * __proto__ === Function.prototype
756 // * A constructor that throws a TypeError. (You can't construct an
758 // * 'prototype' property:
759 // * [[Class]] "CTypeProto"
760 // * __proto__ === Function.prototype
761 // * A constructor that throws a TypeError. (You can't construct an
762 // abstract type instance!)
763 // * 'constructor' property === ctypes.CType
764 // * Provides properties and functions common to all CTypes.
765 JSObject* CTypeProto = InitCTypeClass(cx, parent);
769 // Initialize the ctypes.CData class. This acts as an abstract base class for
770 // instances of the various types, and provides the common API functions.
772 // * [[Class]] "Function"
773 // * __proto__ === Function.prototype
774 // * A constructor that throws a TypeError. (You can't construct an
775 // abstract type instance!)
776 // * 'prototype' property:
777 // * [[Class]] "CDataProto"
778 // * 'constructor' property === ctypes.CData
779 // * Provides properties and functions common to all CDatas.
780 JSObject* CDataProto = InitCDataClass(cx, parent, CTypeProto);
784 // Create and attach the special class constructors: ctypes.PointerType,
785 // ctypes.ArrayType, ctypes.StructType, and ctypes.FunctionType.
786 // Each of these constructors 'c' has, respectively:
787 // * [[Class]] "Function"
788 // * __proto__ === Function.prototype
789 // * A constructor that creates a user-defined type.
790 // * 'prototype' property:
791 // * [[Class]] "CTypeProto"
792 // * __proto__ === ctypes.CType.prototype
793 // * 'constructor' property === 'c'
794 // We also construct an object 'p' to serve, given a type object 't'
795 // constructed from one of these type constructors, as
796 // 't.prototype.__proto__'. This object has:
797 // * [[Class]] "CDataProto"
798 // * __proto__ === ctypes.CData.prototype
799 // * Properties and functions common to all CDatas.
800 // Therefore an instance 't' of ctypes.{Pointer,Array,Struct,Function}Type
802 // * [[Class]] "CType"
803 // * __proto__ === ctypes.{Pointer,Array,Struct,Function}Type.prototype
804 // * A constructor which creates and returns a CData object, containing
805 // binary data of the given type.
806 // * 'prototype' property:
807 // * [[Class]] "CDataProto"
808 // * __proto__ === 'p', the prototype object from above
809 // * 'constructor' property === 't'
810 JSObject* protos[CTYPEPROTO_SLOTS];
811 if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
812 sPointerFunction, NULL, sPointerProps,
813 sPointerInstanceFunctions, sPointerInstanceProps,
814 protos[SLOT_POINTERPROTO], protos[SLOT_POINTERDATAPROTO]))
816 js::AutoObjectRooter proot(cx, protos[SLOT_POINTERDATAPROTO]);
818 if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
819 sArrayFunction, NULL, sArrayProps,
820 sArrayInstanceFunctions, sArrayInstanceProps,
821 protos[SLOT_ARRAYPROTO], protos[SLOT_ARRAYDATAPROTO]))
823 js::AutoObjectRooter aroot(cx, protos[SLOT_ARRAYDATAPROTO]);
825 if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
826 sStructFunction, sStructFunctions, sStructProps,
827 sStructInstanceFunctions, NULL,
828 protos[SLOT_STRUCTPROTO], protos[SLOT_STRUCTDATAPROTO]))
830 js::AutoObjectRooter sroot(cx, protos[SLOT_STRUCTDATAPROTO]);
832 if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
833 sFunctionFunction, NULL, sFunctionProps, NULL, NULL,
834 protos[SLOT_FUNCTIONPROTO], protos[SLOT_FUNCTIONDATAPROTO]))
836 js::AutoObjectRooter froot(cx, protos[SLOT_FUNCTIONDATAPROTO]);
838 protos[SLOT_CDATAPROTO] = CDataProto;
840 // Create and attach the ctypes.{Int64,UInt64} constructors.
841 // Each of these has, respectively:
842 // * [[Class]] "Function"
843 // * __proto__ === Function.prototype
844 // * A constructor that creates a ctypes.{Int64,UInt64} object, respectively.
845 // * 'prototype' property:
846 // * [[Class]] {"Int64Proto","UInt64Proto"}
847 // * 'constructor' property === ctypes.{Int64,UInt64}
848 protos[SLOT_INT64PROTO] = InitInt64Class(cx, parent, &sInt64ProtoClass,
849 Int64::Construct, sInt64Functions, sInt64StaticFunctions);
850 if (!protos[SLOT_INT64PROTO])
852 protos[SLOT_UINT64PROTO] = InitInt64Class(cx, parent, &sUInt64ProtoClass,
853 UInt64::Construct, sUInt64Functions, sUInt64StaticFunctions);
854 if (!protos[SLOT_UINT64PROTO])
857 // Attach the prototypes just created to each of ctypes.CType.prototype,
858 // and the special type constructors, so we can access them when constructing
859 // instances of those types.
860 if (!AttachProtos(cx, CTypeProto, protos) ||
861 !AttachProtos(cx, protos[SLOT_POINTERPROTO], protos) ||
862 !AttachProtos(cx, protos[SLOT_ARRAYPROTO], protos) ||
863 !AttachProtos(cx, protos[SLOT_STRUCTPROTO], protos) ||
864 !AttachProtos(cx, protos[SLOT_FUNCTIONPROTO], protos))
867 // Attach objects representing ABI constants.
868 if (!DefineABIConstant(cx, parent, "default_abi", ABI_DEFAULT) ||
869 !DefineABIConstant(cx, parent, "stdcall_abi", ABI_STDCALL) ||
870 !DefineABIConstant(cx, parent, "winapi_abi", ABI_WINAPI))
873 // Create objects representing the builtin types, and attach them to the
874 // ctypes object. Each type object 't' has:
875 // * [[Class]] "CType"
876 // * __proto__ === ctypes.CType.prototype
877 // * A constructor which creates and returns a CData object, containing
878 // binary data of the given type.
879 // * 'prototype' property:
880 // * [[Class]] "CDataProto"
881 // * __proto__ === ctypes.CData.prototype
882 // * 'constructor' property === 't'
883 #define DEFINE_TYPE(name, type, ffiType) \
884 JSObject* typeObj_##name = \
885 CType::DefineBuiltin(cx, parent, #name, CTypeProto, CDataProto, #name, \
886 TYPE_##name, INT_TO_JSVAL(sizeof(type)), \
887 INT_TO_JSVAL(ffiType.alignment), &ffiType); \
888 if (!typeObj_##name) \
890 #include "typedefs.h"
892 // Alias 'ctypes.unsigned' as 'ctypes.unsigned_int', since they represent
893 // the same type in C.
894 if (!JS_DefineProperty(cx, parent, "unsigned",
895 OBJECT_TO_JSVAL(typeObj_unsigned_int), NULL, NULL,
896 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
899 // Create objects representing the special types void_t and voidptr_t.
901 CType::DefineBuiltin(cx, parent, "void_t", CTypeProto, CDataProto, "void",
902 TYPE_void_t, JSVAL_VOID, JSVAL_VOID, &ffi_type_void);
906 typeObj = PointerType::CreateInternal(cx, typeObj);
909 if (!JS_DefineProperty(cx, parent, "voidptr_t", OBJECT_TO_JSVAL(typeObj),
910 NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
917 IsCTypesGlobal(JSContext* cx, JSObject* obj)
919 return JS_GET_CLASS(cx, obj) == &sCTypesGlobalClass;
922 // Get the JSCTypesCallbacks struct from the 'ctypes' object 'obj'.
924 GetCallbacks(JSContext* cx, JSObject* obj)
926 JS_ASSERT(IsCTypesGlobal(cx, obj));
929 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_CALLBACKS, &result));
930 if (JSVAL_IS_VOID(result))
933 return static_cast<JSCTypesCallbacks*>(JSVAL_TO_PRIVATE(result));
938 JS_PUBLIC_API(JSBool)
939 JS_InitCTypesClass(JSContext* cx, JSObject* global)
941 // attach ctypes property to global object
942 JSObject* ctypes = JS_NewObject(cx, &sCTypesGlobalClass, NULL, NULL);
946 if (!JS_DefineProperty(cx, global, "ctypes", OBJECT_TO_JSVAL(ctypes),
947 JS_PropertyStub, JS_StrictPropertyStub, JSPROP_READONLY | JSPROP_PERMANENT)) {
951 if (!InitTypeClasses(cx, ctypes))
954 // attach API functions
955 if (!JS_DefineFunctions(cx, ctypes, sModuleFunctions))
958 // Seal the ctypes object, to prevent modification.
959 return JS_FreezeObject(cx, ctypes);
962 JS_PUBLIC_API(JSBool)
963 JS_SetCTypesCallbacks(JSContext* cx,
965 JSCTypesCallbacks* callbacks)
967 JS_ASSERT(callbacks);
968 JS_ASSERT(IsCTypesGlobal(cx, ctypesObj));
970 // Set the callbacks on a reserved slot.
971 return JS_SetReservedSlot(cx, ctypesObj, SLOT_CALLBACKS,
972 PRIVATE_TO_JSVAL(callbacks));
977 /*******************************************************************************
978 ** Type conversion functions
979 *******************************************************************************/
981 // Enforce some sanity checks on type widths and properties.
982 // Where the architecture is 64-bit, make sure it's LP64 or LLP64. (ctypes.int
983 // autoconverts to a primitive JS number; to support ILP64 architectures, it
984 // would need to autoconvert to an Int64 object instead. Therefore we enforce
985 // this invariant here.)
986 JS_STATIC_ASSERT(sizeof(bool) == 1 || sizeof(bool) == 4);
987 JS_STATIC_ASSERT(sizeof(char) == 1);
988 JS_STATIC_ASSERT(sizeof(short) == 2);
989 JS_STATIC_ASSERT(sizeof(int) == 4);
990 JS_STATIC_ASSERT(sizeof(unsigned) == 4);
991 JS_STATIC_ASSERT(sizeof(long) == 4 || sizeof(long) == 8);
992 JS_STATIC_ASSERT(sizeof(long long) == 8);
993 JS_STATIC_ASSERT(sizeof(size_t) == sizeof(uintptr_t));
994 JS_STATIC_ASSERT(sizeof(float) == 4);
995 JS_STATIC_ASSERT(sizeof(PRFuncPtr) == sizeof(void*));
996 JS_STATIC_ASSERT(numeric_limits<double>::is_signed);
998 // Templated helper to convert FromType to TargetType, for the default case
999 // where the trivial POD constructor will do.
1000 template<class TargetType, class FromType>
1001 struct ConvertImpl {
1002 static JS_ALWAYS_INLINE TargetType Convert(FromType d) {
1003 return TargetType(d);
1008 // MSVC can't perform double to unsigned __int64 conversion when the
1009 // double is greater than 2^63 - 1. Help it along a little.
1011 struct ConvertImpl<JSUint64, jsdouble> {
1012 static JS_ALWAYS_INLINE JSUint64 Convert(jsdouble d) {
1013 return d > 0x7fffffffffffffffui64 ?
1014 JSUint64(d - 0x8000000000000000ui64) + 0x8000000000000000ui64 :
1020 template<class TargetType, class FromType>
1021 static JS_ALWAYS_INLINE TargetType Convert(FromType d)
1023 return ConvertImpl<TargetType, FromType>::Convert(d);
1026 template<class TargetType, class FromType>
1027 static JS_ALWAYS_INLINE bool IsAlwaysExact()
1029 // Return 'true' if TargetType can always exactly represent FromType.
1031 // 1) TargetType must be the same or more bits wide as FromType. For integers
1032 // represented in 'n' bits, unsigned variants will have 'n' digits while
1033 // signed will have 'n - 1'. For floating point types, 'digits' is the
1035 // 2) If FromType is signed, TargetType must also be signed. (Floating point
1036 // types are always signed.)
1037 // 3) If TargetType is an exact integral type, FromType must be also.
1038 if (numeric_limits<TargetType>::digits < numeric_limits<FromType>::digits)
1041 if (numeric_limits<FromType>::is_signed &&
1042 !numeric_limits<TargetType>::is_signed)
1045 if (!numeric_limits<FromType>::is_exact &&
1046 numeric_limits<TargetType>::is_exact)
1052 // Templated helper to determine if FromType 'i' converts losslessly to
1053 // TargetType 'j'. Default case where both types are the same signedness.
1054 template<class TargetType, class FromType, bool TargetSigned, bool FromSigned>
1055 struct IsExactImpl {
1056 static JS_ALWAYS_INLINE bool Test(FromType i, TargetType j) {
1057 JS_STATIC_ASSERT(numeric_limits<TargetType>::is_exact);
1058 return FromType(j) == i;
1062 // Specialization where TargetType is unsigned, FromType is signed.
1063 template<class TargetType, class FromType>
1064 struct IsExactImpl<TargetType, FromType, false, true> {
1065 static JS_ALWAYS_INLINE bool Test(FromType i, TargetType j) {
1066 JS_STATIC_ASSERT(numeric_limits<TargetType>::is_exact);
1067 return i >= 0 && FromType(j) == i;
1071 // Specialization where TargetType is signed, FromType is unsigned.
1072 template<class TargetType, class FromType>
1073 struct IsExactImpl<TargetType, FromType, true, false> {
1074 static JS_ALWAYS_INLINE bool Test(FromType i, TargetType j) {
1075 JS_STATIC_ASSERT(numeric_limits<TargetType>::is_exact);
1076 return TargetType(i) >= 0 && FromType(j) == i;
1080 // Convert FromType 'i' to TargetType 'result', returning true iff 'result'
1081 // is an exact representation of 'i'.
1082 template<class TargetType, class FromType>
1083 static JS_ALWAYS_INLINE bool ConvertExact(FromType i, TargetType* result)
1085 // Require that TargetType is integral, to simplify conversion.
1086 JS_STATIC_ASSERT(numeric_limits<TargetType>::is_exact);
1088 *result = Convert<TargetType>(i);
1090 // See if we can avoid a dynamic check.
1091 if (IsAlwaysExact<TargetType, FromType>())
1094 // Return 'true' if 'i' is exactly representable in 'TargetType'.
1095 return IsExactImpl<TargetType,
1097 numeric_limits<TargetType>::is_signed,
1098 numeric_limits<FromType>::is_signed>::Test(i, *result);
1101 // Templated helper to determine if Type 'i' is negative. Default case
1102 // where IntegerType is unsigned.
1103 template<class Type, bool IsSigned>
1104 struct IsNegativeImpl {
1105 static JS_ALWAYS_INLINE bool Test(Type i) {
1110 // Specialization where Type is signed.
1111 template<class Type>
1112 struct IsNegativeImpl<Type, true> {
1113 static JS_ALWAYS_INLINE bool Test(Type i) {
1118 // Determine whether Type 'i' is negative.
1119 template<class Type>
1120 static JS_ALWAYS_INLINE bool IsNegative(Type i)
1122 return IsNegativeImpl<Type, numeric_limits<Type>::is_signed>::Test(i);
1125 // Implicitly convert val to bool, allowing JSBool, jsint, and jsdouble
1126 // arguments numerically equal to 0 or 1.
1128 jsvalToBool(JSContext* cx, jsval val, bool* result)
1130 if (JSVAL_IS_BOOLEAN(val)) {
1131 *result = JSVAL_TO_BOOLEAN(val) != JS_FALSE;
1134 if (JSVAL_IS_INT(val)) {
1135 jsint i = JSVAL_TO_INT(val);
1137 return i == 0 || i == 1;
1139 if (JSVAL_IS_DOUBLE(val)) {
1140 jsdouble d = JSVAL_TO_DOUBLE(val);
1143 return d == 1 || d == 0;
1145 // Don't silently convert null to bool. It's probably a mistake.
1149 // Implicitly convert val to IntegerType, allowing JSBool, jsint, jsdouble,
1150 // Int64, UInt64, and CData integer types 't' where all values of 't' are
1151 // representable by IntegerType.
1152 template<class IntegerType>
1154 jsvalToInteger(JSContext* cx, jsval val, IntegerType* result)
1156 JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
1158 if (JSVAL_IS_INT(val)) {
1159 // Make sure the integer fits in the alotted precision, and has the right
1161 jsint i = JSVAL_TO_INT(val);
1162 return ConvertExact(i, result);
1164 if (JSVAL_IS_DOUBLE(val)) {
1165 // Don't silently lose bits here -- check that val really is an
1166 // integer value, and has the right sign.
1167 jsdouble d = JSVAL_TO_DOUBLE(val);
1168 return ConvertExact(d, result);
1170 if (!JSVAL_IS_PRIMITIVE(val)) {
1171 JSObject* obj = JSVAL_TO_OBJECT(val);
1172 if (CData::IsCData(cx, obj)) {
1173 JSObject* typeObj = CData::GetCType(cx, obj);
1174 void* data = CData::GetData(cx, obj);
1176 // Check whether the source type is always representable, with exact
1177 // precision, by the target type. If it is, convert the value.
1178 switch (CType::GetTypeCode(cx, typeObj)) {
1179 #define DEFINE_INT_TYPE(name, fromType, ffiType) \
1181 if (!IsAlwaysExact<IntegerType, fromType>()) \
1183 *result = IntegerType(*static_cast<fromType*>(data)); \
1185 #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
1186 #include "typedefs.h"
1191 case TYPE_float32_t:
1192 case TYPE_float64_t:
1194 case TYPE_signed_char:
1195 case TYPE_unsigned_char:
1201 // Not a compatible number type.
1206 if (Int64::IsInt64(cx, obj)) {
1207 // Make sure the integer fits in IntegerType.
1208 JSInt64 i = Int64Base::GetInt(cx, obj);
1209 return ConvertExact(i, result);
1212 if (UInt64::IsUInt64(cx, obj)) {
1213 // Make sure the integer fits in IntegerType.
1214 JSUint64 i = Int64Base::GetInt(cx, obj);
1215 return ConvertExact(i, result);
1220 if (JSVAL_IS_BOOLEAN(val)) {
1221 // Implicitly promote boolean values to 0 or 1, like C.
1222 *result = JSVAL_TO_BOOLEAN(val);
1223 JS_ASSERT(*result == 0 || *result == 1);
1226 // Don't silently convert null to an integer. It's probably a mistake.
1230 // Implicitly convert val to FloatType, allowing jsint, jsdouble,
1231 // Int64, UInt64, and CData numeric types 't' where all values of 't' are
1232 // representable by FloatType.
1233 template<class FloatType>
1235 jsvalToFloat(JSContext *cx, jsval val, FloatType* result)
1237 JS_STATIC_ASSERT(!numeric_limits<FloatType>::is_exact);
1239 // The following casts may silently throw away some bits, but there's
1240 // no good way around it. Sternly requiring that the 64-bit double
1241 // argument be exactly representable as a 32-bit float is
1242 // unrealistic: it would allow 1/2 to pass but not 1/3.
1243 if (JSVAL_IS_INT(val)) {
1244 *result = FloatType(JSVAL_TO_INT(val));
1247 if (JSVAL_IS_DOUBLE(val)) {
1248 *result = FloatType(JSVAL_TO_DOUBLE(val));
1251 if (!JSVAL_IS_PRIMITIVE(val)) {
1252 JSObject* obj = JSVAL_TO_OBJECT(val);
1253 if (CData::IsCData(cx, obj)) {
1254 JSObject* typeObj = CData::GetCType(cx, obj);
1255 void* data = CData::GetData(cx, obj);
1257 // Check whether the source type is always representable, with exact
1258 // precision, by the target type. If it is, convert the value.
1259 switch (CType::GetTypeCode(cx, typeObj)) {
1260 #define DEFINE_FLOAT_TYPE(name, fromType, ffiType) \
1262 if (!IsAlwaysExact<FloatType, fromType>()) \
1264 *result = FloatType(*static_cast<fromType*>(data)); \
1266 #define DEFINE_INT_TYPE(x, y, z) DEFINE_FLOAT_TYPE(x, y, z)
1267 #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
1268 #include "typedefs.h"
1272 case TYPE_signed_char:
1273 case TYPE_unsigned_char:
1279 // Not a compatible number type.
1284 // Don't silently convert true to 1.0 or false to 0.0, even though C/C++
1285 // does it. It's likely to be a mistake.
1289 template<class IntegerType>
1291 StringToInteger(JSContext* cx, JSString* string, IntegerType* result)
1293 JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
1295 const jschar* cp = string->getChars(NULL);
1299 const jschar* end = cp + string->length();
1303 IntegerType sign = 1;
1305 if (!numeric_limits<IntegerType>::is_signed)
1312 // Assume base-10, unless the string begins with '0x' or '0X'.
1313 IntegerType base = 10;
1314 if (end - cp > 2 && cp[0] == '0' && (cp[1] == 'x' || cp[1] == 'X')) {
1319 // Scan the string left to right and build the number,
1320 // checking for valid characters 0 - 9, a - f, A - F and overflow.
1324 if (c >= '0' && c <= '9')
1326 else if (base == 16 && c >= 'a' && c <= 'f')
1328 else if (base == 16 && c >= 'A' && c <= 'F')
1334 i = ii * base + sign * c;
1335 if (i / base != ii) // overflow
1343 // Implicitly convert val to IntegerType, allowing jsint, jsdouble,
1344 // Int64, UInt64, and optionally a decimal or hexadecimal string argument.
1345 // (This is common code shared by jsvalToSize and the Int64/UInt64 constructors.)
1346 template<class IntegerType>
1348 jsvalToBigInteger(JSContext* cx,
1351 IntegerType* result)
1353 JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
1355 if (JSVAL_IS_INT(val)) {
1356 // Make sure the integer fits in the alotted precision, and has the right
1358 jsint i = JSVAL_TO_INT(val);
1359 return ConvertExact(i, result);
1361 if (JSVAL_IS_DOUBLE(val)) {
1362 // Don't silently lose bits here -- check that val really is an
1363 // integer value, and has the right sign.
1364 jsdouble d = JSVAL_TO_DOUBLE(val);
1365 return ConvertExact(d, result);
1367 if (allowString && JSVAL_IS_STRING(val)) {
1368 // Allow conversion from base-10 or base-16 strings, provided the result
1369 // fits in IntegerType. (This allows an Int64 or UInt64 object to be passed
1370 // to the JS array element operator, which will automatically call
1371 // toString() on the object for us.)
1372 return StringToInteger(cx, JSVAL_TO_STRING(val), result);
1374 if (!JSVAL_IS_PRIMITIVE(val)) {
1375 // Allow conversion from an Int64 or UInt64 object directly.
1376 JSObject* obj = JSVAL_TO_OBJECT(val);
1378 if (UInt64::IsUInt64(cx, obj)) {
1379 // Make sure the integer fits in IntegerType.
1380 JSUint64 i = Int64Base::GetInt(cx, obj);
1381 return ConvertExact(i, result);
1384 if (Int64::IsInt64(cx, obj)) {
1385 // Make sure the integer fits in IntegerType.
1386 JSInt64 i = Int64Base::GetInt(cx, obj);
1387 return ConvertExact(i, result);
1393 // Implicitly convert val to a size value, where the size value is represented
1394 // by size_t but must also fit in a jsdouble.
1396 jsvalToSize(JSContext* cx, jsval val, bool allowString, size_t* result)
1398 if (!jsvalToBigInteger(cx, val, allowString, result))
1401 // Also check that the result fits in a jsdouble.
1402 return Convert<size_t>(jsdouble(*result)) == *result;
1405 // Implicitly convert val to IntegerType, allowing jsint, jsdouble,
1406 // Int64, UInt64, and optionally a decimal or hexadecimal string argument.
1407 // (This is common code shared by jsvalToSize and the Int64/UInt64 constructors.)
1408 template<class IntegerType>
1410 jsidToBigInteger(JSContext* cx,
1413 IntegerType* result)
1415 JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
1417 if (JSID_IS_INT(val)) {
1418 // Make sure the integer fits in the alotted precision, and has the right
1420 jsint i = JSID_TO_INT(val);
1421 return ConvertExact(i, result);
1423 if (allowString && JSID_IS_STRING(val)) {
1424 // Allow conversion from base-10 or base-16 strings, provided the result
1425 // fits in IntegerType. (This allows an Int64 or UInt64 object to be passed
1426 // to the JS array element operator, which will automatically call
1427 // toString() on the object for us.)
1428 return StringToInteger(cx, JSID_TO_STRING(val), result);
1430 if (JSID_IS_OBJECT(val)) {
1431 // Allow conversion from an Int64 or UInt64 object directly.
1432 JSObject* obj = JSID_TO_OBJECT(val);
1434 if (UInt64::IsUInt64(cx, obj)) {
1435 // Make sure the integer fits in IntegerType.
1436 JSUint64 i = Int64Base::GetInt(cx, obj);
1437 return ConvertExact(i, result);
1440 if (Int64::IsInt64(cx, obj)) {
1441 // Make sure the integer fits in IntegerType.
1442 JSInt64 i = Int64Base::GetInt(cx, obj);
1443 return ConvertExact(i, result);
1449 // Implicitly convert val to a size value, where the size value is represented
1450 // by size_t but must also fit in a jsdouble.
1452 jsidToSize(JSContext* cx, jsid val, bool allowString, size_t* result)
1454 if (!jsidToBigInteger(cx, val, allowString, result))
1457 // Also check that the result fits in a jsdouble.
1458 return Convert<size_t>(jsdouble(*result)) == *result;
1461 // Implicitly convert a size value to a jsval, ensuring that the size_t value
1462 // fits in a jsdouble.
1464 SizeTojsval(JSContext* cx, size_t size, jsval* result)
1466 if (Convert<size_t>(jsdouble(size)) != size) {
1467 JS_ReportError(cx, "size overflow");
1471 return JS_NewNumberValue(cx, jsdouble(size), result);
1474 // Forcefully convert val to IntegerType when explicitly requested.
1475 template<class IntegerType>
1477 jsvalToIntegerExplicit(JSContext* cx, jsval val, IntegerType* result)
1479 JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
1481 if (JSVAL_IS_DOUBLE(val)) {
1482 // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
1483 jsdouble d = JSVAL_TO_DOUBLE(val);
1484 *result = FloatIsFinite(d) ? IntegerType(d) : 0;
1487 if (!JSVAL_IS_PRIMITIVE(val)) {
1488 // Convert Int64 and UInt64 values by C-style cast.
1489 JSObject* obj = JSVAL_TO_OBJECT(val);
1490 if (Int64::IsInt64(cx, obj)) {
1491 JSInt64 i = Int64Base::GetInt(cx, obj);
1492 *result = IntegerType(i);
1495 if (UInt64::IsUInt64(cx, obj)) {
1496 JSUint64 i = Int64Base::GetInt(cx, obj);
1497 *result = IntegerType(i);
1504 // Forcefully convert val to a pointer value when explicitly requested.
1506 jsvalToPtrExplicit(JSContext* cx, jsval val, uintptr_t* result)
1508 if (JSVAL_IS_INT(val)) {
1509 // jsint always fits in intptr_t. If the integer is negative, cast through
1510 // an intptr_t intermediate to sign-extend.
1511 jsint i = JSVAL_TO_INT(val);
1512 *result = i < 0 ? uintptr_t(intptr_t(i)) : uintptr_t(i);
1515 if (JSVAL_IS_DOUBLE(val)) {
1516 jsdouble d = JSVAL_TO_DOUBLE(val);
1518 // Cast through an intptr_t intermediate to sign-extend.
1519 intptr_t i = Convert<intptr_t>(d);
1520 if (jsdouble(i) != d)
1523 *result = uintptr_t(i);
1527 // Don't silently lose bits here -- check that val really is an
1528 // integer value, and has the right sign.
1529 *result = Convert<uintptr_t>(d);
1530 return jsdouble(*result) == d;
1532 if (!JSVAL_IS_PRIMITIVE(val)) {
1533 JSObject* obj = JSVAL_TO_OBJECT(val);
1534 if (Int64::IsInt64(cx, obj)) {
1535 JSInt64 i = Int64Base::GetInt(cx, obj);
1536 intptr_t p = intptr_t(i);
1538 // Make sure the integer fits in the alotted precision.
1539 if (JSInt64(p) != i)
1541 *result = uintptr_t(p);
1545 if (UInt64::IsUInt64(cx, obj)) {
1546 JSUint64 i = Int64Base::GetInt(cx, obj);
1548 // Make sure the integer fits in the alotted precision.
1549 *result = uintptr_t(i);
1550 return JSUint64(*result) == i;
1556 template<class IntegerType, class CharType, size_t N, class AP>
1558 IntegerToString(IntegerType i, jsuint radix, Vector<CharType, N, AP>& result)
1560 JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
1562 // The buffer must be big enough for all the bits of IntegerType to fit,
1563 // in base-2, including '-'.
1564 CharType buffer[sizeof(IntegerType) * 8 + 1];
1565 CharType* end = buffer + sizeof(buffer) / sizeof(CharType);
1568 // Build the string in reverse. We use multiplication and subtraction
1569 // instead of modulus because that's much faster.
1570 const bool isNegative = IsNegative(i);
1571 size_t sign = isNegative ? -1 : 1;
1573 IntegerType ii = i / IntegerType(radix);
1574 size_t index = sign * size_t(i - ii * IntegerType(radix));
1575 *--cp = "0123456789abcdefghijklmnopqrstuvwxyz"[index];
1582 JS_ASSERT(cp >= buffer);
1583 result.append(cp, end);
1586 template<class CharType>
1588 strnlen(const CharType* begin, size_t max)
1590 for (const CharType* s = begin; s != begin + max; ++s)
1597 // Convert C binary value 'data' of CType 'typeObj' to a JS primitive, where
1598 // possible; otherwise, construct and return a CData object. The following
1599 // semantics apply when constructing a CData object for return:
1600 // * If 'wantPrimitive' is true, the caller indicates that 'result' must be
1601 // a JS primitive, and ConvertToJS will fail if 'result' would be a CData
1602 // object. Otherwise:
1603 // * If a CData object 'parentObj' is supplied, the new CData object is
1604 // dependent on the given parent and its buffer refers to a slice of the
1606 // * If 'parentObj' is null, the new CData object may or may not own its
1607 // resulting buffer depending on the 'ownResult' argument.
1609 ConvertToJS(JSContext* cx,
1611 JSObject* parentObj,
1617 JS_ASSERT(!parentObj || CData::IsCData(cx, parentObj));
1618 JS_ASSERT(!parentObj || !ownResult);
1619 JS_ASSERT(!wantPrimitive || !ownResult);
1621 TypeCode typeCode = CType::GetTypeCode(cx, typeObj);
1625 *result = JSVAL_VOID;
1628 *result = *static_cast<bool*>(data) ? JSVAL_TRUE : JSVAL_FALSE;
1630 #define DEFINE_INT_TYPE(name, type, ffiType) \
1631 case TYPE_##name: { \
1632 type value = *static_cast<type*>(data); \
1633 if (sizeof(type) < 4) \
1634 *result = INT_TO_JSVAL(jsint(value)); \
1635 else if (!JS_NewNumberValue(cx, jsdouble(value), result)) \
1639 #define DEFINE_WRAPPED_INT_TYPE(name, type, ffiType) \
1640 case TYPE_##name: { \
1641 /* Return an Int64 or UInt64 object - do not convert to a JS number. */ \
1644 if (!numeric_limits<type>::is_signed) { \
1645 value = *static_cast<type*>(data); \
1646 /* Get ctypes.UInt64.prototype from ctypes.CType.prototype. */ \
1647 proto = CType::GetProtoFromType(cx, typeObj, SLOT_UINT64PROTO); \
1649 value = JSInt64(*static_cast<type*>(data)); \
1650 /* Get ctypes.Int64.prototype from ctypes.CType.prototype. */ \
1651 proto = CType::GetProtoFromType(cx, typeObj, SLOT_INT64PROTO); \
1654 JSObject* obj = Int64Base::Construct(cx, proto, value, \
1655 !numeric_limits<type>::is_signed); \
1658 *result = OBJECT_TO_JSVAL(obj); \
1661 #define DEFINE_FLOAT_TYPE(name, type, ffiType) \
1662 case TYPE_##name: { \
1663 type value = *static_cast<type*>(data); \
1664 if (!JS_NewNumberValue(cx, jsdouble(value), result)) \
1668 #define DEFINE_CHAR_TYPE(name, type, ffiType) \
1670 /* Convert to an integer. We have no idea what character encoding to */ \
1671 /* use, if any. */ \
1672 *result = INT_TO_JSVAL(*static_cast<type*>(data)); \
1674 #include "typedefs.h"
1676 // Convert the jschar to a 1-character string.
1677 JSString* str = JS_NewUCStringCopyN(cx, static_cast<jschar*>(data), 1);
1681 *result = STRING_TO_JSVAL(str);
1687 // We're about to create a new CData object to return. If the caller doesn't
1688 // want this, return early.
1689 if (wantPrimitive) {
1690 JS_ReportError(cx, "cannot convert to primitive value");
1694 JSObject* obj = CData::Create(cx, typeObj, parentObj, data, ownResult);
1698 *result = OBJECT_TO_JSVAL(obj);
1702 JS_NOT_REACHED("cannot return a FunctionType");
1708 // Implicitly convert jsval 'val' to a C binary representation of CType
1709 // 'targetType', storing the result in 'buffer'. Adequate space must be
1710 // provided in 'buffer' by the caller. This function generally does minimal
1711 // coercion between types. There are two cases in which this function is used:
1712 // 1) The target buffer is internal to a CData object; we simply write data
1714 // 2) We are converting an argument for an ffi call, in which case 'isArgument'
1715 // will be true. This allows us to handle a special case: if necessary,
1716 // we can autoconvert a JS string primitive to a pointer-to-character type.
1717 // In this case, ownership of the allocated string is handed off to the
1718 // caller; 'freePointer' will be set to indicate this.
1720 ImplicitConvert(JSContext* cx,
1722 JSObject* targetType,
1727 JS_ASSERT(CType::IsSizeDefined(cx, targetType));
1729 // First, check if val is a CData object of type targetType.
1730 JSObject* sourceData = NULL;
1731 JSObject* sourceType = NULL;
1732 if (!JSVAL_IS_PRIMITIVE(val) &&
1733 CData::IsCData(cx, JSVAL_TO_OBJECT(val))) {
1734 sourceData = JSVAL_TO_OBJECT(val);
1735 sourceType = CData::GetCType(cx, sourceData);
1737 // If the types are equal, copy the buffer contained within the CData.
1738 // (Note that the buffers may overlap partially or completely.)
1739 if (CType::TypesEqual(cx, sourceType, targetType)) {
1740 size_t size = CType::GetSize(cx, sourceType);
1741 memmove(buffer, CData::GetData(cx, sourceData), size);
1746 TypeCode targetCode = CType::GetTypeCode(cx, targetType);
1748 switch (targetCode) {
1750 // Do not implicitly lose bits, but allow the values 0, 1, and -0.
1751 // Programs can convert explicitly, if needed, using `Boolean(v)` or `!!v`.
1753 if (!jsvalToBool(cx, val, &result))
1754 return TypeError(cx, "boolean", val);
1755 *static_cast<bool*>(buffer) = result;
1758 #define DEFINE_INT_TYPE(name, type, ffiType) \
1759 case TYPE_##name: { \
1760 /* Do not implicitly lose bits. */ \
1762 if (!jsvalToInteger(cx, val, &result)) \
1763 return TypeError(cx, #name, val); \
1764 *static_cast<type*>(buffer) = result; \
1767 #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
1768 #define DEFINE_FLOAT_TYPE(name, type, ffiType) \
1769 case TYPE_##name: { \
1771 if (!jsvalToFloat(cx, val, &result)) \
1772 return TypeError(cx, #name, val); \
1773 *static_cast<type*>(buffer) = result; \
1776 #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
1777 #define DEFINE_JSCHAR_TYPE(name, type, ffiType) \
1778 case TYPE_##name: { \
1779 /* Convert from a 1-character string, regardless of encoding, */ \
1780 /* or from an integer, provided the result fits in 'type'. */ \
1782 if (JSVAL_IS_STRING(val)) { \
1783 JSString* str = JSVAL_TO_STRING(val); \
1784 if (str->length() != 1) \
1785 return TypeError(cx, #name, val); \
1786 const jschar *chars = str->getChars(cx); \
1789 result = chars[0]; \
1790 } else if (!jsvalToInteger(cx, val, &result)) { \
1791 return TypeError(cx, #name, val); \
1793 *static_cast<type*>(buffer) = result; \
1796 #include "typedefs.h"
1797 case TYPE_pointer: {
1798 if (JSVAL_IS_NULL(val)) {
1799 // Convert to a null pointer.
1800 *static_cast<void**>(buffer) = NULL;
1804 JSObject* baseType = PointerType::GetBaseType(cx, targetType);
1806 // First, determine if the targetType is ctypes.void_t.ptr.
1807 TypeCode sourceCode = CType::GetTypeCode(cx, sourceType);
1808 void* sourceBuffer = CData::GetData(cx, sourceData);
1809 bool voidptrTarget = CType::GetTypeCode(cx, baseType) == TYPE_void_t;
1811 if (sourceCode == TYPE_pointer && voidptrTarget) {
1812 // Autoconvert if targetType is ctypes.voidptr_t.
1813 *static_cast<void**>(buffer) = *static_cast<void**>(sourceBuffer);
1816 if (sourceCode == TYPE_array) {
1817 // Autoconvert an array to a ctypes.void_t.ptr or to
1818 // sourceType.elementType.ptr, just like C.
1819 JSObject* elementType = ArrayType::GetBaseType(cx, sourceType);
1820 if (voidptrTarget || CType::TypesEqual(cx, baseType, elementType)) {
1821 *static_cast<void**>(buffer) = sourceBuffer;
1826 } else if (isArgument && JSVAL_IS_STRING(val)) {
1827 // Convert the string for the ffi call. This requires allocating space
1828 // which the caller assumes ownership of.
1829 // TODO: Extend this so we can safely convert strings at other times also.
1830 JSString* sourceString = JSVAL_TO_STRING(val);
1831 size_t sourceLength = sourceString->length();
1832 const jschar* sourceChars = sourceString->getChars(cx);
1836 switch (CType::GetTypeCode(cx, baseType)) {
1838 case TYPE_signed_char:
1839 case TYPE_unsigned_char: {
1840 // Convert from UTF-16 to UTF-8.
1842 js_GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength);
1843 if (nbytes == (size_t) -1)
1846 char** charBuffer = static_cast<char**>(buffer);
1847 *charBuffer = js_array_new<char>(nbytes + 1);
1849 JS_ReportAllocationOverflow(cx);
1853 ASSERT_OK(js_DeflateStringToUTF8Buffer(cx, sourceChars, sourceLength,
1854 *charBuffer, &nbytes));
1855 (*charBuffer)[nbytes] = 0;
1856 *freePointer = true;
1860 // Copy the jschar string data. (We could provide direct access to the
1861 // JSString's buffer, but this approach is safer if the caller happens
1862 // to modify the string.)
1863 jschar** jscharBuffer = static_cast<jschar**>(buffer);
1864 *jscharBuffer = js_array_new<jschar>(sourceLength + 1);
1865 if (!*jscharBuffer) {
1866 JS_ReportAllocationOverflow(cx);
1870 *freePointer = true;
1871 memcpy(*jscharBuffer, sourceChars, sourceLength * sizeof(jschar));
1872 (*jscharBuffer)[sourceLength] = 0;
1876 return TypeError(cx, "pointer", val);
1880 return TypeError(cx, "pointer", val);
1883 JSObject* baseType = ArrayType::GetBaseType(cx, targetType);
1884 size_t targetLength = ArrayType::GetLength(cx, targetType);
1886 if (JSVAL_IS_STRING(val)) {
1887 JSString* sourceString = JSVAL_TO_STRING(val);
1888 size_t sourceLength = sourceString->length();
1889 const jschar* sourceChars = sourceString->getChars(cx);
1893 switch (CType::GetTypeCode(cx, baseType)) {
1895 case TYPE_signed_char:
1896 case TYPE_unsigned_char: {
1897 // Convert from UTF-16 to UTF-8.
1899 js_GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength);
1900 if (nbytes == (size_t) -1)
1903 if (targetLength < nbytes) {
1904 JS_ReportError(cx, "ArrayType has insufficient length");
1908 char* charBuffer = static_cast<char*>(buffer);
1909 ASSERT_OK(js_DeflateStringToUTF8Buffer(cx, sourceChars, sourceLength,
1910 charBuffer, &nbytes));
1912 if (targetLength > nbytes)
1913 charBuffer[nbytes] = 0;
1918 // Copy the string data, jschar for jschar, including the terminator
1919 // if there's space.
1920 if (targetLength < sourceLength) {
1921 JS_ReportError(cx, "ArrayType has insufficient length");
1925 memcpy(buffer, sourceChars, sourceLength * sizeof(jschar));
1926 if (targetLength > sourceLength)
1927 static_cast<jschar*>(buffer)[sourceLength] = 0;
1932 return TypeError(cx, "array", val);
1935 } else if (!JSVAL_IS_PRIMITIVE(val) &&
1936 JS_IsArrayObject(cx, JSVAL_TO_OBJECT(val))) {
1937 // Convert each element of the array by calling ImplicitConvert.
1938 JSObject* sourceArray = JSVAL_TO_OBJECT(val);
1939 jsuint sourceLength;
1940 if (!JS_GetArrayLength(cx, sourceArray, &sourceLength) ||
1941 targetLength != size_t(sourceLength)) {
1942 JS_ReportError(cx, "ArrayType length does not match source array length");
1946 // Convert into an intermediate, in case of failure.
1947 size_t elementSize = CType::GetSize(cx, baseType);
1948 size_t arraySize = elementSize * targetLength;
1949 AutoPtr<char>::Array intermediate(js_array_new<char>(arraySize));
1950 if (!intermediate) {
1951 JS_ReportAllocationOverflow(cx);
1955 for (jsuint i = 0; i < sourceLength; ++i) {
1956 js::AutoValueRooter item(cx);
1957 if (!JS_GetElement(cx, sourceArray, i, item.jsval_addr()))
1960 char* data = intermediate.get() + elementSize * i;
1961 if (!ImplicitConvert(cx, item.jsval_value(), baseType, data, false, NULL))
1965 memcpy(buffer, intermediate.get(), arraySize);
1968 // Don't implicitly convert to string. Users can implicitly convert
1969 // with `String(x)` or `""+x`.
1970 return TypeError(cx, "array", val);
1975 if (!JSVAL_IS_PRIMITIVE(val) && !sourceData) {
1976 // Enumerate the properties of the object; if they match the struct
1977 // specification, convert the fields.
1978 JSObject* obj = JSVAL_TO_OBJECT(val);
1979 JSObject* iter = JS_NewPropertyIterator(cx, obj);
1982 js::AutoObjectRooter iterroot(cx, iter);
1984 // Convert into an intermediate, in case of failure.
1985 size_t structSize = CType::GetSize(cx, targetType);
1986 AutoPtr<char>::Array intermediate(js_array_new<char>(structSize));
1987 if (!intermediate) {
1988 JS_ReportAllocationOverflow(cx);
1995 if (!JS_NextProperty(cx, iter, &id))
1997 if (JSID_IS_VOID(id))
2000 if (!JSID_IS_STRING(id)) {
2001 JS_ReportError(cx, "property name is not a string");
2005 JSFlatString *name = JSID_TO_FLAT_STRING(id);
2006 const FieldInfo* field = StructType::LookupField(cx, targetType, name);
2010 js::AutoValueRooter prop(cx);
2011 if (!JS_GetPropertyById(cx, obj, id, prop.jsval_addr()))
2014 // Convert the field via ImplicitConvert().
2015 char* fieldData = intermediate.get() + field->mOffset;
2016 if (!ImplicitConvert(cx, prop.jsval_value(), field->mType, fieldData, false, NULL))
2022 const FieldInfoHash* fields = StructType::GetFieldInfo(cx, targetType);
2023 if (i != fields->count()) {
2024 JS_ReportError(cx, "missing fields");
2028 memcpy(buffer, intermediate.get(), structSize);
2032 return TypeError(cx, "struct", val);
2036 JS_NOT_REACHED("invalid type");
2043 // Convert jsval 'val' to a C binary representation of CType 'targetType',
2044 // storing the result in 'buffer'. This function is more forceful than
2047 ExplicitConvert(JSContext* cx, jsval val, JSObject* targetType, void* buffer)
2049 // If ImplicitConvert succeeds, use that result.
2050 if (ImplicitConvert(cx, val, targetType, buffer, false, NULL))
2053 // If ImplicitConvert failed, and there is no pending exception, then assume
2054 // hard failure (out of memory, or some other similarly serious condition).
2055 // We store any pending exception in case we need to re-throw it.
2056 js::AutoValueRooter ex(cx);
2057 if (!JS_GetPendingException(cx, ex.jsval_addr()))
2060 // Otherwise, assume soft failure. Clear the pending exception so that we
2061 // can throw a different one as required.
2062 JS_ClearPendingException(cx);
2064 TypeCode type = CType::GetTypeCode(cx, targetType);
2068 // Convert according to the ECMAScript ToBoolean() function.
2070 ASSERT_OK(JS_ValueToBoolean(cx, val, &result));
2071 *static_cast<bool*>(buffer) = result != JS_FALSE;
2074 #define DEFINE_INT_TYPE(name, type, ffiType) \
2075 case TYPE_##name: { \
2076 /* Convert numeric values with a C-style cast, and */ \
2077 /* allow conversion from a base-10 or base-16 string. */ \
2079 if (!jsvalToIntegerExplicit(cx, val, &result) && \
2080 (!JSVAL_IS_STRING(val) || \
2081 !StringToInteger(cx, JSVAL_TO_STRING(val), &result))) \
2082 return TypeError(cx, #name, val); \
2083 *static_cast<type*>(buffer) = result; \
2086 #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
2087 #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
2088 #define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_CHAR_TYPE(x, y, z)
2089 #include "typedefs.h"
2090 case TYPE_pointer: {
2091 // Convert a number, Int64 object, or UInt64 object to a pointer.
2093 if (!jsvalToPtrExplicit(cx, val, &result))
2094 return TypeError(cx, "pointer", val);
2095 *static_cast<uintptr_t*>(buffer) = result;
2098 case TYPE_float32_t:
2099 case TYPE_float64_t:
2104 // ImplicitConvert is sufficient. Re-throw the exception it generated.
2105 JS_SetPendingException(cx, ex.jsval_value());
2109 JS_NOT_REACHED("invalid type");
2115 // Given a CType 'typeObj', generate a string describing the C type declaration
2116 // corresponding to 'typeObj'. For instance, the CType constructed from
2117 // 'ctypes.int32_t.ptr.array(4).ptr.ptr' will result in the type string
2118 // 'int32_t*(**)[4]'.
2120 BuildTypeName(JSContext* cx, JSObject* typeObj)
2124 // Walk the hierarchy of types, outermost to innermost, building up the type
2125 // string. This consists of the base type, which goes on the left.
2126 // Derived type modifiers (* and []) build from the inside outward, with
2127 // pointers on the left and arrays on the right. An excellent description
2128 // of the rules for building C type declarations can be found at:
2129 // http://unixwiz.net/techtips/reading-cdecl.html
2130 TypeCode prevGrouping = CType::GetTypeCode(cx, typeObj), currentGrouping;
2132 currentGrouping = CType::GetTypeCode(cx, typeObj);
2133 switch (currentGrouping) {
2134 case TYPE_pointer: {
2135 // Pointer types go on the left.
2136 PrependString(result, "*");
2138 typeObj = PointerType::GetBaseType(cx, typeObj);
2139 prevGrouping = currentGrouping;
2143 if (prevGrouping == TYPE_pointer) {
2144 // Outer type is pointer, inner type is array. Grouping is required.
2145 PrependString(result, "(");
2146 AppendString(result, ")");
2149 // Array types go on the right.
2150 AppendString(result, "[");
2152 if (ArrayType::GetSafeLength(cx, typeObj, &length))
2153 IntegerToString(length, 10, result);
2155 AppendString(result, "]");
2157 typeObj = ArrayType::GetBaseType(cx, typeObj);
2158 prevGrouping = currentGrouping;
2161 case TYPE_function: {
2162 FunctionInfo* fninfo = FunctionType::GetFunctionInfo(cx, typeObj);
2164 // Add in the calling convention, if it's not cdecl.
2165 ABICode abi = GetABICode(cx, fninfo->mABI);
2166 if (abi == ABI_STDCALL)
2167 PrependString(result, "__stdcall ");
2168 else if (abi == ABI_WINAPI)
2169 PrependString(result, "WINAPI ");
2171 // Wrap the entire expression so far with parens.
2172 PrependString(result, "(");
2173 AppendString(result, ")");
2175 // Argument list goes on the right.
2176 AppendString(result, "(");
2177 for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
2178 JSString* argName = CType::GetName(cx, fninfo->mArgTypes[i]);
2179 AppendString(result, argName);
2180 if (i != fninfo->mArgTypes.length() - 1 ||
2181 fninfo->mIsVariadic)
2182 AppendString(result, ", ");
2184 if (fninfo->mIsVariadic)
2185 AppendString(result, "...");
2186 AppendString(result, ")");
2188 // Set 'typeObj' to the return type, and let the loop process it.
2189 // 'prevGrouping' doesn't matter here, because functions cannot return
2190 // arrays -- thus the parenthetical rules don't get tickled.
2191 typeObj = fninfo->mReturnType;
2195 // Either a basic or struct type. Use the type's name as the base type.
2201 // Stick the base type and derived type parts together.
2202 JSString* baseName = CType::GetName(cx, typeObj);
2203 PrependString(result, baseName);
2204 return NewUCString(cx, result);
2207 // Given a CType 'typeObj', generate a string 'result' such that 'eval(result)'
2208 // would construct the same CType. If 'makeShort' is true, assume that any
2209 // StructType 't' is bound to an in-scope variable of name 't.name', and use
2210 // that variable in place of generating a string to construct the type 't'.
2211 // (This means the type comparison function CType::TypesEqual will return true
2212 // when comparing the input and output of BuildTypeSource, since struct
2213 // equality is determined by strict JSObject pointer equality.)
2215 BuildTypeSource(JSContext* cx,
2220 // Walk the types, building up the toSource() string.
2221 switch (CType::GetTypeCode(cx, typeObj)) {
2223 #define DEFINE_TYPE(name, type, ffiType) \
2225 #include "typedefs.h"
2227 AppendString(result, "ctypes.");
2228 JSString* nameStr = CType::GetName(cx, typeObj);
2229 AppendString(result, nameStr);
2232 case TYPE_pointer: {
2233 JSObject* baseType = PointerType::GetBaseType(cx, typeObj);
2235 // Specialcase ctypes.voidptr_t.
2236 if (CType::GetTypeCode(cx, baseType) == TYPE_void_t) {
2237 AppendString(result, "ctypes.voidptr_t");
2241 // Recursively build the source string, and append '.ptr'.
2242 BuildTypeSource(cx, baseType, makeShort, result);
2243 AppendString(result, ".ptr");
2246 case TYPE_function: {
2247 FunctionInfo* fninfo = FunctionType::GetFunctionInfo(cx, typeObj);
2249 AppendString(result, "ctypes.FunctionType(");
2251 switch (GetABICode(cx, fninfo->mABI)) {
2253 AppendString(result, "ctypes.default_abi, ");
2256 AppendString(result, "ctypes.stdcall_abi, ");
2259 AppendString(result, "ctypes.winapi_abi, ");
2262 JS_NOT_REACHED("invalid abi");
2266 // Recursively build the source string describing the function return and
2268 BuildTypeSource(cx, fninfo->mReturnType, true, result);
2270 if (fninfo->mArgTypes.length() > 0) {
2271 AppendString(result, ", [");
2272 for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
2273 BuildTypeSource(cx, fninfo->mArgTypes[i], true, result);
2274 if (i != fninfo->mArgTypes.length() - 1 ||
2275 fninfo->mIsVariadic)
2276 AppendString(result, ", ");
2278 if (fninfo->mIsVariadic)
2279 AppendString(result, "\"...\"");
2280 AppendString(result, "]");
2283 AppendString(result, ")");
2287 // Recursively build the source string, and append '.array(n)',
2288 // where n is the array length, or the empty string if the array length
2290 JSObject* baseType = ArrayType::GetBaseType(cx, typeObj);
2291 BuildTypeSource(cx, baseType, makeShort, result);
2292 AppendString(result, ".array(");
2295 if (ArrayType::GetSafeLength(cx, typeObj, &length))
2296 IntegerToString(length, 10, result);
2298 AppendString(result, ")");
2302 JSString* name = CType::GetName(cx, typeObj);
2305 // Shorten the type declaration by assuming that StructType 't' is bound
2306 // to an in-scope variable of name 't.name'.
2307 AppendString(result, name);
2311 // Write the full struct declaration.
2312 AppendString(result, "ctypes.StructType(\"");
2313 AppendString(result, name);
2314 AppendString(result, "\"");
2316 // If it's an opaque struct, we're done.
2317 if (!CType::IsSizeDefined(cx, typeObj)) {
2318 AppendString(result, ")");
2322 AppendString(result, ", [");
2324 const FieldInfoHash* fields = StructType::GetFieldInfo(cx, typeObj);
2325 size_t length = fields->count();
2326 Array<const FieldInfoHash::Entry*, 64> fieldsArray;
2327 if (!fieldsArray.resize(length))
2330 for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront())
2331 fieldsArray[r.front().value.mIndex] = &r.front();
2333 for (size_t i = 0; i < length; ++i) {
2334 const FieldInfoHash::Entry* entry = fieldsArray[i];
2335 AppendString(result, "{ \"");
2336 AppendString(result, entry->key);
2337 AppendString(result, "\": ");
2338 BuildTypeSource(cx, entry->value.mType, true, result);
2339 AppendString(result, " }");
2340 if (i != length - 1)
2341 AppendString(result, ", ");
2344 AppendString(result, "])");
2350 // Given a CData object of CType 'typeObj' with binary value 'data', generate a
2351 // string 'result' such that 'eval(result)' would construct a CData object with
2352 // the same CType and containing the same binary value. This assumes that any
2353 // StructType 't' is bound to an in-scope variable of name 't.name'. (This means
2354 // the type comparison function CType::TypesEqual will return true when
2355 // comparing the types, since struct equality is determined by strict JSObject
2356 // pointer equality.) Further, if 'isImplicit' is true, ensure that the
2357 // resulting string can ImplicitConvert successfully if passed to another data
2358 // constructor. (This is important when called recursively, since fields of
2359 // structs and arrays are converted with ImplicitConvert.)
2361 BuildDataSource(JSContext* cx,
2367 TypeCode type = CType::GetTypeCode(cx, typeObj);
2370 if (*static_cast<bool*>(data))
2371 AppendString(result, "true");
2373 AppendString(result, "false");
2375 #define DEFINE_INT_TYPE(name, type, ffiType) \
2377 /* Serialize as a primitive decimal integer. */ \
2378 IntegerToString(*static_cast<type*>(data), 10, result); \
2380 #define DEFINE_WRAPPED_INT_TYPE(name, type, ffiType) \
2382 /* Serialize as a wrapped decimal integer. */ \
2383 if (!numeric_limits<type>::is_signed) \
2384 AppendString(result, "ctypes.UInt64(\""); \
2386 AppendString(result, "ctypes.Int64(\""); \
2388 IntegerToString(*static_cast<type*>(data), 10, result); \
2389 AppendString(result, "\")"); \
2391 #define DEFINE_FLOAT_TYPE(name, type, ffiType) \
2392 case TYPE_##name: { \
2393 /* Serialize as a primitive double. */ \
2394 double fp = *static_cast<type*>(data); \
2395 ToCStringBuf cbuf; \
2396 char* str = NumberToCString(cx, &cbuf, fp); \
2398 JS_ReportOutOfMemory(cx); \
2402 result.append(str, strlen(str)); \
2405 #define DEFINE_CHAR_TYPE(name, type, ffiType) \
2407 /* Serialize as an integer. */ \
2408 IntegerToString(*static_cast<type*>(data), 10, result); \
2410 #include "typedefs.h"
2412 // Serialize as a 1-character JS string.
2413 JSString* str = JS_NewUCStringCopyN(cx, static_cast<jschar*>(data), 1);
2417 // Escape characters, and quote as necessary.
2418 JSString* src = JS_ValueToSource(cx, STRING_TO_JSVAL(str));
2422 AppendString(result, src);
2426 case TYPE_function: {
2428 // The result must be able to ImplicitConvert successfully.
2429 // Wrap in a type constructor, then serialize for ExplicitConvert.
2430 BuildTypeSource(cx, typeObj, true, result);
2431 AppendString(result, "(");
2434 // Serialize the pointer value as a wrapped hexadecimal integer.
2435 uintptr_t ptr = *static_cast<uintptr_t*>(data);
2436 AppendString(result, "ctypes.UInt64(\"0x");
2437 IntegerToString(ptr, 16, result);
2438 AppendString(result, "\")");
2441 AppendString(result, ")");
2446 // Serialize each element of the array recursively. Each element must
2447 // be able to ImplicitConvert successfully.
2448 JSObject* baseType = ArrayType::GetBaseType(cx, typeObj);
2449 AppendString(result, "[");
2451 size_t length = ArrayType::GetLength(cx, typeObj);
2452 size_t elementSize = CType::GetSize(cx, baseType);
2453 for (size_t i = 0; i < length; ++i) {
2454 char* element = static_cast<char*>(data) + elementSize * i;
2455 if (!BuildDataSource(cx, baseType, element, true, result))
2459 AppendString(result, ", ");
2461 AppendString(result, "]");
2466 // The result must be able to ImplicitConvert successfully.
2467 // Serialize the data as an object with properties, rather than
2468 // a sequence of arguments to the StructType constructor.
2469 AppendString(result, "{");
2472 // Serialize each field of the struct recursively. Each field must
2473 // be able to ImplicitConvert successfully.
2474 const FieldInfoHash* fields = StructType::GetFieldInfo(cx, typeObj);
2475 size_t length = fields->count();
2476 Array<const FieldInfoHash::Entry*, 64> fieldsArray;
2477 if (!fieldsArray.resize(length))
2480 for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront())
2481 fieldsArray[r.front().value.mIndex] = &r.front();
2483 for (size_t i = 0; i < length; ++i) {
2484 const FieldInfoHash::Entry* entry = fieldsArray[i];
2487 AppendString(result, "\"");
2488 AppendString(result, entry->key);
2489 AppendString(result, "\": ");
2492 char* fieldData = static_cast<char*>(data) + entry->value.mOffset;
2493 if (!BuildDataSource(cx, entry->value.mType, fieldData, true, result))
2496 if (i + 1 != length)
2497 AppendString(result, ", ");
2501 AppendString(result, "}");
2506 JS_NOT_REACHED("invalid type");
2513 /*******************************************************************************
2514 ** JSAPI callback function implementations
2515 *******************************************************************************/
2518 ConstructAbstract(JSContext* cx,
2522 // Calling an abstract base class constructor is disallowed.
2523 JS_ReportError(cx, "cannot construct from abstract type");
2527 /*******************************************************************************
2528 ** CType implementation
2529 *******************************************************************************/
2532 CType::ConstructData(JSContext* cx,
2536 // get the callee object...
2537 JSObject* obj = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
2538 if (!CType::IsCType(cx, obj)) {
2539 JS_ReportError(cx, "not a CType");
2543 // How we construct the CData object depends on what type we represent.
2544 // An instance 'd' of a CData object of type 't' has:
2545 // * [[Class]] "CData"
2546 // * __proto__ === t.prototype
2547 switch (GetTypeCode(cx, obj)) {
2549 JS_ReportError(cx, "cannot construct from void_t");
2552 JS_ReportError(cx, "cannot construct from FunctionType; use FunctionType.ptr instead");
2555 return PointerType::ConstructData(cx, obj, argc, vp);
2557 return ArrayType::ConstructData(cx, obj, argc, vp);
2559 return StructType::ConstructData(cx, obj, argc, vp);
2561 return ConstructBasic(cx, obj, argc, vp);
2566 CType::ConstructBasic(JSContext* cx,
2572 JS_ReportError(cx, "CType constructor takes zero or one argument");
2576 // construct a CData object
2577 JSObject* result = CData::Create(cx, obj, NULL, NULL, true);
2582 if (!ExplicitConvert(cx, JS_ARGV(cx, vp)[0], obj, CData::GetData(cx, result)))
2586 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
2591 CType::Create(JSContext* cx,
2592 JSObject* typeProto,
2593 JSObject* dataProto,
2600 JSObject* parent = JS_GetParent(cx, typeProto);
2603 // Create a CType object with the properties and slots common to all CTypes.
2604 // Each type object 't' has:
2605 // * [[Class]] "CType"
2606 // * __proto__ === 'typeProto'; one of ctypes.{CType,PointerType,ArrayType,
2607 // StructType}.prototype
2608 // * A constructor which creates and returns a CData object, containing
2609 // binary data of the given type.
2610 // * 'prototype' property:
2611 // * [[Class]] "CDataProto"
2612 // * __proto__ === 'dataProto'; an object containing properties and
2613 // functions common to all CData objects of types derived from
2614 // 'typeProto'. (For instance, this could be ctypes.CData.prototype
2615 // for simple types, or something representing structs for StructTypes.)
2616 // * 'constructor' property === 't'
2617 // * Additional properties specified by 'ps', as appropriate for the
2618 // specific type instance 't'.
2619 JSObject* typeObj = JS_NewObject(cx, &sCTypeClass, typeProto, parent);
2622 js::AutoObjectRooter root(cx, typeObj);
2624 // Set up the reserved slots.
2625 if (!JS_SetReservedSlot(cx, typeObj, SLOT_TYPECODE, INT_TO_JSVAL(type)) ||
2626 (ffiType && !JS_SetReservedSlot(cx, typeObj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(ffiType))) ||
2627 (name && !JS_SetReservedSlot(cx, typeObj, SLOT_NAME, STRING_TO_JSVAL(name))) ||
2628 !JS_SetReservedSlot(cx, typeObj, SLOT_SIZE, size) ||
2629 !JS_SetReservedSlot(cx, typeObj, SLOT_ALIGN, align))
2633 // Set up the 'prototype' and 'prototype.constructor' properties.
2634 JSObject* prototype = JS_NewObject(cx, &sCDataProtoClass, dataProto, parent);
2637 js::AutoObjectRooter protoroot(cx, prototype);
2639 if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(typeObj),
2640 NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT))
2643 // Set the 'prototype' object.
2644 if (//!JS_FreezeObject(cx, prototype) || // XXX fixme - see bug 541212!
2645 !JS_SetReservedSlot(cx, typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype)))
2649 if (!JS_FreezeObject(cx, typeObj))
2652 // Assert a sanity check on size and alignment: size % alignment should always
2654 JS_ASSERT_IF(IsSizeDefined(cx, typeObj),
2655 GetSize(cx, typeObj) % GetAlignment(cx, typeObj) == 0);
2661 CType::DefineBuiltin(JSContext* cx,
2663 const char* propName,
2664 JSObject* typeProto,
2665 JSObject* dataProto,
2672 JSString* nameStr = JS_NewStringCopyZ(cx, name);
2675 js::AutoStringRooter nameRoot(cx, nameStr);
2677 // Create a new CType object with the common properties and slots.
2678 JSObject* typeObj = Create(cx, typeProto, dataProto, type, nameStr, size,
2683 // Define the CType as a 'propName' property on 'parent'.
2684 if (!JS_DefineProperty(cx, parent, propName, OBJECT_TO_JSVAL(typeObj),
2685 NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
2692 CType::Finalize(JSContext* cx, JSObject* obj)
2694 // Make sure our TypeCode slot is legit. If it's not, bail.
2696 if (!JS_GetReservedSlot(cx, obj, SLOT_TYPECODE, &slot) || JSVAL_IS_VOID(slot))
2699 // The contents of our slots depends on what kind of type we are.
2700 switch (TypeCode(JSVAL_TO_INT(slot))) {
2701 case TYPE_function: {
2702 // Free the FunctionInfo.
2703 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FNINFO, &slot));
2704 if (!JSVAL_IS_VOID(slot))
2705 js_delete(static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot)));
2710 // Free the FieldInfoHash table.
2711 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FIELDINFO, &slot));
2712 if (!JSVAL_IS_VOID(slot)) {
2713 void* info = JSVAL_TO_PRIVATE(slot);
2714 js_delete(static_cast<FieldInfoHash*>(info));
2720 // Free the ffi_type info.
2721 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FFITYPE, &slot));
2722 if (!JSVAL_IS_VOID(slot)) {
2723 ffi_type* ffiType = static_cast<ffi_type*>(JSVAL_TO_PRIVATE(slot));
2724 js_array_delete(ffiType->elements);
2731 // Nothing to do here.
2737 CType::FinalizeProtoClass(JSContext* cx, JSObject* obj)
2739 // Finalize the CTypeProto class. The only important bit here is our
2740 // SLOT_CLOSURECX -- it contains the JSContext that was (lazily) instantiated
2741 // for use with FunctionType closures. And if we're here, in this finalizer,
2742 // we're guaranteed to not need it anymore. Note that this slot will only
2743 // be set for the object (of class CTypeProto) ctypes.FunctionType.prototype.
2745 if (!JS_GetReservedSlot(cx, obj, SLOT_CLOSURECX, &slot) || JSVAL_IS_VOID(slot))
2748 JSContext* closureCx = static_cast<JSContext*>(JSVAL_TO_PRIVATE(slot));
2749 JS_SetContextThread(closureCx);
2750 JS_DestroyContextNoGC(closureCx);
2754 CType::Trace(JSTracer* trc, JSObject* obj)
2756 JSContext* cx = trc->context;
2758 // Make sure our TypeCode slot is legit. If it's not, bail.
2759 jsval slot = js::Jsvalify(obj->getSlot(SLOT_TYPECODE));
2760 if (JSVAL_IS_VOID(slot))
2763 // The contents of our slots depends on what kind of type we are.
2764 switch (TypeCode(JSVAL_TO_INT(slot))) {
2766 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FIELDINFO, &slot));
2767 if (JSVAL_IS_VOID(slot))
2770 FieldInfoHash* fields =
2771 static_cast<FieldInfoHash*>(JSVAL_TO_PRIVATE(slot));
2772 for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
2773 JS_CALL_TRACER(trc, r.front().key, JSTRACE_STRING, "fieldName");
2774 JS_CALL_TRACER(trc, r.front().value.mType, JSTRACE_OBJECT, "fieldType");
2779 case TYPE_function: {
2780 // Check if we have a FunctionInfo.
2781 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FNINFO, &slot));
2782 if (JSVAL_IS_VOID(slot))
2785 FunctionInfo* fninfo = static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot));
2788 // Identify our objects to the tracer.
2789 JS_CALL_TRACER(trc, fninfo->mABI, JSTRACE_OBJECT, "abi");
2790 JS_CALL_TRACER(trc, fninfo->mReturnType, JSTRACE_OBJECT, "returnType");
2791 for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i)
2792 JS_CALL_TRACER(trc, fninfo->mArgTypes[i], JSTRACE_OBJECT, "argType");
2797 // Nothing to do here.
2803 CType::IsCType(JSContext* cx, JSObject* obj)
2805 return JS_GET_CLASS(cx, obj) == &sCTypeClass;
2809 CType::GetTypeCode(JSContext* cx, JSObject* typeObj)
2811 JS_ASSERT(IsCType(cx, typeObj));
2814 ASSERT_OK(JS_GetReservedSlot(cx, typeObj, SLOT_TYPECODE, &result));
2815 return TypeCode(JSVAL_TO_INT(result));
2819 CType::TypesEqual(JSContext* cx, JSObject* t1, JSObject* t2)
2821 JS_ASSERT(IsCType(cx, t1) && IsCType(cx, t2));
2823 // Fast path: check for object equality.
2827 // First, perform shallow comparison.
2828 TypeCode c1 = GetTypeCode(cx, t1);
2829 TypeCode c2 = GetTypeCode(cx, t2);
2833 // Determine whether the types require shallow or deep comparison.
2835 case TYPE_pointer: {
2836 // Compare base types.
2837 JSObject* b1 = PointerType::GetBaseType(cx, t1);
2838 JSObject* b2 = PointerType::GetBaseType(cx, t2);
2839 return TypesEqual(cx, b1, b2);
2841 case TYPE_function: {
2842 FunctionInfo* f1 = FunctionType::GetFunctionInfo(cx, t1);
2843 FunctionInfo* f2 = FunctionType::GetFunctionInfo(cx, t2);
2845 // Compare abi, return type, and argument types.
2846 if (f1->mABI != f2->mABI)
2849 if (!TypesEqual(cx, f1->mReturnType, f2->mReturnType))
2852 if (f1->mArgTypes.length() != f2->mArgTypes.length())
2855 if (f1->mIsVariadic != f2->mIsVariadic)
2858 for (size_t i = 0; i < f1->mArgTypes.length(); ++i) {
2859 if (!TypesEqual(cx, f1->mArgTypes[i], f2->mArgTypes[i]))
2866 // Compare length, then base types.
2867 // An undefined length array matches other undefined length arrays.
2868 size_t s1 = 0, s2 = 0;
2869 bool d1 = ArrayType::GetSafeLength(cx, t1, &s1);
2870 bool d2 = ArrayType::GetSafeLength(cx, t2, &s2);
2871 if (d1 != d2 || (d1 && s1 != s2))
2874 JSObject* b1 = ArrayType::GetBaseType(cx, t1);
2875 JSObject* b2 = ArrayType::GetBaseType(cx, t2);
2876 return TypesEqual(cx, b1, b2);
2879 // Require exact type object equality.
2882 // Shallow comparison is sufficient.
2888 CType::GetSafeSize(JSContext* cx, JSObject* obj, size_t* result)
2890 JS_ASSERT(CType::IsCType(cx, obj));
2893 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_SIZE, &size));
2895 // The "size" property can be a jsint, a jsdouble, or JSVAL_VOID
2896 // (for arrays of undefined length), and must always fit in a size_t.
2897 if (JSVAL_IS_INT(size)) {
2898 *result = JSVAL_TO_INT(size);
2901 if (JSVAL_IS_DOUBLE(size)) {
2902 *result = Convert<size_t>(JSVAL_TO_DOUBLE(size));
2906 JS_ASSERT(JSVAL_IS_VOID(size));
2911 CType::GetSize(JSContext* cx, JSObject* obj)
2913 JS_ASSERT(CType::IsCType(cx, obj));
2916 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_SIZE, &size));
2918 JS_ASSERT(!JSVAL_IS_VOID(size));
2920 // The "size" property can be a jsint, a jsdouble, or JSVAL_VOID
2921 // (for arrays of undefined length), and must always fit in a size_t.
2922 // For callers who know it can never be JSVAL_VOID, return a size_t directly.
2923 if (JSVAL_IS_INT(size))
2924 return JSVAL_TO_INT(size);
2925 return Convert<size_t>(JSVAL_TO_DOUBLE(size));
2929 CType::IsSizeDefined(JSContext* cx, JSObject* obj)
2931 JS_ASSERT(CType::IsCType(cx, obj));
2934 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_SIZE, &size));
2936 // The "size" property can be a jsint, a jsdouble, or JSVAL_VOID
2937 // (for arrays of undefined length), and must always fit in a size_t.
2938 JS_ASSERT(JSVAL_IS_INT(size) || JSVAL_IS_DOUBLE(size) || JSVAL_IS_VOID(size));
2939 return !JSVAL_IS_VOID(size);
2943 CType::GetAlignment(JSContext* cx, JSObject* obj)
2945 JS_ASSERT(CType::IsCType(cx, obj));
2948 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_ALIGN, &slot));
2949 return static_cast<size_t>(JSVAL_TO_INT(slot));
2953 CType::GetFFIType(JSContext* cx, JSObject* obj)
2955 JS_ASSERT(CType::IsCType(cx, obj));
2958 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FFITYPE, &slot));
2960 if (!JSVAL_IS_VOID(slot)) {
2961 return static_cast<ffi_type*>(JSVAL_TO_PRIVATE(slot));
2964 AutoPtr<ffi_type> result;
2965 switch (CType::GetTypeCode(cx, obj)) {
2967 result = ArrayType::BuildFFIType(cx, obj);
2971 result = StructType::BuildFFIType(cx, obj);
2975 JS_NOT_REACHED("simple types must have an ffi_type");
2979 !JS_SetReservedSlot(cx, obj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(result.get())))
2982 return result.forget();
2986 CType::GetName(JSContext* cx, JSObject* obj)
2988 JS_ASSERT(CType::IsCType(cx, obj));
2991 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_NAME, &string));
2992 if (JSVAL_IS_VOID(string)) {
2993 // Build the type name lazily.
2994 JSString* name = BuildTypeName(cx, obj);
2995 if (!name || !JS_SetReservedSlot(cx, obj, SLOT_NAME, STRING_TO_JSVAL(name)))
3001 return JSVAL_TO_STRING(string);
3005 CType::GetProtoFromCtor(JSContext* cx, JSObject* obj, CTypeProtoSlot slot)
3007 // Get ctypes.{Pointer,Array,Struct}Type.prototype from a reserved slot
3008 // on the type constructor.
3010 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FN_CTORPROTO, &protoslot));
3011 JSObject* proto = JSVAL_TO_OBJECT(protoslot);
3013 JS_ASSERT(JS_GET_CLASS(cx, proto) == &sCTypeProtoClass);
3015 // Get the desired prototype.
3017 ASSERT_OK(JS_GetReservedSlot(cx, proto, slot, &result));
3018 return JSVAL_TO_OBJECT(result);
3022 CType::GetProtoFromType(JSContext* cx, JSObject* obj, CTypeProtoSlot slot)
3024 JS_ASSERT(IsCType(cx, obj));
3026 // Get the prototype of the type object.
3027 JSObject* proto = JS_GetPrototype(cx, obj);
3029 JS_ASSERT(JS_GET_CLASS(cx, proto) == &sCTypeProtoClass);
3031 // Get the requested ctypes.{Pointer,Array,Struct,Function}Type.prototype.
3033 ASSERT_OK(JS_GetReservedSlot(cx, proto, slot, &result));
3034 return JSVAL_TO_OBJECT(result);
3038 CType::PrototypeGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
3040 if (!CType::IsCType(cx, obj)) {
3041 JS_ReportError(cx, "not a CType");
3045 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_PROTO, vp));
3046 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp) || JSVAL_IS_VOID(*vp));
3051 CType::NameGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
3053 if (!CType::IsCType(cx, obj)) {
3054 JS_ReportError(cx, "not a CType");
3058 JSString* name = CType::GetName(cx, obj);
3062 *vp = STRING_TO_JSVAL(name);
3067 CType::SizeGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
3069 if (!CType::IsCType(cx, obj)) {
3070 JS_ReportError(cx, "not a CType");
3074 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_SIZE, vp));
3075 JS_ASSERT(JSVAL_IS_NUMBER(*vp) || JSVAL_IS_VOID(*vp));
3080 CType::PtrGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
3082 if (!CType::IsCType(cx, obj)) {
3083 JS_ReportError(cx, "not a CType");
3087 JSObject* pointerType = PointerType::CreateInternal(cx, obj);
3091 *vp = OBJECT_TO_JSVAL(pointerType);
3096 CType::CreateArray(JSContext* cx, uintN argc, jsval* vp)
3098 JSObject* baseType = JS_THIS_OBJECT(cx, vp);
3099 if (!baseType || !CType::IsCType(cx, baseType)) {
3100 JS_ReportError(cx, "not a CType");
3104 // Construct and return a new ArrayType object.
3106 JS_ReportError(cx, "array takes zero or one argument");
3110 // Convert the length argument to a size_t.
3111 jsval* argv = JS_ARGV(cx, vp);
3113 if (argc == 1 && !jsvalToSize(cx, argv[0], false, &length)) {
3114 JS_ReportError(cx, "argument must be a nonnegative integer");
3118 JSObject* result = ArrayType::CreateInternal(cx, baseType, length, argc == 1);
3122 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
3127 CType::ToString(JSContext* cx, uintN argc, jsval* vp)
3129 JSObject* obj = JS_THIS_OBJECT(cx, vp);
3130 if (!obj || !CType::IsCType(cx, obj)) {
3131 JS_ReportError(cx, "not a CType");
3136 AppendString(type, "type ");
3137 AppendString(type, GetName(cx, obj));
3139 JSString* result = NewUCString(cx, type);
3143 JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
3148 CType::ToSource(JSContext* cx, uintN argc, jsval* vp)
3150 JSObject* obj = JS_THIS_OBJECT(cx, vp);
3151 if (!obj || !CType::IsCType(cx, obj)) {
3152 JS_ReportError(cx, "not a CType");
3157 BuildTypeSource(cx, obj, false, source);
3158 JSString* result = NewUCString(cx, source);
3162 JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
3167 CType::HasInstance(JSContext* cx, JSObject* obj, const jsval* v, JSBool* bp)
3169 JS_ASSERT(CType::IsCType(cx, obj));
3172 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_PROTO, &slot));
3173 JSObject* prototype = JSVAL_TO_OBJECT(slot);
3174 JS_ASSERT(prototype);
3175 JS_ASSERT(JS_GET_CLASS(cx, prototype) == &sCDataProtoClass);
3178 if (JSVAL_IS_PRIMITIVE(*v))
3181 JSObject* proto = JSVAL_TO_OBJECT(*v);
3182 while ((proto = JS_GetPrototype(cx, proto))) {
3183 if (proto == prototype) {
3191 /*******************************************************************************
3192 ** PointerType implementation
3193 *******************************************************************************/
3196 PointerType::Create(JSContext* cx, uintN argc, jsval* vp)
3198 // Construct and return a new PointerType object.
3200 JS_ReportError(cx, "PointerType takes one argument");
3204 jsval arg = JS_ARGV(cx, vp)[0];
3205 if (JSVAL_IS_PRIMITIVE(arg) || !CType::IsCType(cx, JSVAL_TO_OBJECT(arg))) {
3206 JS_ReportError(cx, "first argument must be a CType");
3210 JSObject* result = CreateInternal(cx, JSVAL_TO_OBJECT(arg));
3214 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
3219 PointerType::CreateInternal(JSContext* cx, JSObject* baseType)
3221 // check if we have a cached PointerType on our base CType.
3223 ASSERT_OK(JS_GetReservedSlot(cx, baseType, SLOT_PTR, &slot));
3224 if (!JSVAL_IS_VOID(slot))
3225 return JSVAL_TO_OBJECT(slot);
3227 // Get ctypes.PointerType.prototype and the common prototype for CData objects
3229 JSObject* typeProto;
3230 JSObject* dataProto;
3231 typeProto = CType::GetProtoFromType(cx, baseType, SLOT_POINTERPROTO);
3232 dataProto = CType::GetProtoFromType(cx, baseType, SLOT_POINTERDATAPROTO);
3234 // Create a new CType object with the common properties and slots.
3235 JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_pointer,
3236 NULL, INT_TO_JSVAL(sizeof(void*)),
3237 INT_TO_JSVAL(ffi_type_pointer.alignment),
3241 js::AutoObjectRooter root(cx, typeObj);
3243 // Set the target type. (This will be 'null' for an opaque pointer type.)
3244 if (!JS_SetReservedSlot(cx, typeObj, SLOT_TARGET_T, OBJECT_TO_JSVAL(baseType)))
3247 // Finally, cache our newly-created PointerType on our pointed-to CType.
3248 if (!JS_SetReservedSlot(cx, baseType, SLOT_PTR, OBJECT_TO_JSVAL(typeObj)))
3255 PointerType::ConstructData(JSContext* cx,
3260 if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_pointer) {
3261 JS_ReportError(cx, "not a PointerType");
3266 JS_ReportError(cx, "constructor takes 0, 1, or 2 arguments");
3270 JSObject* result = CData::Create(cx, obj, NULL, NULL, true);
3274 // Set return value early, must not observe *vp after
3275 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
3278 // Construct a null pointer.
3282 jsval* argv = JS_ARGV(cx, vp);
3284 JSObject* baseObj = PointerType::GetBaseType(cx, obj);
3285 if (CType::GetTypeCode(cx, baseObj) == TYPE_function &&
3286 JSVAL_IS_OBJECT(argv[0]) &&
3287 JS_ObjectIsCallable(cx, JSVAL_TO_OBJECT(argv[0]))) {
3288 // Construct a FunctionType.ptr from a JS function, and allow an
3289 // optional 'this' argument.
3290 JSObject* thisObj = NULL;
3292 if (JSVAL_IS_OBJECT(argv[1])) {
3293 thisObj = JSVAL_TO_OBJECT(argv[1]);
3294 } else if (!JS_ValueToObject(cx, argv[1], &thisObj)) {
3299 JSObject* fnObj = JSVAL_TO_OBJECT(argv[0]);
3300 return FunctionType::ConstructData(cx, baseObj, result, fnObj, thisObj);
3304 JS_ReportError(cx, "first argument must be a function");
3309 // Construct from a raw pointer value.
3310 return ExplicitConvert(cx, argv[0], obj, CData::GetData(cx, result));
3314 PointerType::GetBaseType(JSContext* cx, JSObject* obj)
3316 JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_pointer);
3319 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_TARGET_T, &type));
3320 JS_ASSERT(!JSVAL_IS_NULL(type));
3321 return JSVAL_TO_OBJECT(type);
3325 PointerType::TargetTypeGetter(JSContext* cx,
3330 if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_pointer) {
3331 JS_ReportError(cx, "not a PointerType");
3335 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_TARGET_T, vp));
3336 JS_ASSERT(JSVAL_IS_OBJECT(*vp));
3341 PointerType::IsNull(JSContext* cx, uintN argc, jsval* vp)
3343 JSObject* obj = JS_THIS_OBJECT(cx, vp);
3344 if (!obj || !CData::IsCData(cx, obj)) {
3345 JS_ReportError(cx, "not a CData");
3349 // Get pointer type and base type.
3350 JSObject* typeObj = CData::GetCType(cx, obj);
3351 if (CType::GetTypeCode(cx, typeObj) != TYPE_pointer) {
3352 JS_ReportError(cx, "not a PointerType");
3356 void* data = *static_cast<void**>(CData::GetData(cx, obj));
3357 jsval result = BOOLEAN_TO_JSVAL(data == NULL);
3358 JS_SET_RVAL(cx, vp, result);
3363 PointerType::ContentsGetter(JSContext* cx,
3368 if (!CData::IsCData(cx, obj)) {
3369 JS_ReportError(cx, "not a CData");
3373 // Get pointer type and base type.
3374 JSObject* typeObj = CData::GetCType(cx, obj);
3375 if (CType::GetTypeCode(cx, typeObj) != TYPE_pointer) {
3376 JS_ReportError(cx, "not a PointerType");
3380 JSObject* baseType = GetBaseType(cx, typeObj);
3381 if (!CType::IsSizeDefined(cx, baseType)) {
3382 JS_ReportError(cx, "cannot get contents of undefined size");
3386 void* data = *static_cast<void**>(CData::GetData(cx, obj));
3388 JS_ReportError(cx, "cannot read contents of null pointer");
3393 if (!ConvertToJS(cx, baseType, NULL, data, false, false, &result))
3396 JS_SET_RVAL(cx, vp, result);
3401 PointerType::ContentsSetter(JSContext* cx,
3407 if (!CData::IsCData(cx, obj)) {
3408 JS_ReportError(cx, "not a CData");
3412 // Get pointer type and base type.
3413 JSObject* typeObj = CData::GetCType(cx, obj);
3414 if (CType::GetTypeCode(cx, typeObj) != TYPE_pointer) {
3415 JS_ReportError(cx, "not a PointerType");
3419 JSObject* baseType = GetBaseType(cx, typeObj);
3420 if (!CType::IsSizeDefined(cx, baseType)) {
3421 JS_ReportError(cx, "cannot set contents of undefined size");
3425 void* data = *static_cast<void**>(CData::GetData(cx, obj));
3427 JS_ReportError(cx, "cannot write contents to null pointer");
3431 return ImplicitConvert(cx, *vp, baseType, data, false, NULL);
3434 /*******************************************************************************
3435 ** ArrayType implementation
3436 *******************************************************************************/
3439 ArrayType::Create(JSContext* cx, uintN argc, jsval* vp)
3441 // Construct and return a new ArrayType object.
3442 if (argc < 1 || argc > 2) {
3443 JS_ReportError(cx, "ArrayType takes one or two arguments");
3447 jsval* argv = JS_ARGV(cx, vp);
3448 if (JSVAL_IS_PRIMITIVE(argv[0]) ||
3449 !CType::IsCType(cx, JSVAL_TO_OBJECT(argv[0]))) {
3450 JS_ReportError(cx, "first argument must be a CType");
3454 // Convert the length argument to a size_t.
3456 if (argc == 2 && !jsvalToSize(cx, argv[1], false, &length)) {
3457 JS_ReportError(cx, "second argument must be a nonnegative integer");
3461 JSObject* baseType = JSVAL_TO_OBJECT(argv[0]);
3462 JSObject* result = CreateInternal(cx, baseType, length, argc == 2);
3466 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
3471 ArrayType::CreateInternal(JSContext* cx,
3476 // Get ctypes.ArrayType.prototype and the common prototype for CData objects
3477 // of this type, from ctypes.CType.prototype.
3478 JSObject* typeProto = CType::GetProtoFromType(cx, baseType, SLOT_ARRAYPROTO);
3479 JSObject* dataProto = CType::GetProtoFromType(cx, baseType, SLOT_ARRAYDATAPROTO);
3481 // Determine the size of the array from the base type, if possible.
3482 // The size of the base type must be defined.
3483 // If our length is undefined, both our size and length will be undefined.
3485 if (!CType::GetSafeSize(cx, baseType, &baseSize)) {
3486 JS_ReportError(cx, "base size must be defined");
3490 jsval sizeVal = JSVAL_VOID;
3491 jsval lengthVal = JSVAL_VOID;
3492 if (lengthDefined) {
3493 // Check for overflow, and convert to a jsint or jsdouble as required.
3494 size_t size = length * baseSize;
3495 if (length > 0 && size / length != baseSize) {
3496 JS_ReportError(cx, "size overflow");
3499 if (!SizeTojsval(cx, size, &sizeVal) ||
3500 !SizeTojsval(cx, length, &lengthVal))
3504 size_t align = CType::GetAlignment(cx, baseType);
3506 // Create a new CType object with the common properties and slots.
3507 JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_array, NULL,
3508 sizeVal, INT_TO_JSVAL(align), NULL);
3511 js::AutoObjectRooter root(cx, typeObj);
3513 // Set the element type.
3514 if (!JS_SetReservedSlot(cx, typeObj, SLOT_ELEMENT_T, OBJECT_TO_JSVAL(baseType)))
3518 if (!JS_SetReservedSlot(cx, typeObj, SLOT_LENGTH, lengthVal))
3525 ArrayType::ConstructData(JSContext* cx,
3530 if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_array) {
3531 JS_ReportError(cx, "not an ArrayType");
3535 // Decide whether we have an object to initialize from. We'll override this
3536 // if we get a length argument instead.
3537 bool convertObject = argc == 1;
3539 // Check if we're an array of undefined length. If we are, allow construction
3540 // with a length argument, or with an actual JS array.
3541 if (CType::IsSizeDefined(cx, obj)) {
3543 JS_ReportError(cx, "constructor takes zero or one argument");
3549 JS_ReportError(cx, "constructor takes one argument");
3553 JSObject* baseType = GetBaseType(cx, obj);
3555 jsval* argv = JS_ARGV(cx, vp);
3557 if (jsvalToSize(cx, argv[0], false, &length)) {
3558 // Have a length, rather than an object to initialize from.
3559 convertObject = false;
3561 } else if (!JSVAL_IS_PRIMITIVE(argv[0])) {
3562 // We were given an object with a .length property.
3563 // This could be a JS array, or a CData array.
3564 JSObject* arg = JSVAL_TO_OBJECT(argv[0]);
3565 js::AutoValueRooter lengthVal(cx);
3566 if (!JS_GetProperty(cx, arg, "length", lengthVal.jsval_addr()) ||
3567 !jsvalToSize(cx, lengthVal.jsval_value(), false, &length)) {
3568 JS_ReportError(cx, "argument must be an array object or length");
3572 } else if (JSVAL_IS_STRING(argv[0])) {
3573 // We were given a string. Size the array to the appropriate length,
3574 // including space for the terminator.
3575 JSString* sourceString = JSVAL_TO_STRING(argv[0]);
3576 size_t sourceLength = sourceString->length();
3577 const jschar* sourceChars = sourceString->getChars(cx);
3581 switch (CType::GetTypeCode(cx, baseType)) {
3583 case TYPE_signed_char:
3584 case TYPE_unsigned_char: {
3585 // Determine the UTF-8 length.
3586 length = js_GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength);
3587 if (length == (size_t) -1)
3594 length = sourceLength + 1;
3597 return TypeError(cx, "array", argv[0]);
3601 JS_ReportError(cx, "argument must be an array object or length");
3605 // Construct a new ArrayType of defined length, for the new CData object.
3606 obj = CreateInternal(cx, baseType, length, true);
3611 // Root the CType object, in case we created one above.
3612 js::AutoObjectRooter root(cx, obj);
3614 JSObject* result = CData::Create(cx, obj, NULL, NULL, true);
3618 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
3620 if (convertObject) {
3621 if (!ExplicitConvert(cx, JS_ARGV(cx, vp)[0], obj, CData::GetData(cx, result)))
3629 ArrayType::GetBaseType(JSContext* cx, JSObject* obj)
3631 JS_ASSERT(CType::IsCType(cx, obj));
3632 JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_array);
3635 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_ELEMENT_T, &type));
3636 JS_ASSERT(!JSVAL_IS_NULL(type));
3637 return JSVAL_TO_OBJECT(type);
3641 ArrayType::GetSafeLength(JSContext* cx, JSObject* obj, size_t* result)
3643 JS_ASSERT(CType::IsCType(cx, obj));
3644 JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_array);
3647 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_LENGTH, &length));
3649 // The "length" property can be a jsint, a jsdouble, or JSVAL_VOID
3650 // (for arrays of undefined length), and must always fit in a size_t.
3651 if (JSVAL_IS_INT(length)) {
3652 *result = JSVAL_TO_INT(length);
3655 if (JSVAL_IS_DOUBLE(length)) {
3656 *result = Convert<size_t>(JSVAL_TO_DOUBLE(length));
3660 JS_ASSERT(JSVAL_IS_VOID(length));
3665 ArrayType::GetLength(JSContext* cx, JSObject* obj)
3667 JS_ASSERT(CType::IsCType(cx, obj));
3668 JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_array);
3671 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_LENGTH, &length));
3673 JS_ASSERT(!JSVAL_IS_VOID(length));
3675 // The "length" property can be a jsint, a jsdouble, or JSVAL_VOID
3676 // (for arrays of undefined length), and must always fit in a size_t.
3677 // For callers who know it can never be JSVAL_VOID, return a size_t directly.
3678 if (JSVAL_IS_INT(length))
3679 return JSVAL_TO_INT(length);
3680 return Convert<size_t>(JSVAL_TO_DOUBLE(length));
3684 ArrayType::BuildFFIType(JSContext* cx, JSObject* obj)
3686 JS_ASSERT(CType::IsCType(cx, obj));
3687 JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_array);
3688 JS_ASSERT(CType::IsSizeDefined(cx, obj));
3690 JSObject* baseType = ArrayType::GetBaseType(cx, obj);
3691 ffi_type* ffiBaseType = CType::GetFFIType(cx, baseType);
3695 size_t length = ArrayType::GetLength(cx, obj);
3697 // Create an ffi_type to represent the array. This is necessary for the case
3698 // where the array is part of a struct. Since libffi has no intrinsic
3699 // support for array types, we approximate it by creating a struct type
3700 // with elements of type 'baseType' and with appropriate size and alignment
3701 // values. It would be nice to not do all the work of setting up 'elements',
3702 // but some libffi platforms currently require that it be meaningful. I'm
3703 // looking at you, x86_64.
3704 AutoPtr<ffi_type> ffiType(js_new<ffi_type>());
3706 JS_ReportOutOfMemory(cx);
3710 ffiType->type = FFI_TYPE_STRUCT;
3711 ffiType->size = CType::GetSize(cx, obj);
3712 ffiType->alignment = CType::GetAlignment(cx, obj);
3713 ffiType->elements = js_array_new<ffi_type*>(length + 1);
3714 if (!ffiType->elements) {
3715 JS_ReportAllocationOverflow(cx);
3719 for (size_t i = 0; i < length; ++i)
3720 ffiType->elements[i] = ffiBaseType;
3721 ffiType->elements[length] = NULL;
3723 return ffiType.forget();
3727 ArrayType::ElementTypeGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
3729 if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_array) {
3730 JS_ReportError(cx, "not an ArrayType");
3734 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_ELEMENT_T, vp));
3735 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
3740 ArrayType::LengthGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
3742 // This getter exists for both CTypes and CDatas of the ArrayType persuasion.
3743 // If we're dealing with a CData, get the CType from it.
3744 if (CData::IsCData(cx, obj))
3745 obj = CData::GetCType(cx, obj);
3747 if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_array) {
3748 JS_ReportError(cx, "not an ArrayType");
3752 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_LENGTH, vp));
3753 JS_ASSERT(JSVAL_IS_NUMBER(*vp) || JSVAL_IS_VOID(*vp));
3758 ArrayType::Getter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
3760 // This should never happen, but we'll check to be safe.
3761 if (!CData::IsCData(cx, obj)) {
3762 JS_ReportError(cx, "not a CData");
3766 // Bail early if we're not an ArrayType. (This setter is present for all
3767 // CData, regardless of CType.)
3768 JSObject* typeObj = CData::GetCType(cx, obj);
3769 if (CType::GetTypeCode(cx, typeObj) != TYPE_array)
3772 // Convert the index to a size_t and bounds-check it.
3774 size_t length = GetLength(cx, typeObj);
3775 bool ok = jsidToSize(cx, idval, true, &index);
3776 if (!ok && JSID_IS_STRING(idval)) {
3777 // String either isn't a number, or doesn't fit in size_t.
3778 // Chances are it's a regular property lookup, so return.
3781 if (!ok || index >= length) {
3782 JS_ReportError(cx, "invalid index");
3786 JSObject* baseType = GetBaseType(cx, typeObj);
3787 size_t elementSize = CType::GetSize(cx, baseType);
3788 char* data = static_cast<char*>(CData::GetData(cx, obj)) + elementSize * index;
3789 return ConvertToJS(cx, baseType, obj, data, false, false, vp);
3793 ArrayType::Setter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict, jsval* vp)
3795 // This should never happen, but we'll check to be safe.
3796 if (!CData::IsCData(cx, obj)) {
3797 JS_ReportError(cx, "not a CData");
3801 // Bail early if we're not an ArrayType. (This setter is present for all
3802 // CData, regardless of CType.)
3803 JSObject* typeObj = CData::GetCType(cx, obj);
3804 if (CType::GetTypeCode(cx, typeObj) != TYPE_array)
3807 // Convert the index to a size_t and bounds-check it.
3809 size_t length = GetLength(cx, typeObj);
3810 bool ok = jsidToSize(cx, idval, true, &index);
3811 if (!ok && JSID_IS_STRING(idval)) {
3812 // String either isn't a number, or doesn't fit in size_t.
3813 // Chances are it's a regular property lookup, so return.
3816 if (!ok || index >= length) {
3817 JS_ReportError(cx, "invalid index");
3821 JSObject* baseType = GetBaseType(cx, typeObj);
3822 size_t elementSize = CType::GetSize(cx, baseType);
3823 char* data = static_cast<char*>(CData::GetData(cx, obj)) + elementSize * index;
3824 return ImplicitConvert(cx, *vp, baseType, data, false, NULL);
3828 ArrayType::AddressOfElement(JSContext* cx, uintN argc, jsval* vp)
3830 JSObject* obj = JS_THIS_OBJECT(cx, vp);
3831 if (!obj || !CData::IsCData(cx, obj)) {
3832 JS_ReportError(cx, "not a CData");
3836 JSObject* typeObj = CData::GetCType(cx, obj);
3837 if (CType::GetTypeCode(cx, typeObj) != TYPE_array) {
3838 JS_ReportError(cx, "not an ArrayType");
3843 JS_ReportError(cx, "addressOfElement takes one argument");
3847 JSObject* baseType = GetBaseType(cx, typeObj);
3848 JSObject* pointerType = PointerType::CreateInternal(cx, baseType);
3851 js::AutoObjectRooter root(cx, pointerType);
3853 // Create a PointerType CData object containing null.
3854 JSObject* result = CData::Create(cx, pointerType, NULL, NULL, true);
3858 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
3860 // Convert the index to a size_t and bounds-check it.
3862 size_t length = GetLength(cx, typeObj);
3863 if (!jsvalToSize(cx, JS_ARGV(cx, vp)[0], false, &index) ||
3865 JS_ReportError(cx, "invalid index");
3869 // Manually set the pointer inside the object, so we skip the conversion step.
3870 void** data = static_cast<void**>(CData::GetData(cx, result));
3871 size_t elementSize = CType::GetSize(cx, baseType);
3872 *data = static_cast<char*>(CData::GetData(cx, obj)) + elementSize * index;
3876 /*******************************************************************************
3877 ** StructType implementation
3878 *******************************************************************************/
3880 // For a struct field descriptor 'val' of the form { name : type }, extract
3881 // 'name' and 'type'.
3882 static JSFlatString*
3883 ExtractStructField(JSContext* cx, jsval val, JSObject** typeObj)
3885 if (JSVAL_IS_PRIMITIVE(val)) {
3886 JS_ReportError(cx, "struct field descriptors require a valid name and type");
3890 JSObject* obj = JSVAL_TO_OBJECT(val);
3891 JSObject* iter = JS_NewPropertyIterator(cx, obj);
3894 js::AutoObjectRooter iterroot(cx, iter);
3897 if (!JS_NextProperty(cx, iter, &nameid))
3899 if (JSID_IS_VOID(nameid)) {
3900 JS_ReportError(cx, "struct field descriptors require a valid name and type");
3904 if (!JSID_IS_STRING(nameid)) {
3905 JS_ReportError(cx, "struct field descriptors require a valid name and type");
3909 // make sure we have one, and only one, property
3911 if (!JS_NextProperty(cx, iter, &id))
3913 if (!JSID_IS_VOID(id)) {
3914 JS_ReportError(cx, "struct field descriptors must contain one property");
3918 js::AutoValueRooter propVal(cx);
3919 if (!JS_GetPropertyById(cx, obj, nameid, propVal.jsval_addr()))
3922 if (propVal.value().isPrimitive() ||
3923 !CType::IsCType(cx, JSVAL_TO_OBJECT(propVal.jsval_value()))) {
3924 JS_ReportError(cx, "struct field descriptors require a valid name and type");
3928 // Undefined size or zero size struct members are illegal.
3929 // (Zero-size arrays are legal as struct members in C++, but libffi will
3930 // choke on a zero-size struct, so we disallow them.)
3931 *typeObj = JSVAL_TO_OBJECT(propVal.jsval_value());
3933 if (!CType::GetSafeSize(cx, *typeObj, &size) || size == 0) {
3934 JS_ReportError(cx, "struct field types must have defined and nonzero size");
3938 return JSID_TO_FLAT_STRING(nameid);
3941 // For a struct field with 'name' and 'type', add an element of the form
3944 AddFieldToArray(JSContext* cx,
3949 JSObject* fieldObj = JS_NewObject(cx, NULL, NULL, NULL);
3953 *element = OBJECT_TO_JSVAL(fieldObj);
3955 if (!JS_DefineUCProperty(cx, fieldObj,
3956 name->chars(), name->length(),
3957 OBJECT_TO_JSVAL(typeObj), NULL, NULL,
3958 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
3961 return JS_FreezeObject(cx, fieldObj);
3965 StructType::Create(JSContext* cx, uintN argc, jsval* vp)
3967 // Construct and return a new StructType object.
3968 if (argc < 1 || argc > 2) {
3969 JS_ReportError(cx, "StructType takes one or two arguments");
3973 jsval* argv = JS_ARGV(cx, vp);
3974 jsval name = argv[0];
3975 if (!JSVAL_IS_STRING(name)) {
3976 JS_ReportError(cx, "first argument must be a string");
3980 // Get ctypes.StructType.prototype from the ctypes.StructType constructor.
3981 JSObject* callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
3982 JSObject* typeProto = CType::GetProtoFromCtor(cx, callee, SLOT_STRUCTPROTO);
3984 // Create a simple StructType with no defined fields. The result will be
3985 // non-instantiable as CData, will have no 'prototype' property, and will
3986 // have undefined size and alignment and no ffi_type.
3987 JSObject* result = CType::Create(cx, typeProto, NULL, TYPE_struct,
3988 JSVAL_TO_STRING(name), JSVAL_VOID, JSVAL_VOID, NULL);
3991 js::AutoObjectRooter root(cx, result);
3994 if (JSVAL_IS_PRIMITIVE(argv[1]) ||
3995 !JS_IsArrayObject(cx, JSVAL_TO_OBJECT(argv[1]))) {
3996 JS_ReportError(cx, "second argument must be an array");
4000 // Define the struct fields.
4001 if (!DefineInternal(cx, result, JSVAL_TO_OBJECT(argv[1])))
4005 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
4010 StructType::DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj)
4013 ASSERT_OK(JS_GetArrayLength(cx, fieldsObj, &len));
4015 // Get the common prototype for CData objects of this type from
4016 // ctypes.CType.prototype.
4017 JSObject* dataProto =
4018 CType::GetProtoFromType(cx, typeObj, SLOT_STRUCTDATAPROTO);
4020 // Set up the 'prototype' and 'prototype.constructor' properties.
4021 // The prototype will reflect the struct fields as properties on CData objects
4022 // created from this type.
4023 JSObject* prototype = JS_NewObject(cx, &sCDataProtoClass, dataProto, NULL);
4026 js::AutoObjectRooter protoroot(cx, prototype);
4028 if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(typeObj),
4029 NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT))
4032 // Create a FieldInfoHash to stash on the type object, and an array to root
4033 // its constituents. (We cannot simply stash the hash in a reserved slot now
4034 // to get GC safety for free, since if anything in this function fails we
4035 // do not want to mutate 'typeObj'.)
4036 AutoPtr<FieldInfoHash> fields(js_new<FieldInfoHash>());
4037 Array<jsval, 16> fieldRootsArray;
4038 if (!fields || !fields->init(len) || !fieldRootsArray.appendN(JSVAL_VOID, len)) {
4039 JS_ReportOutOfMemory(cx);
4042 js::AutoArrayRooter fieldRoots(cx, fieldRootsArray.length(),
4043 fieldRootsArray.begin());
4045 // Process the field types.
4046 size_t structSize, structAlign;
4051 for (jsuint i = 0; i < len; ++i) {
4052 js::AutoValueRooter item(cx);
4053 if (!JS_GetElement(cx, fieldsObj, i, item.jsval_addr()))
4056 JSObject* fieldType = NULL;
4057 JSFlatString* name = ExtractStructField(cx, item.jsval_value(), &fieldType);
4060 fieldRootsArray[i] = OBJECT_TO_JSVAL(fieldType);
4062 // Make sure each field name is unique, and add it to the hash.
4063 FieldInfoHash::AddPtr entryPtr = fields->lookupForAdd(name);
4065 JS_ReportError(cx, "struct fields must have unique names");
4068 ASSERT_OK(fields->add(entryPtr, name, FieldInfo()));
4069 FieldInfo& info = entryPtr->value;
4070 info.mType = fieldType;
4073 // Add the field to the StructType's 'prototype' property.
4074 if (!JS_DefineUCProperty(cx, prototype,
4075 name->chars(), name->length(), JSVAL_VOID,
4076 StructType::FieldGetter, StructType::FieldSetter,
4077 JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_PERMANENT))
4080 size_t fieldSize = CType::GetSize(cx, fieldType);
4081 size_t fieldAlign = CType::GetAlignment(cx, fieldType);
4082 size_t fieldOffset = Align(structSize, fieldAlign);
4083 // Check for overflow. Since we hold invariant that fieldSize % fieldAlign
4084 // be zero, we can safely check fieldOffset + fieldSize without first
4085 // checking fieldOffset for overflow.
4086 if (fieldOffset + fieldSize < structSize) {
4087 JS_ReportError(cx, "size overflow");
4090 info.mOffset = fieldOffset;
4091 structSize = fieldOffset + fieldSize;
4093 if (fieldAlign > structAlign)
4094 structAlign = fieldAlign;
4097 // Pad the struct tail according to struct alignment.
4098 size_t structTail = Align(structSize, structAlign);
4099 if (structTail < structSize) {
4100 JS_ReportError(cx, "size overflow");
4103 structSize = structTail;
4106 // Empty structs are illegal in C, but are legal and have a size of
4107 // 1 byte in C++. We're going to allow them, and trick libffi into
4108 // believing this by adding a char member. The resulting struct will have
4109 // no getters or setters, and will be initialized to zero.
4115 if (!SizeTojsval(cx, structSize, &sizeVal))
4118 if (!JS_SetReservedSlot(cx, typeObj, SLOT_FIELDINFO,
4119 PRIVATE_TO_JSVAL(fields.get())))
4123 if (!JS_SetReservedSlot(cx, typeObj, SLOT_SIZE, sizeVal) ||
4124 !JS_SetReservedSlot(cx, typeObj, SLOT_ALIGN, INT_TO_JSVAL(structAlign)) ||
4125 //!JS_FreezeObject(cx, prototype) || // XXX fixme - see bug 541212!
4126 !JS_SetReservedSlot(cx, typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype)))
4133 StructType::BuildFFIType(JSContext* cx, JSObject* obj)
4135 JS_ASSERT(CType::IsCType(cx, obj));
4136 JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_struct);
4137 JS_ASSERT(CType::IsSizeDefined(cx, obj));
4139 const FieldInfoHash* fields = GetFieldInfo(cx, obj);
4140 size_t len = fields->count();
4142 size_t structSize = CType::GetSize(cx, obj);
4143 size_t structAlign = CType::GetAlignment(cx, obj);
4145 AutoPtr<ffi_type> ffiType(js_new<ffi_type>());
4147 JS_ReportOutOfMemory(cx);
4150 ffiType->type = FFI_TYPE_STRUCT;
4152 AutoPtr<ffi_type*>::Array elements;
4154 elements = js_array_new<ffi_type*>(len + 1);
4156 JS_ReportOutOfMemory(cx);
4159 elements[len] = NULL;
4161 for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
4162 const FieldInfoHash::Entry& entry = r.front();
4163 ffi_type* fieldType = CType::GetFFIType(cx, entry.value.mType);
4166 elements[entry.value.mIndex] = fieldType;
4170 // Represent an empty struct as having a size of 1 byte, just like C++.
4171 JS_ASSERT(structSize == 1);
4172 JS_ASSERT(structAlign == 1);
4173 elements = js_array_new<ffi_type*>(2);
4175 JS_ReportOutOfMemory(cx);
4178 elements[0] = &ffi_type_uint8;
4182 ffiType->elements = elements.get();
4185 // Perform a sanity check: the result of our struct size and alignment
4186 // calculations should match libffi's. We force it to do this calculation
4187 // by calling ffi_prep_cif.
4190 ffiType->alignment = 0;
4191 ffi_status status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 0, ffiType.get(), NULL);
4192 JS_ASSERT(status == FFI_OK);
4193 JS_ASSERT(structSize == ffiType->size);
4194 JS_ASSERT(structAlign == ffiType->alignment);
4196 // Fill in the ffi_type's size and align fields. This makes libffi treat the
4197 // type as initialized; it will not recompute the values. (We assume
4198 // everything agrees; if it doesn't, we really want to know about it, which
4199 // is the purpose of the above debug-only check.)
4200 ffiType->size = structSize;
4201 ffiType->alignment = structAlign;
4205 return ffiType.forget();
4209 StructType::Define(JSContext* cx, uintN argc, jsval* vp)
4211 JSObject* obj = JS_THIS_OBJECT(cx, vp);
4213 !CType::IsCType(cx, obj) ||
4214 CType::GetTypeCode(cx, obj) != TYPE_struct) {
4215 JS_ReportError(cx, "not a StructType");
4219 if (CType::IsSizeDefined(cx, obj)) {
4220 JS_ReportError(cx, "StructType has already been defined");
4225 JS_ReportError(cx, "define takes one argument");
4229 jsval arg = JS_ARGV(cx, vp)[0];
4230 if (JSVAL_IS_PRIMITIVE(arg) ||
4231 !JS_IsArrayObject(cx, JSVAL_TO_OBJECT(arg))) {
4232 JS_ReportError(cx, "argument must be an array");
4236 return DefineInternal(cx, obj, JSVAL_TO_OBJECT(arg));
4240 StructType::ConstructData(JSContext* cx,
4245 if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_struct) {
4246 JS_ReportError(cx, "not a StructType");
4250 if (!CType::IsSizeDefined(cx, obj)) {
4251 JS_ReportError(cx, "cannot construct an opaque StructType");
4255 JSObject* result = CData::Create(cx, obj, NULL, NULL, true);
4259 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
4264 char* buffer = static_cast<char*>(CData::GetData(cx, result));
4265 const FieldInfoHash* fields = GetFieldInfo(cx, obj);
4267 jsval* argv = JS_ARGV(cx, vp);
4269 // There are two possible interpretations of the argument:
4270 // 1) It may be an object '{ ... }' with properties representing the
4271 // struct fields intended to ExplicitConvert wholesale to our StructType.
4272 // 2) If the struct contains one field, the arg may be intended to
4273 // ImplicitConvert directly to that arg's CType.
4274 // Thankfully, the conditions for these two possibilities to succeed
4275 // are mutually exclusive, so we can pick the right one.
4277 // Try option 1) first.
4278 if (ExplicitConvert(cx, argv[0], obj, buffer))
4281 if (fields->count() != 1)
4284 // If ExplicitConvert failed, and there is no pending exception, then assume
4285 // hard failure (out of memory, or some other similarly serious condition).
4286 if (!JS_IsExceptionPending(cx))
4289 // Otherwise, assume soft failure, and clear the pending exception so that we
4290 // can throw a different one as required.
4291 JS_ClearPendingException(cx);
4293 // Fall through to try option 2).
4296 // We have a type constructor of the form 'ctypes.StructType(a, b, c, ...)'.
4297 // ImplicitConvert each field.
4298 if (argc == fields->count()) {
4299 for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
4300 const FieldInfo& field = r.front().value;
4301 STATIC_ASSUME(field.mIndex < fields->count()); /* Quantified invariant */
4302 if (!ImplicitConvert(cx, argv[field.mIndex], field.mType,
4303 buffer + field.mOffset,
4311 JS_ReportError(cx, "constructor takes 0, 1, or %u arguments",
4316 const FieldInfoHash*
4317 StructType::GetFieldInfo(JSContext* cx, JSObject* obj)
4319 JS_ASSERT(CType::IsCType(cx, obj));
4320 JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_struct);
4323 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FIELDINFO, &slot));
4324 JS_ASSERT(!JSVAL_IS_VOID(slot) && JSVAL_TO_PRIVATE(slot));
4326 return static_cast<const FieldInfoHash*>(JSVAL_TO_PRIVATE(slot));
4330 StructType::LookupField(JSContext* cx, JSObject* obj, JSFlatString *name)
4332 JS_ASSERT(CType::IsCType(cx, obj));
4333 JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_struct);
4335 FieldInfoHash::Ptr ptr = GetFieldInfo(cx, obj)->lookup(name);
4339 JSAutoByteString bytes(cx, name);
4343 JS_ReportError(cx, "%s does not name a field", bytes.ptr());
4348 StructType::BuildFieldsArray(JSContext* cx, JSObject* obj)
4350 JS_ASSERT(CType::IsCType(cx, obj));
4351 JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_struct);
4352 JS_ASSERT(CType::IsSizeDefined(cx, obj));
4354 const FieldInfoHash* fields = GetFieldInfo(cx, obj);
4355 size_t len = fields->count();
4357 // Prepare a new array for the 'fields' property of the StructType.
4358 Array<jsval, 16> fieldsVec;
4359 if (!fieldsVec.appendN(JSVAL_VOID, len))
4361 js::AutoArrayRooter root(cx, fieldsVec.length(), fieldsVec.begin());
4363 for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
4364 const FieldInfoHash::Entry& entry = r.front();
4365 // Add the field descriptor to the array.
4366 if (!AddFieldToArray(cx, &fieldsVec[entry.value.mIndex],
4367 entry.key, entry.value.mType))
4371 JSObject* fieldsProp = JS_NewArrayObject(cx, len, fieldsVec.begin());
4375 // Seal the fields array.
4376 if (!JS_FreezeObject(cx, fieldsProp))
4383 StructType::FieldsArrayGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
4385 if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_struct) {
4386 JS_ReportError(cx, "not a StructType");
4390 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FIELDS, vp));
4392 if (!CType::IsSizeDefined(cx, obj)) {
4393 JS_ASSERT(JSVAL_IS_VOID(*vp));
4397 if (JSVAL_IS_VOID(*vp)) {
4398 // Build the 'fields' array lazily.
4399 JSObject* fields = BuildFieldsArray(cx, obj);
4401 !JS_SetReservedSlot(cx, obj, SLOT_FIELDS, OBJECT_TO_JSVAL(fields)))
4404 *vp = OBJECT_TO_JSVAL(fields);
4407 JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp) &&
4408 JS_IsArrayObject(cx, JSVAL_TO_OBJECT(*vp)));
4413 StructType::FieldGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
4415 if (!CData::IsCData(cx, obj)) {
4416 JS_ReportError(cx, "not a CData");
4420 JSObject* typeObj = CData::GetCType(cx, obj);
4421 if (CType::GetTypeCode(cx, typeObj) != TYPE_struct) {
4422 JS_ReportError(cx, "not a StructType");
4426 const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_FLAT_STRING(idval));
4430 char* data = static_cast<char*>(CData::GetData(cx, obj)) + field->mOffset;
4431 return ConvertToJS(cx, field->mType, obj, data, false, false, vp);
4435 StructType::FieldSetter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict, jsval* vp)
4437 if (!CData::IsCData(cx, obj)) {
4438 JS_ReportError(cx, "not a CData");
4442 JSObject* typeObj = CData::GetCType(cx, obj);
4443 if (CType::GetTypeCode(cx, typeObj) != TYPE_struct) {
4444 JS_ReportError(cx, "not a StructType");
4448 const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_FLAT_STRING(idval));
4452 char* data = static_cast<char*>(CData::GetData(cx, obj)) + field->mOffset;
4453 return ImplicitConvert(cx, *vp, field->mType, data, false, NULL);
4457 StructType::AddressOfField(JSContext* cx, uintN argc, jsval* vp)
4459 JSObject* obj = JS_THIS_OBJECT(cx, vp);
4460 if (!obj || !CData::IsCData(cx, obj)) {
4461 JS_ReportError(cx, "not a CData");
4465 JSObject* typeObj = CData::GetCType(cx, obj);
4466 if (CType::GetTypeCode(cx, typeObj) != TYPE_struct) {
4467 JS_ReportError(cx, "not a StructType");
4472 JS_ReportError(cx, "addressOfField takes one argument");
4476 JSFlatString *str = JS_FlattenString(cx, JSVAL_TO_STRING(JS_ARGV(cx, vp)[0]));
4480 const FieldInfo* field = LookupField(cx, typeObj, str);
4484 JSObject* baseType = field->mType;
4485 JSObject* pointerType = PointerType::CreateInternal(cx, baseType);
4488 js::AutoObjectRooter root(cx, pointerType);
4490 // Create a PointerType CData object containing null.
4491 JSObject* result = CData::Create(cx, pointerType, NULL, NULL, true);
4495 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
4497 // Manually set the pointer inside the object, so we skip the conversion step.
4498 void** data = static_cast<void**>(CData::GetData(cx, result));
4499 *data = static_cast<char*>(CData::GetData(cx, obj)) + field->mOffset;
4503 /*******************************************************************************
4504 ** FunctionType implementation
4505 *******************************************************************************/
4507 // Helper class for handling allocation of function arguments.
4510 AutoValue() : mData(NULL) { }
4514 js_array_delete(static_cast<char*>(mData));
4517 bool SizeToType(JSContext* cx, JSObject* type)
4519 // Allocate a minimum of sizeof(ffi_arg) to handle small integers.
4520 size_t size = Align(CType::GetSize(cx, type), sizeof(ffi_arg));
4521 mData = js_array_new<char>(size);
4523 memset(mData, 0, size);
4524 return mData != NULL;
4531 GetABI(JSContext* cx, jsval abiType, ffi_abi* result)
4533 if (JSVAL_IS_PRIMITIVE(abiType))
4536 ABICode abi = GetABICode(cx, JSVAL_TO_OBJECT(abiType));
4538 // determine the ABI from the subset of those available on the
4539 // given platform. ABI_DEFAULT specifies the default
4540 // C calling convention (cdecl) on each platform.
4543 *result = FFI_DEFAULT_ABI;
4547 #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
4548 *result = FFI_STDCALL;
4558 PrepareType(JSContext* cx, jsval type)
4560 if (JSVAL_IS_PRIMITIVE(type) ||
4561 !CType::IsCType(cx, JSVAL_TO_OBJECT(type))) {
4562 JS_ReportError(cx, "not a ctypes type");
4566 JSObject* result = JSVAL_TO_OBJECT(type);
4567 TypeCode typeCode = CType::GetTypeCode(cx, result);
4569 if (typeCode == TYPE_array) {
4570 // convert array argument types to pointers, just like C.
4571 // ImplicitConvert will do the same, when passing an array as data.
4572 JSObject* baseType = ArrayType::GetBaseType(cx, result);
4573 result = PointerType::CreateInternal(cx, baseType);
4577 } else if (typeCode == TYPE_void_t || typeCode == TYPE_function) {
4578 // disallow void or function argument types
4579 JS_ReportError(cx, "Cannot have void or function argument type");
4583 if (!CType::IsSizeDefined(cx, result)) {
4584 JS_ReportError(cx, "Argument type must have defined size");
4588 // libffi cannot pass types of zero size by value.
4589 JS_ASSERT(CType::GetSize(cx, result) != 0);
4595 PrepareReturnType(JSContext* cx, jsval type)
4597 if (JSVAL_IS_PRIMITIVE(type) ||
4598 !CType::IsCType(cx, JSVAL_TO_OBJECT(type))) {
4599 JS_ReportError(cx, "not a ctypes type");
4603 JSObject* result = JSVAL_TO_OBJECT(type);
4604 TypeCode typeCode = CType::GetTypeCode(cx, result);
4606 // Arrays and functions can never be return types.
4607 if (typeCode == TYPE_array || typeCode == TYPE_function) {
4608 JS_ReportError(cx, "Return type cannot be an array or function");
4612 if (typeCode != TYPE_void_t && !CType::IsSizeDefined(cx, result)) {
4613 JS_ReportError(cx, "Return type must have defined size");
4617 // libffi cannot pass types of zero size by value.
4618 JS_ASSERT(typeCode == TYPE_void_t || CType::GetSize(cx, result) != 0);
4623 static JS_ALWAYS_INLINE JSBool
4624 IsEllipsis(JSContext* cx, jsval v, bool* isEllipsis)
4626 *isEllipsis = false;
4627 if (!JSVAL_IS_STRING(v))
4629 JSString* str = JSVAL_TO_STRING(v);
4630 if (str->length() != 3)
4632 const jschar* chars = str->getChars(cx);
4636 *isEllipsis = (chars[0] == dot &&
4643 PrepareCIF(JSContext* cx,
4644 FunctionInfo* fninfo)
4647 if (!GetABI(cx, OBJECT_TO_JSVAL(fninfo->mABI), &abi)) {
4648 JS_ReportError(cx, "Invalid ABI specification");
4652 ffi_type* rtype = CType::GetFFIType(cx, fninfo->mReturnType);
4657 ffi_prep_cif(&fninfo->mCIF,
4659 fninfo->mFFITypes.length(),
4661 fninfo->mFFITypes.begin());
4667 JS_ReportError(cx, "Invalid ABI specification");
4669 case FFI_BAD_TYPEDEF:
4670 JS_ReportError(cx, "Invalid type specification");
4673 JS_ReportError(cx, "Unknown libffi error");
4679 FunctionType::BuildSymbolName(JSContext* cx,
4682 AutoCString& result)
4684 FunctionInfo* fninfo = GetFunctionInfo(cx, typeObj);
4686 switch (GetABICode(cx, fninfo->mABI)) {
4689 // For cdecl or WINAPI functions, no mangling is necessary.
4690 AppendString(result, name);
4694 // On WIN32, stdcall functions look like:
4696 // where 'foo' is the function name, and '40' is the aligned size of the
4698 AppendString(result, "_");
4699 AppendString(result, name);
4700 AppendString(result, "@");
4702 // Compute the suffix by aligning each argument to sizeof(ffi_arg).
4704 for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
4705 JSObject* argType = fninfo->mArgTypes[i];
4706 size += Align(CType::GetSize(cx, argType), sizeof(ffi_arg));
4709 IntegerToString(size, 10, result);
4714 JS_NOT_REACHED("invalid abi");
4719 static FunctionInfo*
4720 NewFunctionInfo(JSContext* cx,
4726 AutoPtr<FunctionInfo> fninfo(js_new<FunctionInfo>());
4728 JS_ReportOutOfMemory(cx);
4733 if (!GetABI(cx, abiType, &abi)) {
4734 JS_ReportError(cx, "Invalid ABI specification");
4737 fninfo->mABI = JSVAL_TO_OBJECT(abiType);
4739 // prepare the result type
4740 fninfo->mReturnType = PrepareReturnType(cx, returnType);
4741 if (!fninfo->mReturnType)
4744 // prepare the argument types
4745 if (!fninfo->mArgTypes.reserve(argLength) ||
4746 !fninfo->mFFITypes.reserve(argLength)) {
4747 JS_ReportOutOfMemory(cx);
4751 fninfo->mIsVariadic = false;
4753 for (JSUint32 i = 0; i < argLength; ++i) {
4755 if (!IsEllipsis(cx, argTypes[i], &isEllipsis))
4758 fninfo->mIsVariadic = true;
4760 JS_ReportError(cx, "\"...\" may not be the first and only parameter "
4761 "type of a variadic function declaration");
4764 if (i < argLength - 1) {
4765 JS_ReportError(cx, "\"...\" must be the last parameter type of a "
4766 "variadic function declaration");
4769 if (GetABICode(cx, fninfo->mABI) != ABI_DEFAULT) {
4770 JS_ReportError(cx, "Variadic functions must use the __cdecl calling "
4777 JSObject* argType = PrepareType(cx, argTypes[i]);
4781 ffi_type* ffiType = CType::GetFFIType(cx, argType);
4785 fninfo->mArgTypes.append(argType);
4786 fninfo->mFFITypes.append(ffiType);
4789 if (fninfo->mIsVariadic)
4790 // wait to PrepareCIF until function is called
4791 return fninfo.forget();
4793 if (!PrepareCIF(cx, fninfo.get()))
4796 return fninfo.forget();
4800 FunctionType::Create(JSContext* cx, uintN argc, jsval* vp)
4802 // Construct and return a new FunctionType object.
4803 if (argc < 2 || argc > 3) {
4804 JS_ReportError(cx, "FunctionType takes two or three arguments");
4808 jsval* argv = JS_ARGV(cx, vp);
4809 Array<jsval, 16> argTypes;
4810 JSObject* arrayObj = NULL;
4813 // Prepare an array of jsvals for the arguments.
4814 if (JSVAL_IS_PRIMITIVE(argv[2]) ||
4815 !JS_IsArrayObject(cx, JSVAL_TO_OBJECT(argv[2]))) {
4816 JS_ReportError(cx, "third argument must be an array");
4820 arrayObj = JSVAL_TO_OBJECT(argv[2]);
4822 ASSERT_OK(JS_GetArrayLength(cx, arrayObj, &len));
4824 if (!argTypes.appendN(JSVAL_VOID, len)) {
4825 JS_ReportOutOfMemory(cx);
4830 // Pull out the argument types from the array, if any.
4831 JS_ASSERT(!argTypes.length() || arrayObj);
4832 js::AutoArrayRooter items(cx, argTypes.length(), argTypes.begin());
4833 for (jsuint i = 0; i < argTypes.length(); ++i) {
4834 if (!JS_GetElement(cx, arrayObj, i, &argTypes[i]))
4838 JSObject* result = CreateInternal(cx, argv[0], argv[1],
4839 argTypes.begin(), argTypes.length());
4843 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
4848 FunctionType::CreateInternal(JSContext* cx,
4854 // Determine and check the types, and prepare the function CIF.
4855 AutoPtr<FunctionInfo> fninfo(NewFunctionInfo(cx, abi, rtype, argtypes, arglen));
4859 // Get ctypes.FunctionType.prototype and the common prototype for CData objects
4860 // of this type, from ctypes.CType.prototype.
4861 JSObject* typeProto = CType::GetProtoFromType(cx, fninfo->mReturnType,
4862 SLOT_FUNCTIONPROTO);
4863 JSObject* dataProto = CType::GetProtoFromType(cx, fninfo->mReturnType,
4864 SLOT_FUNCTIONDATAPROTO);
4866 // Create a new CType object with the common properties and slots.
4867 JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_function,
4868 NULL, JSVAL_VOID, JSVAL_VOID, NULL);
4871 js::AutoObjectRooter root(cx, typeObj);
4873 // Stash the FunctionInfo in a reserved slot.
4874 if (!JS_SetReservedSlot(cx, typeObj, SLOT_FNINFO,
4875 PRIVATE_TO_JSVAL(fninfo.get())))
4882 // Construct a function pointer to a JS function (see CClosure::Create()).
4883 // Regular function pointers are constructed directly in
4884 // PointerType::ConstructData().
4886 FunctionType::ConstructData(JSContext* cx,
4892 JS_ASSERT(CType::GetTypeCode(cx, typeObj) == TYPE_function);
4894 PRFuncPtr* data = static_cast<PRFuncPtr*>(CData::GetData(cx, dataObj));
4896 FunctionInfo* fninfo = FunctionType::GetFunctionInfo(cx, typeObj);
4897 if (fninfo->mIsVariadic) {
4898 JS_ReportError(cx, "Can't declare a variadic callback function");
4901 if (GetABICode(cx, fninfo->mABI) == ABI_WINAPI) {
4902 JS_ReportError(cx, "Can't declare a ctypes.winapi_abi callback function, "
4903 "use ctypes.stdcall_abi instead");
4907 JSObject* closureObj = CClosure::Create(cx, typeObj, fnObj, thisObj, data);
4910 js::AutoObjectRooter root(cx, closureObj);
4912 // Set the closure object as the referent of the new CData object.
4913 if (!JS_SetReservedSlot(cx, dataObj, SLOT_REFERENT,
4914 OBJECT_TO_JSVAL(closureObj)))
4917 // Seal the CData object, to prevent modification of the function pointer.
4918 // This permanently associates this object with the closure, and avoids
4919 // having to do things like reset SLOT_REFERENT when someone tries to
4920 // change the pointer value.
4921 // XXX This will need to change when bug 541212 is fixed -- CData::ValueSetter
4922 // could be called on a frozen object.
4923 return JS_FreezeObject(cx, dataObj);
4926 typedef Array<AutoValue, 16> AutoValueAutoArray;
4929 ConvertArgument(JSContext* cx,
4933 AutoValueAutoArray* strings)
4935 if (!value->SizeToType(cx, type)) {
4936 JS_ReportAllocationOverflow(cx);
4940 bool freePointer = false;
4941 if (!ImplicitConvert(cx, arg, type, value->mData, true, &freePointer))
4945 // ImplicitConvert converted a string for us, which we have to free.
4946 // Keep track of it.
4947 if (!strings->growBy(1)) {
4948 JS_ReportOutOfMemory(cx);
4951 strings->back().mData = *static_cast<char**>(value->mData);
4958 FunctionType::Call(JSContext* cx,
4962 // get the callee object...
4963 JSObject* obj = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
4964 if (!CData::IsCData(cx, obj)) {
4965 JS_ReportError(cx, "not a CData");
4969 JSObject* typeObj = CData::GetCType(cx, obj);
4970 if (CType::GetTypeCode(cx, typeObj) != TYPE_pointer) {
4971 JS_ReportError(cx, "not a FunctionType.ptr");
4975 typeObj = PointerType::GetBaseType(cx, typeObj);
4976 if (CType::GetTypeCode(cx, typeObj) != TYPE_function) {
4977 JS_ReportError(cx, "not a FunctionType.ptr");
4981 FunctionInfo* fninfo = GetFunctionInfo(cx, typeObj);
4982 JSUint32 argcFixed = fninfo->mArgTypes.length();
4984 if ((!fninfo->mIsVariadic && argc != argcFixed) ||
4985 (fninfo->mIsVariadic && argc < argcFixed)) {
4986 JS_ReportError(cx, "Number of arguments does not match declaration");
4990 // Check if we have a Library object. If we do, make sure it's open.
4992 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_REFERENT, &slot));
4993 if (!JSVAL_IS_VOID(slot) && Library::IsLibrary(cx, JSVAL_TO_OBJECT(slot))) {
4994 PRLibrary* library = Library::GetLibrary(cx, JSVAL_TO_OBJECT(slot));
4996 JS_ReportError(cx, "library is not open");
5001 // prepare the values for each argument
5002 AutoValueAutoArray values;
5003 AutoValueAutoArray strings;
5004 if (!values.resize(argc)) {
5005 JS_ReportOutOfMemory(cx);
5009 jsval* argv = JS_ARGV(cx, vp);
5010 for (jsuint i = 0; i < argcFixed; ++i)
5011 if (!ConvertArgument(cx, argv[i], fninfo->mArgTypes[i], &values[i], &strings))
5014 if (fninfo->mIsVariadic) {
5015 if (!fninfo->mFFITypes.resize(argc)) {
5016 JS_ReportOutOfMemory(cx);
5020 JSObject* obj; // Could reuse obj instead of declaring a second
5021 JSObject* type; // JSObject*, but readability would suffer.
5023 for (JSUint32 i = argcFixed; i < argc; ++i) {
5024 if (JSVAL_IS_PRIMITIVE(argv[i]) ||
5025 !CData::IsCData(cx, obj = JSVAL_TO_OBJECT(argv[i]))) {
5026 // Since we know nothing about the CTypes of the ... arguments,
5027 // they absolutely must be CData objects already.
5028 JS_ReportError(cx, "argument %d of type %s is not a CData object",
5029 i, JS_GetTypeName(cx, JS_TypeOfValue(cx, argv[i])));
5032 if (!(type = CData::GetCType(cx, obj)) ||
5033 !(type = PrepareType(cx, OBJECT_TO_JSVAL(type))) ||
5034 // Relying on ImplicitConvert only for the limited purpose of
5035 // converting one CType to another (e.g., T[] to T*).
5036 !ConvertArgument(cx, argv[i], type, &values[i], &strings) ||
5037 !(fninfo->mFFITypes[i] = CType::GetFFIType(cx, type))) {
5038 // These functions report their own errors.
5042 if (!PrepareCIF(cx, fninfo))
5046 // initialize a pointer to an appropriate location, for storing the result
5047 AutoValue returnValue;
5048 TypeCode typeCode = CType::GetTypeCode(cx, fninfo->mReturnType);
5049 if (typeCode != TYPE_void_t &&
5050 !returnValue.SizeToType(cx, fninfo->mReturnType)) {
5051 JS_ReportAllocationOverflow(cx);
5055 uintptr_t fn = *reinterpret_cast<uintptr_t*>(CData::GetData(cx, obj));
5057 // suspend the request before we call into the function, since the call
5058 // may block or otherwise take a long time to return.
5060 JSAutoSuspendRequest suspend(cx);
5061 ffi_call(&fninfo->mCIF, FFI_FN(fn), returnValue.mData,
5062 reinterpret_cast<void**>(values.begin()));
5065 // Small integer types get returned as a word-sized ffi_arg. Coerce it back
5066 // into the correct size for ConvertToJS.
5068 #define DEFINE_INT_TYPE(name, type, ffiType) \
5070 if (sizeof(type) < sizeof(ffi_arg)) { \
5071 ffi_arg data = *static_cast<ffi_arg*>(returnValue.mData); \
5072 *static_cast<type*>(returnValue.mData) = static_cast<type>(data); \
5075 #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
5076 #define DEFINE_BOOL_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
5077 #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
5078 #define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
5079 #include "typedefs.h"
5084 // prepare a JS object from the result
5085 return ConvertToJS(cx, fninfo->mReturnType, NULL, returnValue.mData,
5090 FunctionType::GetFunctionInfo(JSContext* cx, JSObject* obj)
5092 JS_ASSERT(CType::IsCType(cx, obj));
5093 JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_function);
5096 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FNINFO, &slot));
5097 JS_ASSERT(!JSVAL_IS_VOID(slot) && JSVAL_TO_PRIVATE(slot));
5099 return static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot));
5103 CheckFunctionType(JSContext* cx, JSObject* obj)
5105 if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_function) {
5106 JS_ReportError(cx, "not a FunctionType");
5113 FunctionType::ArgTypesGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
5115 if (!CheckFunctionType(cx, obj))
5118 // Check if we have a cached argTypes array.
5119 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_ARGS_T, vp));
5120 if (!JSVAL_IS_VOID(*vp))
5123 FunctionInfo* fninfo = GetFunctionInfo(cx, obj);
5124 size_t len = fninfo->mArgTypes.length();
5126 // Prepare a new array.
5127 Array<jsval, 16> vec;
5128 if (!vec.resize(len))
5131 for (size_t i = 0; i < len; ++i)
5132 vec[i] = OBJECT_TO_JSVAL(fninfo->mArgTypes[i]);
5134 JSObject* argTypes = JS_NewArrayObject(cx, len, vec.begin());
5138 // Seal and cache it.
5139 if (!JS_FreezeObject(cx, argTypes) ||
5140 !JS_SetReservedSlot(cx, obj, SLOT_ARGS_T, OBJECT_TO_JSVAL(argTypes)))
5143 *vp = OBJECT_TO_JSVAL(argTypes);
5148 FunctionType::ReturnTypeGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
5150 if (!CheckFunctionType(cx, obj))
5153 // Get the returnType object from the FunctionInfo.
5154 *vp = OBJECT_TO_JSVAL(GetFunctionInfo(cx, obj)->mReturnType);
5159 FunctionType::ABIGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
5161 if (!CheckFunctionType(cx, obj))
5164 // Get the abi object from the FunctionInfo.
5165 *vp = OBJECT_TO_JSVAL(GetFunctionInfo(cx, obj)->mABI);
5170 FunctionType::IsVariadicGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
5172 if (!CheckFunctionType(cx, obj))
5175 *vp = BOOLEAN_TO_JSVAL(GetFunctionInfo(cx, obj)->mIsVariadic);
5179 /*******************************************************************************
5180 ** CClosure implementation
5181 *******************************************************************************/
5184 CClosure::Create(JSContext* cx,
5192 JSObject* result = JS_NewObject(cx, &sCClosureClass, NULL, NULL);
5195 js::AutoObjectRooter root(cx, result);
5197 // Get the FunctionInfo from the FunctionType.
5198 FunctionInfo* fninfo = FunctionType::GetFunctionInfo(cx, typeObj);
5199 JS_ASSERT(!fninfo->mIsVariadic);
5200 JS_ASSERT(GetABICode(cx, fninfo->mABI) != ABI_WINAPI);
5202 AutoPtr<ClosureInfo> cinfo(js_new<ClosureInfo>());
5204 JS_ReportOutOfMemory(cx);
5208 // Get the prototype of the FunctionType object, of class CTypeProto,
5209 // which stores our JSContext for use with the closure.
5210 JSObject* proto = JS_GetPrototype(cx, typeObj);
5212 JS_ASSERT(JS_GET_CLASS(cx, proto) == &sCTypeProtoClass);
5214 // Get a JSContext for use with the closure.
5216 ASSERT_OK(JS_GetReservedSlot(cx, proto, SLOT_CLOSURECX, &slot));
5217 if (!JSVAL_IS_VOID(slot)) {
5218 // Use the existing JSContext.
5219 cinfo->cx = static_cast<JSContext*>(JSVAL_TO_PRIVATE(slot));
5220 JS_ASSERT(cinfo->cx);
5222 // Lazily instantiate a new JSContext, and stash it on
5223 // ctypes.FunctionType.prototype.
5224 JSRuntime* runtime = JS_GetRuntime(cx);
5225 cinfo->cx = JS_NewContext(runtime, 8192);
5227 JS_ReportOutOfMemory(cx);
5231 if (!JS_SetReservedSlot(cx, proto, SLOT_CLOSURECX,
5232 PRIVATE_TO_JSVAL(cinfo->cx))) {
5233 JS_DestroyContextNoGC(cinfo->cx);
5237 JS_ClearContextThread(cinfo->cx);
5241 // We want *this* context's thread here so use cx instead of cinfo->cx.
5242 cinfo->cxThread = JS_GetContextThread(cx);
5245 cinfo->closureObj = result;
5246 cinfo->typeObj = typeObj;
5247 cinfo->thisObj = thisObj;
5248 cinfo->jsfnObj = fnObj;
5250 // Create an ffi_closure object and initialize it.
5253 static_cast<ffi_closure*>(ffi_closure_alloc(sizeof(ffi_closure), &code));
5254 if (!cinfo->closure || !code) {
5255 JS_ReportError(cx, "couldn't create closure - libffi error");
5259 ffi_status status = ffi_prep_closure_loc(cinfo->closure, &fninfo->mCIF,
5260 CClosure::ClosureStub, cinfo.get(), code);
5261 if (status != FFI_OK) {
5262 ffi_closure_free(cinfo->closure);
5263 JS_ReportError(cx, "couldn't create closure - libffi error");
5267 // Stash the ClosureInfo struct on our new object.
5268 if (!JS_SetReservedSlot(cx, result, SLOT_CLOSUREINFO,
5269 PRIVATE_TO_JSVAL(cinfo.get()))) {
5270 ffi_closure_free(cinfo->closure);
5275 // Casting between void* and a function pointer is forbidden in C and C++.
5276 // Do it via an integral type.
5277 *fnptr = reinterpret_cast<PRFuncPtr>(reinterpret_cast<uintptr_t>(code));
5282 CClosure::Trace(JSTracer* trc, JSObject* obj)
5284 JSContext* cx = trc->context;
5286 // Make sure our ClosureInfo slot is legit. If it's not, bail.
5288 if (!JS_GetReservedSlot(cx, obj, SLOT_CLOSUREINFO, &slot) ||
5289 JSVAL_IS_VOID(slot))
5292 ClosureInfo* cinfo = static_cast<ClosureInfo*>(JSVAL_TO_PRIVATE(slot));
5294 // Identify our objects to the tracer. (There's no need to identify
5295 // 'closureObj', since that's us.)
5296 JS_CALL_OBJECT_TRACER(trc, cinfo->typeObj, "typeObj");
5297 JS_CALL_OBJECT_TRACER(trc, cinfo->jsfnObj, "jsfnObj");
5299 JS_CALL_OBJECT_TRACER(trc, cinfo->thisObj, "thisObj");
5303 CClosure::Finalize(JSContext* cx, JSObject* obj)
5305 // Make sure our ClosureInfo slot is legit. If it's not, bail.
5307 if (!JS_GetReservedSlot(cx, obj, SLOT_CLOSUREINFO, &slot) ||
5308 JSVAL_IS_VOID(slot))
5311 ClosureInfo* cinfo = static_cast<ClosureInfo*>(JSVAL_TO_PRIVATE(slot));
5313 ffi_closure_free(cinfo->closure);
5319 CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData)
5324 JS_ASSERT(userData);
5326 // Retrieve the essentials from our closure object.
5327 ClosureInfo* cinfo = static_cast<ClosureInfo*>(userData);
5328 JSContext* cx = cinfo->cx;
5329 JSObject* typeObj = cinfo->typeObj;
5330 JSObject* thisObj = cinfo->thisObj;
5331 JSObject* jsfnObj = cinfo->jsfnObj;
5333 ScopedContextThread scopedThread(cx);
5335 // Assert that we're on the thread we were created from.
5336 JS_ASSERT(cinfo->cxThread == JS_GetContextThread(cx));
5338 JSAutoRequest ar(cx);
5340 JSAutoEnterCompartment ac;
5341 if (!ac.enter(cx, jsfnObj))
5344 // Assert that our CIFs agree.
5345 FunctionInfo* fninfo = FunctionType::GetFunctionInfo(cx, typeObj);
5346 JS_ASSERT(cif == &fninfo->mCIF);
5348 TypeCode typeCode = CType::GetTypeCode(cx, fninfo->mReturnType);
5350 // Initialize the result to zero, in case something fails. Small integer types
5351 // are promoted to a word-sized ffi_arg, so we must be careful to zero the
5353 if (cif->rtype != &ffi_type_void) {
5354 size_t size = cif->rtype->size;
5356 #define DEFINE_INT_TYPE(name, type, ffiType) \
5358 #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
5359 #define DEFINE_BOOL_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
5360 #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
5361 #define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
5362 #include "typedefs.h"
5363 size = Align(size, sizeof(ffi_arg));
5368 memset(result, 0, size);
5371 // Get a death grip on 'closureObj'.
5372 js::AutoObjectRooter root(cx, cinfo->closureObj);
5374 // Set up an array for converted arguments.
5375 Array<jsval, 16> argv;
5376 if (!argv.appendN(JSVAL_VOID, cif->nargs)) {
5377 JS_ReportOutOfMemory(cx);
5381 js::AutoArrayRooter roots(cx, argv.length(), argv.begin());
5382 for (JSUint32 i = 0; i < cif->nargs; ++i) {
5383 // Convert each argument, and have any CData objects created depend on
5384 // the existing buffers.
5385 if (!ConvertToJS(cx, fninfo->mArgTypes[i], NULL, args[i], false, false,
5390 // Call the JS function. 'thisObj' may be NULL, in which case the JS engine
5391 // will find an appropriate object to use.
5393 if (!JS_CallFunctionValue(cx, thisObj, OBJECT_TO_JSVAL(jsfnObj), cif->nargs,
5394 argv.begin(), &rval))
5397 // Convert the result. Note that we pass 'isArgument = false', such that
5398 // ImplicitConvert will *not* autoconvert a JS string into a pointer-to-char
5399 // type, which would require an allocation that we can't track. The JS
5400 // function must perform this conversion itself and return a PointerType
5401 // CData; thusly, the burden of freeing the data is left to the user.
5402 if (!ImplicitConvert(cx, rval, fninfo->mReturnType, result, false, NULL))
5405 // Small integer types must be returned as a word-sized ffi_arg. Coerce it
5406 // back into the size libffi expects.
5408 #define DEFINE_INT_TYPE(name, type, ffiType) \
5410 if (sizeof(type) < sizeof(ffi_arg)) { \
5411 ffi_arg data = *static_cast<type*>(result); \
5412 *static_cast<ffi_arg*>(result) = data; \
5415 #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
5416 #define DEFINE_BOOL_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
5417 #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
5418 #define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
5419 #include "typedefs.h"
5425 /*******************************************************************************
5426 ** CData implementation
5427 *******************************************************************************/
5429 // Create a new CData object of type 'typeObj' containing binary data supplied
5430 // in 'source', optionally with a referent object 'refObj'.
5432 // * 'typeObj' must be a CType of defined (but possibly zero) size.
5434 // * If an object 'refObj' is supplied, the new CData object stores the
5435 // referent object in a reserved slot for GC safety, such that 'refObj' will
5436 // be held alive by the resulting CData object. 'refObj' may or may not be
5437 // a CData object; merely an object we want to keep alive.
5438 // * If 'refObj' is a CData object, 'ownResult' must be false.
5439 // * Otherwise, 'refObj' is a Library or CClosure object, and 'ownResult'
5440 // may be true or false.
5441 // * Otherwise 'refObj' is NULL. In this case, 'ownResult' may be true or false.
5443 // * If 'ownResult' is true, the CData object will allocate an appropriately
5444 // sized buffer, and free it upon finalization. If 'source' data is
5445 // supplied, the data will be copied from 'source' into the buffer;
5446 // otherwise, the entirety of the new buffer will be initialized to zero.
5447 // * If 'ownResult' is false, the new CData's buffer refers to a slice of
5448 // another buffer kept alive by 'refObj'. 'source' data must be provided,
5449 // and the new CData's buffer will refer to 'source'.
5451 CData::Create(JSContext* cx,
5458 JS_ASSERT(CType::IsCType(cx, typeObj));
5459 JS_ASSERT(CType::IsSizeDefined(cx, typeObj));
5460 JS_ASSERT(ownResult || source);
5461 JS_ASSERT_IF(refObj && CData::IsCData(cx, refObj), !ownResult);
5463 // Get the 'prototype' property from the type.
5465 ASSERT_OK(JS_GetReservedSlot(cx, typeObj, SLOT_PROTO, &slot));
5466 JS_ASSERT(!JSVAL_IS_PRIMITIVE(slot));
5468 JSObject* proto = JSVAL_TO_OBJECT(slot);
5469 JSObject* parent = JS_GetParent(cx, typeObj);
5472 JSObject* dataObj = JS_NewObject(cx, &sCDataClass, proto, parent);
5475 js::AutoObjectRooter root(cx, dataObj);
5477 // set the CData's associated type
5478 if (!JS_SetReservedSlot(cx, dataObj, SLOT_CTYPE, OBJECT_TO_JSVAL(typeObj)))
5481 // Stash the referent object, if any, for GC safety.
5483 !JS_SetReservedSlot(cx, dataObj, SLOT_REFERENT, OBJECT_TO_JSVAL(refObj)))
5486 // Set our ownership flag.
5487 if (!JS_SetReservedSlot(cx, dataObj, SLOT_OWNS, BOOLEAN_TO_JSVAL(ownResult)))
5490 // attach the buffer. since it might not be 2-byte aligned, we need to
5491 // allocate an aligned space for it and store it there. :(
5492 char** buffer = js_new<char*>();
5494 JS_ReportOutOfMemory(cx);
5500 data = static_cast<char*>(source);
5502 // Initialize our own buffer.
5503 size_t size = CType::GetSize(cx, typeObj);
5504 data = js_array_new<char>(size);
5506 // Report a catchable allocation error.
5507 JS_ReportAllocationOverflow(cx);
5513 memset(data, 0, size);
5515 memcpy(data, source, size);
5519 if (!JS_SetReservedSlot(cx, dataObj, SLOT_DATA, PRIVATE_TO_JSVAL(buffer))) {
5521 js_array_delete(data);
5530 CData::Finalize(JSContext* cx, JSObject* obj)
5532 // Delete our buffer, and the data it contains if we own it.
5534 if (!JS_GetReservedSlot(cx, obj, SLOT_OWNS, &slot) || JSVAL_IS_VOID(slot))
5537 JSBool owns = JSVAL_TO_BOOLEAN(slot);
5539 if (!JS_GetReservedSlot(cx, obj, SLOT_DATA, &slot) || JSVAL_IS_VOID(slot))
5541 char** buffer = static_cast<char**>(JSVAL_TO_PRIVATE(slot));
5544 js_array_delete(*buffer);
5549 CData::GetCType(JSContext* cx, JSObject* dataObj)
5551 JS_ASSERT(CData::IsCData(cx, dataObj));
5554 ASSERT_OK(JS_GetReservedSlot(cx, dataObj, SLOT_CTYPE, &slot));
5555 JSObject* typeObj = JSVAL_TO_OBJECT(slot);
5556 JS_ASSERT(CType::IsCType(cx, typeObj));
5561 CData::GetData(JSContext* cx, JSObject* dataObj)
5563 JS_ASSERT(CData::IsCData(cx, dataObj));
5566 ASSERT_OK(JS_GetReservedSlot(cx, dataObj, SLOT_DATA, &slot));
5568 void** buffer = static_cast<void**>(JSVAL_TO_PRIVATE(slot));
5575 CData::IsCData(JSContext* cx, JSObject* obj)
5577 return JS_GET_CLASS(cx, obj) == &sCDataClass;
5581 CData::ValueGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
5583 if (!IsCData(cx, obj)) {
5584 JS_ReportError(cx, "not a CData");
5588 // Convert the value to a primitive; do not create a new CData object.
5589 if (!ConvertToJS(cx, GetCType(cx, obj), NULL, GetData(cx, obj), true, false, vp))
5596 CData::ValueSetter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict, jsval* vp)
5598 if (!IsCData(cx, obj)) {
5599 JS_ReportError(cx, "not a CData");
5603 return ImplicitConvert(cx, *vp, GetCType(cx, obj), GetData(cx, obj), false, NULL);
5607 CData::Address(JSContext* cx, uintN argc, jsval* vp)
5610 JS_ReportError(cx, "address takes zero arguments");
5614 JSObject* obj = JS_THIS_OBJECT(cx, vp);
5615 if (!obj || !IsCData(cx, obj)) {
5616 JS_ReportError(cx, "not a CData");
5620 JSObject* typeObj = CData::GetCType(cx, obj);
5621 JSObject* pointerType = PointerType::CreateInternal(cx, typeObj);
5624 js::AutoObjectRooter root(cx, pointerType);
5626 // Create a PointerType CData object containing null.
5627 JSObject* result = CData::Create(cx, pointerType, NULL, NULL, true);
5631 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
5633 // Manually set the pointer inside the object, so we skip the conversion step.
5634 void** data = static_cast<void**>(GetData(cx, result));
5635 *data = GetData(cx, obj);
5640 CData::Cast(JSContext* cx, uintN argc, jsval* vp)
5643 JS_ReportError(cx, "cast takes two arguments");
5647 jsval* argv = JS_ARGV(cx, vp);
5648 if (JSVAL_IS_PRIMITIVE(argv[0]) ||
5649 !CData::IsCData(cx, JSVAL_TO_OBJECT(argv[0]))) {
5650 JS_ReportError(cx, "first argument must be a CData");
5653 JSObject* sourceData = JSVAL_TO_OBJECT(argv[0]);
5654 JSObject* sourceType = CData::GetCType(cx, sourceData);
5656 if (JSVAL_IS_PRIMITIVE(argv[1]) ||
5657 !CType::IsCType(cx, JSVAL_TO_OBJECT(argv[1]))) {
5658 JS_ReportError(cx, "second argument must be a CType");
5662 JSObject* targetType = JSVAL_TO_OBJECT(argv[1]);
5664 if (!CType::GetSafeSize(cx, targetType, &targetSize) ||
5665 targetSize > CType::GetSize(cx, sourceType)) {
5667 "target CType has undefined or larger size than source CType");
5671 // Construct a new CData object with a type of 'targetType' and a referent
5673 void* data = CData::GetData(cx, sourceData);
5674 JSObject* result = CData::Create(cx, targetType, sourceData, data, false);
5678 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
5683 CData::ReadString(JSContext* cx, uintN argc, jsval* vp)
5686 JS_ReportError(cx, "readString takes zero arguments");
5690 JSObject* obj = JS_THIS_OBJECT(cx, vp);
5691 if (!obj || !IsCData(cx, obj)) {
5692 JS_ReportError(cx, "not a CData");
5696 // Make sure we are a pointer to, or an array of, an 8-bit or 16-bit
5697 // character or integer type.
5699 JSObject* typeObj = GetCType(cx, obj);
5700 TypeCode typeCode = CType::GetTypeCode(cx, typeObj);
5702 size_t maxLength = -1;
5705 baseType = PointerType::GetBaseType(cx, typeObj);
5706 data = *static_cast<void**>(GetData(cx, obj));
5708 JS_ReportError(cx, "cannot read contents of null pointer");
5713 baseType = ArrayType::GetBaseType(cx, typeObj);
5714 data = GetData(cx, obj);
5715 maxLength = ArrayType::GetLength(cx, typeObj);
5718 JS_ReportError(cx, "not a PointerType or ArrayType");
5722 // Convert the string buffer, taking care to determine the correct string
5723 // length in the case of arrays (which may contain embedded nulls).
5725 switch (CType::GetTypeCode(cx, baseType)) {
5729 case TYPE_signed_char:
5730 case TYPE_unsigned_char: {
5731 char* bytes = static_cast<char*>(data);
5732 size_t length = strnlen(bytes, maxLength);
5734 // Determine the length.
5736 if (!js_InflateUTF8StringToBuffer(cx, bytes, length, NULL, &dstlen))
5740 static_cast<jschar*>(JS_malloc(cx, (dstlen + 1) * sizeof(jschar)));
5744 ASSERT_OK(js_InflateUTF8StringToBuffer(cx, bytes, length, dst, &dstlen));
5747 result = JS_NewUCString(cx, dst, dstlen);
5753 case TYPE_unsigned_short:
5755 jschar* chars = static_cast<jschar*>(data);
5756 size_t length = strnlen(chars, maxLength);
5757 result = JS_NewUCStringCopyN(cx, chars, length);
5762 "base type is not an 8-bit or 16-bit integer or character type");
5769 JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
5774 CData::ToSource(JSContext* cx, uintN argc, jsval* vp)
5777 JS_ReportError(cx, "toSource takes zero arguments");
5781 JSObject* obj = JS_THIS_OBJECT(cx, vp);
5782 if (!obj || !CData::IsCData(cx, obj)) {
5783 JS_ReportError(cx, "not a CData");
5787 JSObject* typeObj = CData::GetCType(cx, obj);
5788 void* data = CData::GetData(cx, obj);
5790 // Walk the types, building up the toSource() string.
5791 // First, we build up the type expression:
5792 // 't.ptr' for pointers;
5793 // 't.array([n])' for arrays;
5794 // 'n' for structs, where n = t.name, the struct's name. (We assume this is
5795 // bound to a variable in the current scope.)
5797 BuildTypeSource(cx, typeObj, true, source);
5798 AppendString(source, "(");
5799 if (!BuildDataSource(cx, typeObj, data, false, source))
5802 AppendString(source, ")");
5804 JSString* result = NewUCString(cx, source);
5808 JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
5812 /*******************************************************************************
5813 ** Int64 and UInt64 implementation
5814 *******************************************************************************/
5817 Int64Base::Construct(JSContext* cx,
5822 JSClass* clasp = isUnsigned ? &sUInt64Class : &sInt64Class;
5823 JSObject* result = JS_NewObject(cx, clasp, proto, JS_GetParent(cx, proto));
5826 js::AutoObjectRooter root(cx, result);
5828 // attach the Int64's data
5829 JSUint64* buffer = js_new<JSUint64>(data);
5831 JS_ReportOutOfMemory(cx);
5835 if (!JS_SetReservedSlot(cx, result, SLOT_INT64, PRIVATE_TO_JSVAL(buffer))) {
5840 if (!JS_FreezeObject(cx, result))
5847 Int64Base::Finalize(JSContext* cx, JSObject* obj)
5850 if (!JS_GetReservedSlot(cx, obj, SLOT_INT64, &slot) || JSVAL_IS_VOID(slot))
5853 js_delete(static_cast<JSUint64*>(JSVAL_TO_PRIVATE(slot)));
5857 Int64Base::GetInt(JSContext* cx, JSObject* obj) {
5858 JS_ASSERT(Int64::IsInt64(cx, obj) || UInt64::IsUInt64(cx, obj));
5861 ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_INT64, &slot));
5862 return *static_cast<JSUint64*>(JSVAL_TO_PRIVATE(slot));
5866 Int64Base::ToString(JSContext* cx,
5873 JS_ReportError(cx, "toString takes zero or one argument");
5879 jsval arg = JS_ARGV(cx, vp)[0];
5880 if (JSVAL_IS_INT(arg))
5881 radix = JSVAL_TO_INT(arg);
5882 if (!JSVAL_IS_INT(arg) || radix < 2 || radix > 36) {
5883 JS_ReportError(cx, "radix argument must be an integer between 2 and 36");
5888 AutoString intString;
5890 IntegerToString(GetInt(cx, obj), radix, intString);
5892 IntegerToString(static_cast<JSInt64>(GetInt(cx, obj)), radix, intString);
5895 JSString *result = NewUCString(cx, intString);
5899 JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
5904 Int64Base::ToSource(JSContext* cx,
5911 JS_ReportError(cx, "toSource takes zero arguments");
5915 // Return a decimal string suitable for constructing the number.
5918 AppendString(source, "ctypes.UInt64(\"");
5919 IntegerToString(GetInt(cx, obj), 10, source);
5921 AppendString(source, "ctypes.Int64(\"");
5922 IntegerToString(static_cast<JSInt64>(GetInt(cx, obj)), 10, source);
5924 AppendString(source, "\")");
5926 JSString *result = NewUCString(cx, source);
5930 JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
5935 Int64::Construct(JSContext* cx,
5939 // Construct and return a new Int64 object.
5941 JS_ReportError(cx, "Int64 takes one argument");
5945 jsval* argv = JS_ARGV(cx, vp);
5947 if (!jsvalToBigInteger(cx, argv[0], true, &i))
5948 return TypeError(cx, "int64", argv[0]);
5950 // Get ctypes.Int64.prototype from the 'prototype' property of the ctor.
5952 ASSERT_OK(JS_GetProperty(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)),
5953 "prototype", &slot));
5954 JSObject* proto = JSVAL_TO_OBJECT(slot);
5955 JS_ASSERT(JS_GET_CLASS(cx, proto) == &sInt64ProtoClass);
5957 JSObject* result = Int64Base::Construct(cx, proto, i, false);
5961 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
5966 Int64::IsInt64(JSContext* cx, JSObject* obj)
5968 return JS_GET_CLASS(cx, obj) == &sInt64Class;
5972 Int64::ToString(JSContext* cx, uintN argc, jsval* vp)
5974 JSObject* obj = JS_THIS_OBJECT(cx, vp);
5975 if (!obj || !Int64::IsInt64(cx, obj)) {
5976 JS_ReportError(cx, "not an Int64");
5980 return Int64Base::ToString(cx, obj, argc, vp, false);
5984 Int64::ToSource(JSContext* cx, uintN argc, jsval* vp)
5986 JSObject* obj = JS_THIS_OBJECT(cx, vp);
5987 if (!obj || !Int64::IsInt64(cx, obj)) {
5988 JS_ReportError(cx, "not an Int64");
5992 return Int64Base::ToSource(cx, obj, argc, vp, false);
5996 Int64::Compare(JSContext* cx, uintN argc, jsval* vp)
5998 jsval* argv = JS_ARGV(cx, vp);
6000 JSVAL_IS_PRIMITIVE(argv[0]) ||
6001 JSVAL_IS_PRIMITIVE(argv[1]) ||
6002 !Int64::IsInt64(cx, JSVAL_TO_OBJECT(argv[0])) ||
6003 !Int64::IsInt64(cx, JSVAL_TO_OBJECT(argv[1]))) {
6004 JS_ReportError(cx, "compare takes two Int64 arguments");
6008 JSObject* obj1 = JSVAL_TO_OBJECT(argv[0]);
6009 JSObject* obj2 = JSVAL_TO_OBJECT(argv[1]);
6011 JSInt64 i1 = Int64Base::GetInt(cx, obj1);
6012 JSInt64 i2 = Int64Base::GetInt(cx, obj2);
6015 JS_SET_RVAL(cx, vp, INT_TO_JSVAL(0));
6017 JS_SET_RVAL(cx, vp, INT_TO_JSVAL(-1));
6019 JS_SET_RVAL(cx, vp, INT_TO_JSVAL(1));
6024 #define LO_MASK ((JSUint64(1) << 32) - 1)
6025 #define INT64_LO(i) ((i) & LO_MASK)
6026 #define INT64_HI(i) ((i) >> 32)
6029 Int64::Lo(JSContext* cx, uintN argc, jsval* vp)
6031 jsval* argv = JS_ARGV(cx, vp);
6032 if (argc != 1 || JSVAL_IS_PRIMITIVE(argv[0]) ||
6033 !Int64::IsInt64(cx, JSVAL_TO_OBJECT(argv[0]))) {
6034 JS_ReportError(cx, "lo takes one Int64 argument");
6038 JSObject* obj = JSVAL_TO_OBJECT(argv[0]);
6039 JSInt64 u = Int64Base::GetInt(cx, obj);
6040 jsdouble d = JSUint32(INT64_LO(u));
6043 if (!JS_NewNumberValue(cx, d, &result))
6046 JS_SET_RVAL(cx, vp, result);
6051 Int64::Hi(JSContext* cx, uintN argc, jsval* vp)
6053 jsval* argv = JS_ARGV(cx, vp);
6054 if (argc != 1 || JSVAL_IS_PRIMITIVE(argv[0]) ||
6055 !Int64::IsInt64(cx, JSVAL_TO_OBJECT(argv[0]))) {
6056 JS_ReportError(cx, "hi takes one Int64 argument");
6060 JSObject* obj = JSVAL_TO_OBJECT(argv[0]);
6061 JSInt64 u = Int64Base::GetInt(cx, obj);
6062 jsdouble d = JSInt32(INT64_HI(u));
6065 if (!JS_NewNumberValue(cx, d, &result))
6068 JS_SET_RVAL(cx, vp, result);
6073 Int64::Join(JSContext* cx, uintN argc, jsval* vp)
6076 JS_ReportError(cx, "join takes two arguments");
6080 jsval* argv = JS_ARGV(cx, vp);
6083 if (!jsvalToInteger(cx, argv[0], &hi))
6084 return TypeError(cx, "int32", argv[0]);
6085 if (!jsvalToInteger(cx, argv[1], &lo))
6086 return TypeError(cx, "uint32", argv[1]);
6088 JSInt64 i = (JSInt64(hi) << 32) + JSInt64(lo);
6090 // Get Int64.prototype from the function's reserved slot.
6091 JSObject* callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
6094 ASSERT_OK(JS_GetReservedSlot(cx, callee, SLOT_FN_INT64PROTO, &slot));
6095 JSObject* proto = JSVAL_TO_OBJECT(slot);
6096 JS_ASSERT(JS_GET_CLASS(cx, proto) == &sInt64ProtoClass);
6098 JSObject* result = Int64Base::Construct(cx, proto, i, false);
6102 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
6107 UInt64::Construct(JSContext* cx,
6111 // Construct and return a new UInt64 object.
6113 JS_ReportError(cx, "UInt64 takes one argument");
6117 jsval* argv = JS_ARGV(cx, vp);
6119 if (!jsvalToBigInteger(cx, argv[0], true, &u))
6120 return TypeError(cx, "uint64", argv[0]);
6122 // Get ctypes.UInt64.prototype from the 'prototype' property of the ctor.
6124 ASSERT_OK(JS_GetProperty(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)),
6125 "prototype", &slot));
6126 JSObject* proto = JSVAL_TO_OBJECT(slot);
6127 JS_ASSERT(JS_GET_CLASS(cx, proto) == &sUInt64ProtoClass);
6129 JSObject* result = Int64Base::Construct(cx, proto, u, true);
6133 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
6138 UInt64::IsUInt64(JSContext* cx, JSObject* obj)
6140 return JS_GET_CLASS(cx, obj) == &sUInt64Class;
6144 UInt64::ToString(JSContext* cx, uintN argc, jsval* vp)
6146 JSObject* obj = JS_THIS_OBJECT(cx, vp);
6147 if (!obj || !UInt64::IsUInt64(cx, obj)) {
6148 JS_ReportError(cx, "not a UInt64");
6152 return Int64Base::ToString(cx, obj, argc, vp, true);
6156 UInt64::ToSource(JSContext* cx, uintN argc, jsval* vp)
6158 JSObject* obj = JS_THIS_OBJECT(cx, vp);
6159 if (!obj || !UInt64::IsUInt64(cx, obj)) {
6160 JS_ReportError(cx, "not a UInt64");
6164 return Int64Base::ToSource(cx, obj, argc, vp, true);
6168 UInt64::Compare(JSContext* cx, uintN argc, jsval* vp)
6170 jsval* argv = JS_ARGV(cx, vp);
6172 JSVAL_IS_PRIMITIVE(argv[0]) ||
6173 JSVAL_IS_PRIMITIVE(argv[1]) ||
6174 !UInt64::IsUInt64(cx, JSVAL_TO_OBJECT(argv[0])) ||
6175 !UInt64::IsUInt64(cx, JSVAL_TO_OBJECT(argv[1]))) {
6176 JS_ReportError(cx, "compare takes two UInt64 arguments");
6180 JSObject* obj1 = JSVAL_TO_OBJECT(argv[0]);
6181 JSObject* obj2 = JSVAL_TO_OBJECT(argv[1]);
6183 JSUint64 u1 = Int64Base::GetInt(cx, obj1);
6184 JSUint64 u2 = Int64Base::GetInt(cx, obj2);
6187 JS_SET_RVAL(cx, vp, INT_TO_JSVAL(0));
6189 JS_SET_RVAL(cx, vp, INT_TO_JSVAL(-1));
6191 JS_SET_RVAL(cx, vp, INT_TO_JSVAL(1));
6197 UInt64::Lo(JSContext* cx, uintN argc, jsval* vp)
6199 jsval* argv = JS_ARGV(cx, vp);
6200 if (argc != 1 || JSVAL_IS_PRIMITIVE(argv[0]) ||
6201 !UInt64::IsUInt64(cx, JSVAL_TO_OBJECT(argv[0]))) {
6202 JS_ReportError(cx, "lo takes one UInt64 argument");
6206 JSObject* obj = JSVAL_TO_OBJECT(argv[0]);
6207 JSUint64 u = Int64Base::GetInt(cx, obj);
6208 jsdouble d = JSUint32(INT64_LO(u));
6211 if (!JS_NewNumberValue(cx, d, &result))
6214 JS_SET_RVAL(cx, vp, result);
6219 UInt64::Hi(JSContext* cx, uintN argc, jsval* vp)
6221 jsval* argv = JS_ARGV(cx, vp);
6222 if (argc != 1 || JSVAL_IS_PRIMITIVE(argv[0]) ||
6223 !UInt64::IsUInt64(cx, JSVAL_TO_OBJECT(argv[0]))) {
6224 JS_ReportError(cx, "hi takes one UInt64 argument");
6228 JSObject* obj = JSVAL_TO_OBJECT(argv[0]);
6229 JSUint64 u = Int64Base::GetInt(cx, obj);
6230 jsdouble d = JSUint32(INT64_HI(u));
6233 if (!JS_NewNumberValue(cx, d, &result))
6236 JS_SET_RVAL(cx, vp, result);
6241 UInt64::Join(JSContext* cx, uintN argc, jsval* vp)
6244 JS_ReportError(cx, "join takes two arguments");
6248 jsval* argv = JS_ARGV(cx, vp);
6251 if (!jsvalToInteger(cx, argv[0], &hi))
6252 return TypeError(cx, "uint32_t", argv[0]);
6253 if (!jsvalToInteger(cx, argv[1], &lo))
6254 return TypeError(cx, "uint32_t", argv[1]);
6256 JSUint64 u = (JSUint64(hi) << 32) + JSUint64(lo);
6258 // Get UInt64.prototype from the function's reserved slot.
6259 JSObject* callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
6262 ASSERT_OK(JS_GetReservedSlot(cx, callee, SLOT_FN_INT64PROTO, &slot));
6263 JSObject* proto = JSVAL_TO_OBJECT(slot);
6264 JS_ASSERT(JS_GET_CLASS(cx, proto) == &sUInt64ProtoClass);
6266 JSObject* result = Int64Base::Construct(cx, proto, u, true);
6270 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));