Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / src / ctypes / CTypes.cpp
1 /* -*-  Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  * http://www.mozilla.org/MPL/
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the
13  * License.
14  *
15  * The Original Code is js-ctypes.
16  *
17  * The Initial Developer of the Original Code is
18  * The Mozilla Foundation <http://www.mozilla.org/>.
19  * Portions created by the Initial Developer are Copyright (C) 2009
20  * the Initial Developer. All Rights Reserved.
21  *
22  * Contributor(s):
23  *  Dan Witte <dwitte@mozilla.com>
24  *
25  * Alternatively, the contents of this file may be used under the terms of
26  * either the GNU General Public License Version 2 or later (the "GPL"), or
27  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28  * in which case the provisions of the GPL or the LGPL are applicable instead
29  * of those above. If you wish to allow use of your version of this file only
30  * under the terms of either the GPL or the LGPL, and not to allow others to
31  * use your version of this file under the terms of the MPL, indicate your
32  * decision by deleting the provisions above and replace them with the notice
33  * and other provisions required by the GPL or the LGPL. If you do not delete
34  * the provisions above, a recipient may use your version of this file under
35  * the terms of any one of the MPL, the GPL or the LGPL.
36  *
37  * ***** END LICENSE BLOCK ***** */
38
39 #include "CTypes.h"
40 #include "Library.h"
41 #include "jsnum.h"
42 #include <limits>
43
44 #include <math.h>
45 #if defined(XP_WIN) || defined(XP_OS2)
46 #include <float.h>
47 #endif
48
49 #if defined(SOLARIS)
50 #include <ieeefp.h>
51 #endif
52
53 #ifdef HAVE_SSIZE_T
54 #include <sys/types.h>
55 #endif
56
57 using namespace std;
58
59 namespace js {
60 namespace ctypes {
61
62 /*******************************************************************************
63 ** Helper classes
64 *******************************************************************************/
65
66 class ScopedContextThread
67 {
68 public:
69   ScopedContextThread(JSContext* cx) : mCx(cx) { JS_SetContextThread(cx); }
70   ~ScopedContextThread() { JS_ClearContextThread(mCx); }
71 private:
72   JSContext* mCx;
73 };
74
75 /*******************************************************************************
76 ** JSAPI function prototypes
77 *******************************************************************************/
78
79 static JSBool ConstructAbstract(JSContext* cx, uintN argc, jsval* vp);
80
81 namespace CType {
82   static JSBool ConstructData(JSContext* cx, uintN argc, jsval* vp);
83   static JSBool ConstructBasic(JSContext* cx, JSObject* obj, uintN argc, jsval* vp);
84
85   static void Trace(JSTracer* trc, JSObject* obj);
86   static void Finalize(JSContext* cx, JSObject* obj);
87   static void FinalizeProtoClass(JSContext* cx, JSObject* obj);
88
89   static JSBool PrototypeGetter(JSContext* cx, JSObject* obj, jsid idval,
90     jsval* vp);
91   static JSBool NameGetter(JSContext* cx, JSObject* obj, jsid idval,
92     jsval* vp);
93   static JSBool SizeGetter(JSContext* cx, JSObject* obj, jsid idval,
94     jsval* vp);
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);
100 }
101
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);
105
106   static JSBool TargetTypeGetter(JSContext* cx, JSObject* obj, jsid idval,
107     jsval* vp);
108   static JSBool ContentsGetter(JSContext* cx, JSObject* obj, jsid idval,
109     jsval* vp);
110   static JSBool ContentsSetter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict,
111     jsval* vp);
112   static JSBool IsNull(JSContext* cx, uintN argc, jsval* vp);
113 }
114
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);
118
119   static JSBool ElementTypeGetter(JSContext* cx, JSObject* obj, jsid idval,
120     jsval* vp);
121   static JSBool LengthGetter(JSContext* cx, JSObject* obj, jsid idval,
122     jsval* vp);
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);
126 }
127
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);
131
132   static JSBool FieldsArrayGetter(JSContext* cx, JSObject* obj, jsid idval,
133     jsval* vp);
134   static JSBool FieldGetter(JSContext* cx, JSObject* obj, jsid idval,
135     jsval* vp);
136   static JSBool FieldSetter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict,
137                             jsval* vp);
138   static JSBool AddressOfField(JSContext* cx, uintN argc, jsval* vp);
139   static JSBool Define(JSContext* cx, uintN argc, jsval* vp);
140 }
141
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);
146
147   static JSBool Call(JSContext* cx, uintN argc, jsval* vp);
148
149   static JSBool ArgTypesGetter(JSContext* cx, JSObject* obj, jsid idval,
150     jsval* vp);
151   static JSBool ReturnTypeGetter(JSContext* cx, JSObject* obj, jsid idval,
152     jsval* vp);
153   static JSBool ABIGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp);
154   static JSBool IsVariadicGetter(JSContext* cx, JSObject* obj, jsid idval,
155     jsval* vp);
156 }
157
158 namespace CClosure {
159   static void Trace(JSTracer* trc, JSObject* obj);
160   static void Finalize(JSContext* cx, JSObject* obj);
161
162   // libffi callback
163   static void ClosureStub(ffi_cif* cif, void* result, void** args,
164     void* userData);
165 }
166
167 namespace CData {
168   static void Finalize(JSContext* cx, JSObject* obj);
169
170   static JSBool ValueGetter(JSContext* cx, JSObject* obj, jsid idval,
171                             jsval* vp);
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);
177 }
178
179 // Int64Base provides functions common to Int64 and UInt64.
180 namespace Int64Base {
181   JSObject* Construct(JSContext* cx, JSObject* proto, JSUint64 data,
182     bool isUnsigned);
183
184   JSUint64 GetInt(JSContext* cx, JSObject* obj);
185
186   JSBool ToString(JSContext* cx, JSObject* obj, uintN argc, jsval* vp,
187     bool isUnsigned);
188
189   JSBool ToSource(JSContext* cx, JSObject* obj, uintN argc, jsval* vp,
190     bool isUnsigned);
191
192   static void Finalize(JSContext* cx, JSObject* obj);
193 }
194
195 namespace Int64 {
196   static JSBool Construct(JSContext* cx, uintN argc, jsval* vp);
197
198   static JSBool ToString(JSContext* cx, uintN argc, jsval* vp);
199   static JSBool ToSource(JSContext* cx, uintN argc, jsval* vp);
200
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);
205 }
206
207 namespace UInt64 {
208   static JSBool Construct(JSContext* cx, uintN argc, jsval* vp);
209
210   static JSBool ToString(JSContext* cx, uintN argc, jsval* vp);
211   static JSBool ToSource(JSContext* cx, uintN argc, jsval* vp);
212
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);
217 }
218
219 /*******************************************************************************
220 ** JSClass definitions and initialization functions
221 *******************************************************************************/
222
223 // Class representing the 'ctypes' object itself. This exists to contain the
224 // JSCTypesCallbacks set of function pointers.
225 static JSClass sCTypesGlobalClass = {
226   "ctypes",
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
231 };
232
233 static JSClass sCABIClass = {
234   "CABI",
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
239 };
240
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 = {
245   "CType",
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
250 };
251
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 = {
255   "CData",
256   0,
257   JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
258   JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
259   JSCLASS_NO_OPTIONAL_MEMBERS
260 };
261
262 static JSClass sCTypeClass = {
263   "CType",
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
269 };
270
271 static JSClass sCDataClass = {
272   "CData",
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
277 };
278
279 static JSClass sCClosureClass = {
280   "CClosure",
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
285 };
286
287 #define CTYPESFN_FLAGS \
288   (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
289
290 #define CTYPESCTOR_FLAGS \
291   (CTYPESFN_FLAGS | JSFUN_CONSTRUCTOR)
292
293 #define CTYPESPROP_FLAGS \
294   (JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
295
296 #define CDATAFN_FLAGS \
297   (JSPROP_READONLY | JSPROP_PERMANENT)
298
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 }
305 };
306
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),
311   JS_FS_END
312 };
313
314 static JSPropertySpec sCDataProps[] = {
315   { "value", 0, JSPROP_SHARED | JSPROP_PERMANENT,
316     CData::ValueGetter, CData::ValueSetter },
317   { 0, 0, 0, NULL, NULL }
318 };
319
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),
325   JS_FS_END
326 };
327
328 static JSFunctionSpec sPointerFunction =
329   JS_FN("PointerType", PointerType::Create, 1, CTYPESCTOR_FLAGS);
330
331 static JSPropertySpec sPointerProps[] = {
332   { "targetType", 0, CTYPESPROP_FLAGS, PointerType::TargetTypeGetter, NULL },
333   { 0, 0, 0, NULL, NULL }
334 };
335
336 static JSFunctionSpec sPointerInstanceFunctions[] = {
337   JS_FN("isNull", PointerType::IsNull, 0, CTYPESFN_FLAGS),
338   JS_FS_END
339 };
340   
341 static JSPropertySpec sPointerInstanceProps[] = {
342   { "contents", 0, JSPROP_SHARED | JSPROP_PERMANENT,
343     PointerType::ContentsGetter, PointerType::ContentsSetter },
344   { 0, 0, 0, NULL, NULL }
345 };
346
347 static JSFunctionSpec sArrayFunction =
348   JS_FN("ArrayType", ArrayType::Create, 1, CTYPESCTOR_FLAGS);
349
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 }
354 };
355
356 static JSFunctionSpec sArrayInstanceFunctions[] = {
357   JS_FN("addressOfElement", ArrayType::AddressOfElement, 1, CDATAFN_FLAGS),
358   JS_FS_END
359 };
360
361 static JSPropertySpec sArrayInstanceProps[] = {
362   { "length", 0, JSPROP_SHARED | JSPROP_READONLY | JSPROP_PERMANENT,
363     ArrayType::LengthGetter, NULL },
364   { 0, 0, 0, NULL, NULL }
365 };
366
367 static JSFunctionSpec sStructFunction =
368   JS_FN("StructType", StructType::Create, 2, CTYPESCTOR_FLAGS);
369
370 static JSPropertySpec sStructProps[] = {
371   { "fields", 0, CTYPESPROP_FLAGS, StructType::FieldsArrayGetter, NULL },
372   { 0, 0, 0, NULL, NULL }
373 };
374
375 static JSFunctionSpec sStructFunctions[] = {
376   JS_FN("define", StructType::Define, 1, CDATAFN_FLAGS),
377   JS_FS_END
378 };
379
380 static JSFunctionSpec sStructInstanceFunctions[] = {
381   JS_FN("addressOfField", StructType::AddressOfField, 1, CDATAFN_FLAGS),
382   JS_FS_END
383 };
384
385 static JSFunctionSpec sFunctionFunction =
386   JS_FN("FunctionType", FunctionType::Create, 2, CTYPESCTOR_FLAGS);
387
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 }
394 };
395
396 static JSClass sInt64ProtoClass = {
397   "Int64",
398   0,
399   JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
400   JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
401   JSCLASS_NO_OPTIONAL_MEMBERS
402 };
403
404 static JSClass sUInt64ProtoClass = {
405   "UInt64",
406   0,
407   JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
408   JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
409   JSCLASS_NO_OPTIONAL_MEMBERS
410 };
411
412 static JSClass sInt64Class = {
413   "Int64",
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
418 };
419
420 static JSClass sUInt64Class = {
421   "UInt64",
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
426 };
427
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),
433   JS_FS_END
434 };
435
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),
441   JS_FS_END
442 };
443
444 static JSFunctionSpec sInt64Functions[] = {
445   JS_FN("toString", Int64::ToString, 0, CTYPESFN_FLAGS),
446   JS_FN("toSource", Int64::ToSource, 0, CTYPESFN_FLAGS),
447   JS_FS_END
448 };
449
450 static JSFunctionSpec sUInt64Functions[] = {
451   JS_FN("toString", UInt64::ToString, 0, CTYPESFN_FLAGS),
452   JS_FN("toSource", UInt64::ToSource, 0, CTYPESFN_FLAGS),
453   JS_FS_END
454 };
455
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),
460   JS_FS_END
461 };
462
463 static inline bool FloatIsFinite(jsdouble f) {
464 #ifdef WIN32
465   return _finite(f) != 0;
466 #else
467   return finite(f);
468 #endif
469 }
470
471 JS_ALWAYS_INLINE JSString*
472 NewUCString(JSContext* cx, const AutoString& from)
473 {
474   return JS_NewUCStringCopyN(cx, from.begin(), from.length());
475 }
476
477 JS_ALWAYS_INLINE size_t
478 Align(size_t val, size_t align)
479 {
480   return ((val - 1) | (align - 1)) + 1;
481 }
482
483 static ABICode
484 GetABICode(JSContext* cx, JSObject* obj)
485 {
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)
489     return INVALID_ABI;
490
491   jsval result;
492   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_ABICODE, &result));
493
494   return ABICode(JSVAL_TO_INT(result));
495 }
496
497 JSErrorFormatString ErrorFormatString[CTYPESERR_LIMIT] = {
498 #define MSG_DEF(name, number, count, exception, format) \
499   { format, count, exception } ,
500 #include "ctypes.msg"
501 #undef MSG_DEF
502 };
503
504 const JSErrorFormatString*
505 GetErrorMessage(void* userRef, const char* locale, const uintN errorNumber)
506 {
507   if (0 < errorNumber && errorNumber < CTYPESERR_LIMIT)
508     return &ErrorFormatString[errorNumber];
509   return NULL;
510 }
511
512 JSBool
513 TypeError(JSContext* cx, const char* expected, jsval actual)
514 {
515   JSString* str = JS_ValueToSource(cx, actual);
516   JSAutoByteString bytes;
517   
518   const char* src;
519   if (str) {
520     src = bytes.encode(cx, str);
521     if (!src)
522       return false;
523   } else {
524     JS_ClearPendingException(cx);
525     src = "<<error converting value to string>>";
526   }
527   JS_ReportErrorNumber(cx, GetErrorMessage, NULL,
528                        CTYPESMSG_TYPE_ERROR, expected, src);
529   return false;
530 }
531
532 static JSObject*
533 InitCTypeClass(JSContext* cx, JSObject* parent)
534 {
535   JSFunction* fun = JS_DefineFunction(cx, parent, "CType", ConstructAbstract, 0,
536                       CTYPESCTOR_FLAGS);
537   if (!fun)
538     return NULL;
539
540   JSObject* ctor = JS_GetFunctionObject(fun);
541   JSObject* fnproto = JS_GetPrototype(cx, ctor);
542   JS_ASSERT(ctor);
543   JS_ASSERT(fnproto);
544
545   // Set up ctypes.CType.prototype.
546   JSObject* prototype = JS_NewObject(cx, &sCTypeProtoClass, fnproto, parent);
547   if (!prototype)
548     return NULL;
549
550   if (!JS_DefineProperty(cx, ctor, "prototype", OBJECT_TO_JSVAL(prototype),
551          NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
552     return NULL;
553
554   if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(ctor),
555          NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
556     return NULL;
557
558   // Define properties and functions common to all CTypes.
559   if (!JS_DefineProperties(cx, prototype, sCTypeProps) ||
560       !JS_DefineFunctions(cx, prototype, sCTypeFunctions))
561     return NULL;
562
563   if (!JS_FreezeObject(cx, ctor) || !JS_FreezeObject(cx, prototype))
564     return NULL;
565
566   return prototype;
567 }
568
569 static JSObject*
570 InitCDataClass(JSContext* cx, JSObject* parent, JSObject* CTypeProto)
571 {
572   JSFunction* fun = JS_DefineFunction(cx, parent, "CData", ConstructAbstract, 0,
573                       CTYPESCTOR_FLAGS);
574   if (!fun)
575     return NULL;
576
577   JSObject* ctor = JS_GetFunctionObject(fun);
578   JS_ASSERT(ctor);
579
580   // Set up ctypes.CData.__proto__ === ctypes.CType.prototype.
581   // (Note that 'ctypes.CData instanceof Function' is still true, thanks to the
582   // prototype chain.)
583   if (!JS_SetPrototype(cx, ctor, CTypeProto))
584     return NULL;
585
586   // Set up ctypes.CData.prototype.
587   JSObject* prototype = JS_NewObject(cx, &sCDataProtoClass, NULL, parent);
588   if (!prototype)
589     return NULL;
590
591   if (!JS_DefineProperty(cx, ctor, "prototype", OBJECT_TO_JSVAL(prototype),
592          NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
593     return NULL;
594
595   if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(ctor),
596          NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
597     return NULL;
598
599   // Define properties and functions common to all CDatas.
600   if (!JS_DefineProperties(cx, prototype, sCDataProps) ||
601       !JS_DefineFunctions(cx, prototype, sCDataFunctions))
602     return NULL;
603
604   if (//!JS_FreezeObject(cx, prototype) || // XXX fixme - see bug 541212!
605       !JS_FreezeObject(cx, ctor))
606     return NULL;
607
608   return prototype;
609 }
610
611 static JSBool
612 DefineABIConstant(JSContext* cx,
613                   JSObject* parent,
614                   const char* name,
615                   ABICode code)
616 {
617   JSObject* obj = JS_DefineObject(cx, parent, name, &sCABIClass, NULL,
618                     JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
619   if (!obj)
620     return false;
621   if (!JS_SetReservedSlot(cx, obj, SLOT_ABICODE, INT_TO_JSVAL(code)))
622     return false;
623   return JS_FreezeObject(cx, obj);
624 }
625
626 // Set up a single type constructor for
627 // ctypes.{Pointer,Array,Struct,Function}Type.
628 static JSBool
629 InitTypeConstructor(JSContext* cx,
630                     JSObject* parent,
631                     JSObject* CTypeProto,
632                     JSObject* CDataProto,
633                     JSFunctionSpec spec,
634                     JSFunctionSpec* fns,
635                     JSPropertySpec* props,
636                     JSFunctionSpec* instanceFns,
637                     JSPropertySpec* instanceProps,
638                     JSObject*& typeProto,
639                     JSObject*& dataProto)
640 {
641   JSFunction* fun = JS_DefineFunction(cx, parent, spec.name, spec.call, 
642                       spec.nargs, spec.flags);
643   if (!fun)
644     return false;
645
646   JSObject* obj = JS_GetFunctionObject(fun);
647   if (!obj)
648     return false;
649
650   // Set up the .prototype and .prototype.constructor properties.
651   typeProto = JS_NewObject(cx, &sCTypeProtoClass, CTypeProto, parent);
652   if (!typeProto)
653     return false;
654
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))
658     return false;
659
660   if (fns && !JS_DefineFunctions(cx, typeProto, fns))
661     return false;
662
663   if (!JS_DefineProperties(cx, typeProto, props))
664     return false;
665
666   if (!JS_DefineProperty(cx, typeProto, "constructor", OBJECT_TO_JSVAL(obj),
667          NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
668     return false;
669
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)))
673     return false;
674
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);
680   if (!dataProto)
681     return false;
682   js::AutoObjectRooter protoroot(cx, dataProto);
683
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))
688     return false;
689
690   if (instanceProps && !JS_DefineProperties(cx, dataProto, instanceProps))
691     return false;
692
693   if (!JS_FreezeObject(cx, obj) ||
694       //!JS_FreezeObject(cx, dataProto) || // XXX fixme - see bug 541212!
695       !JS_FreezeObject(cx, typeProto))
696     return false;
697
698   return true;
699 }
700
701 JSObject*
702 InitInt64Class(JSContext* cx,
703                JSObject* parent,
704                JSClass* clasp,
705                JSNative construct,
706                JSFunctionSpec* fs,
707                JSFunctionSpec* static_fs)
708 {
709   // Init type class and constructor
710   JSObject* prototype = JS_InitClass(cx, parent, NULL, clasp, construct,
711     0, NULL, fs, NULL, static_fs);
712   if (!prototype)
713     return NULL;
714
715   JSObject* ctor = JS_GetConstructor(cx, prototype);
716   if (!ctor)
717     return NULL;
718   if (!JS_FreezeObject(cx, ctor))
719     return NULL;
720
721   // Stash ctypes.{Int64,UInt64}.prototype on a reserved slot of the 'join'
722   // function.
723   jsval 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)))
727     return NULL;
728
729   if (!JS_FreezeObject(cx, prototype))
730     return NULL;
731
732   return prototype;
733 }
734
735 static JSBool
736 AttachProtos(JSContext* cx, JSObject* proto, JSObject** protos)
737 {
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])))
743       return false;
744   }
745
746   return true;
747 }
748
749 JSBool
750 InitTypeClasses(JSContext* cx, JSObject* parent)
751 {
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
757   //     abstract type!)
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);
766   if (!CTypeProto)
767     return false;
768
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.
771   // It has:
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);
781   if (!CDataProto)
782     return false;
783
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
801   // will have, resp.:
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]))
815     return false;
816   js::AutoObjectRooter proot(cx, protos[SLOT_POINTERDATAPROTO]);
817
818   if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
819          sArrayFunction, NULL, sArrayProps,
820          sArrayInstanceFunctions, sArrayInstanceProps,
821          protos[SLOT_ARRAYPROTO], protos[SLOT_ARRAYDATAPROTO]))
822     return false;
823   js::AutoObjectRooter aroot(cx, protos[SLOT_ARRAYDATAPROTO]);
824
825   if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
826          sStructFunction, sStructFunctions, sStructProps,
827          sStructInstanceFunctions, NULL,
828          protos[SLOT_STRUCTPROTO], protos[SLOT_STRUCTDATAPROTO]))
829     return false;
830   js::AutoObjectRooter sroot(cx, protos[SLOT_STRUCTDATAPROTO]);
831
832   if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
833          sFunctionFunction, NULL, sFunctionProps, NULL, NULL,
834          protos[SLOT_FUNCTIONPROTO], protos[SLOT_FUNCTIONDATAPROTO]))
835     return false;
836   js::AutoObjectRooter froot(cx, protos[SLOT_FUNCTIONDATAPROTO]);
837
838   protos[SLOT_CDATAPROTO] = CDataProto;
839
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])
851     return false;
852   protos[SLOT_UINT64PROTO] = InitInt64Class(cx, parent, &sUInt64ProtoClass,
853     UInt64::Construct, sUInt64Functions, sUInt64StaticFunctions);
854   if (!protos[SLOT_UINT64PROTO])
855     return false;
856
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))
865      return false;
866
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))
871     return false;
872
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)                                                         \
889     return false;
890 #include "typedefs.h"
891
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))
897     return false;
898
899   // Create objects representing the special types void_t and voidptr_t.
900   JSObject* typeObj =
901     CType::DefineBuiltin(cx, parent, "void_t", CTypeProto, CDataProto, "void",
902       TYPE_void_t, JSVAL_VOID, JSVAL_VOID, &ffi_type_void);
903   if (!typeObj)
904     return false;
905
906   typeObj = PointerType::CreateInternal(cx, typeObj);
907   if (!typeObj)
908     return false;
909   if (!JS_DefineProperty(cx, parent, "voidptr_t", OBJECT_TO_JSVAL(typeObj),
910          NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
911     return false;
912
913   return true;
914 }
915
916 bool
917 IsCTypesGlobal(JSContext* cx, JSObject* obj)
918 {
919   return JS_GET_CLASS(cx, obj) == &sCTypesGlobalClass;
920 }
921
922 // Get the JSCTypesCallbacks struct from the 'ctypes' object 'obj'.
923 JSCTypesCallbacks*
924 GetCallbacks(JSContext* cx, JSObject* obj)
925 {
926   JS_ASSERT(IsCTypesGlobal(cx, obj));
927
928   jsval result;
929   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_CALLBACKS, &result));
930   if (JSVAL_IS_VOID(result))
931     return NULL;
932
933   return static_cast<JSCTypesCallbacks*>(JSVAL_TO_PRIVATE(result));
934 }
935
936 JS_BEGIN_EXTERN_C
937
938 JS_PUBLIC_API(JSBool)
939 JS_InitCTypesClass(JSContext* cx, JSObject* global)
940 {
941   // attach ctypes property to global object
942   JSObject* ctypes = JS_NewObject(cx, &sCTypesGlobalClass, NULL, NULL);
943   if (!ctypes)
944     return false;
945
946   if (!JS_DefineProperty(cx, global, "ctypes", OBJECT_TO_JSVAL(ctypes),
947          JS_PropertyStub, JS_StrictPropertyStub, JSPROP_READONLY | JSPROP_PERMANENT)) {
948     return false;
949   }
950
951   if (!InitTypeClasses(cx, ctypes))
952     return false;
953
954   // attach API functions
955   if (!JS_DefineFunctions(cx, ctypes, sModuleFunctions))
956     return false;
957
958   // Seal the ctypes object, to prevent modification.
959   return JS_FreezeObject(cx, ctypes);
960 }
961
962 JS_PUBLIC_API(JSBool)
963 JS_SetCTypesCallbacks(JSContext* cx,
964                       JSObject* ctypesObj,
965                       JSCTypesCallbacks* callbacks)
966 {
967   JS_ASSERT(callbacks);
968   JS_ASSERT(IsCTypesGlobal(cx, ctypesObj));
969
970   // Set the callbacks on a reserved slot.
971   return JS_SetReservedSlot(cx, ctypesObj, SLOT_CALLBACKS,
972     PRIVATE_TO_JSVAL(callbacks));
973 }
974
975 JS_END_EXTERN_C
976
977 /*******************************************************************************
978 ** Type conversion functions
979 *******************************************************************************/
980
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);
997
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);
1004   }
1005 };
1006
1007 #ifdef _MSC_VER
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.
1010 template<>
1011 struct ConvertImpl<JSUint64, jsdouble> {
1012   static JS_ALWAYS_INLINE JSUint64 Convert(jsdouble d) {
1013     return d > 0x7fffffffffffffffui64 ?
1014            JSUint64(d - 0x8000000000000000ui64) + 0x8000000000000000ui64 :
1015            JSUint64(d);
1016   }
1017 };
1018 #endif
1019
1020 template<class TargetType, class FromType>
1021 static JS_ALWAYS_INLINE TargetType Convert(FromType d)
1022 {
1023   return ConvertImpl<TargetType, FromType>::Convert(d);
1024 }
1025
1026 template<class TargetType, class FromType>
1027 static JS_ALWAYS_INLINE bool IsAlwaysExact()
1028 {
1029   // Return 'true' if TargetType can always exactly represent FromType.
1030   // This means that:
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
1034   //    mantissa width.
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)
1039     return false;
1040
1041   if (numeric_limits<FromType>::is_signed &&
1042       !numeric_limits<TargetType>::is_signed)
1043     return false;
1044
1045   if (!numeric_limits<FromType>::is_exact &&
1046       numeric_limits<TargetType>::is_exact)
1047     return false;
1048
1049   return true;
1050 }
1051
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;
1059   }
1060 };
1061
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;
1068   }
1069 };
1070
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;
1077   }
1078 };
1079
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)
1084 {
1085   // Require that TargetType is integral, to simplify conversion.
1086   JS_STATIC_ASSERT(numeric_limits<TargetType>::is_exact);
1087
1088   *result = Convert<TargetType>(i);
1089
1090   // See if we can avoid a dynamic check.
1091   if (IsAlwaysExact<TargetType, FromType>())
1092     return true;
1093
1094   // Return 'true' if 'i' is exactly representable in 'TargetType'.
1095   return IsExactImpl<TargetType,
1096                      FromType,
1097                      numeric_limits<TargetType>::is_signed,
1098                      numeric_limits<FromType>::is_signed>::Test(i, *result);
1099 }
1100
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) {
1106     return false;
1107   }
1108 };
1109
1110 // Specialization where Type is signed.
1111 template<class Type>
1112 struct IsNegativeImpl<Type, true> {
1113   static JS_ALWAYS_INLINE bool Test(Type i) {
1114     return i < 0;
1115   }
1116 };
1117
1118 // Determine whether Type 'i' is negative.
1119 template<class Type>
1120 static JS_ALWAYS_INLINE bool IsNegative(Type i)
1121 {
1122   return IsNegativeImpl<Type, numeric_limits<Type>::is_signed>::Test(i);
1123 }
1124
1125 // Implicitly convert val to bool, allowing JSBool, jsint, and jsdouble
1126 // arguments numerically equal to 0 or 1.
1127 static bool
1128 jsvalToBool(JSContext* cx, jsval val, bool* result)
1129 {
1130   if (JSVAL_IS_BOOLEAN(val)) {
1131     *result = JSVAL_TO_BOOLEAN(val) != JS_FALSE;
1132     return true;
1133   }
1134   if (JSVAL_IS_INT(val)) {
1135     jsint i = JSVAL_TO_INT(val);
1136     *result = i != 0;
1137     return i == 0 || i == 1;
1138   }
1139   if (JSVAL_IS_DOUBLE(val)) {
1140     jsdouble d = JSVAL_TO_DOUBLE(val);
1141     *result = d != 0;
1142     // Allow -0.
1143     return d == 1 || d == 0;
1144   }
1145   // Don't silently convert null to bool. It's probably a mistake.
1146   return false;
1147 }
1148
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>
1153 static bool
1154 jsvalToInteger(JSContext* cx, jsval val, IntegerType* result)
1155 {
1156   JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
1157
1158   if (JSVAL_IS_INT(val)) {
1159     // Make sure the integer fits in the alotted precision, and has the right
1160     // sign.
1161     jsint i = JSVAL_TO_INT(val);
1162     return ConvertExact(i, result);
1163   }
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);
1169   }
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);
1175
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)                               \
1180       case TYPE_##name:                                                        \
1181         if (!IsAlwaysExact<IntegerType, fromType>())                           \
1182           return false;                                                        \
1183         *result = IntegerType(*static_cast<fromType*>(data));                  \
1184         return true;
1185 #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
1186 #include "typedefs.h"
1187       case TYPE_void_t:
1188       case TYPE_bool:
1189       case TYPE_float:
1190       case TYPE_double:
1191       case TYPE_float32_t:
1192       case TYPE_float64_t:
1193       case TYPE_char:
1194       case TYPE_signed_char:
1195       case TYPE_unsigned_char:
1196       case TYPE_jschar:
1197       case TYPE_pointer:
1198       case TYPE_function:
1199       case TYPE_array:
1200       case TYPE_struct:
1201         // Not a compatible number type.
1202         return false;
1203       }
1204     }
1205
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);
1210     }
1211
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);
1216     }
1217
1218     return false; 
1219   }
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);
1224     return true;
1225   }
1226   // Don't silently convert null to an integer. It's probably a mistake.
1227   return false;
1228 }
1229
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>
1234 static bool
1235 jsvalToFloat(JSContext *cx, jsval val, FloatType* result)
1236 {
1237   JS_STATIC_ASSERT(!numeric_limits<FloatType>::is_exact);
1238
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));
1245     return true;
1246   }
1247   if (JSVAL_IS_DOUBLE(val)) {
1248     *result = FloatType(JSVAL_TO_DOUBLE(val));
1249     return true;
1250   }
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);
1256
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)                             \
1261       case TYPE_##name:                                                        \
1262         if (!IsAlwaysExact<FloatType, fromType>())                             \
1263           return false;                                                        \
1264         *result = FloatType(*static_cast<fromType*>(data));                    \
1265         return true;
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"
1269       case TYPE_void_t:
1270       case TYPE_bool:
1271       case TYPE_char:
1272       case TYPE_signed_char:
1273       case TYPE_unsigned_char:
1274       case TYPE_jschar:
1275       case TYPE_pointer:
1276       case TYPE_function:
1277       case TYPE_array:
1278       case TYPE_struct:
1279         // Not a compatible number type.
1280         return false;
1281       }
1282     }
1283   }
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.
1286   return false;
1287 }
1288
1289 template<class IntegerType>
1290 static bool
1291 StringToInteger(JSContext* cx, JSString* string, IntegerType* result)
1292 {
1293   JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
1294
1295   const jschar* cp = string->getChars(NULL);
1296   if (!cp)
1297     return false;
1298
1299   const jschar* end = cp + string->length();
1300   if (cp == end)
1301     return false;
1302
1303   IntegerType sign = 1;
1304   if (cp[0] == '-') {
1305     if (!numeric_limits<IntegerType>::is_signed)
1306       return false;
1307
1308     sign = -1;
1309     ++cp;
1310   }
1311
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')) {
1315     cp += 2;
1316     base = 16;
1317   }
1318
1319   // Scan the string left to right and build the number,
1320   // checking for valid characters 0 - 9, a - f, A - F and overflow.
1321   IntegerType i = 0;
1322   while (cp != end) {
1323     jschar c = *cp++;
1324     if (c >= '0' && c <= '9')
1325       c -= '0';
1326     else if (base == 16 && c >= 'a' && c <= 'f')
1327       c = c - 'a' + 10;
1328     else if (base == 16 && c >= 'A' && c <= 'F')
1329       c = c - 'A' + 10;
1330     else
1331       return false;
1332
1333     IntegerType ii = i;
1334     i = ii * base + sign * c;
1335     if (i / base != ii) // overflow
1336       return false;
1337   }
1338
1339   *result = i;
1340   return true;
1341 }
1342
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>
1347 static bool
1348 jsvalToBigInteger(JSContext* cx,
1349                   jsval val,
1350                   bool allowString,
1351                   IntegerType* result)
1352 {
1353   JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
1354
1355   if (JSVAL_IS_INT(val)) {
1356     // Make sure the integer fits in the alotted precision, and has the right
1357     // sign.
1358     jsint i = JSVAL_TO_INT(val);
1359     return ConvertExact(i, result);
1360   }
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);
1366   }
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);
1373   }
1374   if (!JSVAL_IS_PRIMITIVE(val)) {
1375     // Allow conversion from an Int64 or UInt64 object directly.
1376     JSObject* obj = JSVAL_TO_OBJECT(val);
1377
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);
1382     }
1383
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);
1388     }
1389   }
1390   return false;
1391 }
1392
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.
1395 static bool
1396 jsvalToSize(JSContext* cx, jsval val, bool allowString, size_t* result)
1397 {
1398   if (!jsvalToBigInteger(cx, val, allowString, result))
1399     return false;
1400
1401   // Also check that the result fits in a jsdouble.
1402   return Convert<size_t>(jsdouble(*result)) == *result;
1403 }
1404
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>
1409 static bool
1410 jsidToBigInteger(JSContext* cx,
1411                   jsid val,
1412                   bool allowString,
1413                   IntegerType* result)
1414 {
1415   JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
1416
1417   if (JSID_IS_INT(val)) {
1418     // Make sure the integer fits in the alotted precision, and has the right
1419     // sign.
1420     jsint i = JSID_TO_INT(val);
1421     return ConvertExact(i, result);
1422   }
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);
1429   }
1430   if (JSID_IS_OBJECT(val)) {
1431     // Allow conversion from an Int64 or UInt64 object directly.
1432     JSObject* obj = JSID_TO_OBJECT(val);
1433
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);
1438     }
1439
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);
1444     }
1445   }
1446   return false;
1447 }
1448
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.
1451 static bool
1452 jsidToSize(JSContext* cx, jsid val, bool allowString, size_t* result)
1453 {
1454   if (!jsidToBigInteger(cx, val, allowString, result))
1455     return false;
1456
1457   // Also check that the result fits in a jsdouble.
1458   return Convert<size_t>(jsdouble(*result)) == *result;
1459 }
1460
1461 // Implicitly convert a size value to a jsval, ensuring that the size_t value
1462 // fits in a jsdouble.
1463 static JSBool
1464 SizeTojsval(JSContext* cx, size_t size, jsval* result)
1465 {
1466   if (Convert<size_t>(jsdouble(size)) != size) {
1467     JS_ReportError(cx, "size overflow");
1468     return false;
1469   }
1470
1471   return JS_NewNumberValue(cx, jsdouble(size), result);
1472 }
1473
1474 // Forcefully convert val to IntegerType when explicitly requested.
1475 template<class IntegerType>
1476 static bool
1477 jsvalToIntegerExplicit(JSContext* cx, jsval val, IntegerType* result)
1478 {
1479   JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
1480
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;
1485     return true;
1486   }
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);
1493       return true;
1494     }
1495     if (UInt64::IsUInt64(cx, obj)) {
1496       JSUint64 i = Int64Base::GetInt(cx, obj);
1497       *result = IntegerType(i);
1498       return true;
1499     }
1500   }
1501   return false;
1502 }
1503
1504 // Forcefully convert val to a pointer value when explicitly requested.
1505 static bool
1506 jsvalToPtrExplicit(JSContext* cx, jsval val, uintptr_t* result)
1507 {
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);
1513     return true;
1514   }
1515   if (JSVAL_IS_DOUBLE(val)) {
1516     jsdouble d = JSVAL_TO_DOUBLE(val);
1517     if (d < 0) {
1518       // Cast through an intptr_t intermediate to sign-extend.
1519       intptr_t i = Convert<intptr_t>(d);
1520       if (jsdouble(i) != d)
1521         return false;
1522
1523       *result = uintptr_t(i);
1524       return true;
1525     }
1526
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;
1531   }
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);
1537
1538       // Make sure the integer fits in the alotted precision.
1539       if (JSInt64(p) != i)
1540         return false;
1541       *result = uintptr_t(p);
1542       return true;
1543     }
1544
1545     if (UInt64::IsUInt64(cx, obj)) {
1546       JSUint64 i = Int64Base::GetInt(cx, obj);
1547
1548       // Make sure the integer fits in the alotted precision.
1549       *result = uintptr_t(i);
1550       return JSUint64(*result) == i;
1551     }
1552   }
1553   return false;
1554 }
1555
1556 template<class IntegerType, class CharType, size_t N, class AP>
1557 void
1558 IntegerToString(IntegerType i, jsuint radix, Vector<CharType, N, AP>& result)
1559 {
1560   JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
1561
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);
1566   CharType* cp = end;
1567
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;
1572   do {
1573     IntegerType ii = i / IntegerType(radix);
1574     size_t index = sign * size_t(i - ii * IntegerType(radix));
1575     *--cp = "0123456789abcdefghijklmnopqrstuvwxyz"[index];
1576     i = ii;
1577   } while (i != 0);
1578
1579   if (isNegative)
1580     *--cp = '-';
1581
1582   JS_ASSERT(cp >= buffer);
1583   result.append(cp, end);
1584 }
1585
1586 template<class CharType>
1587 static size_t
1588 strnlen(const CharType* begin, size_t max)
1589 {
1590   for (const CharType* s = begin; s != begin + max; ++s)
1591     if (*s == 0)
1592       return s - begin;
1593
1594   return max;
1595 }
1596
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
1605 //   parent's buffer.
1606 // * If 'parentObj' is null, the new CData object may or may not own its
1607 //   resulting buffer depending on the 'ownResult' argument.
1608 JSBool
1609 ConvertToJS(JSContext* cx,
1610             JSObject* typeObj,
1611             JSObject* parentObj,
1612             void* data,
1613             bool wantPrimitive,
1614             bool ownResult,
1615             jsval* result)
1616 {
1617   JS_ASSERT(!parentObj || CData::IsCData(cx, parentObj));
1618   JS_ASSERT(!parentObj || !ownResult);
1619   JS_ASSERT(!wantPrimitive || !ownResult);
1620
1621   TypeCode typeCode = CType::GetTypeCode(cx, typeObj);
1622
1623   switch (typeCode) {
1624   case TYPE_void_t:
1625     *result = JSVAL_VOID;
1626     break;
1627   case TYPE_bool:
1628     *result = *static_cast<bool*>(data) ? JSVAL_TRUE : JSVAL_FALSE;
1629     break;
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))                  \
1636       return false;                                                            \
1637     break;                                                                     \
1638   }
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. */    \
1642     JSUint64 value;                                                            \
1643     JSObject* proto;                                                           \
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);          \
1648     } else {                                                                   \
1649       value = JSInt64(*static_cast<type*>(data));                              \
1650       /* Get ctypes.Int64.prototype from ctypes.CType.prototype. */            \
1651       proto = CType::GetProtoFromType(cx, typeObj, SLOT_INT64PROTO);           \
1652     }                                                                          \
1653                                                                                \
1654     JSObject* obj = Int64Base::Construct(cx, proto, value,                     \
1655       !numeric_limits<type>::is_signed);                                       \
1656     if (!obj)                                                                  \
1657       return false;                                                            \
1658     *result = OBJECT_TO_JSVAL(obj);                                            \
1659     break;                                                                     \
1660   }
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))                       \
1665       return false;                                                            \
1666     break;                                                                     \
1667   }
1668 #define DEFINE_CHAR_TYPE(name, type, ffiType)                                  \
1669   case TYPE_##name:                                                            \
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));                         \
1673     break;
1674 #include "typedefs.h"
1675   case TYPE_jschar: {
1676     // Convert the jschar to a 1-character string.
1677     JSString* str = JS_NewUCStringCopyN(cx, static_cast<jschar*>(data), 1);
1678     if (!str)
1679       return false;
1680
1681     *result = STRING_TO_JSVAL(str);
1682     break;
1683   }
1684   case TYPE_pointer:
1685   case TYPE_array:
1686   case TYPE_struct: {
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");
1691       return false;
1692     }
1693
1694     JSObject* obj = CData::Create(cx, typeObj, parentObj, data, ownResult);
1695     if (!obj)
1696       return false;
1697
1698     *result = OBJECT_TO_JSVAL(obj);
1699     break;
1700   }
1701   case TYPE_function:
1702     JS_NOT_REACHED("cannot return a FunctionType");
1703   }
1704
1705   return true;
1706 }
1707
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
1713 //    into it.
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.
1719 JSBool
1720 ImplicitConvert(JSContext* cx,
1721                 jsval val,
1722                 JSObject* targetType,
1723                 void* buffer,
1724                 bool isArgument,
1725                 bool* freePointer)
1726 {
1727   JS_ASSERT(CType::IsSizeDefined(cx, targetType));
1728
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);
1736
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);
1742       return true;
1743     }
1744   }
1745
1746   TypeCode targetCode = CType::GetTypeCode(cx, targetType);
1747
1748   switch (targetCode) {
1749   case TYPE_bool: {
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`.
1752     bool result;
1753     if (!jsvalToBool(cx, val, &result))
1754       return TypeError(cx, "boolean", val);
1755     *static_cast<bool*>(buffer) = result;
1756     break;
1757   }
1758 #define DEFINE_INT_TYPE(name, type, ffiType)                                   \
1759   case TYPE_##name: {                                                          \
1760     /* Do not implicitly lose bits. */                                         \
1761     type result;                                                               \
1762     if (!jsvalToInteger(cx, val, &result))                                     \
1763       return TypeError(cx, #name, val);                                        \
1764     *static_cast<type*>(buffer) = result;                                      \
1765     break;                                                                     \
1766   }
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: {                                                          \
1770     type result;                                                               \
1771     if (!jsvalToFloat(cx, val, &result))                                       \
1772       return TypeError(cx, #name, val);                                        \
1773     *static_cast<type*>(buffer) = result;                                      \
1774     break;                                                                     \
1775   }
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'. */              \
1781     type result;                                                               \
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);                                 \
1787       if (!chars)                                                              \
1788         return false;                                                          \
1789       result = chars[0];                                                       \
1790     } else if (!jsvalToInteger(cx, val, &result)) {                            \
1791       return TypeError(cx, #name, val);                                        \
1792     }                                                                          \
1793     *static_cast<type*>(buffer) = result;                                      \
1794     break;                                                                     \
1795   }
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;
1801       break;
1802     }
1803
1804     JSObject* baseType = PointerType::GetBaseType(cx, targetType);
1805     if (sourceData) {
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;
1810
1811       if (sourceCode == TYPE_pointer && voidptrTarget) {
1812         // Autoconvert if targetType is ctypes.voidptr_t.
1813         *static_cast<void**>(buffer) = *static_cast<void**>(sourceBuffer);
1814         break;
1815       }
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;
1822           break;
1823         }
1824       }
1825
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);
1833       if (!sourceChars)
1834         return false;
1835
1836       switch (CType::GetTypeCode(cx, baseType)) {
1837       case TYPE_char:
1838       case TYPE_signed_char:
1839       case TYPE_unsigned_char: {
1840         // Convert from UTF-16 to UTF-8.
1841         size_t nbytes =
1842           js_GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength);
1843         if (nbytes == (size_t) -1)
1844           return false;
1845
1846         char** charBuffer = static_cast<char**>(buffer);
1847         *charBuffer = js_array_new<char>(nbytes + 1);
1848         if (!*charBuffer) {
1849           JS_ReportAllocationOverflow(cx);
1850           return false;
1851         }
1852
1853         ASSERT_OK(js_DeflateStringToUTF8Buffer(cx, sourceChars, sourceLength,
1854                     *charBuffer, &nbytes));
1855         (*charBuffer)[nbytes] = 0;
1856         *freePointer = true;
1857         break;
1858       }
1859       case TYPE_jschar: {
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);
1867           return false;
1868         }
1869
1870         *freePointer = true;
1871         memcpy(*jscharBuffer, sourceChars, sourceLength * sizeof(jschar));
1872         (*jscharBuffer)[sourceLength] = 0;
1873         break;
1874       }
1875       default:
1876         return TypeError(cx, "pointer", val);
1877       }
1878       break;
1879     }
1880     return TypeError(cx, "pointer", val);
1881   }
1882   case TYPE_array: {
1883     JSObject* baseType = ArrayType::GetBaseType(cx, targetType);
1884     size_t targetLength = ArrayType::GetLength(cx, targetType);
1885
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);
1890       if (!sourceChars)
1891         return false;
1892
1893       switch (CType::GetTypeCode(cx, baseType)) {
1894       case TYPE_char:
1895       case TYPE_signed_char:
1896       case TYPE_unsigned_char: {
1897         // Convert from UTF-16 to UTF-8.
1898         size_t nbytes =
1899           js_GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength);
1900         if (nbytes == (size_t) -1)
1901           return false;
1902
1903         if (targetLength < nbytes) {
1904           JS_ReportError(cx, "ArrayType has insufficient length");
1905           return false;
1906         }
1907
1908         char* charBuffer = static_cast<char*>(buffer);
1909         ASSERT_OK(js_DeflateStringToUTF8Buffer(cx, sourceChars, sourceLength,
1910                     charBuffer, &nbytes));
1911
1912         if (targetLength > nbytes)
1913           charBuffer[nbytes] = 0;
1914
1915         break;
1916       }
1917       case TYPE_jschar: {
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");
1922           return false;
1923         }
1924
1925         memcpy(buffer, sourceChars, sourceLength * sizeof(jschar));
1926         if (targetLength > sourceLength)
1927           static_cast<jschar*>(buffer)[sourceLength] = 0;
1928
1929         break;
1930       }
1931       default:
1932         return TypeError(cx, "array", val);
1933       }
1934
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");
1943         return false;
1944       }
1945
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);
1952         return false;
1953       }
1954
1955       for (jsuint i = 0; i < sourceLength; ++i) {
1956         js::AutoValueRooter item(cx);
1957         if (!JS_GetElement(cx, sourceArray, i, item.jsval_addr()))
1958           return false;
1959
1960         char* data = intermediate.get() + elementSize * i;
1961         if (!ImplicitConvert(cx, item.jsval_value(), baseType, data, false, NULL))
1962           return false;
1963       }
1964
1965       memcpy(buffer, intermediate.get(), arraySize);
1966
1967     } else {
1968       // Don't implicitly convert to string. Users can implicitly convert
1969       // with `String(x)` or `""+x`.
1970       return TypeError(cx, "array", val);
1971     }
1972     break;
1973   }
1974   case TYPE_struct: {
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);
1980       if (!iter)
1981         return false;
1982       js::AutoObjectRooter iterroot(cx, iter);
1983
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);
1989         return false;
1990       }
1991
1992       jsid id;
1993       size_t i = 0;
1994       while (1) {
1995         if (!JS_NextProperty(cx, iter, &id))
1996           return false;
1997         if (JSID_IS_VOID(id))
1998           break;
1999
2000         if (!JSID_IS_STRING(id)) {
2001           JS_ReportError(cx, "property name is not a string");
2002           return false;
2003         }
2004
2005         JSFlatString *name = JSID_TO_FLAT_STRING(id);
2006         const FieldInfo* field = StructType::LookupField(cx, targetType, name);
2007         if (!field)
2008           return false;
2009
2010         js::AutoValueRooter prop(cx);
2011         if (!JS_GetPropertyById(cx, obj, id, prop.jsval_addr()))
2012           return false;
2013
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))
2017           return false;
2018
2019         ++i;
2020       }
2021
2022       const FieldInfoHash* fields = StructType::GetFieldInfo(cx, targetType);
2023       if (i != fields->count()) {
2024         JS_ReportError(cx, "missing fields");
2025         return false;
2026       }
2027
2028       memcpy(buffer, intermediate.get(), structSize);
2029       break;
2030     }
2031
2032     return TypeError(cx, "struct", val);
2033   }
2034   case TYPE_void_t:
2035   case TYPE_function:
2036     JS_NOT_REACHED("invalid type");
2037     return false;
2038   }
2039
2040   return true;
2041 }
2042
2043 // Convert jsval 'val' to a C binary representation of CType 'targetType',
2044 // storing the result in 'buffer'. This function is more forceful than
2045 // ImplicitConvert.
2046 JSBool
2047 ExplicitConvert(JSContext* cx, jsval val, JSObject* targetType, void* buffer)
2048 {
2049   // If ImplicitConvert succeeds, use that result.
2050   if (ImplicitConvert(cx, val, targetType, buffer, false, NULL))
2051     return true;
2052
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()))
2058     return false;
2059
2060   // Otherwise, assume soft failure. Clear the pending exception so that we
2061   // can throw a different one as required.
2062   JS_ClearPendingException(cx);
2063
2064   TypeCode type = CType::GetTypeCode(cx, targetType);
2065
2066   switch (type) {
2067   case TYPE_bool: {
2068     // Convert according to the ECMAScript ToBoolean() function.
2069     JSBool result;
2070     ASSERT_OK(JS_ValueToBoolean(cx, val, &result));
2071     *static_cast<bool*>(buffer) = result != JS_FALSE;
2072     break;
2073   }
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. */                   \
2078     type result;                                                               \
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;                                      \
2084     break;                                                                     \
2085   }
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.
2092     uintptr_t result;
2093     if (!jsvalToPtrExplicit(cx, val, &result))
2094       return TypeError(cx, "pointer", val);
2095     *static_cast<uintptr_t*>(buffer) = result;
2096     break;
2097   }
2098   case TYPE_float32_t:
2099   case TYPE_float64_t:
2100   case TYPE_float:
2101   case TYPE_double:
2102   case TYPE_array:
2103   case TYPE_struct:
2104     // ImplicitConvert is sufficient. Re-throw the exception it generated.
2105     JS_SetPendingException(cx, ex.jsval_value());
2106     return false;
2107   case TYPE_void_t:
2108   case TYPE_function:
2109     JS_NOT_REACHED("invalid type");
2110     return false;
2111   }
2112   return true;
2113 }
2114
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]'.
2119 static JSString*
2120 BuildTypeName(JSContext* cx, JSObject* typeObj)
2121 {
2122   AutoString result;
2123
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;
2131   while (1) {
2132     currentGrouping = CType::GetTypeCode(cx, typeObj);
2133     switch (currentGrouping) {
2134     case TYPE_pointer: {
2135       // Pointer types go on the left.
2136       PrependString(result, "*");
2137
2138       typeObj = PointerType::GetBaseType(cx, typeObj);
2139       prevGrouping = currentGrouping;
2140       continue;
2141     }
2142     case TYPE_array: {
2143       if (prevGrouping == TYPE_pointer) {
2144         // Outer type is pointer, inner type is array. Grouping is required.
2145         PrependString(result, "(");
2146         AppendString(result, ")");
2147       } 
2148
2149       // Array types go on the right.
2150       AppendString(result, "[");
2151       size_t length;
2152       if (ArrayType::GetSafeLength(cx, typeObj, &length))
2153         IntegerToString(length, 10, result);
2154
2155       AppendString(result, "]");
2156
2157       typeObj = ArrayType::GetBaseType(cx, typeObj);
2158       prevGrouping = currentGrouping;
2159       continue;
2160     }
2161     case TYPE_function: {
2162       FunctionInfo* fninfo = FunctionType::GetFunctionInfo(cx, typeObj);
2163
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 ");
2170
2171       // Wrap the entire expression so far with parens.
2172       PrependString(result, "(");
2173       AppendString(result, ")");
2174
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, ", ");
2183       }
2184       if (fninfo->mIsVariadic)
2185         AppendString(result, "...");
2186       AppendString(result, ")");
2187
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;
2192       continue;
2193     }
2194     default:
2195       // Either a basic or struct type. Use the type's name as the base type.
2196       break;
2197     }
2198     break;
2199   }
2200
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);
2205 }
2206
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.)
2214 static void
2215 BuildTypeSource(JSContext* cx,
2216                 JSObject* typeObj, 
2217                 bool makeShort, 
2218                 AutoString& result)
2219 {
2220   // Walk the types, building up the toSource() string.
2221   switch (CType::GetTypeCode(cx, typeObj)) {
2222   case TYPE_void_t:
2223 #define DEFINE_TYPE(name, type, ffiType)  \
2224   case TYPE_##name:
2225 #include "typedefs.h"
2226   {
2227     AppendString(result, "ctypes.");
2228     JSString* nameStr = CType::GetName(cx, typeObj);
2229     AppendString(result, nameStr);
2230     break;
2231   }
2232   case TYPE_pointer: {
2233     JSObject* baseType = PointerType::GetBaseType(cx, typeObj);
2234
2235     // Specialcase ctypes.voidptr_t.
2236     if (CType::GetTypeCode(cx, baseType) == TYPE_void_t) {
2237       AppendString(result, "ctypes.voidptr_t");
2238       break;
2239     }
2240
2241     // Recursively build the source string, and append '.ptr'.
2242     BuildTypeSource(cx, baseType, makeShort, result);
2243     AppendString(result, ".ptr");
2244     break;
2245   }
2246   case TYPE_function: {
2247     FunctionInfo* fninfo = FunctionType::GetFunctionInfo(cx, typeObj);
2248
2249     AppendString(result, "ctypes.FunctionType(");
2250
2251     switch (GetABICode(cx, fninfo->mABI)) {
2252     case ABI_DEFAULT:
2253       AppendString(result, "ctypes.default_abi, ");
2254       break;
2255     case ABI_STDCALL:
2256       AppendString(result, "ctypes.stdcall_abi, ");
2257       break;
2258     case ABI_WINAPI:
2259       AppendString(result, "ctypes.winapi_abi, ");
2260       break;
2261     case INVALID_ABI:
2262       JS_NOT_REACHED("invalid abi");
2263       break;
2264     }
2265
2266     // Recursively build the source string describing the function return and
2267     // argument types.
2268     BuildTypeSource(cx, fninfo->mReturnType, true, result);
2269
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, ", ");
2277       }
2278       if (fninfo->mIsVariadic)
2279         AppendString(result, "\"...\"");
2280       AppendString(result, "]");
2281     }
2282
2283     AppendString(result, ")");
2284     break;
2285   }
2286   case TYPE_array: {
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
2289     // is undefined.
2290     JSObject* baseType = ArrayType::GetBaseType(cx, typeObj);
2291     BuildTypeSource(cx, baseType, makeShort, result);
2292     AppendString(result, ".array(");
2293
2294     size_t length;
2295     if (ArrayType::GetSafeLength(cx, typeObj, &length))
2296       IntegerToString(length, 10, result);
2297
2298     AppendString(result, ")");
2299     break;
2300   }
2301   case TYPE_struct: {
2302     JSString* name = CType::GetName(cx, typeObj);
2303
2304     if (makeShort) {
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);
2308       break;
2309     }
2310
2311     // Write the full struct declaration.
2312     AppendString(result, "ctypes.StructType(\"");
2313     AppendString(result, name);
2314     AppendString(result, "\"");
2315
2316     // If it's an opaque struct, we're done.
2317     if (!CType::IsSizeDefined(cx, typeObj)) {
2318       AppendString(result, ")");
2319       break;
2320     }
2321
2322     AppendString(result, ", [");
2323
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))
2328       break;
2329
2330     for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront())
2331       fieldsArray[r.front().value.mIndex] = &r.front();
2332
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, ", ");
2342     }
2343
2344     AppendString(result, "])");
2345     break;
2346   }
2347   }
2348 }
2349
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.)
2360 static JSBool
2361 BuildDataSource(JSContext* cx,
2362                 JSObject* typeObj, 
2363                 void* data, 
2364                 bool isImplicit, 
2365                 AutoString& result)
2366 {
2367   TypeCode type = CType::GetTypeCode(cx, typeObj);
2368   switch (type) {
2369   case TYPE_bool:
2370     if (*static_cast<bool*>(data))
2371       AppendString(result, "true");
2372     else
2373       AppendString(result, "false");
2374     break;
2375 #define DEFINE_INT_TYPE(name, type, ffiType)                                   \
2376   case TYPE_##name:                                                            \
2377     /* Serialize as a primitive decimal integer. */                            \
2378     IntegerToString(*static_cast<type*>(data), 10, result);                    \
2379     break;
2380 #define DEFINE_WRAPPED_INT_TYPE(name, type, ffiType)                           \
2381   case TYPE_##name:                                                            \
2382     /* Serialize as a wrapped decimal integer. */                              \
2383     if (!numeric_limits<type>::is_signed)                                      \
2384       AppendString(result, "ctypes.UInt64(\"");                                \
2385     else                                                                       \
2386       AppendString(result, "ctypes.Int64(\"");                                 \
2387                                                                                \
2388     IntegerToString(*static_cast<type*>(data), 10, result);                    \
2389     AppendString(result, "\")");                                               \
2390     break;
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);                                \
2397     if (!str) {                                                                \
2398       JS_ReportOutOfMemory(cx);                                                \
2399       return false;                                                            \
2400     }                                                                          \
2401                                                                                \
2402     result.append(str, strlen(str));                                           \
2403     break;                                                                     \
2404   }
2405 #define DEFINE_CHAR_TYPE(name, type, ffiType)                                  \
2406   case TYPE_##name:                                                            \
2407     /* Serialize as an integer. */                                             \
2408     IntegerToString(*static_cast<type*>(data), 10, result);                    \
2409     break;
2410 #include "typedefs.h"
2411   case TYPE_jschar: {
2412     // Serialize as a 1-character JS string.
2413     JSString* str = JS_NewUCStringCopyN(cx, static_cast<jschar*>(data), 1);
2414     if (!str)
2415       return false;
2416
2417     // Escape characters, and quote as necessary.
2418     JSString* src = JS_ValueToSource(cx, STRING_TO_JSVAL(str));
2419     if (!src)
2420       return false;
2421
2422     AppendString(result, src);
2423     break;
2424   }
2425   case TYPE_pointer:
2426   case TYPE_function: {
2427     if (isImplicit) {
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, "(");
2432     }
2433
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, "\")");
2439
2440     if (isImplicit)
2441       AppendString(result, ")");
2442
2443     break;
2444   }
2445   case TYPE_array: {
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, "[");
2450
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))
2456         return false;
2457
2458       if (i + 1 < length)
2459         AppendString(result, ", ");
2460     }
2461     AppendString(result, "]");
2462     break;
2463   }
2464   case TYPE_struct: {
2465     if (isImplicit) {
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, "{");
2470     }
2471
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))
2478       return false;
2479
2480     for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront())
2481       fieldsArray[r.front().value.mIndex] = &r.front();
2482
2483     for (size_t i = 0; i < length; ++i) {
2484       const FieldInfoHash::Entry* entry = fieldsArray[i];
2485
2486       if (isImplicit) {
2487         AppendString(result, "\"");
2488         AppendString(result, entry->key);
2489         AppendString(result, "\": ");
2490       }
2491
2492       char* fieldData = static_cast<char*>(data) + entry->value.mOffset;
2493       if (!BuildDataSource(cx, entry->value.mType, fieldData, true, result))
2494         return false;
2495
2496       if (i + 1 != length)
2497         AppendString(result, ", ");
2498     }
2499
2500     if (isImplicit)
2501       AppendString(result, "}");
2502
2503     break;
2504   }
2505   case TYPE_void_t:
2506     JS_NOT_REACHED("invalid type");
2507     break;
2508   }
2509
2510   return true;
2511 }
2512
2513 /*******************************************************************************
2514 ** JSAPI callback function implementations
2515 *******************************************************************************/
2516
2517 JSBool
2518 ConstructAbstract(JSContext* cx,
2519                   uintN argc,
2520                   jsval* vp)
2521 {
2522   // Calling an abstract base class constructor is disallowed.
2523   JS_ReportError(cx, "cannot construct from abstract type");
2524   return JS_FALSE;
2525 }
2526
2527 /*******************************************************************************
2528 ** CType implementation
2529 *******************************************************************************/
2530
2531 JSBool
2532 CType::ConstructData(JSContext* cx,
2533                      uintN argc,
2534                      jsval* vp)
2535 {
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");
2540     return JS_FALSE;
2541   }
2542
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)) {
2548   case TYPE_void_t:
2549     JS_ReportError(cx, "cannot construct from void_t");
2550     return JS_FALSE;
2551   case TYPE_function:
2552     JS_ReportError(cx, "cannot construct from FunctionType; use FunctionType.ptr instead");
2553     return JS_FALSE;
2554   case TYPE_pointer:
2555     return PointerType::ConstructData(cx, obj, argc, vp);
2556   case TYPE_array:
2557     return ArrayType::ConstructData(cx, obj, argc, vp);
2558   case TYPE_struct:
2559     return StructType::ConstructData(cx, obj, argc, vp);
2560   default:
2561     return ConstructBasic(cx, obj, argc, vp);
2562   }
2563 }
2564
2565 JSBool
2566 CType::ConstructBasic(JSContext* cx,
2567                       JSObject* obj,
2568                       uintN argc,
2569                       jsval* vp)
2570 {
2571   if (argc > 1) {
2572     JS_ReportError(cx, "CType constructor takes zero or one argument");
2573     return JS_FALSE;
2574   }
2575
2576   // construct a CData object
2577   JSObject* result = CData::Create(cx, obj, NULL, NULL, true);
2578   if (!result)
2579     return JS_FALSE;
2580
2581   if (argc == 1) {
2582     if (!ExplicitConvert(cx, JS_ARGV(cx, vp)[0], obj, CData::GetData(cx, result)))
2583       return JS_FALSE;
2584   }
2585
2586   JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
2587   return JS_TRUE;
2588 }
2589
2590 JSObject*
2591 CType::Create(JSContext* cx,
2592               JSObject* typeProto,
2593               JSObject* dataProto,
2594               TypeCode type,
2595               JSString* name,
2596               jsval size,
2597               jsval align,
2598               ffi_type* ffiType)
2599 {
2600   JSObject* parent = JS_GetParent(cx, typeProto);
2601   JS_ASSERT(parent);
2602
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);
2620   if (!typeObj)
2621     return NULL;
2622   js::AutoObjectRooter root(cx, typeObj);
2623
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))
2630     return NULL;
2631
2632   if (dataProto) {
2633     // Set up the 'prototype' and 'prototype.constructor' properties.
2634     JSObject* prototype = JS_NewObject(cx, &sCDataProtoClass, dataProto, parent);
2635     if (!prototype)
2636       return NULL;
2637     js::AutoObjectRooter protoroot(cx, prototype);
2638
2639     if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(typeObj),
2640            NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT))
2641       return NULL;
2642
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)))
2646       return NULL;
2647   }
2648
2649   if (!JS_FreezeObject(cx, typeObj))
2650     return NULL;
2651
2652   // Assert a sanity check on size and alignment: size % alignment should always
2653   // be zero.
2654   JS_ASSERT_IF(IsSizeDefined(cx, typeObj),
2655                GetSize(cx, typeObj) % GetAlignment(cx, typeObj) == 0);
2656
2657   return typeObj;
2658 }
2659
2660 JSObject*
2661 CType::DefineBuiltin(JSContext* cx,
2662                      JSObject* parent,
2663                      const char* propName,
2664                      JSObject* typeProto,
2665                      JSObject* dataProto,
2666                      const char* name,
2667                      TypeCode type,
2668                      jsval size,
2669                      jsval align,
2670                      ffi_type* ffiType)
2671 {
2672   JSString* nameStr = JS_NewStringCopyZ(cx, name);
2673   if (!nameStr)
2674     return NULL;
2675   js::AutoStringRooter nameRoot(cx, nameStr);
2676
2677   // Create a new CType object with the common properties and slots.
2678   JSObject* typeObj = Create(cx, typeProto, dataProto, type, nameStr, size,
2679                         align, ffiType);
2680   if (!typeObj)
2681     return NULL;
2682
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))
2686     return NULL;
2687
2688   return typeObj;
2689 }
2690
2691 void
2692 CType::Finalize(JSContext* cx, JSObject* obj)
2693 {
2694   // Make sure our TypeCode slot is legit. If it's not, bail.
2695   jsval slot;
2696   if (!JS_GetReservedSlot(cx, obj, SLOT_TYPECODE, &slot) || JSVAL_IS_VOID(slot))
2697     return;
2698
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)));
2706     break;
2707   }
2708
2709   case TYPE_struct: {
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));
2715     }
2716   }
2717
2718     // Fall through.
2719   case TYPE_array: {
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);
2725       js_delete(ffiType);
2726     }
2727
2728     break;
2729   }
2730   default:
2731     // Nothing to do here.
2732     break;
2733   }
2734 }
2735
2736 void
2737 CType::FinalizeProtoClass(JSContext* cx, JSObject* obj)
2738 {
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.
2744   jsval slot;
2745   if (!JS_GetReservedSlot(cx, obj, SLOT_CLOSURECX, &slot) || JSVAL_IS_VOID(slot))
2746     return;
2747
2748   JSContext* closureCx = static_cast<JSContext*>(JSVAL_TO_PRIVATE(slot));
2749   JS_SetContextThread(closureCx);
2750   JS_DestroyContextNoGC(closureCx);
2751 }
2752
2753 void
2754 CType::Trace(JSTracer* trc, JSObject* obj)
2755 {
2756   JSContext* cx = trc->context;
2757
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))
2761     return;
2762
2763   // The contents of our slots depends on what kind of type we are.
2764   switch (TypeCode(JSVAL_TO_INT(slot))) {
2765   case TYPE_struct: {
2766     ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FIELDINFO, &slot));
2767     if (JSVAL_IS_VOID(slot))
2768       return;
2769
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");
2775     }
2776
2777     break;
2778   }
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))
2783       return;
2784
2785     FunctionInfo* fninfo = static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot));
2786     JS_ASSERT(fninfo);
2787
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");
2793
2794     break;
2795   }
2796   default:
2797     // Nothing to do here.
2798     break;
2799   }
2800 }
2801
2802 bool
2803 CType::IsCType(JSContext* cx, JSObject* obj)
2804 {
2805   return JS_GET_CLASS(cx, obj) == &sCTypeClass;
2806 }
2807
2808 TypeCode
2809 CType::GetTypeCode(JSContext* cx, JSObject* typeObj)
2810 {
2811   JS_ASSERT(IsCType(cx, typeObj));
2812
2813   jsval result;
2814   ASSERT_OK(JS_GetReservedSlot(cx, typeObj, SLOT_TYPECODE, &result));
2815   return TypeCode(JSVAL_TO_INT(result));
2816 }
2817
2818 bool
2819 CType::TypesEqual(JSContext* cx, JSObject* t1, JSObject* t2)
2820 {
2821   JS_ASSERT(IsCType(cx, t1) && IsCType(cx, t2));
2822
2823   // Fast path: check for object equality.
2824   if (t1 == t2)
2825     return true;
2826
2827   // First, perform shallow comparison.
2828   TypeCode c1 = GetTypeCode(cx, t1);
2829   TypeCode c2 = GetTypeCode(cx, t2);
2830   if (c1 != c2)
2831     return false;
2832
2833   // Determine whether the types require shallow or deep comparison.
2834   switch (c1) {
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);
2840   }
2841   case TYPE_function: {
2842     FunctionInfo* f1 = FunctionType::GetFunctionInfo(cx, t1);
2843     FunctionInfo* f2 = FunctionType::GetFunctionInfo(cx, t2);
2844
2845     // Compare abi, return type, and argument types.
2846     if (f1->mABI != f2->mABI)
2847       return false;
2848
2849     if (!TypesEqual(cx, f1->mReturnType, f2->mReturnType))
2850       return false;
2851
2852     if (f1->mArgTypes.length() != f2->mArgTypes.length())
2853       return false;
2854
2855     if (f1->mIsVariadic != f2->mIsVariadic)
2856       return false;
2857
2858     for (size_t i = 0; i < f1->mArgTypes.length(); ++i) {
2859       if (!TypesEqual(cx, f1->mArgTypes[i], f2->mArgTypes[i]))
2860         return false;
2861     }
2862
2863     return true;
2864   }
2865   case TYPE_array: {
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))
2872       return false;
2873
2874     JSObject* b1 = ArrayType::GetBaseType(cx, t1);
2875     JSObject* b2 = ArrayType::GetBaseType(cx, t2);
2876     return TypesEqual(cx, b1, b2);
2877   }
2878   case TYPE_struct:
2879     // Require exact type object equality.
2880     return false;
2881   default:
2882     // Shallow comparison is sufficient.
2883     return true;
2884   }
2885 }
2886
2887 bool
2888 CType::GetSafeSize(JSContext* cx, JSObject* obj, size_t* result)
2889 {
2890   JS_ASSERT(CType::IsCType(cx, obj));
2891
2892   jsval size;
2893   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_SIZE, &size));
2894
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);
2899     return true;
2900   }
2901   if (JSVAL_IS_DOUBLE(size)) {
2902     *result = Convert<size_t>(JSVAL_TO_DOUBLE(size));
2903     return true;
2904   }
2905
2906   JS_ASSERT(JSVAL_IS_VOID(size));
2907   return false;
2908 }
2909
2910 size_t
2911 CType::GetSize(JSContext* cx, JSObject* obj)
2912 {
2913   JS_ASSERT(CType::IsCType(cx, obj));
2914
2915   jsval size;
2916   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_SIZE, &size));
2917
2918   JS_ASSERT(!JSVAL_IS_VOID(size));
2919
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));
2926 }
2927
2928 bool
2929 CType::IsSizeDefined(JSContext* cx, JSObject* obj)
2930 {
2931   JS_ASSERT(CType::IsCType(cx, obj));
2932
2933   jsval size;
2934   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_SIZE, &size));
2935
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);
2940 }
2941
2942 size_t
2943 CType::GetAlignment(JSContext* cx, JSObject* obj)
2944 {
2945   JS_ASSERT(CType::IsCType(cx, obj));
2946
2947   jsval slot;
2948   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_ALIGN, &slot));
2949   return static_cast<size_t>(JSVAL_TO_INT(slot));
2950 }
2951
2952 ffi_type*
2953 CType::GetFFIType(JSContext* cx, JSObject* obj)
2954 {
2955   JS_ASSERT(CType::IsCType(cx, obj));
2956
2957   jsval slot;
2958   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FFITYPE, &slot));
2959
2960   if (!JSVAL_IS_VOID(slot)) {
2961     return static_cast<ffi_type*>(JSVAL_TO_PRIVATE(slot));
2962   }
2963
2964   AutoPtr<ffi_type> result;
2965   switch (CType::GetTypeCode(cx, obj)) {
2966   case TYPE_array:
2967     result = ArrayType::BuildFFIType(cx, obj);
2968     break;
2969
2970   case TYPE_struct:
2971     result = StructType::BuildFFIType(cx, obj);
2972     break;
2973
2974   default:
2975     JS_NOT_REACHED("simple types must have an ffi_type");
2976   }
2977
2978   if (!result ||
2979       !JS_SetReservedSlot(cx, obj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(result.get())))
2980     return NULL;
2981
2982   return result.forget();
2983 }
2984
2985 JSString*
2986 CType::GetName(JSContext* cx, JSObject* obj)
2987 {
2988   JS_ASSERT(CType::IsCType(cx, obj));
2989
2990   jsval string;
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)))
2996       return NULL;
2997
2998     return name;
2999   }
3000
3001   return JSVAL_TO_STRING(string);
3002 }
3003
3004 JSObject*
3005 CType::GetProtoFromCtor(JSContext* cx, JSObject* obj, CTypeProtoSlot slot)
3006 {
3007   // Get ctypes.{Pointer,Array,Struct}Type.prototype from a reserved slot
3008   // on the type constructor.
3009   jsval protoslot;
3010   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FN_CTORPROTO, &protoslot));
3011   JSObject* proto = JSVAL_TO_OBJECT(protoslot);
3012   JS_ASSERT(proto);
3013   JS_ASSERT(JS_GET_CLASS(cx, proto) == &sCTypeProtoClass);
3014
3015   // Get the desired prototype.
3016   jsval result;
3017   ASSERT_OK(JS_GetReservedSlot(cx, proto, slot, &result));
3018   return JSVAL_TO_OBJECT(result);
3019 }
3020
3021 JSObject*
3022 CType::GetProtoFromType(JSContext* cx, JSObject* obj, CTypeProtoSlot slot)
3023 {
3024   JS_ASSERT(IsCType(cx, obj));
3025
3026   // Get the prototype of the type object.
3027   JSObject* proto = JS_GetPrototype(cx, obj);
3028   JS_ASSERT(proto);
3029   JS_ASSERT(JS_GET_CLASS(cx, proto) == &sCTypeProtoClass);
3030
3031   // Get the requested ctypes.{Pointer,Array,Struct,Function}Type.prototype.
3032   jsval result;
3033   ASSERT_OK(JS_GetReservedSlot(cx, proto, slot, &result));
3034   return JSVAL_TO_OBJECT(result);
3035 }
3036
3037 JSBool
3038 CType::PrototypeGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
3039 {
3040   if (!CType::IsCType(cx, obj)) {
3041     JS_ReportError(cx, "not a CType");
3042     return JS_FALSE;
3043   }
3044
3045   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_PROTO, vp));
3046   JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp) || JSVAL_IS_VOID(*vp));
3047   return JS_TRUE;
3048 }
3049
3050 JSBool
3051 CType::NameGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
3052 {
3053   if (!CType::IsCType(cx, obj)) {
3054     JS_ReportError(cx, "not a CType");
3055     return JS_FALSE;
3056   }
3057
3058   JSString* name = CType::GetName(cx, obj);
3059   if (!name)
3060     return JS_FALSE;
3061
3062   *vp = STRING_TO_JSVAL(name);
3063   return JS_TRUE;
3064 }
3065
3066 JSBool
3067 CType::SizeGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
3068 {
3069   if (!CType::IsCType(cx, obj)) {
3070     JS_ReportError(cx, "not a CType");
3071     return JS_FALSE;
3072   }
3073
3074   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_SIZE, vp));
3075   JS_ASSERT(JSVAL_IS_NUMBER(*vp) || JSVAL_IS_VOID(*vp));
3076   return JS_TRUE;
3077 }
3078
3079 JSBool
3080 CType::PtrGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
3081 {
3082   if (!CType::IsCType(cx, obj)) {
3083     JS_ReportError(cx, "not a CType");
3084     return JS_FALSE;
3085   }
3086
3087   JSObject* pointerType = PointerType::CreateInternal(cx, obj);
3088   if (!pointerType)
3089     return JS_FALSE;
3090
3091   *vp = OBJECT_TO_JSVAL(pointerType);
3092   return JS_TRUE;
3093 }
3094
3095 JSBool
3096 CType::CreateArray(JSContext* cx, uintN argc, jsval* vp)
3097 {
3098   JSObject* baseType = JS_THIS_OBJECT(cx, vp);
3099   if (!baseType || !CType::IsCType(cx, baseType)) {
3100     JS_ReportError(cx, "not a CType");
3101     return JS_FALSE;
3102   }
3103
3104   // Construct and return a new ArrayType object.
3105   if (argc > 1) {
3106     JS_ReportError(cx, "array takes zero or one argument");
3107     return JS_FALSE;
3108   }
3109
3110   // Convert the length argument to a size_t.
3111   jsval* argv = JS_ARGV(cx, vp);
3112   size_t length = 0;
3113   if (argc == 1 && !jsvalToSize(cx, argv[0], false, &length)) {
3114     JS_ReportError(cx, "argument must be a nonnegative integer");
3115     return JS_FALSE;
3116   }
3117
3118   JSObject* result = ArrayType::CreateInternal(cx, baseType, length, argc == 1);
3119   if (!result)
3120     return JS_FALSE;
3121
3122   JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
3123   return JS_TRUE;
3124 }
3125
3126 JSBool
3127 CType::ToString(JSContext* cx, uintN argc, jsval* vp)
3128 {
3129   JSObject* obj = JS_THIS_OBJECT(cx, vp);
3130   if (!obj || !CType::IsCType(cx, obj)) {
3131     JS_ReportError(cx, "not a CType");
3132     return JS_FALSE;
3133   }
3134
3135   AutoString type;
3136   AppendString(type, "type ");
3137   AppendString(type, GetName(cx, obj));
3138
3139   JSString* result = NewUCString(cx, type);
3140   if (!result)
3141     return JS_FALSE;
3142   
3143   JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
3144   return JS_TRUE;
3145 }
3146
3147 JSBool
3148 CType::ToSource(JSContext* cx, uintN argc, jsval* vp)
3149 {
3150   JSObject* obj = JS_THIS_OBJECT(cx, vp);
3151   if (!obj || !CType::IsCType(cx, obj)) {
3152     JS_ReportError(cx, "not a CType");
3153     return JS_FALSE;
3154   }
3155
3156   AutoString source;
3157   BuildTypeSource(cx, obj, false, source);
3158   JSString* result = NewUCString(cx, source);
3159   if (!result)
3160     return JS_FALSE;
3161   
3162   JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
3163   return JS_TRUE;
3164 }
3165
3166 JSBool
3167 CType::HasInstance(JSContext* cx, JSObject* obj, const jsval* v, JSBool* bp)
3168 {
3169   JS_ASSERT(CType::IsCType(cx, obj));
3170
3171   jsval slot;
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);
3176
3177   *bp = JS_FALSE;
3178   if (JSVAL_IS_PRIMITIVE(*v))
3179     return JS_TRUE;
3180
3181   JSObject* proto = JSVAL_TO_OBJECT(*v);
3182   while ((proto = JS_GetPrototype(cx, proto))) {
3183     if (proto == prototype) {
3184       *bp = JS_TRUE;
3185       break;
3186     }
3187   }
3188   return JS_TRUE;
3189 }
3190
3191 /*******************************************************************************
3192 ** PointerType implementation
3193 *******************************************************************************/
3194
3195 JSBool
3196 PointerType::Create(JSContext* cx, uintN argc, jsval* vp)
3197 {
3198   // Construct and return a new PointerType object.
3199   if (argc != 1) {
3200     JS_ReportError(cx, "PointerType takes one argument");
3201     return JS_FALSE;
3202   }
3203
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");
3207     return JS_FALSE;
3208   }
3209
3210   JSObject* result = CreateInternal(cx, JSVAL_TO_OBJECT(arg));
3211   if (!result)
3212     return JS_FALSE;
3213
3214   JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
3215   return JS_TRUE;
3216 }
3217
3218 JSObject*
3219 PointerType::CreateInternal(JSContext* cx, JSObject* baseType)
3220 {
3221   // check if we have a cached PointerType on our base CType.
3222   jsval slot;
3223   ASSERT_OK(JS_GetReservedSlot(cx, baseType, SLOT_PTR, &slot));
3224   if (!JSVAL_IS_VOID(slot))
3225     return JSVAL_TO_OBJECT(slot);
3226
3227   // Get ctypes.PointerType.prototype and the common prototype for CData objects
3228   // of this type.
3229   JSObject* typeProto;
3230   JSObject* dataProto;
3231   typeProto = CType::GetProtoFromType(cx, baseType, SLOT_POINTERPROTO);
3232   dataProto = CType::GetProtoFromType(cx, baseType, SLOT_POINTERDATAPROTO);
3233
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),
3238                         &ffi_type_pointer);
3239   if (!typeObj)
3240     return NULL;
3241   js::AutoObjectRooter root(cx, typeObj);
3242
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)))
3245     return NULL;
3246
3247   // Finally, cache our newly-created PointerType on our pointed-to CType.
3248   if (!JS_SetReservedSlot(cx, baseType, SLOT_PTR, OBJECT_TO_JSVAL(typeObj)))
3249     return NULL;
3250
3251   return typeObj;
3252 }
3253
3254 JSBool
3255 PointerType::ConstructData(JSContext* cx,
3256                            JSObject* obj,
3257                            uintN argc,
3258                            jsval* vp)
3259 {
3260   if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_pointer) {
3261     JS_ReportError(cx, "not a PointerType");
3262     return JS_FALSE;
3263   }
3264
3265   if (argc > 2) {
3266     JS_ReportError(cx, "constructor takes 0, 1, or 2 arguments");
3267     return JS_FALSE;
3268   }
3269
3270   JSObject* result = CData::Create(cx, obj, NULL, NULL, true);
3271   if (!result)
3272     return JS_FALSE;
3273
3274   // Set return value early, must not observe *vp after
3275   JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
3276
3277   if (argc == 0) {
3278     // Construct a null pointer.
3279     return JS_TRUE;
3280   }
3281
3282   jsval* argv = JS_ARGV(cx, vp);
3283   if (argc >= 1) {
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;
3291       if (argc == 2) {
3292         if (JSVAL_IS_OBJECT(argv[1])) {
3293           thisObj = JSVAL_TO_OBJECT(argv[1]);
3294         } else if (!JS_ValueToObject(cx, argv[1], &thisObj)) {
3295           return JS_FALSE;
3296         }
3297       }
3298
3299       JSObject* fnObj = JSVAL_TO_OBJECT(argv[0]);
3300       return FunctionType::ConstructData(cx, baseObj, result, fnObj, thisObj);
3301     }
3302
3303     if (argc == 2) {
3304       JS_ReportError(cx, "first argument must be a function");
3305       return JS_FALSE;
3306     }
3307   }
3308
3309   // Construct from a raw pointer value.
3310   return ExplicitConvert(cx, argv[0], obj, CData::GetData(cx, result));
3311 }
3312
3313 JSObject*
3314 PointerType::GetBaseType(JSContext* cx, JSObject* obj)
3315 {
3316   JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_pointer);
3317
3318   jsval type;
3319   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_TARGET_T, &type));
3320   JS_ASSERT(!JSVAL_IS_NULL(type));
3321   return JSVAL_TO_OBJECT(type);
3322 }
3323
3324 JSBool
3325 PointerType::TargetTypeGetter(JSContext* cx,
3326                               JSObject* obj,
3327                               jsid idval,
3328                               jsval* vp)
3329 {
3330   if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_pointer) {
3331     JS_ReportError(cx, "not a PointerType");
3332     return JS_FALSE;
3333   }
3334
3335   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_TARGET_T, vp));
3336   JS_ASSERT(JSVAL_IS_OBJECT(*vp));
3337   return JS_TRUE;
3338 }
3339
3340 JSBool
3341 PointerType::IsNull(JSContext* cx, uintN argc, jsval* vp)
3342 {
3343   JSObject* obj = JS_THIS_OBJECT(cx, vp);
3344   if (!obj || !CData::IsCData(cx, obj)) {
3345     JS_ReportError(cx, "not a CData");
3346     return JS_FALSE;
3347   }
3348
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");
3353     return JS_FALSE;
3354   }
3355
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);
3359   return JS_TRUE;
3360 }
3361
3362 JSBool
3363 PointerType::ContentsGetter(JSContext* cx,
3364                             JSObject* obj,
3365                             jsid idval,
3366                             jsval* vp)
3367 {
3368   if (!CData::IsCData(cx, obj)) {
3369     JS_ReportError(cx, "not a CData");
3370     return JS_FALSE;
3371   }
3372
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");
3377     return JS_FALSE;
3378   }
3379
3380   JSObject* baseType = GetBaseType(cx, typeObj);
3381   if (!CType::IsSizeDefined(cx, baseType)) {
3382     JS_ReportError(cx, "cannot get contents of undefined size");
3383     return JS_FALSE;
3384   }
3385
3386   void* data = *static_cast<void**>(CData::GetData(cx, obj));
3387   if (data == NULL) {
3388     JS_ReportError(cx, "cannot read contents of null pointer");
3389     return JS_FALSE;
3390   }
3391
3392   jsval result;
3393   if (!ConvertToJS(cx, baseType, NULL, data, false, false, &result))
3394     return JS_FALSE;
3395
3396   JS_SET_RVAL(cx, vp, result);
3397   return JS_TRUE;
3398 }
3399
3400 JSBool
3401 PointerType::ContentsSetter(JSContext* cx,
3402                             JSObject* obj,
3403                             jsid idval,
3404                             JSBool strict,
3405                             jsval* vp)
3406 {
3407   if (!CData::IsCData(cx, obj)) {
3408     JS_ReportError(cx, "not a CData");
3409     return JS_FALSE;
3410   }
3411
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");
3416     return JS_FALSE;
3417   }
3418
3419   JSObject* baseType = GetBaseType(cx, typeObj);
3420   if (!CType::IsSizeDefined(cx, baseType)) {
3421     JS_ReportError(cx, "cannot set contents of undefined size");
3422     return JS_FALSE;
3423   }
3424
3425   void* data = *static_cast<void**>(CData::GetData(cx, obj));
3426   if (data == NULL) {
3427     JS_ReportError(cx, "cannot write contents to null pointer");
3428     return JS_FALSE;
3429   }
3430
3431   return ImplicitConvert(cx, *vp, baseType, data, false, NULL);
3432 }
3433
3434 /*******************************************************************************
3435 ** ArrayType implementation
3436 *******************************************************************************/
3437
3438 JSBool
3439 ArrayType::Create(JSContext* cx, uintN argc, jsval* vp)
3440 {
3441   // Construct and return a new ArrayType object.
3442   if (argc < 1 || argc > 2) {
3443     JS_ReportError(cx, "ArrayType takes one or two arguments");
3444     return JS_FALSE;
3445   }
3446
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");
3451     return JS_FALSE;
3452   }
3453
3454   // Convert the length argument to a size_t.
3455   size_t length = 0;
3456   if (argc == 2 && !jsvalToSize(cx, argv[1], false, &length)) {
3457     JS_ReportError(cx, "second argument must be a nonnegative integer");
3458     return JS_FALSE;
3459   }
3460
3461   JSObject* baseType = JSVAL_TO_OBJECT(argv[0]);
3462   JSObject* result = CreateInternal(cx, baseType, length, argc == 2);
3463   if (!result)
3464     return JS_FALSE;
3465
3466   JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
3467   return JS_TRUE;
3468 }
3469
3470 JSObject*
3471 ArrayType::CreateInternal(JSContext* cx,
3472                           JSObject* baseType,
3473                           size_t length,
3474                           bool lengthDefined)
3475 {
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);
3480
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.
3484   size_t baseSize;
3485   if (!CType::GetSafeSize(cx, baseType, &baseSize)) {
3486     JS_ReportError(cx, "base size must be defined");
3487     return NULL;
3488   }
3489
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");
3497       return NULL;
3498     }
3499     if (!SizeTojsval(cx, size, &sizeVal) ||
3500         !SizeTojsval(cx, length, &lengthVal))
3501       return NULL;
3502   }
3503
3504   size_t align = CType::GetAlignment(cx, baseType);
3505
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);
3509   if (!typeObj)
3510     return NULL;
3511   js::AutoObjectRooter root(cx, typeObj);
3512
3513   // Set the element type.
3514   if (!JS_SetReservedSlot(cx, typeObj, SLOT_ELEMENT_T, OBJECT_TO_JSVAL(baseType)))
3515     return NULL;
3516
3517   // Set the length.
3518   if (!JS_SetReservedSlot(cx, typeObj, SLOT_LENGTH, lengthVal))
3519     return NULL;
3520
3521   return typeObj;
3522 }
3523
3524 JSBool
3525 ArrayType::ConstructData(JSContext* cx,
3526                          JSObject* obj,
3527                          uintN argc,
3528                          jsval* vp)
3529 {
3530   if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_array) {
3531     JS_ReportError(cx, "not an ArrayType");
3532     return JS_FALSE;
3533   }
3534
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;
3538
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)) {
3542     if (argc > 1) {
3543       JS_ReportError(cx, "constructor takes zero or one argument");
3544       return JS_FALSE;
3545     }
3546
3547   } else {
3548     if (argc != 1) {
3549       JS_ReportError(cx, "constructor takes one argument");
3550       return JS_FALSE;
3551     }
3552
3553     JSObject* baseType = GetBaseType(cx, obj);
3554
3555     jsval* argv = JS_ARGV(cx, vp);
3556     size_t length;
3557     if (jsvalToSize(cx, argv[0], false, &length)) {
3558       // Have a length, rather than an object to initialize from.
3559       convertObject = false;
3560
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");
3569         return JS_FALSE;
3570       }
3571
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);
3578       if (!sourceChars)
3579         return false;
3580
3581       switch (CType::GetTypeCode(cx, baseType)) {
3582       case TYPE_char:
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)
3588           return false;
3589
3590         ++length;
3591         break;
3592       }
3593       case TYPE_jschar:
3594         length = sourceLength + 1;
3595         break;
3596       default:
3597         return TypeError(cx, "array", argv[0]);
3598       }
3599
3600     } else {
3601       JS_ReportError(cx, "argument must be an array object or length");
3602       return JS_FALSE;
3603     }
3604
3605     // Construct a new ArrayType of defined length, for the new CData object.
3606     obj = CreateInternal(cx, baseType, length, true);
3607     if (!obj)
3608       return JS_FALSE;
3609   }
3610
3611   // Root the CType object, in case we created one above.
3612   js::AutoObjectRooter root(cx, obj);
3613
3614   JSObject* result = CData::Create(cx, obj, NULL, NULL, true);
3615   if (!result)
3616     return JS_FALSE;
3617
3618   JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
3619
3620   if (convertObject) {
3621     if (!ExplicitConvert(cx, JS_ARGV(cx, vp)[0], obj, CData::GetData(cx, result)))
3622       return JS_FALSE;
3623   }
3624
3625   return JS_TRUE;
3626 }
3627
3628 JSObject*
3629 ArrayType::GetBaseType(JSContext* cx, JSObject* obj)
3630 {
3631   JS_ASSERT(CType::IsCType(cx, obj));
3632   JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_array);
3633
3634   jsval type;
3635   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_ELEMENT_T, &type));
3636   JS_ASSERT(!JSVAL_IS_NULL(type));
3637   return JSVAL_TO_OBJECT(type);
3638 }
3639
3640 bool
3641 ArrayType::GetSafeLength(JSContext* cx, JSObject* obj, size_t* result)
3642 {
3643   JS_ASSERT(CType::IsCType(cx, obj));
3644   JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_array);
3645
3646   jsval length;
3647   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_LENGTH, &length));
3648
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);
3653     return true;
3654   }
3655   if (JSVAL_IS_DOUBLE(length)) {
3656     *result = Convert<size_t>(JSVAL_TO_DOUBLE(length));
3657     return true;
3658   }
3659
3660   JS_ASSERT(JSVAL_IS_VOID(length));
3661   return false;
3662 }
3663
3664 size_t
3665 ArrayType::GetLength(JSContext* cx, JSObject* obj)
3666 {
3667   JS_ASSERT(CType::IsCType(cx, obj));
3668   JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_array);
3669
3670   jsval length;
3671   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_LENGTH, &length));
3672
3673   JS_ASSERT(!JSVAL_IS_VOID(length));
3674
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));
3681 }
3682
3683 ffi_type*
3684 ArrayType::BuildFFIType(JSContext* cx, JSObject* obj)
3685 {
3686   JS_ASSERT(CType::IsCType(cx, obj));
3687   JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_array);
3688   JS_ASSERT(CType::IsSizeDefined(cx, obj));
3689
3690   JSObject* baseType = ArrayType::GetBaseType(cx, obj);
3691   ffi_type* ffiBaseType = CType::GetFFIType(cx, baseType);
3692   if (!ffiBaseType)
3693     return NULL;
3694
3695   size_t length = ArrayType::GetLength(cx, obj);
3696
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>());
3705   if (!ffiType) {
3706     JS_ReportOutOfMemory(cx);
3707     return NULL;
3708   }
3709
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);
3716     return NULL;
3717   }
3718
3719   for (size_t i = 0; i < length; ++i)
3720     ffiType->elements[i] = ffiBaseType;
3721   ffiType->elements[length] = NULL;
3722
3723   return ffiType.forget();
3724 }
3725
3726 JSBool
3727 ArrayType::ElementTypeGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
3728 {
3729   if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_array) {
3730     JS_ReportError(cx, "not an ArrayType");
3731     return JS_FALSE;
3732   }
3733
3734   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_ELEMENT_T, vp));
3735   JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
3736   return JS_TRUE;
3737 }
3738
3739 JSBool
3740 ArrayType::LengthGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
3741 {
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);
3746
3747   if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_array) {
3748     JS_ReportError(cx, "not an ArrayType");
3749     return JS_FALSE;
3750   }
3751
3752   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_LENGTH, vp));
3753   JS_ASSERT(JSVAL_IS_NUMBER(*vp) || JSVAL_IS_VOID(*vp));
3754   return JS_TRUE;
3755 }
3756
3757 JSBool
3758 ArrayType::Getter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
3759 {
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");
3763     return JS_FALSE;
3764   }
3765
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)
3770     return JS_TRUE;
3771
3772   // Convert the index to a size_t and bounds-check it.
3773   size_t index;
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.
3779     return JS_TRUE;
3780   }
3781   if (!ok || index >= length) {
3782     JS_ReportError(cx, "invalid index");
3783     return JS_FALSE;
3784   }
3785
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);
3790 }
3791
3792 JSBool
3793 ArrayType::Setter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict, jsval* vp)
3794 {
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");
3798     return JS_FALSE;
3799   }
3800
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)
3805     return JS_TRUE;
3806
3807   // Convert the index to a size_t and bounds-check it.
3808   size_t index;
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.
3814     return JS_TRUE;
3815   }
3816   if (!ok || index >= length) {
3817     JS_ReportError(cx, "invalid index");
3818     return JS_FALSE;
3819   }
3820
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);
3825 }
3826
3827 JSBool
3828 ArrayType::AddressOfElement(JSContext* cx, uintN argc, jsval* vp)
3829 {
3830   JSObject* obj = JS_THIS_OBJECT(cx, vp);
3831   if (!obj || !CData::IsCData(cx, obj)) {
3832     JS_ReportError(cx, "not a CData");
3833     return JS_FALSE;
3834   }
3835
3836   JSObject* typeObj = CData::GetCType(cx, obj);
3837   if (CType::GetTypeCode(cx, typeObj) != TYPE_array) {
3838     JS_ReportError(cx, "not an ArrayType");
3839     return JS_FALSE;
3840   }
3841
3842   if (argc != 1) {
3843     JS_ReportError(cx, "addressOfElement takes one argument");
3844     return JS_FALSE;
3845   }
3846
3847   JSObject* baseType = GetBaseType(cx, typeObj);
3848   JSObject* pointerType = PointerType::CreateInternal(cx, baseType);
3849   if (!pointerType)
3850     return JS_FALSE;
3851   js::AutoObjectRooter root(cx, pointerType);
3852
3853   // Create a PointerType CData object containing null.
3854   JSObject* result = CData::Create(cx, pointerType, NULL, NULL, true);
3855   if (!result)
3856     return JS_FALSE;
3857
3858   JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
3859
3860   // Convert the index to a size_t and bounds-check it.
3861   size_t index;
3862   size_t length = GetLength(cx, typeObj);
3863   if (!jsvalToSize(cx, JS_ARGV(cx, vp)[0], false, &index) ||
3864       index >= length) {
3865     JS_ReportError(cx, "invalid index");
3866     return JS_FALSE;
3867   }
3868
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;
3873   return JS_TRUE;
3874 }
3875
3876 /*******************************************************************************
3877 ** StructType implementation
3878 *******************************************************************************/
3879
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)
3884 {
3885   if (JSVAL_IS_PRIMITIVE(val)) {
3886     JS_ReportError(cx, "struct field descriptors require a valid name and type");
3887     return NULL;
3888   }
3889
3890   JSObject* obj = JSVAL_TO_OBJECT(val);
3891   JSObject* iter = JS_NewPropertyIterator(cx, obj);
3892   if (!iter)
3893     return NULL;
3894   js::AutoObjectRooter iterroot(cx, iter);
3895
3896   jsid nameid;
3897   if (!JS_NextProperty(cx, iter, &nameid))
3898     return NULL;
3899   if (JSID_IS_VOID(nameid)) {
3900     JS_ReportError(cx, "struct field descriptors require a valid name and type");
3901     return NULL;
3902   }
3903
3904   if (!JSID_IS_STRING(nameid)) {
3905     JS_ReportError(cx, "struct field descriptors require a valid name and type");
3906     return NULL;
3907   }
3908
3909   // make sure we have one, and only one, property
3910   jsid id;
3911   if (!JS_NextProperty(cx, iter, &id))
3912     return NULL;
3913   if (!JSID_IS_VOID(id)) {
3914     JS_ReportError(cx, "struct field descriptors must contain one property");
3915     return NULL;
3916   }
3917
3918   js::AutoValueRooter propVal(cx);
3919   if (!JS_GetPropertyById(cx, obj, nameid, propVal.jsval_addr()))
3920     return NULL;
3921
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");
3925     return NULL;
3926   }
3927
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());
3932   size_t size;
3933   if (!CType::GetSafeSize(cx, *typeObj, &size) || size == 0) {
3934     JS_ReportError(cx, "struct field types must have defined and nonzero size");
3935     return NULL;
3936   }
3937
3938   return JSID_TO_FLAT_STRING(nameid);
3939 }
3940
3941 // For a struct field with 'name' and 'type', add an element of the form
3942 // { name : type }.
3943 static JSBool
3944 AddFieldToArray(JSContext* cx,
3945                 jsval* element,
3946                 JSFlatString* name,
3947                 JSObject* typeObj)
3948 {
3949   JSObject* fieldObj = JS_NewObject(cx, NULL, NULL, NULL);
3950   if (!fieldObj)
3951     return false;
3952
3953   *element = OBJECT_TO_JSVAL(fieldObj);
3954
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))
3959     return false;
3960
3961   return JS_FreezeObject(cx, fieldObj);
3962 }
3963
3964 JSBool
3965 StructType::Create(JSContext* cx, uintN argc, jsval* vp)
3966 {
3967   // Construct and return a new StructType object.
3968   if (argc < 1 || argc > 2) {
3969     JS_ReportError(cx, "StructType takes one or two arguments");
3970     return JS_FALSE;
3971   }
3972
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");
3977     return JS_FALSE;
3978   }
3979
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);
3983
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);
3989   if (!result)
3990     return JS_FALSE;
3991   js::AutoObjectRooter root(cx, result);
3992
3993   if (argc == 2) {
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");
3997       return JS_FALSE;
3998     }
3999
4000     // Define the struct fields.
4001     if (!DefineInternal(cx, result, JSVAL_TO_OBJECT(argv[1])))
4002       return JS_FALSE;
4003   }
4004
4005   JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
4006   return JS_TRUE;
4007 }
4008
4009 JSBool
4010 StructType::DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj)
4011 {
4012   jsuint len;
4013   ASSERT_OK(JS_GetArrayLength(cx, fieldsObj, &len));
4014
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);
4019
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);
4024   if (!prototype)
4025     return JS_FALSE;
4026   js::AutoObjectRooter protoroot(cx, prototype);
4027
4028   if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(typeObj),
4029          NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT))
4030     return JS_FALSE;
4031
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);
4040     return JS_FALSE;
4041   }
4042   js::AutoArrayRooter fieldRoots(cx, fieldRootsArray.length(), 
4043     fieldRootsArray.begin());
4044
4045   // Process the field types.
4046   size_t structSize, structAlign;
4047   if (len != 0) {
4048     structSize = 0;
4049     structAlign = 0;
4050
4051     for (jsuint i = 0; i < len; ++i) {
4052       js::AutoValueRooter item(cx);
4053       if (!JS_GetElement(cx, fieldsObj, i, item.jsval_addr()))
4054         return JS_FALSE;
4055
4056       JSObject* fieldType = NULL;
4057       JSFlatString* name = ExtractStructField(cx, item.jsval_value(), &fieldType);
4058       if (!name)
4059         return JS_FALSE;
4060       fieldRootsArray[i] = OBJECT_TO_JSVAL(fieldType);
4061
4062       // Make sure each field name is unique, and add it to the hash.
4063       FieldInfoHash::AddPtr entryPtr = fields->lookupForAdd(name);
4064       if (entryPtr) {
4065         JS_ReportError(cx, "struct fields must have unique names");
4066         return JS_FALSE;
4067       }
4068       ASSERT_OK(fields->add(entryPtr, name, FieldInfo()));
4069       FieldInfo& info = entryPtr->value;
4070       info.mType = fieldType;
4071       info.mIndex = i;
4072
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))
4078         return JS_FALSE;
4079
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");
4088         return JS_FALSE;
4089       }
4090       info.mOffset = fieldOffset;
4091       structSize = fieldOffset + fieldSize;
4092
4093       if (fieldAlign > structAlign)
4094         structAlign = fieldAlign;
4095     }
4096
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");
4101       return JS_FALSE;
4102     }
4103     structSize = structTail;
4104
4105   } else {
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.
4110     structSize = 1;
4111     structAlign = 1;
4112   }
4113
4114   jsval sizeVal;
4115   if (!SizeTojsval(cx, structSize, &sizeVal))
4116     return JS_FALSE;
4117
4118   if (!JS_SetReservedSlot(cx, typeObj, SLOT_FIELDINFO,
4119          PRIVATE_TO_JSVAL(fields.get())))
4120     return JS_FALSE;
4121   fields.forget();
4122
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)))
4127     return JS_FALSE;
4128
4129   return JS_TRUE;
4130 }
4131
4132 ffi_type*
4133 StructType::BuildFFIType(JSContext* cx, JSObject* obj)
4134 {
4135   JS_ASSERT(CType::IsCType(cx, obj));
4136   JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_struct);
4137   JS_ASSERT(CType::IsSizeDefined(cx, obj));
4138
4139   const FieldInfoHash* fields = GetFieldInfo(cx, obj);
4140   size_t len = fields->count();
4141
4142   size_t structSize = CType::GetSize(cx, obj);
4143   size_t structAlign = CType::GetAlignment(cx, obj);
4144
4145   AutoPtr<ffi_type> ffiType(js_new<ffi_type>());
4146   if (!ffiType) {
4147     JS_ReportOutOfMemory(cx);
4148     return NULL;
4149   }
4150   ffiType->type = FFI_TYPE_STRUCT;
4151
4152   AutoPtr<ffi_type*>::Array elements;
4153   if (len != 0) {
4154     elements = js_array_new<ffi_type*>(len + 1);
4155     if (!elements) {
4156       JS_ReportOutOfMemory(cx);
4157       return NULL;
4158     }
4159     elements[len] = NULL;
4160
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);
4164       if (!fieldType)
4165         return NULL;
4166       elements[entry.value.mIndex] = fieldType;
4167     }
4168
4169   } else {
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);
4174     if (!elements) {
4175       JS_ReportOutOfMemory(cx);
4176       return NULL;
4177     }
4178     elements[0] = &ffi_type_uint8;
4179     elements[1] = NULL;
4180   }
4181
4182   ffiType->elements = elements.get();
4183
4184 #ifdef DEBUG
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.
4188   ffi_cif cif;
4189   ffiType->size = 0;
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);
4195 #else
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;
4202 #endif
4203
4204   elements.forget();
4205   return ffiType.forget();
4206 }
4207
4208 JSBool
4209 StructType::Define(JSContext* cx, uintN argc, jsval* vp)
4210 {
4211   JSObject* obj = JS_THIS_OBJECT(cx, vp);
4212   if (!obj ||
4213       !CType::IsCType(cx, obj) ||
4214       CType::GetTypeCode(cx, obj) != TYPE_struct) {
4215     JS_ReportError(cx, "not a StructType");
4216     return JS_FALSE;
4217   }
4218
4219   if (CType::IsSizeDefined(cx, obj)) {
4220     JS_ReportError(cx, "StructType has already been defined");
4221     return JS_FALSE;
4222   }
4223
4224   if (argc != 1) {
4225     JS_ReportError(cx, "define takes one argument");
4226     return JS_FALSE;
4227   }
4228
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");
4233     return JS_FALSE;
4234   }
4235
4236   return DefineInternal(cx, obj, JSVAL_TO_OBJECT(arg));
4237 }
4238
4239 JSBool
4240 StructType::ConstructData(JSContext* cx,
4241                           JSObject* obj,
4242                           uintN argc,
4243                           jsval* vp)
4244 {
4245   if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_struct) {
4246     JS_ReportError(cx, "not a StructType");
4247     return JS_FALSE;
4248   }
4249
4250   if (!CType::IsSizeDefined(cx, obj)) {
4251     JS_ReportError(cx, "cannot construct an opaque StructType");
4252     return JS_FALSE;
4253   }
4254
4255   JSObject* result = CData::Create(cx, obj, NULL, NULL, true);
4256   if (!result)
4257     return JS_FALSE;
4258
4259   JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
4260
4261   if (argc == 0)
4262     return JS_TRUE;
4263
4264   char* buffer = static_cast<char*>(CData::GetData(cx, result));
4265   const FieldInfoHash* fields = GetFieldInfo(cx, obj);
4266
4267   jsval* argv = JS_ARGV(cx, vp);
4268   if (argc == 1) {
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.
4276
4277     // Try option 1) first.
4278     if (ExplicitConvert(cx, argv[0], obj, buffer))
4279       return JS_TRUE;
4280
4281     if (fields->count() != 1)
4282       return JS_FALSE;
4283
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))
4287       return JS_FALSE;
4288
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);
4292
4293     // Fall through to try option 2).
4294   }
4295
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,
4304              false, NULL))
4305         return JS_FALSE;
4306     }
4307
4308     return JS_TRUE;
4309   }
4310
4311   JS_ReportError(cx, "constructor takes 0, 1, or %u arguments",
4312     fields->count());
4313   return JS_FALSE;
4314 }
4315
4316 const FieldInfoHash*
4317 StructType::GetFieldInfo(JSContext* cx, JSObject* obj)
4318 {
4319   JS_ASSERT(CType::IsCType(cx, obj));
4320   JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_struct);
4321
4322   jsval slot;
4323   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FIELDINFO, &slot));
4324   JS_ASSERT(!JSVAL_IS_VOID(slot) && JSVAL_TO_PRIVATE(slot));
4325
4326   return static_cast<const FieldInfoHash*>(JSVAL_TO_PRIVATE(slot));
4327 }
4328
4329 const FieldInfo*
4330 StructType::LookupField(JSContext* cx, JSObject* obj, JSFlatString *name)
4331 {
4332   JS_ASSERT(CType::IsCType(cx, obj));
4333   JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_struct);
4334
4335   FieldInfoHash::Ptr ptr = GetFieldInfo(cx, obj)->lookup(name);
4336   if (ptr)
4337     return &ptr->value;
4338
4339   JSAutoByteString bytes(cx, name);
4340   if (!bytes)
4341     return NULL;
4342
4343   JS_ReportError(cx, "%s does not name a field", bytes.ptr());
4344   return NULL;
4345 }
4346
4347 JSObject*
4348 StructType::BuildFieldsArray(JSContext* cx, JSObject* obj)
4349 {
4350   JS_ASSERT(CType::IsCType(cx, obj));
4351   JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_struct);
4352   JS_ASSERT(CType::IsSizeDefined(cx, obj));
4353
4354   const FieldInfoHash* fields = GetFieldInfo(cx, obj);
4355   size_t len = fields->count();
4356
4357   // Prepare a new array for the 'fields' property of the StructType.
4358   Array<jsval, 16> fieldsVec;
4359   if (!fieldsVec.appendN(JSVAL_VOID, len))
4360     return NULL;
4361   js::AutoArrayRooter root(cx, fieldsVec.length(), fieldsVec.begin());
4362
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))
4368       return NULL;
4369   }
4370
4371   JSObject* fieldsProp = JS_NewArrayObject(cx, len, fieldsVec.begin());
4372   if (!fieldsProp)
4373     return NULL;
4374
4375   // Seal the fields array.
4376   if (!JS_FreezeObject(cx, fieldsProp))
4377     return NULL;
4378
4379   return fieldsProp;
4380 }
4381
4382 JSBool
4383 StructType::FieldsArrayGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
4384 {
4385   if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_struct) {
4386     JS_ReportError(cx, "not a StructType");
4387     return JS_FALSE;
4388   }
4389
4390   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FIELDS, vp));
4391
4392   if (!CType::IsSizeDefined(cx, obj)) {
4393     JS_ASSERT(JSVAL_IS_VOID(*vp));
4394     return JS_TRUE;
4395   }
4396
4397   if (JSVAL_IS_VOID(*vp)) {
4398     // Build the 'fields' array lazily.
4399     JSObject* fields = BuildFieldsArray(cx, obj);
4400     if (!fields ||
4401         !JS_SetReservedSlot(cx, obj, SLOT_FIELDS, OBJECT_TO_JSVAL(fields)))
4402       return JS_FALSE;
4403
4404     *vp = OBJECT_TO_JSVAL(fields);
4405   }
4406
4407   JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp) &&
4408             JS_IsArrayObject(cx, JSVAL_TO_OBJECT(*vp)));
4409   return JS_TRUE;
4410 }
4411
4412 JSBool
4413 StructType::FieldGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
4414 {
4415   if (!CData::IsCData(cx, obj)) {
4416     JS_ReportError(cx, "not a CData");
4417     return JS_FALSE;
4418   }
4419
4420   JSObject* typeObj = CData::GetCType(cx, obj);
4421   if (CType::GetTypeCode(cx, typeObj) != TYPE_struct) {
4422     JS_ReportError(cx, "not a StructType");
4423     return JS_FALSE;
4424   }
4425
4426   const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_FLAT_STRING(idval));
4427   if (!field)
4428     return JS_FALSE;
4429
4430   char* data = static_cast<char*>(CData::GetData(cx, obj)) + field->mOffset;
4431   return ConvertToJS(cx, field->mType, obj, data, false, false, vp);
4432 }
4433
4434 JSBool
4435 StructType::FieldSetter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict, jsval* vp)
4436 {
4437   if (!CData::IsCData(cx, obj)) {
4438     JS_ReportError(cx, "not a CData");
4439     return JS_FALSE;
4440   }
4441
4442   JSObject* typeObj = CData::GetCType(cx, obj);
4443   if (CType::GetTypeCode(cx, typeObj) != TYPE_struct) {
4444     JS_ReportError(cx, "not a StructType");
4445     return JS_FALSE;
4446   }
4447
4448   const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_FLAT_STRING(idval));
4449   if (!field)
4450     return JS_FALSE;
4451
4452   char* data = static_cast<char*>(CData::GetData(cx, obj)) + field->mOffset;
4453   return ImplicitConvert(cx, *vp, field->mType, data, false, NULL);
4454 }
4455
4456 JSBool
4457 StructType::AddressOfField(JSContext* cx, uintN argc, jsval* vp)
4458 {
4459   JSObject* obj = JS_THIS_OBJECT(cx, vp);
4460   if (!obj || !CData::IsCData(cx, obj)) {
4461     JS_ReportError(cx, "not a CData");
4462     return JS_FALSE;
4463   }
4464
4465   JSObject* typeObj = CData::GetCType(cx, obj);
4466   if (CType::GetTypeCode(cx, typeObj) != TYPE_struct) {
4467     JS_ReportError(cx, "not a StructType");
4468     return JS_FALSE;
4469   }
4470
4471   if (argc != 1) {
4472     JS_ReportError(cx, "addressOfField takes one argument");
4473     return JS_FALSE;
4474   }
4475
4476   JSFlatString *str = JS_FlattenString(cx, JSVAL_TO_STRING(JS_ARGV(cx, vp)[0]));
4477   if (!str)
4478     return JS_FALSE;
4479
4480   const FieldInfo* field = LookupField(cx, typeObj, str);
4481   if (!field)
4482     return JS_FALSE;
4483
4484   JSObject* baseType = field->mType;
4485   JSObject* pointerType = PointerType::CreateInternal(cx, baseType);
4486   if (!pointerType)
4487     return JS_FALSE;
4488   js::AutoObjectRooter root(cx, pointerType);
4489
4490   // Create a PointerType CData object containing null.
4491   JSObject* result = CData::Create(cx, pointerType, NULL, NULL, true);
4492   if (!result)
4493     return JS_FALSE;
4494
4495   JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
4496
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;
4500   return JS_TRUE;
4501 }
4502
4503 /*******************************************************************************
4504 ** FunctionType implementation
4505 *******************************************************************************/
4506
4507 // Helper class for handling allocation of function arguments.
4508 struct AutoValue
4509 {
4510   AutoValue() : mData(NULL) { }
4511
4512   ~AutoValue()
4513   {
4514     js_array_delete(static_cast<char*>(mData));
4515   }
4516
4517   bool SizeToType(JSContext* cx, JSObject* type)
4518   {
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);
4522     if (mData)
4523       memset(mData, 0, size);
4524     return mData != NULL;
4525   }
4526
4527   void* mData;
4528 };
4529
4530 static bool
4531 GetABI(JSContext* cx, jsval abiType, ffi_abi* result)
4532 {
4533   if (JSVAL_IS_PRIMITIVE(abiType))
4534     return false;
4535
4536   ABICode abi = GetABICode(cx, JSVAL_TO_OBJECT(abiType));
4537
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.
4541   switch (abi) {
4542   case ABI_DEFAULT:
4543     *result = FFI_DEFAULT_ABI;
4544     return true;
4545   case ABI_STDCALL:
4546   case ABI_WINAPI:
4547 #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
4548     *result = FFI_STDCALL;
4549     return true;
4550 #endif
4551   case INVALID_ABI:
4552     break;
4553   }
4554   return false;
4555 }
4556
4557 static JSObject*
4558 PrepareType(JSContext* cx, jsval type)
4559 {
4560   if (JSVAL_IS_PRIMITIVE(type) ||
4561       !CType::IsCType(cx, JSVAL_TO_OBJECT(type))) {
4562     JS_ReportError(cx, "not a ctypes type");
4563     return NULL;
4564   }
4565
4566   JSObject* result = JSVAL_TO_OBJECT(type);
4567   TypeCode typeCode = CType::GetTypeCode(cx, result);
4568
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);
4574     if (!result)
4575       return NULL;
4576
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");
4580     return NULL;
4581   }
4582
4583   if (!CType::IsSizeDefined(cx, result)) {
4584     JS_ReportError(cx, "Argument type must have defined size");
4585     return NULL;
4586   }
4587
4588   // libffi cannot pass types of zero size by value.
4589   JS_ASSERT(CType::GetSize(cx, result) != 0);
4590
4591   return result;
4592 }
4593
4594 static JSObject*
4595 PrepareReturnType(JSContext* cx, jsval type)
4596 {
4597   if (JSVAL_IS_PRIMITIVE(type) ||
4598       !CType::IsCType(cx, JSVAL_TO_OBJECT(type))) {
4599     JS_ReportError(cx, "not a ctypes type");
4600     return NULL;
4601   }
4602
4603   JSObject* result = JSVAL_TO_OBJECT(type);
4604   TypeCode typeCode = CType::GetTypeCode(cx, result);
4605
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");
4609     return NULL;
4610   }
4611
4612   if (typeCode != TYPE_void_t && !CType::IsSizeDefined(cx, result)) {
4613     JS_ReportError(cx, "Return type must have defined size");
4614     return NULL;
4615   }
4616
4617   // libffi cannot pass types of zero size by value.
4618   JS_ASSERT(typeCode == TYPE_void_t || CType::GetSize(cx, result) != 0);
4619
4620   return result;
4621 }
4622
4623 static JS_ALWAYS_INLINE JSBool
4624 IsEllipsis(JSContext* cx, jsval v, bool* isEllipsis)
4625 {
4626   *isEllipsis = false;
4627   if (!JSVAL_IS_STRING(v))
4628     return true;
4629   JSString* str = JSVAL_TO_STRING(v);
4630   if (str->length() != 3)
4631     return true;
4632   const jschar* chars = str->getChars(cx);
4633   if (!chars)
4634     return false;
4635   jschar dot = '.';
4636   *isEllipsis = (chars[0] == dot &&
4637                  chars[1] == dot &&
4638                  chars[2] == dot);
4639   return true;
4640 }
4641
4642 static JSBool
4643 PrepareCIF(JSContext* cx,
4644            FunctionInfo* fninfo)
4645 {
4646   ffi_abi abi;
4647   if (!GetABI(cx, OBJECT_TO_JSVAL(fninfo->mABI), &abi)) {
4648     JS_ReportError(cx, "Invalid ABI specification");
4649     return false;
4650   }
4651
4652   ffi_type* rtype = CType::GetFFIType(cx, fninfo->mReturnType);
4653   if (!rtype)
4654     return false;
4655
4656   ffi_status status =
4657     ffi_prep_cif(&fninfo->mCIF,
4658                  abi,
4659                  fninfo->mFFITypes.length(),
4660                  rtype,
4661                  fninfo->mFFITypes.begin());
4662
4663   switch (status) {
4664   case FFI_OK:
4665     return true;
4666   case FFI_BAD_ABI:
4667     JS_ReportError(cx, "Invalid ABI specification");
4668     return false;
4669   case FFI_BAD_TYPEDEF:
4670     JS_ReportError(cx, "Invalid type specification");
4671     return false;
4672   default:
4673     JS_ReportError(cx, "Unknown libffi error");
4674     return false;
4675   }
4676 }
4677
4678 void
4679 FunctionType::BuildSymbolName(JSContext* cx,
4680                               JSString* name,
4681                               JSObject* typeObj,
4682                               AutoCString& result)
4683 {
4684   FunctionInfo* fninfo = GetFunctionInfo(cx, typeObj);
4685
4686   switch (GetABICode(cx, fninfo->mABI)) {
4687   case ABI_DEFAULT:
4688   case ABI_WINAPI:
4689     // For cdecl or WINAPI functions, no mangling is necessary.
4690     AppendString(result, name);
4691     break;
4692
4693   case ABI_STDCALL: {
4694     // On WIN32, stdcall functions look like:
4695     //   _foo@40
4696     // where 'foo' is the function name, and '40' is the aligned size of the
4697     // arguments.
4698     AppendString(result, "_");
4699     AppendString(result, name);
4700     AppendString(result, "@");
4701
4702     // Compute the suffix by aligning each argument to sizeof(ffi_arg).
4703     size_t size = 0;
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));
4707     }
4708
4709     IntegerToString(size, 10, result);
4710     break;
4711   }
4712
4713   case INVALID_ABI:
4714     JS_NOT_REACHED("invalid abi");
4715     break;
4716   }
4717 }
4718
4719 static FunctionInfo*
4720 NewFunctionInfo(JSContext* cx,
4721                 jsval abiType,
4722                 jsval returnType,
4723                 jsval* argTypes,
4724                 uintN argLength)
4725 {
4726   AutoPtr<FunctionInfo> fninfo(js_new<FunctionInfo>());
4727   if (!fninfo) {
4728     JS_ReportOutOfMemory(cx);
4729     return NULL;
4730   }
4731
4732   ffi_abi abi;
4733   if (!GetABI(cx, abiType, &abi)) {
4734     JS_ReportError(cx, "Invalid ABI specification");
4735     return NULL;
4736   }
4737   fninfo->mABI = JSVAL_TO_OBJECT(abiType);
4738
4739   // prepare the result type
4740   fninfo->mReturnType = PrepareReturnType(cx, returnType);
4741   if (!fninfo->mReturnType)
4742     return NULL;
4743
4744   // prepare the argument types
4745   if (!fninfo->mArgTypes.reserve(argLength) ||
4746       !fninfo->mFFITypes.reserve(argLength)) {
4747     JS_ReportOutOfMemory(cx);
4748     return NULL;
4749   }
4750
4751   fninfo->mIsVariadic = false;
4752
4753   for (JSUint32 i = 0; i < argLength; ++i) {
4754     bool isEllipsis;
4755     if (!IsEllipsis(cx, argTypes[i], &isEllipsis))
4756       return false;
4757     if (isEllipsis) {
4758       fninfo->mIsVariadic = true;
4759       if (i < 1) {
4760         JS_ReportError(cx, "\"...\" may not be the first and only parameter "
4761                        "type of a variadic function declaration");
4762         return NULL;
4763       }
4764       if (i < argLength - 1) {
4765         JS_ReportError(cx, "\"...\" must be the last parameter type of a "
4766                        "variadic function declaration");
4767         return NULL;
4768       }
4769       if (GetABICode(cx, fninfo->mABI) != ABI_DEFAULT) {
4770         JS_ReportError(cx, "Variadic functions must use the __cdecl calling "
4771                        "convention");
4772         return NULL;
4773       }
4774       break;
4775     }
4776
4777     JSObject* argType = PrepareType(cx, argTypes[i]);
4778     if (!argType)
4779       return NULL;
4780
4781     ffi_type* ffiType = CType::GetFFIType(cx, argType);
4782     if (!ffiType)
4783       return NULL;
4784
4785     fninfo->mArgTypes.append(argType);
4786     fninfo->mFFITypes.append(ffiType);
4787   }
4788
4789   if (fninfo->mIsVariadic)
4790     // wait to PrepareCIF until function is called
4791     return fninfo.forget();
4792
4793   if (!PrepareCIF(cx, fninfo.get()))
4794     return NULL;
4795
4796   return fninfo.forget();
4797 }
4798
4799 JSBool
4800 FunctionType::Create(JSContext* cx, uintN argc, jsval* vp)
4801 {
4802   // Construct and return a new FunctionType object.
4803   if (argc < 2 || argc > 3) {
4804     JS_ReportError(cx, "FunctionType takes two or three arguments");
4805     return JS_FALSE;
4806   }
4807
4808   jsval* argv = JS_ARGV(cx, vp);
4809   Array<jsval, 16> argTypes;
4810   JSObject* arrayObj = NULL;
4811
4812   if (argc == 3) {
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");
4817       return JS_FALSE;
4818     }
4819
4820     arrayObj = JSVAL_TO_OBJECT(argv[2]);
4821     jsuint len;
4822     ASSERT_OK(JS_GetArrayLength(cx, arrayObj, &len));
4823
4824     if (!argTypes.appendN(JSVAL_VOID, len)) {
4825       JS_ReportOutOfMemory(cx);
4826       return JS_FALSE;
4827     }
4828   }
4829
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]))
4835       return JS_FALSE;
4836   }
4837
4838   JSObject* result = CreateInternal(cx, argv[0], argv[1],
4839       argTypes.begin(), argTypes.length());
4840   if (!result)
4841     return JS_FALSE;
4842
4843   JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
4844   return JS_TRUE;
4845 }
4846
4847 JSObject*
4848 FunctionType::CreateInternal(JSContext* cx,
4849                              jsval abi,
4850                              jsval rtype,
4851                              jsval* argtypes,
4852                              jsuint arglen)
4853 {
4854   // Determine and check the types, and prepare the function CIF.
4855   AutoPtr<FunctionInfo> fninfo(NewFunctionInfo(cx, abi, rtype, argtypes, arglen));
4856   if (!fninfo)
4857     return NULL;
4858
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);
4865
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);
4869   if (!typeObj)
4870     return NULL;
4871   js::AutoObjectRooter root(cx, typeObj);
4872
4873   // Stash the FunctionInfo in a reserved slot.
4874   if (!JS_SetReservedSlot(cx, typeObj, SLOT_FNINFO,
4875          PRIVATE_TO_JSVAL(fninfo.get())))
4876     return NULL;
4877   fninfo.forget();
4878
4879   return typeObj;
4880 }
4881
4882 // Construct a function pointer to a JS function (see CClosure::Create()).
4883 // Regular function pointers are constructed directly in
4884 // PointerType::ConstructData().
4885 JSBool
4886 FunctionType::ConstructData(JSContext* cx,
4887                             JSObject* typeObj,
4888                             JSObject* dataObj,
4889                             JSObject* fnObj,
4890                             JSObject* thisObj)
4891 {
4892   JS_ASSERT(CType::GetTypeCode(cx, typeObj) == TYPE_function);
4893
4894   PRFuncPtr* data = static_cast<PRFuncPtr*>(CData::GetData(cx, dataObj));
4895
4896   FunctionInfo* fninfo = FunctionType::GetFunctionInfo(cx, typeObj);
4897   if (fninfo->mIsVariadic) {
4898     JS_ReportError(cx, "Can't declare a variadic callback function");
4899     return JS_FALSE;
4900   }
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");
4904     return JS_FALSE;
4905   }
4906
4907   JSObject* closureObj = CClosure::Create(cx, typeObj, fnObj, thisObj, data);
4908   if (!closureObj)
4909     return JS_FALSE;
4910   js::AutoObjectRooter root(cx, closureObj);
4911
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)))
4915     return JS_FALSE;
4916
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);
4924 }
4925
4926 typedef Array<AutoValue, 16> AutoValueAutoArray;
4927
4928 static JSBool
4929 ConvertArgument(JSContext* cx,
4930                 jsval arg,
4931                 JSObject* type,
4932                 AutoValue* value,
4933                 AutoValueAutoArray* strings)
4934 {
4935   if (!value->SizeToType(cx, type)) {
4936     JS_ReportAllocationOverflow(cx);
4937     return false;
4938   }
4939
4940   bool freePointer = false;
4941   if (!ImplicitConvert(cx, arg, type, value->mData, true, &freePointer))
4942     return false;
4943
4944   if (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);
4949       return false;
4950     }
4951     strings->back().mData = *static_cast<char**>(value->mData);
4952   }
4953
4954   return true;
4955 }
4956
4957 JSBool
4958 FunctionType::Call(JSContext* cx,
4959                    uintN argc,
4960                    jsval* vp)
4961 {
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");
4966     return false;
4967   }
4968
4969   JSObject* typeObj = CData::GetCType(cx, obj);
4970   if (CType::GetTypeCode(cx, typeObj) != TYPE_pointer) {
4971     JS_ReportError(cx, "not a FunctionType.ptr");
4972     return false;
4973   }
4974
4975   typeObj = PointerType::GetBaseType(cx, typeObj);
4976   if (CType::GetTypeCode(cx, typeObj) != TYPE_function) {
4977     JS_ReportError(cx, "not a FunctionType.ptr");
4978     return false;
4979   }
4980
4981   FunctionInfo* fninfo = GetFunctionInfo(cx, typeObj);
4982   JSUint32 argcFixed = fninfo->mArgTypes.length();
4983
4984   if ((!fninfo->mIsVariadic && argc != argcFixed) ||
4985       (fninfo->mIsVariadic && argc < argcFixed)) {
4986     JS_ReportError(cx, "Number of arguments does not match declaration");
4987     return false;
4988   }
4989
4990   // Check if we have a Library object. If we do, make sure it's open.
4991   jsval slot;
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));
4995     if (!library) {
4996       JS_ReportError(cx, "library is not open");
4997       return false;
4998     }
4999   }
5000
5001   // prepare the values for each argument
5002   AutoValueAutoArray values;
5003   AutoValueAutoArray strings;
5004   if (!values.resize(argc)) {
5005     JS_ReportOutOfMemory(cx);
5006     return false;
5007   }
5008
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))
5012       return false;
5013
5014   if (fninfo->mIsVariadic) {
5015     if (!fninfo->mFFITypes.resize(argc)) {
5016       JS_ReportOutOfMemory(cx);
5017       return false;
5018     }
5019
5020     JSObject* obj;  // Could reuse obj instead of declaring a second
5021     JSObject* type; // JSObject*, but readability would suffer.
5022
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])));
5030         return false;
5031       }
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.
5039         return false;
5040       }
5041     }
5042     if (!PrepareCIF(cx, fninfo))
5043       return false;
5044   }
5045
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);
5052     return false;
5053   }
5054
5055   uintptr_t fn = *reinterpret_cast<uintptr_t*>(CData::GetData(cx, obj));
5056
5057   // suspend the request before we call into the function, since the call
5058   // may block or otherwise take a long time to return.
5059   {
5060     JSAutoSuspendRequest suspend(cx);
5061     ffi_call(&fninfo->mCIF, FFI_FN(fn), returnValue.mData,
5062              reinterpret_cast<void**>(values.begin()));
5063   }
5064
5065   // Small integer types get returned as a word-sized ffi_arg. Coerce it back
5066   // into the correct size for ConvertToJS.
5067   switch (typeCode) {
5068 #define DEFINE_INT_TYPE(name, type, ffiType)                                   \
5069   case TYPE_##name:                                                            \
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);        \
5073     }                                                                          \
5074     break;
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"
5080   default:
5081     break;
5082   }
5083
5084   // prepare a JS object from the result
5085   return ConvertToJS(cx, fninfo->mReturnType, NULL, returnValue.mData,
5086                      false, true, vp);
5087 }
5088
5089 FunctionInfo*
5090 FunctionType::GetFunctionInfo(JSContext* cx, JSObject* obj)
5091 {
5092   JS_ASSERT(CType::IsCType(cx, obj));
5093   JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_function);
5094
5095   jsval slot;
5096   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FNINFO, &slot));
5097   JS_ASSERT(!JSVAL_IS_VOID(slot) && JSVAL_TO_PRIVATE(slot));
5098
5099   return static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot));
5100 }
5101
5102 static JSBool
5103 CheckFunctionType(JSContext* cx, JSObject* obj)
5104 {
5105   if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_function) {
5106     JS_ReportError(cx, "not a FunctionType");
5107     return JS_FALSE;
5108   }
5109   return JS_TRUE;
5110 }
5111
5112 JSBool
5113 FunctionType::ArgTypesGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
5114 {
5115   if (!CheckFunctionType(cx, obj))
5116     return JS_FALSE;
5117
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))
5121     return JS_TRUE;
5122
5123   FunctionInfo* fninfo = GetFunctionInfo(cx, obj);
5124   size_t len = fninfo->mArgTypes.length();
5125
5126   // Prepare a new array.
5127   Array<jsval, 16> vec;
5128   if (!vec.resize(len))
5129     return JS_FALSE;
5130
5131   for (size_t i = 0; i < len; ++i)
5132     vec[i] = OBJECT_TO_JSVAL(fninfo->mArgTypes[i]);
5133
5134   JSObject* argTypes = JS_NewArrayObject(cx, len, vec.begin());
5135   if (!argTypes)
5136     return JS_FALSE;
5137
5138   // Seal and cache it.
5139   if (!JS_FreezeObject(cx, argTypes) ||
5140       !JS_SetReservedSlot(cx, obj, SLOT_ARGS_T, OBJECT_TO_JSVAL(argTypes)))
5141     return JS_FALSE;
5142
5143   *vp = OBJECT_TO_JSVAL(argTypes);
5144   return JS_TRUE;
5145 }
5146
5147 JSBool
5148 FunctionType::ReturnTypeGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
5149 {
5150   if (!CheckFunctionType(cx, obj))
5151     return JS_FALSE;
5152
5153   // Get the returnType object from the FunctionInfo.
5154   *vp = OBJECT_TO_JSVAL(GetFunctionInfo(cx, obj)->mReturnType);
5155   return JS_TRUE;
5156 }
5157
5158 JSBool
5159 FunctionType::ABIGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
5160 {
5161   if (!CheckFunctionType(cx, obj))
5162     return JS_FALSE;
5163
5164   // Get the abi object from the FunctionInfo.
5165   *vp = OBJECT_TO_JSVAL(GetFunctionInfo(cx, obj)->mABI);
5166   return JS_TRUE;
5167 }
5168
5169 JSBool
5170 FunctionType::IsVariadicGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
5171 {
5172   if (!CheckFunctionType(cx, obj))
5173     return JS_FALSE;
5174
5175   *vp = BOOLEAN_TO_JSVAL(GetFunctionInfo(cx, obj)->mIsVariadic);
5176   return JS_TRUE;
5177 }
5178
5179 /*******************************************************************************
5180 ** CClosure implementation
5181 *******************************************************************************/
5182
5183 JSObject*
5184 CClosure::Create(JSContext* cx,
5185                  JSObject* typeObj,
5186                  JSObject* fnObj,
5187                  JSObject* thisObj,
5188                  PRFuncPtr* fnptr)
5189 {
5190   JS_ASSERT(fnObj);
5191
5192   JSObject* result = JS_NewObject(cx, &sCClosureClass, NULL, NULL);
5193   if (!result)
5194     return NULL;
5195   js::AutoObjectRooter root(cx, result);
5196
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);
5201
5202   AutoPtr<ClosureInfo> cinfo(js_new<ClosureInfo>());
5203   if (!cinfo) {
5204     JS_ReportOutOfMemory(cx);
5205     return NULL;
5206   }
5207
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);
5211   JS_ASSERT(proto);
5212   JS_ASSERT(JS_GET_CLASS(cx, proto) == &sCTypeProtoClass);
5213
5214   // Get a JSContext for use with the closure.
5215   jsval slot;
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);
5221   } else {
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);
5226     if (!cinfo->cx) {
5227       JS_ReportOutOfMemory(cx);
5228       return NULL;
5229     }
5230
5231     if (!JS_SetReservedSlot(cx, proto, SLOT_CLOSURECX,
5232            PRIVATE_TO_JSVAL(cinfo->cx))) {
5233       JS_DestroyContextNoGC(cinfo->cx);
5234       return NULL;
5235     }
5236
5237     JS_ClearContextThread(cinfo->cx);
5238   }
5239
5240 #ifdef DEBUG
5241   // We want *this* context's thread here so use cx instead of cinfo->cx.
5242   cinfo->cxThread = JS_GetContextThread(cx);
5243 #endif
5244
5245   cinfo->closureObj = result;
5246   cinfo->typeObj = typeObj;
5247   cinfo->thisObj = thisObj;
5248   cinfo->jsfnObj = fnObj;
5249
5250   // Create an ffi_closure object and initialize it.
5251   void* code;
5252   cinfo->closure =
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");
5256     return NULL;
5257   }
5258
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");
5264     return NULL;
5265   }
5266
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);
5271     return NULL;
5272   }
5273   cinfo.forget();
5274
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));
5278   return result;
5279 }
5280
5281 void
5282 CClosure::Trace(JSTracer* trc, JSObject* obj)
5283 {
5284   JSContext* cx = trc->context;
5285
5286   // Make sure our ClosureInfo slot is legit. If it's not, bail.
5287   jsval slot;
5288   if (!JS_GetReservedSlot(cx, obj, SLOT_CLOSUREINFO, &slot) ||
5289       JSVAL_IS_VOID(slot))
5290     return;
5291
5292   ClosureInfo* cinfo = static_cast<ClosureInfo*>(JSVAL_TO_PRIVATE(slot));
5293
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");
5298   if (cinfo->thisObj)
5299     JS_CALL_OBJECT_TRACER(trc, cinfo->thisObj, "thisObj");
5300 }
5301
5302 void
5303 CClosure::Finalize(JSContext* cx, JSObject* obj)
5304 {
5305   // Make sure our ClosureInfo slot is legit. If it's not, bail.
5306   jsval slot;
5307   if (!JS_GetReservedSlot(cx, obj, SLOT_CLOSUREINFO, &slot) ||
5308       JSVAL_IS_VOID(slot))
5309     return;
5310
5311   ClosureInfo* cinfo = static_cast<ClosureInfo*>(JSVAL_TO_PRIVATE(slot));
5312   if (cinfo->closure)
5313     ffi_closure_free(cinfo->closure);
5314
5315   js_delete(cinfo);
5316 }
5317
5318 void
5319 CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData)
5320 {
5321   JS_ASSERT(cif);
5322   JS_ASSERT(result);
5323   JS_ASSERT(args);
5324   JS_ASSERT(userData);
5325
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;
5332
5333   ScopedContextThread scopedThread(cx);
5334
5335   // Assert that we're on the thread we were created from.
5336   JS_ASSERT(cinfo->cxThread == JS_GetContextThread(cx));
5337
5338   JSAutoRequest ar(cx);
5339
5340   JSAutoEnterCompartment ac;
5341   if (!ac.enter(cx, jsfnObj))
5342     return;
5343
5344   // Assert that our CIFs agree.
5345   FunctionInfo* fninfo = FunctionType::GetFunctionInfo(cx, typeObj);
5346   JS_ASSERT(cif == &fninfo->mCIF);
5347
5348   TypeCode typeCode = CType::GetTypeCode(cx, fninfo->mReturnType);
5349
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
5352   // whole word.
5353   if (cif->rtype != &ffi_type_void) {
5354     size_t size = cif->rtype->size;
5355     switch (typeCode) {
5356 #define DEFINE_INT_TYPE(name, type, ffiType)                                   \
5357     case TYPE_##name:
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));
5364       break;
5365     default:
5366       break;
5367     }
5368     memset(result, 0, size);
5369   }
5370
5371   // Get a death grip on 'closureObj'.
5372   js::AutoObjectRooter root(cx, cinfo->closureObj);
5373
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);
5378     return;
5379   }
5380
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,
5386            &argv[i]))
5387       return;
5388   }
5389
5390   // Call the JS function. 'thisObj' may be NULL, in which case the JS engine
5391   // will find an appropriate object to use.
5392   jsval rval;
5393   if (!JS_CallFunctionValue(cx, thisObj, OBJECT_TO_JSVAL(jsfnObj), cif->nargs,
5394        argv.begin(), &rval))
5395     return;
5396
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))
5403     return;
5404
5405   // Small integer types must be returned as a word-sized ffi_arg. Coerce it
5406   // back into the size libffi expects.
5407   switch (typeCode) {
5408 #define DEFINE_INT_TYPE(name, type, ffiType)                                   \
5409   case TYPE_##name:                                                            \
5410     if (sizeof(type) < sizeof(ffi_arg)) {                                      \
5411       ffi_arg data = *static_cast<type*>(result);                              \
5412       *static_cast<ffi_arg*>(result) = data;                                   \
5413     }                                                                          \
5414     break;
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"
5420   default:
5421     break;
5422   }
5423 }
5424
5425 /*******************************************************************************
5426 ** CData implementation
5427 *******************************************************************************/
5428
5429 // Create a new CData object of type 'typeObj' containing binary data supplied
5430 // in 'source', optionally with a referent object 'refObj'.
5431 //
5432 // * 'typeObj' must be a CType of defined (but possibly zero) size.
5433 //
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.
5442 //
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'.
5450 JSObject*
5451 CData::Create(JSContext* cx,
5452               JSObject* typeObj,
5453               JSObject* refObj,
5454               void* source,
5455               bool ownResult)
5456 {
5457   JS_ASSERT(typeObj);
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);
5462
5463   // Get the 'prototype' property from the type.
5464   jsval slot;
5465   ASSERT_OK(JS_GetReservedSlot(cx, typeObj, SLOT_PROTO, &slot));
5466   JS_ASSERT(!JSVAL_IS_PRIMITIVE(slot));
5467
5468   JSObject* proto = JSVAL_TO_OBJECT(slot);
5469   JSObject* parent = JS_GetParent(cx, typeObj);
5470   JS_ASSERT(parent);
5471
5472   JSObject* dataObj = JS_NewObject(cx, &sCDataClass, proto, parent);
5473   if (!dataObj)
5474     return NULL;
5475   js::AutoObjectRooter root(cx, dataObj);
5476
5477   // set the CData's associated type
5478   if (!JS_SetReservedSlot(cx, dataObj, SLOT_CTYPE, OBJECT_TO_JSVAL(typeObj)))
5479     return NULL;
5480
5481   // Stash the referent object, if any, for GC safety.
5482   if (refObj &&
5483       !JS_SetReservedSlot(cx, dataObj, SLOT_REFERENT, OBJECT_TO_JSVAL(refObj)))
5484     return NULL;
5485
5486   // Set our ownership flag.
5487   if (!JS_SetReservedSlot(cx, dataObj, SLOT_OWNS, BOOLEAN_TO_JSVAL(ownResult)))
5488     return NULL;
5489
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*>();
5493   if (!buffer) {
5494     JS_ReportOutOfMemory(cx);
5495     return NULL;
5496   }
5497
5498   char* data;
5499   if (!ownResult) {
5500     data = static_cast<char*>(source);
5501   } else {
5502     // Initialize our own buffer.
5503     size_t size = CType::GetSize(cx, typeObj);
5504     data = js_array_new<char>(size);
5505     if (!data) {
5506       // Report a catchable allocation error.
5507       JS_ReportAllocationOverflow(cx);
5508       js_delete(buffer);
5509       return NULL;
5510     }
5511
5512     if (!source)
5513       memset(data, 0, size);
5514     else
5515       memcpy(data, source, size);
5516   }
5517
5518   *buffer = data;
5519   if (!JS_SetReservedSlot(cx, dataObj, SLOT_DATA, PRIVATE_TO_JSVAL(buffer))) {
5520     if (ownResult)
5521       js_array_delete(data);
5522     js_delete(buffer);
5523     return NULL;
5524   }
5525
5526   return dataObj;
5527 }
5528
5529 void
5530 CData::Finalize(JSContext* cx, JSObject* obj)
5531 {
5532   // Delete our buffer, and the data it contains if we own it.
5533   jsval slot;
5534   if (!JS_GetReservedSlot(cx, obj, SLOT_OWNS, &slot) || JSVAL_IS_VOID(slot))
5535     return;
5536
5537   JSBool owns = JSVAL_TO_BOOLEAN(slot);
5538
5539   if (!JS_GetReservedSlot(cx, obj, SLOT_DATA, &slot) || JSVAL_IS_VOID(slot))
5540     return;
5541   char** buffer = static_cast<char**>(JSVAL_TO_PRIVATE(slot));
5542
5543   if (owns)
5544     js_array_delete(*buffer);
5545   js_delete(buffer);
5546 }
5547
5548 JSObject*
5549 CData::GetCType(JSContext* cx, JSObject* dataObj)
5550 {
5551   JS_ASSERT(CData::IsCData(cx, dataObj));
5552
5553   jsval slot;
5554   ASSERT_OK(JS_GetReservedSlot(cx, dataObj, SLOT_CTYPE, &slot));
5555   JSObject* typeObj = JSVAL_TO_OBJECT(slot);
5556   JS_ASSERT(CType::IsCType(cx, typeObj));
5557   return typeObj;
5558 }
5559
5560 void*
5561 CData::GetData(JSContext* cx, JSObject* dataObj)
5562 {
5563   JS_ASSERT(CData::IsCData(cx, dataObj));
5564
5565   jsval slot;
5566   ASSERT_OK(JS_GetReservedSlot(cx, dataObj, SLOT_DATA, &slot));
5567
5568   void** buffer = static_cast<void**>(JSVAL_TO_PRIVATE(slot));
5569   JS_ASSERT(buffer);
5570   JS_ASSERT(*buffer);
5571   return *buffer;
5572 }
5573
5574 bool
5575 CData::IsCData(JSContext* cx, JSObject* obj)
5576 {
5577   return JS_GET_CLASS(cx, obj) == &sCDataClass;
5578 }
5579
5580 JSBool
5581 CData::ValueGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
5582 {
5583   if (!IsCData(cx, obj)) {
5584     JS_ReportError(cx, "not a CData");
5585     return JS_FALSE;
5586   }
5587
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))
5590     return JS_FALSE;
5591
5592   return JS_TRUE;
5593 }
5594
5595 JSBool
5596 CData::ValueSetter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict, jsval* vp)
5597 {
5598   if (!IsCData(cx, obj)) {
5599     JS_ReportError(cx, "not a CData");
5600     return JS_FALSE;
5601   }
5602
5603   return ImplicitConvert(cx, *vp, GetCType(cx, obj), GetData(cx, obj), false, NULL);
5604 }
5605
5606 JSBool
5607 CData::Address(JSContext* cx, uintN argc, jsval* vp)
5608 {
5609   if (argc != 0) {
5610     JS_ReportError(cx, "address takes zero arguments");
5611     return JS_FALSE;
5612   }
5613
5614   JSObject* obj = JS_THIS_OBJECT(cx, vp);
5615   if (!obj || !IsCData(cx, obj)) {
5616     JS_ReportError(cx, "not a CData");
5617     return JS_FALSE;
5618   }
5619
5620   JSObject* typeObj = CData::GetCType(cx, obj);
5621   JSObject* pointerType = PointerType::CreateInternal(cx, typeObj);
5622   if (!pointerType)
5623     return JS_FALSE;
5624   js::AutoObjectRooter root(cx, pointerType);
5625
5626   // Create a PointerType CData object containing null.
5627   JSObject* result = CData::Create(cx, pointerType, NULL, NULL, true);
5628   if (!result)
5629     return JS_FALSE;
5630
5631   JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
5632
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);
5636   return JS_TRUE;
5637 }
5638
5639 JSBool
5640 CData::Cast(JSContext* cx, uintN argc, jsval* vp)
5641 {
5642   if (argc != 2) {
5643     JS_ReportError(cx, "cast takes two arguments");
5644     return JS_FALSE;
5645   }
5646
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");
5651     return JS_FALSE;
5652   }
5653   JSObject* sourceData = JSVAL_TO_OBJECT(argv[0]);
5654   JSObject* sourceType = CData::GetCType(cx, sourceData);
5655
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");
5659     return JS_FALSE;
5660   }
5661
5662   JSObject* targetType = JSVAL_TO_OBJECT(argv[1]);
5663   size_t targetSize;
5664   if (!CType::GetSafeSize(cx, targetType, &targetSize) ||
5665       targetSize > CType::GetSize(cx, sourceType)) {
5666     JS_ReportError(cx,
5667       "target CType has undefined or larger size than source CType");
5668     return JS_FALSE;
5669   }
5670
5671   // Construct a new CData object with a type of 'targetType' and a referent
5672   // of 'sourceData'.
5673   void* data = CData::GetData(cx, sourceData);
5674   JSObject* result = CData::Create(cx, targetType, sourceData, data, false);
5675   if (!result)
5676     return JS_FALSE;
5677
5678   JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
5679   return JS_TRUE;
5680 }
5681
5682 JSBool
5683 CData::ReadString(JSContext* cx, uintN argc, jsval* vp)
5684 {
5685   if (argc != 0) {
5686     JS_ReportError(cx, "readString takes zero arguments");
5687     return JS_FALSE;
5688   }
5689
5690   JSObject* obj = JS_THIS_OBJECT(cx, vp);
5691   if (!obj || !IsCData(cx, obj)) {
5692     JS_ReportError(cx, "not a CData");
5693     return JS_FALSE;
5694   }
5695
5696   // Make sure we are a pointer to, or an array of, an 8-bit or 16-bit
5697   // character or integer type.
5698   JSObject* baseType;
5699   JSObject* typeObj = GetCType(cx, obj);
5700   TypeCode typeCode = CType::GetTypeCode(cx, typeObj);
5701   void* data;
5702   size_t maxLength = -1;
5703   switch (typeCode) {
5704   case TYPE_pointer:
5705     baseType = PointerType::GetBaseType(cx, typeObj);
5706     data = *static_cast<void**>(GetData(cx, obj));
5707     if (data == NULL) {
5708       JS_ReportError(cx, "cannot read contents of null pointer");
5709       return JS_FALSE;
5710     }
5711     break;
5712   case TYPE_array:
5713     baseType = ArrayType::GetBaseType(cx, typeObj);
5714     data = GetData(cx, obj);
5715     maxLength = ArrayType::GetLength(cx, typeObj);
5716     break;
5717   default:
5718     JS_ReportError(cx, "not a PointerType or ArrayType");
5719     return JS_FALSE;
5720   }
5721
5722   // Convert the string buffer, taking care to determine the correct string
5723   // length in the case of arrays (which may contain embedded nulls).
5724   JSString* result;
5725   switch (CType::GetTypeCode(cx, baseType)) {
5726   case TYPE_int8_t:
5727   case TYPE_uint8_t:
5728   case TYPE_char:
5729   case TYPE_signed_char:
5730   case TYPE_unsigned_char: {
5731     char* bytes = static_cast<char*>(data);
5732     size_t length = strnlen(bytes, maxLength);
5733
5734     // Determine the length.
5735     size_t dstlen;
5736     if (!js_InflateUTF8StringToBuffer(cx, bytes, length, NULL, &dstlen))
5737       return JS_FALSE;
5738
5739     jschar* dst =
5740       static_cast<jschar*>(JS_malloc(cx, (dstlen + 1) * sizeof(jschar)));
5741     if (!dst)
5742       return JS_FALSE;
5743
5744     ASSERT_OK(js_InflateUTF8StringToBuffer(cx, bytes, length, dst, &dstlen));
5745     dst[dstlen] = 0;
5746
5747     result = JS_NewUCString(cx, dst, dstlen);
5748     break;
5749   }
5750   case TYPE_int16_t:
5751   case TYPE_uint16_t:
5752   case TYPE_short:
5753   case TYPE_unsigned_short:
5754   case TYPE_jschar: {
5755     jschar* chars = static_cast<jschar*>(data);
5756     size_t length = strnlen(chars, maxLength);
5757     result = JS_NewUCStringCopyN(cx, chars, length);
5758     break;
5759   }
5760   default:
5761     JS_ReportError(cx,
5762       "base type is not an 8-bit or 16-bit integer or character type");
5763     return JS_FALSE;
5764   }
5765
5766   if (!result)
5767     return JS_FALSE;
5768
5769   JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
5770   return JS_TRUE;
5771 }
5772
5773 JSBool
5774 CData::ToSource(JSContext* cx, uintN argc, jsval* vp)
5775 {
5776   if (argc != 0) {
5777     JS_ReportError(cx, "toSource takes zero arguments");
5778     return JS_FALSE;
5779   }
5780
5781   JSObject* obj = JS_THIS_OBJECT(cx, vp);
5782   if (!obj || !CData::IsCData(cx, obj)) {
5783     JS_ReportError(cx, "not a CData");
5784     return JS_FALSE;
5785   }
5786
5787   JSObject* typeObj = CData::GetCType(cx, obj);
5788   void* data = CData::GetData(cx, obj);
5789
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.)
5796   AutoString source;
5797   BuildTypeSource(cx, typeObj, true, source);
5798   AppendString(source, "(");
5799   if (!BuildDataSource(cx, typeObj, data, false, source))
5800     return JS_FALSE;
5801
5802   AppendString(source, ")");
5803
5804   JSString* result = NewUCString(cx, source);
5805   if (!result)
5806     return JS_FALSE;
5807
5808   JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
5809   return JS_TRUE;
5810 }
5811
5812 /*******************************************************************************
5813 ** Int64 and UInt64 implementation
5814 *******************************************************************************/
5815
5816 JSObject*
5817 Int64Base::Construct(JSContext* cx,
5818                      JSObject* proto,
5819                      JSUint64 data,
5820                      bool isUnsigned)
5821 {
5822   JSClass* clasp = isUnsigned ? &sUInt64Class : &sInt64Class;
5823   JSObject* result = JS_NewObject(cx, clasp, proto, JS_GetParent(cx, proto));
5824   if (!result)
5825     return NULL;
5826   js::AutoObjectRooter root(cx, result);
5827
5828   // attach the Int64's data
5829   JSUint64* buffer = js_new<JSUint64>(data);
5830   if (!buffer) {
5831     JS_ReportOutOfMemory(cx);
5832     return NULL;
5833   }
5834
5835   if (!JS_SetReservedSlot(cx, result, SLOT_INT64, PRIVATE_TO_JSVAL(buffer))) {
5836     js_delete(buffer);
5837     return NULL;
5838   }
5839
5840   if (!JS_FreezeObject(cx, result))
5841     return NULL;
5842
5843   return result;
5844 }
5845
5846 void
5847 Int64Base::Finalize(JSContext* cx, JSObject* obj)
5848 {
5849   jsval slot;
5850   if (!JS_GetReservedSlot(cx, obj, SLOT_INT64, &slot) || JSVAL_IS_VOID(slot))
5851     return;
5852
5853   js_delete(static_cast<JSUint64*>(JSVAL_TO_PRIVATE(slot)));
5854 }
5855
5856 JSUint64
5857 Int64Base::GetInt(JSContext* cx, JSObject* obj) {
5858   JS_ASSERT(Int64::IsInt64(cx, obj) || UInt64::IsUInt64(cx, obj));
5859
5860   jsval slot;
5861   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_INT64, &slot));
5862   return *static_cast<JSUint64*>(JSVAL_TO_PRIVATE(slot));
5863 }
5864
5865 JSBool
5866 Int64Base::ToString(JSContext* cx,
5867                     JSObject* obj,
5868                     uintN argc,
5869                     jsval* vp,
5870                     bool isUnsigned)
5871 {
5872   if (argc > 1) {
5873     JS_ReportError(cx, "toString takes zero or one argument");
5874     return JS_FALSE;
5875   }
5876
5877   jsuint radix = 10;
5878   if (argc == 1) {
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");
5884       return JS_FALSE;
5885     }
5886   }
5887
5888   AutoString intString;
5889   if (isUnsigned) {
5890     IntegerToString(GetInt(cx, obj), radix, intString);
5891   } else {
5892     IntegerToString(static_cast<JSInt64>(GetInt(cx, obj)), radix, intString);
5893   }
5894
5895   JSString *result = NewUCString(cx, intString);
5896   if (!result)
5897     return JS_FALSE;
5898
5899   JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
5900   return JS_TRUE;
5901 }
5902
5903 JSBool
5904 Int64Base::ToSource(JSContext* cx,
5905                     JSObject* obj,
5906                     uintN argc,
5907                     jsval* vp,
5908                     bool isUnsigned)
5909 {
5910   if (argc != 0) {
5911     JS_ReportError(cx, "toSource takes zero arguments");
5912     return JS_FALSE;
5913   }
5914
5915   // Return a decimal string suitable for constructing the number.
5916   AutoString source;
5917   if (isUnsigned) {
5918     AppendString(source, "ctypes.UInt64(\"");
5919     IntegerToString(GetInt(cx, obj), 10, source);
5920   } else {
5921     AppendString(source, "ctypes.Int64(\"");
5922     IntegerToString(static_cast<JSInt64>(GetInt(cx, obj)), 10, source);
5923   }
5924   AppendString(source, "\")");
5925
5926   JSString *result = NewUCString(cx, source);
5927   if (!result)
5928     return JS_FALSE;
5929
5930   JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
5931   return JS_TRUE;
5932 }
5933
5934 JSBool
5935 Int64::Construct(JSContext* cx,
5936                  uintN argc,
5937                  jsval* vp)
5938 {
5939   // Construct and return a new Int64 object.
5940   if (argc != 1) {
5941     JS_ReportError(cx, "Int64 takes one argument");
5942     return JS_FALSE;
5943   }
5944
5945   jsval* argv = JS_ARGV(cx, vp);
5946   JSInt64 i = 0;
5947   if (!jsvalToBigInteger(cx, argv[0], true, &i))
5948     return TypeError(cx, "int64", argv[0]);
5949
5950   // Get ctypes.Int64.prototype from the 'prototype' property of the ctor.
5951   jsval slot;
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);
5956
5957   JSObject* result = Int64Base::Construct(cx, proto, i, false);
5958   if (!result)
5959     return JS_FALSE;
5960
5961   JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
5962   return JS_TRUE;
5963 }
5964
5965 bool
5966 Int64::IsInt64(JSContext* cx, JSObject* obj)
5967 {
5968   return JS_GET_CLASS(cx, obj) == &sInt64Class;
5969 }
5970
5971 JSBool
5972 Int64::ToString(JSContext* cx, uintN argc, jsval* vp)
5973 {
5974   JSObject* obj = JS_THIS_OBJECT(cx, vp);
5975   if (!obj || !Int64::IsInt64(cx, obj)) {
5976     JS_ReportError(cx, "not an Int64");
5977     return JS_FALSE;
5978   }
5979
5980   return Int64Base::ToString(cx, obj, argc, vp, false);
5981 }
5982
5983 JSBool
5984 Int64::ToSource(JSContext* cx, uintN argc, jsval* vp)
5985 {
5986   JSObject* obj = JS_THIS_OBJECT(cx, vp);
5987   if (!obj || !Int64::IsInt64(cx, obj)) {
5988     JS_ReportError(cx, "not an Int64");
5989     return JS_FALSE;
5990   }
5991
5992   return Int64Base::ToSource(cx, obj, argc, vp, false);
5993 }
5994
5995 JSBool
5996 Int64::Compare(JSContext* cx, uintN argc, jsval* vp)
5997 {
5998   jsval* argv = JS_ARGV(cx, vp);
5999   if (argc != 2 ||
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");
6005     return JS_FALSE;
6006   }
6007
6008   JSObject* obj1 = JSVAL_TO_OBJECT(argv[0]);
6009   JSObject* obj2 = JSVAL_TO_OBJECT(argv[1]);
6010
6011   JSInt64 i1 = Int64Base::GetInt(cx, obj1);
6012   JSInt64 i2 = Int64Base::GetInt(cx, obj2);
6013
6014   if (i1 == i2)
6015     JS_SET_RVAL(cx, vp, INT_TO_JSVAL(0));
6016   else if (i1 < i2)
6017     JS_SET_RVAL(cx, vp, INT_TO_JSVAL(-1));
6018   else
6019     JS_SET_RVAL(cx, vp, INT_TO_JSVAL(1));
6020
6021   return JS_TRUE;
6022 }
6023
6024 #define LO_MASK ((JSUint64(1) << 32) - 1)
6025 #define INT64_LO(i) ((i) & LO_MASK)
6026 #define INT64_HI(i) ((i) >> 32)
6027
6028 JSBool
6029 Int64::Lo(JSContext* cx, uintN argc, jsval* vp)
6030 {
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");
6035     return JS_FALSE;
6036   }
6037
6038   JSObject* obj = JSVAL_TO_OBJECT(argv[0]);
6039   JSInt64 u = Int64Base::GetInt(cx, obj);
6040   jsdouble d = JSUint32(INT64_LO(u));
6041
6042   jsval result;
6043   if (!JS_NewNumberValue(cx, d, &result))
6044     return JS_FALSE;
6045
6046   JS_SET_RVAL(cx, vp, result);
6047   return JS_TRUE;
6048 }
6049
6050 JSBool
6051 Int64::Hi(JSContext* cx, uintN argc, jsval* vp)
6052 {
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");
6057     return JS_FALSE;
6058   }
6059
6060   JSObject* obj = JSVAL_TO_OBJECT(argv[0]);
6061   JSInt64 u = Int64Base::GetInt(cx, obj);
6062   jsdouble d = JSInt32(INT64_HI(u));
6063
6064   jsval result;
6065   if (!JS_NewNumberValue(cx, d, &result))
6066     return JS_FALSE;
6067
6068   JS_SET_RVAL(cx, vp, result);
6069   return JS_TRUE;
6070 }
6071
6072 JSBool
6073 Int64::Join(JSContext* cx, uintN argc, jsval* vp)
6074 {
6075   if (argc != 2) {
6076     JS_ReportError(cx, "join takes two arguments");
6077     return JS_FALSE;
6078   }
6079
6080   jsval* argv = JS_ARGV(cx, vp);
6081   JSInt32 hi;
6082   JSUint32 lo;
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]);
6087
6088   JSInt64 i = (JSInt64(hi) << 32) + JSInt64(lo);
6089
6090   // Get Int64.prototype from the function's reserved slot.
6091   JSObject* callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
6092
6093   jsval slot;
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);
6097
6098   JSObject* result = Int64Base::Construct(cx, proto, i, false);
6099   if (!result)
6100     return JS_FALSE;
6101
6102   JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
6103   return JS_TRUE;
6104 }
6105
6106 JSBool
6107 UInt64::Construct(JSContext* cx,
6108                   uintN argc,
6109                   jsval* vp)
6110 {
6111   // Construct and return a new UInt64 object.
6112   if (argc != 1) {
6113     JS_ReportError(cx, "UInt64 takes one argument");
6114     return JS_FALSE;
6115   }
6116
6117   jsval* argv = JS_ARGV(cx, vp);
6118   JSUint64 u = 0;
6119   if (!jsvalToBigInteger(cx, argv[0], true, &u))
6120     return TypeError(cx, "uint64", argv[0]);
6121
6122   // Get ctypes.UInt64.prototype from the 'prototype' property of the ctor.
6123   jsval slot;
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);
6128
6129   JSObject* result = Int64Base::Construct(cx, proto, u, true);
6130   if (!result)
6131     return JS_FALSE;
6132
6133   JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
6134   return JS_TRUE;
6135 }
6136
6137 bool
6138 UInt64::IsUInt64(JSContext* cx, JSObject* obj)
6139 {
6140   return JS_GET_CLASS(cx, obj) == &sUInt64Class;
6141 }
6142
6143 JSBool
6144 UInt64::ToString(JSContext* cx, uintN argc, jsval* vp)
6145 {
6146   JSObject* obj = JS_THIS_OBJECT(cx, vp);
6147   if (!obj || !UInt64::IsUInt64(cx, obj)) {
6148     JS_ReportError(cx, "not a UInt64");
6149     return JS_FALSE;
6150   }
6151
6152   return Int64Base::ToString(cx, obj, argc, vp, true);
6153 }
6154
6155 JSBool
6156 UInt64::ToSource(JSContext* cx, uintN argc, jsval* vp)
6157 {
6158   JSObject* obj = JS_THIS_OBJECT(cx, vp);
6159   if (!obj || !UInt64::IsUInt64(cx, obj)) {
6160     JS_ReportError(cx, "not a UInt64");
6161     return JS_FALSE;
6162   }
6163
6164   return Int64Base::ToSource(cx, obj, argc, vp, true);
6165 }
6166
6167 JSBool
6168 UInt64::Compare(JSContext* cx, uintN argc, jsval* vp)
6169 {
6170   jsval* argv = JS_ARGV(cx, vp);
6171   if (argc != 2 ||
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");
6177     return JS_FALSE;
6178   }
6179
6180   JSObject* obj1 = JSVAL_TO_OBJECT(argv[0]);
6181   JSObject* obj2 = JSVAL_TO_OBJECT(argv[1]);
6182
6183   JSUint64 u1 = Int64Base::GetInt(cx, obj1);
6184   JSUint64 u2 = Int64Base::GetInt(cx, obj2);
6185
6186   if (u1 == u2)
6187     JS_SET_RVAL(cx, vp, INT_TO_JSVAL(0));
6188   else if (u1 < u2)
6189     JS_SET_RVAL(cx, vp, INT_TO_JSVAL(-1));
6190   else
6191     JS_SET_RVAL(cx, vp, INT_TO_JSVAL(1));
6192
6193   return JS_TRUE;
6194 }
6195
6196 JSBool
6197 UInt64::Lo(JSContext* cx, uintN argc, jsval* vp)
6198 {
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");
6203     return JS_FALSE;
6204   }
6205
6206   JSObject* obj = JSVAL_TO_OBJECT(argv[0]);
6207   JSUint64 u = Int64Base::GetInt(cx, obj);
6208   jsdouble d = JSUint32(INT64_LO(u));
6209
6210   jsval result;
6211   if (!JS_NewNumberValue(cx, d, &result))
6212     return JS_FALSE;
6213
6214   JS_SET_RVAL(cx, vp, result);
6215   return JS_TRUE;
6216 }
6217
6218 JSBool
6219 UInt64::Hi(JSContext* cx, uintN argc, jsval* vp)
6220 {
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");
6225     return JS_FALSE;
6226   }
6227
6228   JSObject* obj = JSVAL_TO_OBJECT(argv[0]);
6229   JSUint64 u = Int64Base::GetInt(cx, obj);
6230   jsdouble d = JSUint32(INT64_HI(u));
6231
6232   jsval result;
6233   if (!JS_NewNumberValue(cx, d, &result))
6234     return JS_FALSE;
6235
6236   JS_SET_RVAL(cx, vp, result);
6237   return JS_TRUE;
6238 }
6239
6240 JSBool
6241 UInt64::Join(JSContext* cx, uintN argc, jsval* vp)
6242 {
6243   if (argc != 2) {
6244     JS_ReportError(cx, "join takes two arguments");
6245     return JS_FALSE;
6246   }
6247
6248   jsval* argv = JS_ARGV(cx, vp);
6249   JSUint32 hi;
6250   JSUint32 lo;
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]);
6255
6256   JSUint64 u = (JSUint64(hi) << 32) + JSUint64(lo);
6257
6258   // Get UInt64.prototype from the function's reserved slot.
6259   JSObject* callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
6260
6261   jsval slot;
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);
6265
6266   JSObject* result = Int64Base::Construct(cx, proto, u, true);
6267   if (!result)
6268     return JS_FALSE;
6269
6270   JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
6271   return JS_TRUE;
6272 }
6273
6274 }
6275 }
6276