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 "jscompartment.h"
43 : #include "jsobjinlines.h"
44 : #include <limits>
45 :
46 : #include <math.h>
47 : #if defined(XP_WIN) || defined(XP_OS2)
48 : #include <float.h>
49 : #endif
50 :
51 : #if defined(SOLARIS)
52 : #include <ieeefp.h>
53 : #endif
54 :
55 : #ifdef HAVE_SSIZE_T
56 : #include <sys/types.h>
57 : #endif
58 :
59 : using namespace std;
60 :
61 : namespace js {
62 : namespace ctypes {
63 :
64 : /*******************************************************************************
65 : ** JSAPI function prototypes
66 : *******************************************************************************/
67 :
68 : static JSBool ConstructAbstract(JSContext* cx, unsigned argc, jsval* vp);
69 :
70 : namespace CType {
71 : static JSBool ConstructData(JSContext* cx, unsigned argc, jsval* vp);
72 : static JSBool ConstructBasic(JSContext* cx, JSObject* obj, unsigned argc, jsval* vp);
73 :
74 : static void Trace(JSTracer* trc, JSObject* obj);
75 : static void Finalize(JSContext* cx, JSObject* obj);
76 : static void FinalizeProtoClass(JSContext* cx, JSObject* obj);
77 :
78 : static JSBool PrototypeGetter(JSContext* cx, JSObject* obj, jsid idval,
79 : jsval* vp);
80 : static JSBool NameGetter(JSContext* cx, JSObject* obj, jsid idval,
81 : jsval* vp);
82 : static JSBool SizeGetter(JSContext* cx, JSObject* obj, jsid idval,
83 : jsval* vp);
84 : static JSBool PtrGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp);
85 : static JSBool CreateArray(JSContext* cx, unsigned argc, jsval* vp);
86 : static JSBool ToString(JSContext* cx, unsigned argc, jsval* vp);
87 : static JSBool ToSource(JSContext* cx, unsigned argc, jsval* vp);
88 : static JSBool HasInstance(JSContext* cx, JSObject* obj, const jsval* v, JSBool* bp);
89 : }
90 :
91 : namespace PointerType {
92 : static JSBool Create(JSContext* cx, unsigned argc, jsval* vp);
93 : static JSBool ConstructData(JSContext* cx, JSObject* obj, unsigned argc, jsval* vp);
94 :
95 : static JSBool TargetTypeGetter(JSContext* cx, JSObject* obj, jsid idval,
96 : jsval* vp);
97 : static JSBool ContentsGetter(JSContext* cx, JSObject* obj, jsid idval,
98 : jsval* vp);
99 : static JSBool ContentsSetter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict,
100 : jsval* vp);
101 : static JSBool IsNull(JSContext* cx, unsigned argc, jsval* vp);
102 : static JSBool Increment(JSContext* cx, unsigned argc, jsval* vp);
103 : static JSBool Decrement(JSContext* cx, unsigned argc, jsval* vp);
104 : // The following is not an instance function, since we don't want to expose arbitrary
105 : // pointer arithmetic at this moment.
106 : static JSBool OffsetBy(JSContext* cx, int offset, jsval* vp);
107 : }
108 :
109 : namespace ArrayType {
110 : static JSBool Create(JSContext* cx, unsigned argc, jsval* vp);
111 : static JSBool ConstructData(JSContext* cx, JSObject* obj, unsigned argc, jsval* vp);
112 :
113 : static JSBool ElementTypeGetter(JSContext* cx, JSObject* obj, jsid idval,
114 : jsval* vp);
115 : static JSBool LengthGetter(JSContext* cx, JSObject* obj, jsid idval,
116 : jsval* vp);
117 : static JSBool Getter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp);
118 : static JSBool Setter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict, jsval* vp);
119 : static JSBool AddressOfElement(JSContext* cx, unsigned argc, jsval* vp);
120 : }
121 :
122 : namespace StructType {
123 : static JSBool Create(JSContext* cx, unsigned argc, jsval* vp);
124 : static JSBool ConstructData(JSContext* cx, JSObject* obj, unsigned argc, jsval* vp);
125 :
126 : static JSBool FieldsArrayGetter(JSContext* cx, JSObject* obj, jsid idval,
127 : jsval* vp);
128 : static JSBool FieldGetter(JSContext* cx, JSObject* obj, jsid idval,
129 : jsval* vp);
130 : static JSBool FieldSetter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict,
131 : jsval* vp);
132 : static JSBool AddressOfField(JSContext* cx, unsigned argc, jsval* vp);
133 : static JSBool Define(JSContext* cx, unsigned argc, jsval* vp);
134 : }
135 :
136 : namespace FunctionType {
137 : static JSBool Create(JSContext* cx, unsigned argc, jsval* vp);
138 : static JSBool ConstructData(JSContext* cx, JSObject* typeObj,
139 : JSObject* dataObj, JSObject* fnObj, JSObject* thisObj, jsval errVal);
140 :
141 : static JSBool Call(JSContext* cx, unsigned argc, jsval* vp);
142 :
143 : static JSBool ArgTypesGetter(JSContext* cx, JSObject* obj, jsid idval,
144 : jsval* vp);
145 : static JSBool ReturnTypeGetter(JSContext* cx, JSObject* obj, jsid idval,
146 : jsval* vp);
147 : static JSBool ABIGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp);
148 : static JSBool IsVariadicGetter(JSContext* cx, JSObject* obj, jsid idval,
149 : jsval* vp);
150 : }
151 :
152 : namespace CClosure {
153 : static void Trace(JSTracer* trc, JSObject* obj);
154 : static void Finalize(JSContext* cx, JSObject* obj);
155 :
156 : // libffi callback
157 : static void ClosureStub(ffi_cif* cif, void* result, void** args,
158 : void* userData);
159 : }
160 :
161 : namespace CData {
162 : static void Finalize(JSContext* cx, JSObject* obj);
163 :
164 : static JSBool ValueGetter(JSContext* cx, JSObject* obj, jsid idval,
165 : jsval* vp);
166 : static JSBool ValueSetter(JSContext* cx, JSObject* obj, jsid idval,
167 : JSBool strict, jsval* vp);
168 : static JSBool Address(JSContext* cx, unsigned argc, jsval* vp);
169 : static JSBool ReadString(JSContext* cx, unsigned argc, jsval* vp);
170 : static JSBool ToSource(JSContext* cx, unsigned argc, jsval* vp);
171 : }
172 :
173 : // Int64Base provides functions common to Int64 and UInt64.
174 : namespace Int64Base {
175 : JSObject* Construct(JSContext* cx, JSObject* proto, uint64_t data,
176 : bool isUnsigned);
177 :
178 : uint64_t GetInt(JSObject* obj);
179 :
180 : JSBool ToString(JSContext* cx, JSObject* obj, unsigned argc, jsval* vp,
181 : bool isUnsigned);
182 :
183 : JSBool ToSource(JSContext* cx, JSObject* obj, unsigned argc, jsval* vp,
184 : bool isUnsigned);
185 :
186 : static void Finalize(JSContext* cx, JSObject* obj);
187 : }
188 :
189 : namespace Int64 {
190 : static JSBool Construct(JSContext* cx, unsigned argc, jsval* vp);
191 :
192 : static JSBool ToString(JSContext* cx, unsigned argc, jsval* vp);
193 : static JSBool ToSource(JSContext* cx, unsigned argc, jsval* vp);
194 :
195 : static JSBool Compare(JSContext* cx, unsigned argc, jsval* vp);
196 : static JSBool Lo(JSContext* cx, unsigned argc, jsval* vp);
197 : static JSBool Hi(JSContext* cx, unsigned argc, jsval* vp);
198 : static JSBool Join(JSContext* cx, unsigned argc, jsval* vp);
199 : }
200 :
201 : namespace UInt64 {
202 : static JSBool Construct(JSContext* cx, unsigned argc, jsval* vp);
203 :
204 : static JSBool ToString(JSContext* cx, unsigned argc, jsval* vp);
205 : static JSBool ToSource(JSContext* cx, unsigned argc, jsval* vp);
206 :
207 : static JSBool Compare(JSContext* cx, unsigned argc, jsval* vp);
208 : static JSBool Lo(JSContext* cx, unsigned argc, jsval* vp);
209 : static JSBool Hi(JSContext* cx, unsigned argc, jsval* vp);
210 : static JSBool Join(JSContext* cx, unsigned argc, jsval* vp);
211 : }
212 :
213 : /*******************************************************************************
214 : ** JSClass definitions and initialization functions
215 : *******************************************************************************/
216 :
217 : // Class representing the 'ctypes' object itself. This exists to contain the
218 : // JSCTypesCallbacks set of function pointers.
219 : static JSClass sCTypesGlobalClass = {
220 : "ctypes",
221 : JSCLASS_HAS_RESERVED_SLOTS(CTYPESGLOBAL_SLOTS),
222 : JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
223 : JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
224 : JSCLASS_NO_OPTIONAL_MEMBERS
225 : };
226 :
227 : static JSClass sCABIClass = {
228 : "CABI",
229 : JSCLASS_HAS_RESERVED_SLOTS(CABI_SLOTS),
230 : JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
231 : JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
232 : JSCLASS_NO_OPTIONAL_MEMBERS
233 : };
234 :
235 : // Class representing ctypes.{C,Pointer,Array,Struct,Function}Type.prototype.
236 : // This exists to give said prototypes a class of "CType", and to provide
237 : // reserved slots for stashing various other prototype objects.
238 : static JSClass sCTypeProtoClass = {
239 : "CType",
240 : JSCLASS_HAS_RESERVED_SLOTS(CTYPEPROTO_SLOTS),
241 : JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
242 : JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CType::FinalizeProtoClass,
243 : NULL, ConstructAbstract, ConstructAbstract
244 : };
245 :
246 : // Class representing ctypes.CData.prototype and the 'prototype' properties
247 : // of CTypes. This exists to give said prototypes a class of "CData".
248 : static JSClass sCDataProtoClass = {
249 : "CData",
250 : 0,
251 : JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
252 : JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
253 : JSCLASS_NO_OPTIONAL_MEMBERS
254 : };
255 :
256 : static JSClass sCTypeClass = {
257 : "CType",
258 : JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS),
259 : JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
260 : JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CType::Finalize,
261 : NULL, CType::ConstructData, CType::ConstructData,
262 : CType::HasInstance, CType::Trace
263 : };
264 :
265 : static JSClass sCDataClass = {
266 : "CData",
267 : JSCLASS_HAS_RESERVED_SLOTS(CDATA_SLOTS),
268 : JS_PropertyStub, JS_PropertyStub, ArrayType::Getter, ArrayType::Setter,
269 : JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CData::Finalize,
270 : NULL, FunctionType::Call, FunctionType::Call
271 : };
272 :
273 : static JSClass sCClosureClass = {
274 : "CClosure",
275 : JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS),
276 : JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
277 : JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CClosure::Finalize,
278 : NULL, NULL, NULL, NULL, CClosure::Trace
279 : };
280 :
281 : #define CTYPESFN_FLAGS \
282 : (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
283 :
284 : #define CTYPESCTOR_FLAGS \
285 : (CTYPESFN_FLAGS | JSFUN_CONSTRUCTOR)
286 :
287 : #define CTYPESPROP_FLAGS \
288 : (JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
289 :
290 : #define CDATAFN_FLAGS \
291 : (JSPROP_READONLY | JSPROP_PERMANENT)
292 :
293 : static JSPropertySpec sCTypeProps[] = {
294 : { "name", 0, CTYPESPROP_FLAGS, CType::NameGetter, NULL },
295 : { "size", 0, CTYPESPROP_FLAGS, CType::SizeGetter, NULL },
296 : { "ptr", 0, CTYPESPROP_FLAGS, CType::PtrGetter, NULL },
297 : { "prototype", 0, CTYPESPROP_FLAGS, CType::PrototypeGetter, NULL },
298 : { 0, 0, 0, NULL, NULL }
299 : };
300 :
301 : static JSFunctionSpec sCTypeFunctions[] = {
302 : JS_FN("array", CType::CreateArray, 0, CTYPESFN_FLAGS),
303 : JS_FN("toString", CType::ToString, 0, CTYPESFN_FLAGS),
304 : JS_FN("toSource", CType::ToSource, 0, CTYPESFN_FLAGS),
305 : JS_FS_END
306 : };
307 :
308 : static JSPropertySpec sCDataProps[] = {
309 : { "value", 0, JSPROP_SHARED | JSPROP_PERMANENT,
310 : CData::ValueGetter, CData::ValueSetter },
311 : { 0, 0, 0, NULL, NULL }
312 : };
313 :
314 : static JSFunctionSpec sCDataFunctions[] = {
315 : JS_FN("address", CData::Address, 0, CDATAFN_FLAGS),
316 : JS_FN("readString", CData::ReadString, 0, CDATAFN_FLAGS),
317 : JS_FN("toSource", CData::ToSource, 0, CDATAFN_FLAGS),
318 : JS_FN("toString", CData::ToSource, 0, CDATAFN_FLAGS),
319 : JS_FS_END
320 : };
321 :
322 : static JSFunctionSpec sPointerFunction =
323 : JS_FN("PointerType", PointerType::Create, 1, CTYPESCTOR_FLAGS);
324 :
325 : static JSPropertySpec sPointerProps[] = {
326 : { "targetType", 0, CTYPESPROP_FLAGS, PointerType::TargetTypeGetter, NULL },
327 : { 0, 0, 0, NULL, NULL }
328 : };
329 :
330 : static JSFunctionSpec sPointerInstanceFunctions[] = {
331 : JS_FN("isNull", PointerType::IsNull, 0, CTYPESFN_FLAGS),
332 : JS_FN("increment", PointerType::Increment, 0, CTYPESFN_FLAGS),
333 : JS_FN("decrement", PointerType::Decrement, 0, CTYPESFN_FLAGS),
334 : JS_FS_END
335 : };
336 :
337 : static JSPropertySpec sPointerInstanceProps[] = {
338 : { "contents", 0, JSPROP_SHARED | JSPROP_PERMANENT,
339 : PointerType::ContentsGetter, PointerType::ContentsSetter },
340 : { 0, 0, 0, NULL, NULL }
341 : };
342 :
343 : static JSFunctionSpec sArrayFunction =
344 : JS_FN("ArrayType", ArrayType::Create, 1, CTYPESCTOR_FLAGS);
345 :
346 : static JSPropertySpec sArrayProps[] = {
347 : { "elementType", 0, CTYPESPROP_FLAGS, ArrayType::ElementTypeGetter, NULL },
348 : { "length", 0, CTYPESPROP_FLAGS, ArrayType::LengthGetter, NULL },
349 : { 0, 0, 0, NULL, NULL }
350 : };
351 :
352 : static JSFunctionSpec sArrayInstanceFunctions[] = {
353 : JS_FN("addressOfElement", ArrayType::AddressOfElement, 1, CDATAFN_FLAGS),
354 : JS_FS_END
355 : };
356 :
357 : static JSPropertySpec sArrayInstanceProps[] = {
358 : { "length", 0, JSPROP_SHARED | JSPROP_READONLY | JSPROP_PERMANENT,
359 : ArrayType::LengthGetter, NULL },
360 : { 0, 0, 0, NULL, NULL }
361 : };
362 :
363 : static JSFunctionSpec sStructFunction =
364 : JS_FN("StructType", StructType::Create, 2, CTYPESCTOR_FLAGS);
365 :
366 : static JSPropertySpec sStructProps[] = {
367 : { "fields", 0, CTYPESPROP_FLAGS, StructType::FieldsArrayGetter, NULL },
368 : { 0, 0, 0, NULL, NULL }
369 : };
370 :
371 : static JSFunctionSpec sStructFunctions[] = {
372 : JS_FN("define", StructType::Define, 1, CDATAFN_FLAGS),
373 : JS_FS_END
374 : };
375 :
376 : static JSFunctionSpec sStructInstanceFunctions[] = {
377 : JS_FN("addressOfField", StructType::AddressOfField, 1, CDATAFN_FLAGS),
378 : JS_FS_END
379 : };
380 :
381 : static JSFunctionSpec sFunctionFunction =
382 : JS_FN("FunctionType", FunctionType::Create, 2, CTYPESCTOR_FLAGS);
383 :
384 : static JSPropertySpec sFunctionProps[] = {
385 : { "argTypes", 0, CTYPESPROP_FLAGS, FunctionType::ArgTypesGetter, NULL },
386 : { "returnType", 0, CTYPESPROP_FLAGS, FunctionType::ReturnTypeGetter, NULL },
387 : { "abi", 0, CTYPESPROP_FLAGS, FunctionType::ABIGetter, NULL },
388 : { "isVariadic", 0, CTYPESPROP_FLAGS, FunctionType::IsVariadicGetter, NULL },
389 : { 0, 0, 0, NULL, NULL }
390 : };
391 :
392 : static JSClass sInt64ProtoClass = {
393 : "Int64",
394 : 0,
395 : JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
396 : JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
397 : JSCLASS_NO_OPTIONAL_MEMBERS
398 : };
399 :
400 : static JSClass sUInt64ProtoClass = {
401 : "UInt64",
402 : 0,
403 : JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
404 : JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
405 : JSCLASS_NO_OPTIONAL_MEMBERS
406 : };
407 :
408 : static JSClass sInt64Class = {
409 : "Int64",
410 : JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS),
411 : JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
412 : JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Int64Base::Finalize,
413 : JSCLASS_NO_OPTIONAL_MEMBERS
414 : };
415 :
416 : static JSClass sUInt64Class = {
417 : "UInt64",
418 : JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS),
419 : JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
420 : JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Int64Base::Finalize,
421 : JSCLASS_NO_OPTIONAL_MEMBERS
422 : };
423 :
424 : static JSFunctionSpec sInt64StaticFunctions[] = {
425 : JS_FN("compare", Int64::Compare, 2, CTYPESFN_FLAGS),
426 : JS_FN("lo", Int64::Lo, 1, CTYPESFN_FLAGS),
427 : JS_FN("hi", Int64::Hi, 1, CTYPESFN_FLAGS),
428 : JS_FN("join", Int64::Join, 2, CTYPESFN_FLAGS),
429 : JS_FS_END
430 : };
431 :
432 : static JSFunctionSpec sUInt64StaticFunctions[] = {
433 : JS_FN("compare", UInt64::Compare, 2, CTYPESFN_FLAGS),
434 : JS_FN("lo", UInt64::Lo, 1, CTYPESFN_FLAGS),
435 : JS_FN("hi", UInt64::Hi, 1, CTYPESFN_FLAGS),
436 : JS_FN("join", UInt64::Join, 2, CTYPESFN_FLAGS),
437 : JS_FS_END
438 : };
439 :
440 : static JSFunctionSpec sInt64Functions[] = {
441 : JS_FN("toString", Int64::ToString, 0, CTYPESFN_FLAGS),
442 : JS_FN("toSource", Int64::ToSource, 0, CTYPESFN_FLAGS),
443 : JS_FS_END
444 : };
445 :
446 : static JSFunctionSpec sUInt64Functions[] = {
447 : JS_FN("toString", UInt64::ToString, 0, CTYPESFN_FLAGS),
448 : JS_FN("toSource", UInt64::ToSource, 0, CTYPESFN_FLAGS),
449 : JS_FS_END
450 : };
451 :
452 : static JSFunctionSpec sModuleFunctions[] = {
453 : JS_FN("open", Library::Open, 1, CTYPESFN_FLAGS),
454 : JS_FN("cast", CData::Cast, 2, CTYPESFN_FLAGS),
455 : JS_FN("getRuntime", CData::GetRuntime, 1, CTYPESFN_FLAGS),
456 : JS_FN("libraryName", Library::Name, 1, CTYPESFN_FLAGS),
457 : JS_FS_END
458 : };
459 :
460 0 : static inline bool FloatIsFinite(double f) {
461 : #ifdef WIN32
462 : return _finite(f) != 0;
463 : #else
464 0 : return finite(f);
465 : #endif
466 : }
467 :
468 : JS_ALWAYS_INLINE JSString*
469 3348 : NewUCString(JSContext* cx, const AutoString& from)
470 : {
471 3348 : return JS_NewUCStringCopyN(cx, from.begin(), from.length());
472 : }
473 :
474 : JS_ALWAYS_INLINE size_t
475 22598 : Align(size_t val, size_t align)
476 : {
477 22598 : return ((val - 1) | (align - 1)) + 1;
478 : }
479 :
480 : static ABICode
481 4516 : GetABICode(JSObject* obj)
482 : {
483 : // make sure we have an object representing a CABI class,
484 : // and extract the enumerated class type from the reserved slot.
485 4516 : if (JS_GetClass(obj) != &sCABIClass)
486 1 : return INVALID_ABI;
487 :
488 4515 : jsval result = JS_GetReservedSlot(obj, SLOT_ABICODE);
489 4515 : return ABICode(JSVAL_TO_INT(result));
490 : }
491 :
492 : JSErrorFormatString ErrorFormatString[CTYPESERR_LIMIT] = {
493 : #define MSG_DEF(name, number, count, exception, format) \
494 : { format, count, exception } ,
495 : #include "ctypes.msg"
496 : #undef MSG_DEF
497 : };
498 :
499 : const JSErrorFormatString*
500 1086 : GetErrorMessage(void* userRef, const char* locale, const unsigned errorNumber)
501 : {
502 1086 : if (0 < errorNumber && errorNumber < CTYPESERR_LIMIT)
503 1086 : return &ErrorFormatString[errorNumber];
504 0 : return NULL;
505 : }
506 :
507 : JSBool
508 543 : TypeError(JSContext* cx, const char* expected, jsval actual)
509 : {
510 543 : JSString* str = JS_ValueToSource(cx, actual);
511 1086 : JSAutoByteString bytes;
512 :
513 : const char* src;
514 543 : if (str) {
515 543 : src = bytes.encode(cx, str);
516 543 : if (!src)
517 0 : return false;
518 : } else {
519 0 : JS_ClearPendingException(cx);
520 0 : src = "<<error converting value to string>>";
521 : }
522 : JS_ReportErrorNumber(cx, GetErrorMessage, NULL,
523 543 : CTYPESMSG_TYPE_ERROR, expected, src);
524 543 : return false;
525 : }
526 :
527 : static JSObject*
528 23098 : InitCTypeClass(JSContext* cx, JSObject* parent)
529 : {
530 : JSFunction* fun = JS_DefineFunction(cx, parent, "CType", ConstructAbstract, 0,
531 23098 : CTYPESCTOR_FLAGS);
532 23098 : if (!fun)
533 0 : return NULL;
534 :
535 23098 : JSObject* ctor = JS_GetFunctionObject(fun);
536 23098 : JSObject* fnproto = JS_GetPrototype(ctor);
537 23098 : JS_ASSERT(ctor);
538 23098 : JS_ASSERT(fnproto);
539 :
540 : // Set up ctypes.CType.prototype.
541 23098 : JSObject* prototype = JS_NewObject(cx, &sCTypeProtoClass, fnproto, parent);
542 23098 : if (!prototype)
543 0 : return NULL;
544 :
545 23098 : if (!JS_DefineProperty(cx, ctor, "prototype", OBJECT_TO_JSVAL(prototype),
546 23098 : NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
547 0 : return NULL;
548 :
549 23098 : if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(ctor),
550 23098 : NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
551 0 : return NULL;
552 :
553 : // Define properties and functions common to all CTypes.
554 46196 : if (!JS_DefineProperties(cx, prototype, sCTypeProps) ||
555 23098 : !JS_DefineFunctions(cx, prototype, sCTypeFunctions))
556 0 : return NULL;
557 :
558 23098 : if (!JS_FreezeObject(cx, ctor) || !JS_FreezeObject(cx, prototype))
559 0 : return NULL;
560 :
561 23098 : return prototype;
562 : }
563 :
564 : static JSObject*
565 23098 : InitCDataClass(JSContext* cx, JSObject* parent, JSObject* CTypeProto)
566 : {
567 : JSFunction* fun = JS_DefineFunction(cx, parent, "CData", ConstructAbstract, 0,
568 23098 : CTYPESCTOR_FLAGS);
569 23098 : if (!fun)
570 0 : return NULL;
571 :
572 23098 : JSObject* ctor = JS_GetFunctionObject(fun);
573 23098 : JS_ASSERT(ctor);
574 :
575 : // Set up ctypes.CData.__proto__ === ctypes.CType.prototype.
576 : // (Note that 'ctypes.CData instanceof Function' is still true, thanks to the
577 : // prototype chain.)
578 23098 : if (!JS_SetPrototype(cx, ctor, CTypeProto))
579 0 : return NULL;
580 :
581 : // Set up ctypes.CData.prototype.
582 23098 : JSObject* prototype = JS_NewObject(cx, &sCDataProtoClass, NULL, parent);
583 23098 : if (!prototype)
584 0 : return NULL;
585 :
586 23098 : if (!JS_DefineProperty(cx, ctor, "prototype", OBJECT_TO_JSVAL(prototype),
587 23098 : NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
588 0 : return NULL;
589 :
590 23098 : if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(ctor),
591 23098 : NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
592 0 : return NULL;
593 :
594 : // Define properties and functions common to all CDatas.
595 46196 : if (!JS_DefineProperties(cx, prototype, sCDataProps) ||
596 23098 : !JS_DefineFunctions(cx, prototype, sCDataFunctions))
597 0 : return NULL;
598 :
599 23098 : if (//!JS_FreezeObject(cx, prototype) || // XXX fixme - see bug 541212!
600 23098 : !JS_FreezeObject(cx, ctor))
601 0 : return NULL;
602 :
603 23098 : return prototype;
604 : }
605 :
606 : static JSBool
607 69294 : DefineABIConstant(JSContext* cx,
608 : JSObject* parent,
609 : const char* name,
610 : ABICode code)
611 : {
612 : JSObject* obj = JS_DefineObject(cx, parent, name, &sCABIClass, NULL,
613 69294 : JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
614 69294 : if (!obj)
615 0 : return false;
616 69294 : JS_SetReservedSlot(obj, SLOT_ABICODE, INT_TO_JSVAL(code));
617 69294 : return JS_FreezeObject(cx, obj);
618 : }
619 :
620 : // Set up a single type constructor for
621 : // ctypes.{Pointer,Array,Struct,Function}Type.
622 : static JSBool
623 92392 : InitTypeConstructor(JSContext* cx,
624 : JSObject* parent,
625 : JSObject* CTypeProto,
626 : JSObject* CDataProto,
627 : JSFunctionSpec spec,
628 : JSFunctionSpec* fns,
629 : JSPropertySpec* props,
630 : JSFunctionSpec* instanceFns,
631 : JSPropertySpec* instanceProps,
632 : JSObject*& typeProto,
633 : JSObject*& dataProto)
634 : {
635 : JSFunction* fun = js::DefineFunctionWithReserved(cx, parent, spec.name, spec.call,
636 92392 : spec.nargs, spec.flags);
637 92392 : if (!fun)
638 0 : return false;
639 :
640 92392 : JSObject* obj = JS_GetFunctionObject(fun);
641 92392 : if (!obj)
642 0 : return false;
643 :
644 : // Set up the .prototype and .prototype.constructor properties.
645 92392 : typeProto = JS_NewObject(cx, &sCTypeProtoClass, CTypeProto, parent);
646 92392 : if (!typeProto)
647 0 : return false;
648 :
649 : // Define property before proceeding, for GC safety.
650 184784 : if (!JS_DefineProperty(cx, obj, "prototype", OBJECT_TO_JSVAL(typeProto),
651 92392 : NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
652 0 : return false;
653 :
654 92392 : if (fns && !JS_DefineFunctions(cx, typeProto, fns))
655 0 : return false;
656 :
657 92392 : if (!JS_DefineProperties(cx, typeProto, props))
658 0 : return false;
659 :
660 92392 : if (!JS_DefineProperty(cx, typeProto, "constructor", OBJECT_TO_JSVAL(obj),
661 92392 : NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
662 0 : return false;
663 :
664 : // Stash ctypes.{Pointer,Array,Struct}Type.prototype on a reserved slot of
665 : // the type constructor, for faster lookup.
666 92392 : js::SetFunctionNativeReserved(obj, SLOT_FN_CTORPROTO, OBJECT_TO_JSVAL(typeProto));
667 :
668 : // Create an object to serve as the common ancestor for all CData objects
669 : // created from the given type constructor. This has ctypes.CData.prototype
670 : // as its prototype, such that it inherits the properties and functions
671 : // common to all CDatas.
672 92392 : dataProto = JS_NewObject(cx, &sCDataProtoClass, CDataProto, parent);
673 92392 : if (!dataProto)
674 0 : return false;
675 184784 : js::AutoObjectRooter protoroot(cx, dataProto);
676 :
677 : // Define functions and properties on the 'dataProto' object that are common
678 : // to all CData objects created from this type constructor. (These will
679 : // become functions and properties on CData objects created from this type.)
680 92392 : if (instanceFns && !JS_DefineFunctions(cx, dataProto, instanceFns))
681 0 : return false;
682 :
683 92392 : if (instanceProps && !JS_DefineProperties(cx, dataProto, instanceProps))
684 0 : return false;
685 :
686 : // Link the type prototype to the data prototype.
687 92392 : JS_SetReservedSlot(typeProto, SLOT_OURDATAPROTO, OBJECT_TO_JSVAL(dataProto));
688 :
689 184784 : if (!JS_FreezeObject(cx, obj) ||
690 : //!JS_FreezeObject(cx, dataProto) || // XXX fixme - see bug 541212!
691 92392 : !JS_FreezeObject(cx, typeProto))
692 0 : return false;
693 :
694 92392 : return true;
695 : }
696 :
697 : JSObject*
698 46196 : InitInt64Class(JSContext* cx,
699 : JSObject* parent,
700 : JSClass* clasp,
701 : JSNative construct,
702 : JSFunctionSpec* fs,
703 : JSFunctionSpec* static_fs)
704 : {
705 : // Init type class and constructor
706 : JSObject* prototype = JS_InitClass(cx, parent, NULL, clasp, construct,
707 46196 : 0, NULL, fs, NULL, static_fs);
708 46196 : if (!prototype)
709 0 : return NULL;
710 :
711 46196 : JSObject* ctor = JS_GetConstructor(cx, prototype);
712 46196 : if (!ctor)
713 0 : return NULL;
714 46196 : if (!JS_FreezeObject(cx, ctor))
715 0 : return NULL;
716 :
717 : // Redefine the 'join' function as an extended native and stash
718 : // ctypes.{Int64,UInt64}.prototype in a reserved slot of the new function.
719 46196 : JS_ASSERT(clasp == &sInt64ProtoClass || clasp == &sUInt64ProtoClass);
720 46196 : JSNative native = (clasp == &sInt64ProtoClass) ? Int64::Join : UInt64::Join;
721 : JSFunction* fun = js::DefineFunctionWithReserved(cx, ctor, "join", native,
722 46196 : 2, CTYPESFN_FLAGS);
723 46196 : if (!fun)
724 0 : return NULL;
725 :
726 : js::SetFunctionNativeReserved(fun, SLOT_FN_INT64PROTO,
727 46196 : OBJECT_TO_JSVAL(prototype));
728 :
729 46196 : if (!JS_FreezeObject(cx, prototype))
730 0 : return NULL;
731 :
732 46196 : return prototype;
733 : }
734 :
735 : static void
736 115490 : AttachProtos(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" that we fill in this automated manner.)
741 1385880 : for (uint32_t i = 0; i <= SLOT_UINT64PROTO; ++i)
742 1270390 : JS_SetReservedSlot(proto, i, OBJECT_TO_JSVAL(protos[i]));
743 115490 : }
744 :
745 : JSBool
746 23098 : InitTypeClasses(JSContext* cx, JSObject* parent)
747 : {
748 : // Initialize the ctypes.CType class. This acts as an abstract base class for
749 : // the various types, and provides the common API functions. It has:
750 : // * [[Class]] "Function"
751 : // * __proto__ === Function.prototype
752 : // * A constructor that throws a TypeError. (You can't construct an
753 : // abstract type!)
754 : // * 'prototype' property:
755 : // * [[Class]] "CTypeProto"
756 : // * __proto__ === Function.prototype
757 : // * A constructor that throws a TypeError. (You can't construct an
758 : // abstract type instance!)
759 : // * 'constructor' property === ctypes.CType
760 : // * Provides properties and functions common to all CTypes.
761 23098 : JSObject* CTypeProto = InitCTypeClass(cx, parent);
762 23098 : if (!CTypeProto)
763 0 : return false;
764 :
765 : // Initialize the ctypes.CData class. This acts as an abstract base class for
766 : // instances of the various types, and provides the common API functions.
767 : // It has:
768 : // * [[Class]] "Function"
769 : // * __proto__ === Function.prototype
770 : // * A constructor that throws a TypeError. (You can't construct an
771 : // abstract type instance!)
772 : // * 'prototype' property:
773 : // * [[Class]] "CDataProto"
774 : // * 'constructor' property === ctypes.CData
775 : // * Provides properties and functions common to all CDatas.
776 23098 : JSObject* CDataProto = InitCDataClass(cx, parent, CTypeProto);
777 23098 : if (!CDataProto)
778 0 : return false;
779 :
780 : // Link CTypeProto to CDataProto.
781 : JS_SetReservedSlot(CTypeProto, SLOT_OURDATAPROTO,
782 23098 : OBJECT_TO_JSVAL(CDataProto));
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 23098 : if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
812 : sPointerFunction, NULL, sPointerProps,
813 : sPointerInstanceFunctions, sPointerInstanceProps,
814 23098 : protos[SLOT_POINTERPROTO], protos[SLOT_POINTERDATAPROTO]))
815 0 : return false;
816 46196 : js::AutoObjectRooter proot(cx, protos[SLOT_POINTERDATAPROTO]);
817 :
818 23098 : if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
819 : sArrayFunction, NULL, sArrayProps,
820 : sArrayInstanceFunctions, sArrayInstanceProps,
821 23098 : protos[SLOT_ARRAYPROTO], protos[SLOT_ARRAYDATAPROTO]))
822 0 : return false;
823 46196 : js::AutoObjectRooter aroot(cx, protos[SLOT_ARRAYDATAPROTO]);
824 :
825 23098 : if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
826 : sStructFunction, sStructFunctions, sStructProps,
827 : sStructInstanceFunctions, NULL,
828 23098 : protos[SLOT_STRUCTPROTO], protos[SLOT_STRUCTDATAPROTO]))
829 0 : return false;
830 46196 : js::AutoObjectRooter sroot(cx, protos[SLOT_STRUCTDATAPROTO]);
831 :
832 23098 : if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
833 : sFunctionFunction, NULL, sFunctionProps, NULL, NULL,
834 23098 : protos[SLOT_FUNCTIONPROTO], protos[SLOT_FUNCTIONDATAPROTO]))
835 0 : return false;
836 46196 : js::AutoObjectRooter froot(cx, protos[SLOT_FUNCTIONDATAPROTO]);
837 :
838 23098 : 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 23098 : Int64::Construct, sInt64Functions, sInt64StaticFunctions);
850 23098 : if (!protos[SLOT_INT64PROTO])
851 0 : return false;
852 : protos[SLOT_UINT64PROTO] = InitInt64Class(cx, parent, &sUInt64ProtoClass,
853 23098 : UInt64::Construct, sUInt64Functions, sUInt64StaticFunctions);
854 23098 : if (!protos[SLOT_UINT64PROTO])
855 0 : 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 23098 : AttachProtos(CTypeProto, protos);
861 23098 : AttachProtos(protos[SLOT_POINTERPROTO], protos);
862 23098 : AttachProtos(protos[SLOT_ARRAYPROTO], protos);
863 23098 : AttachProtos(protos[SLOT_STRUCTPROTO], protos);
864 23098 : AttachProtos(protos[SLOT_FUNCTIONPROTO], protos);
865 :
866 : // Attach objects representing ABI constants.
867 69294 : if (!DefineABIConstant(cx, parent, "default_abi", ABI_DEFAULT) ||
868 23098 : !DefineABIConstant(cx, parent, "stdcall_abi", ABI_STDCALL) ||
869 23098 : !DefineABIConstant(cx, parent, "winapi_abi", ABI_WINAPI))
870 0 : return false;
871 :
872 : // Create objects representing the builtin types, and attach them to the
873 : // ctypes object. Each type object 't' has:
874 : // * [[Class]] "CType"
875 : // * __proto__ === ctypes.CType.prototype
876 : // * A constructor which creates and returns a CData object, containing
877 : // binary data of the given type.
878 : // * 'prototype' property:
879 : // * [[Class]] "CDataProto"
880 : // * __proto__ === ctypes.CData.prototype
881 : // * 'constructor' property === 't'
882 : #define DEFINE_TYPE(name, type, ffiType) \
883 : JSObject* typeObj_##name = \
884 : CType::DefineBuiltin(cx, parent, #name, CTypeProto, CDataProto, #name, \
885 : TYPE_##name, INT_TO_JSVAL(sizeof(type)), \
886 : INT_TO_JSVAL(ffiType.alignment), &ffiType); \
887 : if (!typeObj_##name) \
888 : return false;
889 : #include "typedefs.h"
890 :
891 : // Alias 'ctypes.unsigned' as 'ctypes.unsigned_int', since they represent
892 : // the same type in C.
893 23098 : if (!JS_DefineProperty(cx, parent, "unsigned",
894 : OBJECT_TO_JSVAL(typeObj_unsigned_int), NULL, NULL,
895 23098 : JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
896 0 : return false;
897 :
898 : // Create objects representing the special types void_t and voidptr_t.
899 : JSObject* typeObj =
900 : CType::DefineBuiltin(cx, parent, "void_t", CTypeProto, CDataProto, "void",
901 23098 : TYPE_void_t, JSVAL_VOID, JSVAL_VOID, &ffi_type_void);
902 23098 : if (!typeObj)
903 0 : return false;
904 :
905 23098 : typeObj = PointerType::CreateInternal(cx, typeObj);
906 23098 : if (!typeObj)
907 0 : return false;
908 23098 : if (!JS_DefineProperty(cx, parent, "voidptr_t", OBJECT_TO_JSVAL(typeObj),
909 23098 : NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
910 0 : return false;
911 :
912 23098 : return true;
913 : }
914 :
915 : bool
916 388 : IsCTypesGlobal(JSObject* obj)
917 : {
918 388 : return JS_GetClass(obj) == &sCTypesGlobalClass;
919 : }
920 :
921 : // Get the JSCTypesCallbacks struct from the 'ctypes' object 'obj'.
922 : JSCTypesCallbacks*
923 57 : GetCallbacks(JSObject* obj)
924 : {
925 57 : JS_ASSERT(IsCTypesGlobal(obj));
926 :
927 57 : jsval result = JS_GetReservedSlot(obj, SLOT_CALLBACKS);
928 57 : if (JSVAL_IS_VOID(result))
929 0 : return NULL;
930 :
931 57 : return static_cast<JSCTypesCallbacks*>(JSVAL_TO_PRIVATE(result));
932 : }
933 :
934 : JS_BEGIN_EXTERN_C
935 :
936 : JS_PUBLIC_API(JSBool)
937 23098 : JS_InitCTypesClass(JSContext* cx, JSObject* global)
938 : {
939 : // attach ctypes property to global object
940 23098 : JSObject* ctypes = JS_NewObject(cx, &sCTypesGlobalClass, NULL, NULL);
941 23098 : if (!ctypes)
942 0 : return false;
943 :
944 23098 : if (!JS_DefineProperty(cx, global, "ctypes", OBJECT_TO_JSVAL(ctypes),
945 23098 : JS_PropertyStub, JS_StrictPropertyStub, JSPROP_READONLY | JSPROP_PERMANENT)) {
946 0 : return false;
947 : }
948 :
949 23098 : if (!InitTypeClasses(cx, ctypes))
950 0 : return false;
951 :
952 : // attach API functions
953 23098 : if (!JS_DefineFunctions(cx, ctypes, sModuleFunctions))
954 0 : return false;
955 :
956 : // Seal the ctypes object, to prevent modification.
957 23098 : return JS_FreezeObject(cx, ctypes);
958 : }
959 :
960 : JS_PUBLIC_API(void)
961 274 : JS_SetCTypesCallbacks(JSObject* ctypesObj,
962 : JSCTypesCallbacks* callbacks)
963 : {
964 274 : JS_ASSERT(callbacks);
965 274 : JS_ASSERT(IsCTypesGlobal(ctypesObj));
966 :
967 : // Set the callbacks on a reserved slot.
968 274 : JS_SetReservedSlot(ctypesObj, SLOT_CALLBACKS, PRIVATE_TO_JSVAL(callbacks));
969 274 : }
970 :
971 : JS_END_EXTERN_C
972 :
973 : /*******************************************************************************
974 : ** Type conversion functions
975 : *******************************************************************************/
976 :
977 : // Enforce some sanity checks on type widths and properties.
978 : // Where the architecture is 64-bit, make sure it's LP64 or LLP64. (ctypes.int
979 : // autoconverts to a primitive JS number; to support ILP64 architectures, it
980 : // would need to autoconvert to an Int64 object instead. Therefore we enforce
981 : // this invariant here.)
982 : JS_STATIC_ASSERT(sizeof(bool) == 1 || sizeof(bool) == 4);
983 : JS_STATIC_ASSERT(sizeof(char) == 1);
984 : JS_STATIC_ASSERT(sizeof(short) == 2);
985 : JS_STATIC_ASSERT(sizeof(int) == 4);
986 : JS_STATIC_ASSERT(sizeof(unsigned) == 4);
987 : JS_STATIC_ASSERT(sizeof(long) == 4 || sizeof(long) == 8);
988 : JS_STATIC_ASSERT(sizeof(long long) == 8);
989 : JS_STATIC_ASSERT(sizeof(size_t) == sizeof(uintptr_t));
990 : JS_STATIC_ASSERT(sizeof(float) == 4);
991 : JS_STATIC_ASSERT(sizeof(PRFuncPtr) == sizeof(void*));
992 : JS_STATIC_ASSERT(numeric_limits<double>::is_signed);
993 :
994 : // Templated helper to convert FromType to TargetType, for the default case
995 : // where the trivial POD constructor will do.
996 : template<class TargetType, class FromType>
997 : struct ConvertImpl {
998 336924 : static JS_ALWAYS_INLINE TargetType Convert(FromType d) {
999 336924 : return TargetType(d);
1000 : }
1001 : };
1002 :
1003 : #ifdef _MSC_VER
1004 : // MSVC can't perform double to unsigned __int64 conversion when the
1005 : // double is greater than 2^63 - 1. Help it along a little.
1006 : template<>
1007 : struct ConvertImpl<uint64_t, double> {
1008 : static JS_ALWAYS_INLINE uint64_t Convert(double d) {
1009 : return d > 0x7fffffffffffffffui64 ?
1010 : uint64_t(d - 0x8000000000000000ui64) + 0x8000000000000000ui64 :
1011 : uint64_t(d);
1012 : }
1013 : };
1014 : #endif
1015 :
1016 : // C++ doesn't guarantee that exact values are the only ones that will
1017 : // round-trip. In fact, on some platforms, including SPARC, there are pairs of
1018 : // values, a uint64_t and a double, such that neither value is exactly
1019 : // representable in the other type, but they cast to each other.
1020 : #ifdef SPARC
1021 : // Simulate x86 overflow behavior
1022 : template<>
1023 : struct ConvertImpl<uint64_t, double> {
1024 : static JS_ALWAYS_INLINE uint64_t Convert(double d) {
1025 : return d >= 0xffffffffffffffff ?
1026 : 0x8000000000000000 : uint64_t(d);
1027 : }
1028 : };
1029 :
1030 : template<>
1031 : struct ConvertImpl<int64_t, double> {
1032 : static JS_ALWAYS_INLINE int64_t Convert(double d) {
1033 : return d >= 0x7fffffffffffffff ?
1034 : 0x8000000000000000 : int64_t(d);
1035 : }
1036 : };
1037 : #endif
1038 :
1039 : template<class TargetType, class FromType>
1040 336924 : static JS_ALWAYS_INLINE TargetType Convert(FromType d)
1041 : {
1042 336924 : return ConvertImpl<TargetType, FromType>::Convert(d);
1043 : }
1044 :
1045 : template<class TargetType, class FromType>
1046 182057 : static JS_ALWAYS_INLINE bool IsAlwaysExact()
1047 : {
1048 : // Return 'true' if TargetType can always exactly represent FromType.
1049 : // This means that:
1050 : // 1) TargetType must be the same or more bits wide as FromType. For integers
1051 : // represented in 'n' bits, unsigned variants will have 'n' digits while
1052 : // signed will have 'n - 1'. For floating point types, 'digits' is the
1053 : // mantissa width.
1054 : // 2) If FromType is signed, TargetType must also be signed. (Floating point
1055 : // types are always signed.)
1056 : // 3) If TargetType is an exact integral type, FromType must be also.
1057 : if (numeric_limits<TargetType>::digits < numeric_limits<FromType>::digits)
1058 27537 : return false;
1059 :
1060 : if (numeric_limits<FromType>::is_signed &&
1061 : !numeric_limits<TargetType>::is_signed)
1062 152072 : return false;
1063 :
1064 : if (!numeric_limits<FromType>::is_exact &&
1065 : numeric_limits<TargetType>::is_exact)
1066 26 : return false;
1067 :
1068 2422 : return true;
1069 : }
1070 :
1071 : // Templated helper to determine if FromType 'i' converts losslessly to
1072 : // TargetType 'j'. Default case where both types are the same signedness.
1073 : template<class TargetType, class FromType, bool TargetSigned, bool FromSigned>
1074 : struct IsExactImpl {
1075 2746 : static JS_ALWAYS_INLINE bool Test(FromType i, TargetType j) {
1076 : JS_STATIC_ASSERT(numeric_limits<TargetType>::is_exact);
1077 2746 : return FromType(j) == i;
1078 : }
1079 : };
1080 :
1081 : // Specialization where TargetType is unsigned, FromType is signed.
1082 : template<class TargetType, class FromType>
1083 : struct IsExactImpl<TargetType, FromType, false, true> {
1084 176874 : static JS_ALWAYS_INLINE bool Test(FromType i, TargetType j) {
1085 : JS_STATIC_ASSERT(numeric_limits<TargetType>::is_exact);
1086 176874 : return i >= 0 && FromType(j) == i;
1087 : }
1088 : };
1089 :
1090 : // Specialization where TargetType is signed, FromType is unsigned.
1091 : template<class TargetType, class FromType>
1092 : struct IsExactImpl<TargetType, FromType, true, false> {
1093 15 : static JS_ALWAYS_INLINE bool Test(FromType i, TargetType j) {
1094 : JS_STATIC_ASSERT(numeric_limits<TargetType>::is_exact);
1095 15 : return TargetType(i) >= 0 && FromType(j) == i;
1096 : }
1097 : };
1098 :
1099 : // Convert FromType 'i' to TargetType 'result', returning true iff 'result'
1100 : // is an exact representation of 'i'.
1101 : template<class TargetType, class FromType>
1102 182057 : static JS_ALWAYS_INLINE bool ConvertExact(FromType i, TargetType* result)
1103 : {
1104 : // Require that TargetType is integral, to simplify conversion.
1105 : JS_STATIC_ASSERT(numeric_limits<TargetType>::is_exact);
1106 :
1107 182057 : *result = Convert<TargetType>(i);
1108 :
1109 : // See if we can avoid a dynamic check.
1110 182057 : if (IsAlwaysExact<TargetType, FromType>())
1111 2422 : return true;
1112 :
1113 : // Return 'true' if 'i' is exactly representable in 'TargetType'.
1114 : return IsExactImpl<TargetType,
1115 : FromType,
1116 : numeric_limits<TargetType>::is_signed,
1117 179635 : numeric_limits<FromType>::is_signed>::Test(i, *result);
1118 : }
1119 :
1120 : // Templated helper to determine if Type 'i' is negative. Default case
1121 : // where IntegerType is unsigned.
1122 : template<class Type, bool IsSigned>
1123 : struct IsNegativeImpl {
1124 2543 : static JS_ALWAYS_INLINE bool Test(Type i) {
1125 2543 : return false;
1126 : }
1127 : };
1128 :
1129 : // Specialization where Type is signed.
1130 : template<class Type>
1131 : struct IsNegativeImpl<Type, true> {
1132 576 : static JS_ALWAYS_INLINE bool Test(Type i) {
1133 576 : return i < 0;
1134 : }
1135 : };
1136 :
1137 : // Determine whether Type 'i' is negative.
1138 : template<class Type>
1139 3119 : static JS_ALWAYS_INLINE bool IsNegative(Type i)
1140 : {
1141 3119 : return IsNegativeImpl<Type, numeric_limits<Type>::is_signed>::Test(i);
1142 : }
1143 :
1144 : // Implicitly convert val to bool, allowing JSBool, int, and double
1145 : // arguments numerically equal to 0 or 1.
1146 : static bool
1147 110 : jsvalToBool(JSContext* cx, jsval val, bool* result)
1148 : {
1149 110 : if (JSVAL_IS_BOOLEAN(val)) {
1150 90 : *result = JSVAL_TO_BOOLEAN(val) != JS_FALSE;
1151 90 : return true;
1152 : }
1153 20 : if (JSVAL_IS_INT(val)) {
1154 4 : int32_t i = JSVAL_TO_INT(val);
1155 4 : *result = i != 0;
1156 4 : return i == 0 || i == 1;
1157 : }
1158 16 : if (JSVAL_IS_DOUBLE(val)) {
1159 5 : double d = JSVAL_TO_DOUBLE(val);
1160 5 : *result = d != 0;
1161 : // Allow -0.
1162 5 : return d == 1 || d == 0;
1163 : }
1164 : // Don't silently convert null to bool. It's probably a mistake.
1165 11 : return false;
1166 : }
1167 :
1168 : // Implicitly convert val to IntegerType, allowing JSBool, int, double,
1169 : // Int64, UInt64, and CData integer types 't' where all values of 't' are
1170 : // representable by IntegerType.
1171 : template<class IntegerType>
1172 : static bool
1173 33166 : jsvalToInteger(JSContext* cx, jsval val, IntegerType* result)
1174 : {
1175 : JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
1176 :
1177 33166 : if (JSVAL_IS_INT(val)) {
1178 : // Make sure the integer fits in the alotted precision, and has the right
1179 : // sign.
1180 30571 : int32_t i = JSVAL_TO_INT(val);
1181 30571 : return ConvertExact(i, result);
1182 : }
1183 2595 : if (JSVAL_IS_DOUBLE(val)) {
1184 : // Don't silently lose bits here -- check that val really is an
1185 : // integer value, and has the right sign.
1186 183 : double d = JSVAL_TO_DOUBLE(val);
1187 183 : return ConvertExact(d, result);
1188 : }
1189 2412 : if (!JSVAL_IS_PRIMITIVE(val)) {
1190 1311 : JSObject* obj = JSVAL_TO_OBJECT(val);
1191 1311 : if (CData::IsCData(obj)) {
1192 0 : JSObject* typeObj = CData::GetCType(obj);
1193 0 : void* data = CData::GetData(obj);
1194 :
1195 : // Check whether the source type is always representable, with exact
1196 : // precision, by the target type. If it is, convert the value.
1197 0 : switch (CType::GetTypeCode(typeObj)) {
1198 : #define DEFINE_INT_TYPE(name, fromType, ffiType) \
1199 : case TYPE_##name: \
1200 : if (!IsAlwaysExact<IntegerType, fromType>()) \
1201 : return false; \
1202 : *result = IntegerType(*static_cast<fromType*>(data)); \
1203 : return true;
1204 : #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
1205 : #include "typedefs.h"
1206 : case TYPE_void_t:
1207 : case TYPE_bool:
1208 : case TYPE_float:
1209 : case TYPE_double:
1210 : case TYPE_float32_t:
1211 : case TYPE_float64_t:
1212 : case TYPE_char:
1213 : case TYPE_signed_char:
1214 : case TYPE_unsigned_char:
1215 : case TYPE_jschar:
1216 : case TYPE_pointer:
1217 : case TYPE_function:
1218 : case TYPE_array:
1219 : case TYPE_struct:
1220 : // Not a compatible number type.
1221 0 : return false;
1222 : }
1223 : }
1224 :
1225 1311 : if (Int64::IsInt64(obj)) {
1226 : // Make sure the integer fits in IntegerType.
1227 83 : int64_t i = Int64Base::GetInt(obj);
1228 83 : return ConvertExact(i, result);
1229 : }
1230 :
1231 1228 : if (UInt64::IsUInt64(obj)) {
1232 : // Make sure the integer fits in IntegerType.
1233 1103 : uint64_t i = Int64Base::GetInt(obj);
1234 1103 : return ConvertExact(i, result);
1235 : }
1236 :
1237 125 : return false;
1238 : }
1239 1101 : if (JSVAL_IS_BOOLEAN(val)) {
1240 : // Implicitly promote boolean values to 0 or 1, like C.
1241 992 : *result = JSVAL_TO_BOOLEAN(val);
1242 992 : JS_ASSERT(*result == 0 || *result == 1);
1243 992 : return true;
1244 : }
1245 : // Don't silently convert null to an integer. It's probably a mistake.
1246 109 : return false;
1247 : }
1248 :
1249 : // Implicitly convert val to FloatType, allowing int, double,
1250 : // Int64, UInt64, and CData numeric types 't' where all values of 't' are
1251 : // representable by FloatType.
1252 : template<class FloatType>
1253 : static bool
1254 1573 : jsvalToFloat(JSContext *cx, jsval val, FloatType* result)
1255 : {
1256 : JS_STATIC_ASSERT(!numeric_limits<FloatType>::is_exact);
1257 :
1258 : // The following casts may silently throw away some bits, but there's
1259 : // no good way around it. Sternly requiring that the 64-bit double
1260 : // argument be exactly representable as a 32-bit float is
1261 : // unrealistic: it would allow 1/2 to pass but not 1/3.
1262 1573 : if (JSVAL_IS_INT(val)) {
1263 1352 : *result = FloatType(JSVAL_TO_INT(val));
1264 1352 : return true;
1265 : }
1266 221 : if (JSVAL_IS_DOUBLE(val)) {
1267 163 : *result = FloatType(JSVAL_TO_DOUBLE(val));
1268 163 : return true;
1269 : }
1270 58 : if (!JSVAL_IS_PRIMITIVE(val)) {
1271 34 : JSObject* obj = JSVAL_TO_OBJECT(val);
1272 34 : if (CData::IsCData(obj)) {
1273 0 : JSObject* typeObj = CData::GetCType(obj);
1274 0 : void* data = CData::GetData(obj);
1275 :
1276 : // Check whether the source type is always representable, with exact
1277 : // precision, by the target type. If it is, convert the value.
1278 0 : switch (CType::GetTypeCode(typeObj)) {
1279 : #define DEFINE_FLOAT_TYPE(name, fromType, ffiType) \
1280 : case TYPE_##name: \
1281 : if (!IsAlwaysExact<FloatType, fromType>()) \
1282 : return false; \
1283 : *result = FloatType(*static_cast<fromType*>(data)); \
1284 : return true;
1285 : #define DEFINE_INT_TYPE(x, y, z) DEFINE_FLOAT_TYPE(x, y, z)
1286 : #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
1287 : #include "typedefs.h"
1288 : case TYPE_void_t:
1289 : case TYPE_bool:
1290 : case TYPE_char:
1291 : case TYPE_signed_char:
1292 : case TYPE_unsigned_char:
1293 : case TYPE_jschar:
1294 : case TYPE_pointer:
1295 : case TYPE_function:
1296 : case TYPE_array:
1297 : case TYPE_struct:
1298 : // Not a compatible number type.
1299 0 : return false;
1300 : }
1301 : }
1302 : }
1303 : // Don't silently convert true to 1.0 or false to 0.0, even though C/C++
1304 : // does it. It's likely to be a mistake.
1305 58 : return false;
1306 : }
1307 :
1308 : template<class IntegerType>
1309 : static bool
1310 54 : StringToInteger(JSContext* cx, JSString* string, IntegerType* result)
1311 : {
1312 : JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
1313 :
1314 54 : const jschar* cp = string->getChars(NULL);
1315 54 : if (!cp)
1316 0 : return false;
1317 :
1318 54 : const jschar* end = cp + string->length();
1319 54 : if (cp == end)
1320 2 : return false;
1321 :
1322 52 : IntegerType sign = 1;
1323 52 : if (cp[0] == '-') {
1324 : if (!numeric_limits<IntegerType>::is_signed)
1325 2 : return false;
1326 :
1327 13 : sign = -1;
1328 13 : ++cp;
1329 : }
1330 :
1331 : // Assume base-10, unless the string begins with '0x' or '0X'.
1332 50 : IntegerType base = 10;
1333 50 : if (end - cp > 2 && cp[0] == '0' && (cp[1] == 'x' || cp[1] == 'X')) {
1334 21 : cp += 2;
1335 21 : base = 16;
1336 : }
1337 :
1338 : // Scan the string left to right and build the number,
1339 : // checking for valid characters 0 - 9, a - f, A - F and overflow.
1340 50 : IntegerType i = 0;
1341 818 : while (cp != end) {
1342 721 : jschar c = *cp++;
1343 721 : if (c >= '0' && c <= '9')
1344 574 : c -= '0';
1345 147 : else if (base == 16 && c >= 'a' && c <= 'f')
1346 141 : c = c - 'a' + 10;
1347 6 : else if (base == 16 && c >= 'A' && c <= 'F')
1348 6 : c = c - 'A' + 10;
1349 : else
1350 0 : return false;
1351 :
1352 721 : IntegerType ii = i;
1353 721 : i = ii * base + sign * c;
1354 721 : if (i / base != ii) // overflow
1355 3 : return false;
1356 : }
1357 :
1358 47 : *result = i;
1359 47 : return true;
1360 : }
1361 :
1362 : // Implicitly convert val to IntegerType, allowing int, double,
1363 : // Int64, UInt64, and optionally a decimal or hexadecimal string argument.
1364 : // (This is common code shared by jsvalToSize and the Int64/UInt64 constructors.)
1365 : template<class IntegerType>
1366 : static bool
1367 4084 : jsvalToBigInteger(JSContext* cx,
1368 : jsval val,
1369 : bool allowString,
1370 : IntegerType* result)
1371 : {
1372 : JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
1373 :
1374 4084 : if (JSVAL_IS_INT(val)) {
1375 : // Make sure the integer fits in the alotted precision, and has the right
1376 : // sign.
1377 3704 : int32_t i = JSVAL_TO_INT(val);
1378 3704 : return ConvertExact(i, result);
1379 : }
1380 380 : if (JSVAL_IS_DOUBLE(val)) {
1381 : // Don't silently lose bits here -- check that val really is an
1382 : // integer value, and has the right sign.
1383 42 : double d = JSVAL_TO_DOUBLE(val);
1384 42 : return ConvertExact(d, result);
1385 : }
1386 338 : if (allowString && JSVAL_IS_STRING(val)) {
1387 : // Allow conversion from base-10 or base-16 strings, provided the result
1388 : // fits in IntegerType. (This allows an Int64 or UInt64 object to be passed
1389 : // to the JS array element operator, which will automatically call
1390 : // toString() on the object for us.)
1391 54 : return StringToInteger(cx, JSVAL_TO_STRING(val), result);
1392 : }
1393 284 : if (!JSVAL_IS_PRIMITIVE(val)) {
1394 : // Allow conversion from an Int64 or UInt64 object directly.
1395 24 : JSObject* obj = JSVAL_TO_OBJECT(val);
1396 :
1397 24 : if (UInt64::IsUInt64(obj)) {
1398 : // Make sure the integer fits in IntegerType.
1399 7 : uint64_t i = Int64Base::GetInt(obj);
1400 7 : return ConvertExact(i, result);
1401 : }
1402 :
1403 17 : if (Int64::IsInt64(obj)) {
1404 : // Make sure the integer fits in IntegerType.
1405 7 : int64_t i = Int64Base::GetInt(obj);
1406 7 : return ConvertExact(i, result);
1407 : }
1408 : }
1409 270 : return false;
1410 : }
1411 :
1412 : // Implicitly convert val to a size value, where the size value is represented
1413 : // by size_t but must also fit in a double.
1414 : static bool
1415 3823 : jsvalToSize(JSContext* cx, jsval val, bool allowString, size_t* result)
1416 : {
1417 3823 : if (!jsvalToBigInteger(cx, val, allowString, result))
1418 258 : return false;
1419 :
1420 : // Also check that the result fits in a double.
1421 3565 : return Convert<size_t>(double(*result)) == *result;
1422 : }
1423 :
1424 : // Implicitly convert val to IntegerType, allowing int, double,
1425 : // Int64, UInt64, and optionally a decimal or hexadecimal string argument.
1426 : // (This is common code shared by jsvalToSize and the Int64/UInt64 constructors.)
1427 : template<class IntegerType>
1428 : static bool
1429 146357 : jsidToBigInteger(JSContext* cx,
1430 : jsid val,
1431 : bool allowString,
1432 : IntegerType* result)
1433 : {
1434 : JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
1435 :
1436 146357 : if (JSID_IS_INT(val)) {
1437 : // Make sure the integer fits in the alotted precision, and has the right
1438 : // sign.
1439 146357 : int32_t i = JSID_TO_INT(val);
1440 146357 : return ConvertExact(i, result);
1441 : }
1442 0 : if (allowString && JSID_IS_STRING(val)) {
1443 : // Allow conversion from base-10 or base-16 strings, provided the result
1444 : // fits in IntegerType. (This allows an Int64 or UInt64 object to be passed
1445 : // to the JS array element operator, which will automatically call
1446 : // toString() on the object for us.)
1447 0 : return StringToInteger(cx, JSID_TO_STRING(val), result);
1448 : }
1449 0 : if (JSID_IS_OBJECT(val)) {
1450 : // Allow conversion from an Int64 or UInt64 object directly.
1451 0 : JSObject* obj = JSID_TO_OBJECT(val);
1452 :
1453 0 : if (UInt64::IsUInt64(obj)) {
1454 : // Make sure the integer fits in IntegerType.
1455 0 : uint64_t i = Int64Base::GetInt(obj);
1456 0 : return ConvertExact(i, result);
1457 : }
1458 :
1459 0 : if (Int64::IsInt64(obj)) {
1460 : // Make sure the integer fits in IntegerType.
1461 0 : int64_t i = Int64Base::GetInt(obj);
1462 0 : return ConvertExact(i, result);
1463 : }
1464 : }
1465 0 : return false;
1466 : }
1467 :
1468 : // Implicitly convert val to a size value, where the size value is represented
1469 : // by size_t but must also fit in a double.
1470 : static bool
1471 146357 : jsidToSize(JSContext* cx, jsid val, bool allowString, size_t* result)
1472 : {
1473 146357 : if (!jsidToBigInteger(cx, val, allowString, result))
1474 1 : return false;
1475 :
1476 : // Also check that the result fits in a double.
1477 146356 : return Convert<size_t>(double(*result)) == *result;
1478 : }
1479 :
1480 : // Implicitly convert a size value to a jsval, ensuring that the size_t value
1481 : // fits in a double.
1482 : static JSBool
1483 4934 : SizeTojsval(JSContext* cx, size_t size, jsval* result)
1484 : {
1485 4934 : if (Convert<size_t>(double(size)) != size) {
1486 0 : JS_ReportError(cx, "size overflow");
1487 0 : return false;
1488 : }
1489 :
1490 4934 : return JS_NewNumberValue(cx, double(size), result);
1491 : }
1492 :
1493 : // Forcefully convert val to IntegerType when explicitly requested.
1494 : template<class IntegerType>
1495 : static bool
1496 0 : jsvalToIntegerExplicit(jsval val, IntegerType* result)
1497 : {
1498 : JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
1499 :
1500 0 : if (JSVAL_IS_DOUBLE(val)) {
1501 : // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
1502 0 : double d = JSVAL_TO_DOUBLE(val);
1503 0 : *result = FloatIsFinite(d) ? IntegerType(d) : 0;
1504 0 : return true;
1505 : }
1506 0 : if (!JSVAL_IS_PRIMITIVE(val)) {
1507 : // Convert Int64 and UInt64 values by C-style cast.
1508 0 : JSObject* obj = JSVAL_TO_OBJECT(val);
1509 0 : if (Int64::IsInt64(obj)) {
1510 0 : int64_t i = Int64Base::GetInt(obj);
1511 0 : *result = IntegerType(i);
1512 0 : return true;
1513 : }
1514 0 : if (UInt64::IsUInt64(obj)) {
1515 0 : uint64_t i = Int64Base::GetInt(obj);
1516 0 : *result = IntegerType(i);
1517 0 : return true;
1518 : }
1519 : }
1520 0 : return false;
1521 : }
1522 :
1523 : // Forcefully convert val to a pointer value when explicitly requested.
1524 : static bool
1525 13 : jsvalToPtrExplicit(JSContext* cx, jsval val, uintptr_t* result)
1526 : {
1527 13 : if (JSVAL_IS_INT(val)) {
1528 : // int32_t always fits in intptr_t. If the integer is negative, cast through
1529 : // an intptr_t intermediate to sign-extend.
1530 6 : int32_t i = JSVAL_TO_INT(val);
1531 6 : *result = i < 0 ? uintptr_t(intptr_t(i)) : uintptr_t(i);
1532 6 : return true;
1533 : }
1534 7 : if (JSVAL_IS_DOUBLE(val)) {
1535 0 : double d = JSVAL_TO_DOUBLE(val);
1536 0 : if (d < 0) {
1537 : // Cast through an intptr_t intermediate to sign-extend.
1538 0 : intptr_t i = Convert<intptr_t>(d);
1539 0 : if (double(i) != d)
1540 0 : return false;
1541 :
1542 0 : *result = uintptr_t(i);
1543 0 : return true;
1544 : }
1545 :
1546 : // Don't silently lose bits here -- check that val really is an
1547 : // integer value, and has the right sign.
1548 0 : *result = Convert<uintptr_t>(d);
1549 0 : return double(*result) == d;
1550 : }
1551 7 : if (!JSVAL_IS_PRIMITIVE(val)) {
1552 3 : JSObject* obj = JSVAL_TO_OBJECT(val);
1553 3 : if (Int64::IsInt64(obj)) {
1554 0 : int64_t i = Int64Base::GetInt(obj);
1555 0 : intptr_t p = intptr_t(i);
1556 :
1557 : // Make sure the integer fits in the alotted precision.
1558 0 : if (int64_t(p) != i)
1559 0 : return false;
1560 0 : *result = uintptr_t(p);
1561 0 : return true;
1562 : }
1563 :
1564 3 : if (UInt64::IsUInt64(obj)) {
1565 2 : uint64_t i = Int64Base::GetInt(obj);
1566 :
1567 : // Make sure the integer fits in the alotted precision.
1568 2 : *result = uintptr_t(i);
1569 2 : return uint64_t(*result) == i;
1570 : }
1571 : }
1572 5 : return false;
1573 : }
1574 :
1575 : template<class IntegerType, class CharType, size_t N, class AP>
1576 : void
1577 3119 : IntegerToString(IntegerType i, int radix, Vector<CharType, N, AP>& result)
1578 : {
1579 : JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
1580 :
1581 : // The buffer must be big enough for all the bits of IntegerType to fit,
1582 : // in base-2, including '-'.
1583 : CharType buffer[sizeof(IntegerType) * 8 + 1];
1584 3119 : CharType* end = buffer + sizeof(buffer) / sizeof(CharType);
1585 3119 : CharType* cp = end;
1586 :
1587 : // Build the string in reverse. We use multiplication and subtraction
1588 : // instead of modulus because that's much faster.
1589 3119 : const bool isNegative = IsNegative(i);
1590 3119 : size_t sign = isNegative ? -1 : 1;
1591 19942 : do {
1592 19942 : IntegerType ii = i / IntegerType(radix);
1593 19942 : size_t index = sign * size_t(i - ii * IntegerType(radix));
1594 19942 : *--cp = "0123456789abcdefghijklmnopqrstuvwxyz"[index];
1595 19942 : i = ii;
1596 : } while (i != 0);
1597 :
1598 3119 : if (isNegative)
1599 89 : *--cp = '-';
1600 :
1601 3119 : JS_ASSERT(cp >= buffer);
1602 3119 : result.append(cp, end);
1603 3119 : }
1604 :
1605 : template<class CharType>
1606 : static size_t
1607 135 : strnlen(const CharType* begin, size_t max)
1608 : {
1609 13301 : for (const CharType* s = begin; s != begin + max; ++s)
1610 13181 : if (*s == 0)
1611 15 : return s - begin;
1612 :
1613 120 : return max;
1614 : }
1615 :
1616 : // Convert C binary value 'data' of CType 'typeObj' to a JS primitive, where
1617 : // possible; otherwise, construct and return a CData object. The following
1618 : // semantics apply when constructing a CData object for return:
1619 : // * If 'wantPrimitive' is true, the caller indicates that 'result' must be
1620 : // a JS primitive, and ConvertToJS will fail if 'result' would be a CData
1621 : // object. Otherwise:
1622 : // * If a CData object 'parentObj' is supplied, the new CData object is
1623 : // dependent on the given parent and its buffer refers to a slice of the
1624 : // parent's buffer.
1625 : // * If 'parentObj' is null, the new CData object may or may not own its
1626 : // resulting buffer depending on the 'ownResult' argument.
1627 : JSBool
1628 133528 : ConvertToJS(JSContext* cx,
1629 : JSObject* typeObj,
1630 : JSObject* parentObj,
1631 : void* data,
1632 : bool wantPrimitive,
1633 : bool ownResult,
1634 : jsval* result)
1635 : {
1636 133528 : JS_ASSERT(!parentObj || CData::IsCData(parentObj));
1637 133528 : JS_ASSERT(!parentObj || !ownResult);
1638 133528 : JS_ASSERT(!wantPrimitive || !ownResult);
1639 :
1640 133528 : TypeCode typeCode = CType::GetTypeCode(typeObj);
1641 :
1642 133528 : switch (typeCode) {
1643 : case TYPE_void_t:
1644 1558 : *result = JSVAL_VOID;
1645 1558 : break;
1646 : case TYPE_bool:
1647 36 : *result = *static_cast<bool*>(data) ? JSVAL_TRUE : JSVAL_FALSE;
1648 36 : break;
1649 : #define DEFINE_INT_TYPE(name, type, ffiType) \
1650 : case TYPE_##name: { \
1651 : type value = *static_cast<type*>(data); \
1652 : if (sizeof(type) < 4) \
1653 : *result = INT_TO_JSVAL(int32_t(value)); \
1654 : else if (!JS_NewNumberValue(cx, double(value), result)) \
1655 : return false; \
1656 : break; \
1657 : }
1658 : #define DEFINE_WRAPPED_INT_TYPE(name, type, ffiType) \
1659 : case TYPE_##name: { \
1660 : /* Return an Int64 or UInt64 object - do not convert to a JS number. */ \
1661 : uint64_t value; \
1662 : JSObject* proto; \
1663 : if (!numeric_limits<type>::is_signed) { \
1664 : value = *static_cast<type*>(data); \
1665 : /* Get ctypes.UInt64.prototype from ctypes.CType.prototype. */ \
1666 : proto = CType::GetProtoFromType(typeObj, SLOT_UINT64PROTO); \
1667 : } else { \
1668 : value = int64_t(*static_cast<type*>(data)); \
1669 : /* Get ctypes.Int64.prototype from ctypes.CType.prototype. */ \
1670 : proto = CType::GetProtoFromType(typeObj, SLOT_INT64PROTO); \
1671 : } \
1672 : \
1673 : JSObject* obj = Int64Base::Construct(cx, proto, value, \
1674 : !numeric_limits<type>::is_signed); \
1675 : if (!obj) \
1676 : return false; \
1677 : *result = OBJECT_TO_JSVAL(obj); \
1678 : break; \
1679 : }
1680 : #define DEFINE_FLOAT_TYPE(name, type, ffiType) \
1681 : case TYPE_##name: { \
1682 : type value = *static_cast<type*>(data); \
1683 : if (!JS_NewNumberValue(cx, double(value), result)) \
1684 : return false; \
1685 : break; \
1686 : }
1687 : #define DEFINE_CHAR_TYPE(name, type, ffiType) \
1688 : case TYPE_##name: \
1689 : /* Convert to an integer. We have no idea what character encoding to */ \
1690 : /* use, if any. */ \
1691 : *result = INT_TO_JSVAL(*static_cast<type*>(data)); \
1692 : break;
1693 : #include "typedefs.h"
1694 : case TYPE_jschar: {
1695 : // Convert the jschar to a 1-character string.
1696 54 : JSString* str = JS_NewUCStringCopyN(cx, static_cast<jschar*>(data), 1);
1697 54 : if (!str)
1698 0 : return false;
1699 :
1700 54 : *result = STRING_TO_JSVAL(str);
1701 54 : break;
1702 : }
1703 : case TYPE_pointer:
1704 : case TYPE_array:
1705 : case TYPE_struct: {
1706 : // We're about to create a new CData object to return. If the caller doesn't
1707 : // want this, return early.
1708 4080 : if (wantPrimitive) {
1709 4 : JS_ReportError(cx, "cannot convert to primitive value");
1710 4 : return false;
1711 : }
1712 :
1713 4076 : JSObject* obj = CData::Create(cx, typeObj, parentObj, data, ownResult);
1714 4076 : if (!obj)
1715 0 : return false;
1716 :
1717 4076 : *result = OBJECT_TO_JSVAL(obj);
1718 4076 : break;
1719 : }
1720 : case TYPE_function:
1721 0 : JS_NOT_REACHED("cannot return a FunctionType");
1722 : }
1723 :
1724 133524 : return true;
1725 : }
1726 :
1727 : // Implicitly convert jsval 'val' to a C binary representation of CType
1728 : // 'targetType', storing the result in 'buffer'. Adequate space must be
1729 : // provided in 'buffer' by the caller. This function generally does minimal
1730 : // coercion between types. There are two cases in which this function is used:
1731 : // 1) The target buffer is internal to a CData object; we simply write data
1732 : // into it.
1733 : // 2) We are converting an argument for an ffi call, in which case 'isArgument'
1734 : // will be true. This allows us to handle a special case: if necessary,
1735 : // we can autoconvert a JS string primitive to a pointer-to-character type.
1736 : // In this case, ownership of the allocated string is handed off to the
1737 : // caller; 'freePointer' will be set to indicate this.
1738 : JSBool
1739 42986 : ImplicitConvert(JSContext* cx,
1740 : jsval val,
1741 : JSObject* targetType,
1742 : void* buffer,
1743 : bool isArgument,
1744 : bool* freePointer)
1745 : {
1746 42986 : JS_ASSERT(CType::IsSizeDefined(targetType));
1747 :
1748 : // First, check if val is a CData object of type targetType.
1749 42986 : JSObject* sourceData = NULL;
1750 42986 : JSObject* sourceType = NULL;
1751 51171 : if (!JSVAL_IS_PRIMITIVE(val) &&
1752 8185 : CData::IsCData(JSVAL_TO_OBJECT(val))) {
1753 6790 : sourceData = JSVAL_TO_OBJECT(val);
1754 6790 : sourceType = CData::GetCType(sourceData);
1755 :
1756 : // If the types are equal, copy the buffer contained within the CData.
1757 : // (Note that the buffers may overlap partially or completely.)
1758 6790 : if (CType::TypesEqual(sourceType, targetType)) {
1759 5698 : size_t size = CType::GetSize(sourceType);
1760 5698 : memmove(buffer, CData::GetData(sourceData), size);
1761 5698 : return true;
1762 : }
1763 : }
1764 :
1765 37288 : TypeCode targetCode = CType::GetTypeCode(targetType);
1766 :
1767 37288 : switch (targetCode) {
1768 : case TYPE_bool: {
1769 : // Do not implicitly lose bits, but allow the values 0, 1, and -0.
1770 : // Programs can convert explicitly, if needed, using `Boolean(v)` or `!!v`.
1771 : bool result;
1772 110 : if (!jsvalToBool(cx, val, &result))
1773 17 : return TypeError(cx, "boolean", val);
1774 93 : *static_cast<bool*>(buffer) = result;
1775 93 : break;
1776 : }
1777 : #define DEFINE_INT_TYPE(name, type, ffiType) \
1778 : case TYPE_##name: { \
1779 : /* Do not implicitly lose bits. */ \
1780 : type result; \
1781 : if (!jsvalToInteger(cx, val, &result)) \
1782 : return TypeError(cx, #name, val); \
1783 : *static_cast<type*>(buffer) = result; \
1784 : break; \
1785 : }
1786 : #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
1787 : #define DEFINE_FLOAT_TYPE(name, type, ffiType) \
1788 : case TYPE_##name: { \
1789 : type result; \
1790 : if (!jsvalToFloat(cx, val, &result)) \
1791 : return TypeError(cx, #name, val); \
1792 : *static_cast<type*>(buffer) = result; \
1793 : break; \
1794 : }
1795 : #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
1796 : #define DEFINE_JSCHAR_TYPE(name, type, ffiType) \
1797 : case TYPE_##name: { \
1798 : /* Convert from a 1-character string, regardless of encoding, */ \
1799 : /* or from an integer, provided the result fits in 'type'. */ \
1800 : type result; \
1801 : if (JSVAL_IS_STRING(val)) { \
1802 : JSString* str = JSVAL_TO_STRING(val); \
1803 : if (str->length() != 1) \
1804 : return TypeError(cx, #name, val); \
1805 : const jschar *chars = str->getChars(cx); \
1806 : if (!chars) \
1807 : return false; \
1808 : result = chars[0]; \
1809 : } else if (!jsvalToInteger(cx, val, &result)) { \
1810 : return TypeError(cx, #name, val); \
1811 : } \
1812 : *static_cast<type*>(buffer) = result; \
1813 : break; \
1814 : }
1815 : #include "typedefs.h"
1816 : case TYPE_pointer: {
1817 2173 : if (JSVAL_IS_NULL(val)) {
1818 : // Convert to a null pointer.
1819 1058 : *static_cast<void**>(buffer) = NULL;
1820 1058 : break;
1821 : }
1822 :
1823 1115 : JSObject* baseType = PointerType::GetBaseType(targetType);
1824 1115 : if (sourceData) {
1825 : // First, determine if the targetType is ctypes.void_t.ptr.
1826 1090 : TypeCode sourceCode = CType::GetTypeCode(sourceType);
1827 1090 : void* sourceBuffer = CData::GetData(sourceData);
1828 1090 : bool voidptrTarget = CType::GetTypeCode(baseType) == TYPE_void_t;
1829 :
1830 1090 : if (sourceCode == TYPE_pointer && voidptrTarget) {
1831 : // Autoconvert if targetType is ctypes.voidptr_t.
1832 2 : *static_cast<void**>(buffer) = *static_cast<void**>(sourceBuffer);
1833 2 : break;
1834 : }
1835 1088 : if (sourceCode == TYPE_array) {
1836 : // Autoconvert an array to a ctypes.void_t.ptr or to
1837 : // sourceType.elementType.ptr, just like C.
1838 1081 : JSObject* elementType = ArrayType::GetBaseType(sourceType);
1839 1081 : if (voidptrTarget || CType::TypesEqual(baseType, elementType)) {
1840 1081 : *static_cast<void**>(buffer) = sourceBuffer;
1841 1081 : break;
1842 : }
1843 : }
1844 :
1845 25 : } else if (isArgument && JSVAL_IS_STRING(val)) {
1846 : // Convert the string for the ffi call. This requires allocating space
1847 : // which the caller assumes ownership of.
1848 : // TODO: Extend this so we can safely convert strings at other times also.
1849 4 : JSString* sourceString = JSVAL_TO_STRING(val);
1850 4 : size_t sourceLength = sourceString->length();
1851 4 : const jschar* sourceChars = sourceString->getChars(cx);
1852 4 : if (!sourceChars)
1853 0 : return false;
1854 :
1855 4 : switch (CType::GetTypeCode(baseType)) {
1856 : case TYPE_char:
1857 : case TYPE_signed_char:
1858 : case TYPE_unsigned_char: {
1859 : // Convert from UTF-16 to UTF-8.
1860 : size_t nbytes =
1861 3 : GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength);
1862 3 : if (nbytes == (size_t) -1)
1863 0 : return false;
1864 :
1865 3 : char** charBuffer = static_cast<char**>(buffer);
1866 3 : *charBuffer = cx->array_new<char>(nbytes + 1);
1867 3 : if (!*charBuffer) {
1868 0 : JS_ReportAllocationOverflow(cx);
1869 0 : return false;
1870 : }
1871 :
1872 : ASSERT_OK(DeflateStringToUTF8Buffer(cx, sourceChars, sourceLength,
1873 3 : *charBuffer, &nbytes));
1874 3 : (*charBuffer)[nbytes] = 0;
1875 3 : *freePointer = true;
1876 3 : break;
1877 : }
1878 : case TYPE_jschar: {
1879 : // Copy the jschar string data. (We could provide direct access to the
1880 : // JSString's buffer, but this approach is safer if the caller happens
1881 : // to modify the string.)
1882 1 : jschar** jscharBuffer = static_cast<jschar**>(buffer);
1883 1 : *jscharBuffer = cx->array_new<jschar>(sourceLength + 1);
1884 1 : if (!*jscharBuffer) {
1885 0 : JS_ReportAllocationOverflow(cx);
1886 0 : return false;
1887 : }
1888 :
1889 1 : *freePointer = true;
1890 1 : memcpy(*jscharBuffer, sourceChars, sourceLength * sizeof(jschar));
1891 1 : (*jscharBuffer)[sourceLength] = 0;
1892 1 : break;
1893 : }
1894 : default:
1895 0 : return TypeError(cx, "pointer", val);
1896 : }
1897 4 : break;
1898 : }
1899 28 : return TypeError(cx, "pointer", val);
1900 : }
1901 : case TYPE_array: {
1902 280 : JSObject* baseType = ArrayType::GetBaseType(targetType);
1903 280 : size_t targetLength = ArrayType::GetLength(targetType);
1904 :
1905 280 : if (JSVAL_IS_STRING(val)) {
1906 264 : JSString* sourceString = JSVAL_TO_STRING(val);
1907 264 : size_t sourceLength = sourceString->length();
1908 264 : const jschar* sourceChars = sourceString->getChars(cx);
1909 264 : if (!sourceChars)
1910 0 : return false;
1911 :
1912 264 : switch (CType::GetTypeCode(baseType)) {
1913 : case TYPE_char:
1914 : case TYPE_signed_char:
1915 : case TYPE_unsigned_char: {
1916 : // Convert from UTF-16 to UTF-8.
1917 : size_t nbytes =
1918 261 : GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength);
1919 261 : if (nbytes == (size_t) -1)
1920 0 : return false;
1921 :
1922 261 : if (targetLength < nbytes) {
1923 3 : JS_ReportError(cx, "ArrayType has insufficient length");
1924 3 : return false;
1925 : }
1926 :
1927 258 : char* charBuffer = static_cast<char*>(buffer);
1928 : ASSERT_OK(DeflateStringToUTF8Buffer(cx, sourceChars, sourceLength,
1929 258 : charBuffer, &nbytes));
1930 :
1931 258 : if (targetLength > nbytes)
1932 258 : charBuffer[nbytes] = 0;
1933 :
1934 258 : break;
1935 : }
1936 : case TYPE_jschar: {
1937 : // Copy the string data, jschar for jschar, including the terminator
1938 : // if there's space.
1939 3 : if (targetLength < sourceLength) {
1940 1 : JS_ReportError(cx, "ArrayType has insufficient length");
1941 1 : return false;
1942 : }
1943 :
1944 2 : memcpy(buffer, sourceChars, sourceLength * sizeof(jschar));
1945 2 : if (targetLength > sourceLength)
1946 2 : static_cast<jschar*>(buffer)[sourceLength] = 0;
1947 :
1948 2 : break;
1949 : }
1950 : default:
1951 0 : return TypeError(cx, "array", val);
1952 : }
1953 :
1954 32 : } else if (!JSVAL_IS_PRIMITIVE(val) &&
1955 16 : JS_IsArrayObject(cx, JSVAL_TO_OBJECT(val))) {
1956 : // Convert each element of the array by calling ImplicitConvert.
1957 16 : JSObject* sourceArray = JSVAL_TO_OBJECT(val);
1958 : uint32_t sourceLength;
1959 16 : if (!JS_GetArrayLength(cx, sourceArray, &sourceLength) ||
1960 : targetLength != size_t(sourceLength)) {
1961 3 : JS_ReportError(cx, "ArrayType length does not match source array length");
1962 3 : return false;
1963 : }
1964 :
1965 : // Convert into an intermediate, in case of failure.
1966 13 : size_t elementSize = CType::GetSize(baseType);
1967 13 : size_t arraySize = elementSize * targetLength;
1968 26 : AutoPtr<char>::Array intermediate(cx->array_new<char>(arraySize));
1969 13 : if (!intermediate) {
1970 0 : JS_ReportAllocationOverflow(cx);
1971 0 : return false;
1972 : }
1973 :
1974 53 : for (uint32_t i = 0; i < sourceLength; ++i) {
1975 82 : js::AutoValueRooter item(cx);
1976 41 : if (!JS_GetElement(cx, sourceArray, i, item.jsval_addr()))
1977 0 : return false;
1978 :
1979 41 : char* data = intermediate.get() + elementSize * i;
1980 41 : if (!ImplicitConvert(cx, item.jsval_value(), baseType, data, false, NULL))
1981 1 : return false;
1982 : }
1983 :
1984 25 : memcpy(buffer, intermediate.get(), arraySize);
1985 :
1986 : } else {
1987 : // Don't implicitly convert to string. Users can implicitly convert
1988 : // with `String(x)` or `""+x`.
1989 0 : return TypeError(cx, "array", val);
1990 : }
1991 272 : break;
1992 : }
1993 : case TYPE_struct: {
1994 30 : if (!JSVAL_IS_PRIMITIVE(val) && !sourceData) {
1995 : // Enumerate the properties of the object; if they match the struct
1996 : // specification, convert the fields.
1997 22 : JSObject* obj = JSVAL_TO_OBJECT(val);
1998 22 : JSObject* iter = JS_NewPropertyIterator(cx, obj);
1999 22 : if (!iter)
2000 0 : return false;
2001 44 : js::AutoObjectRooter iterroot(cx, iter);
2002 :
2003 : // Convert into an intermediate, in case of failure.
2004 22 : size_t structSize = CType::GetSize(targetType);
2005 44 : AutoPtr<char>::Array intermediate(cx->array_new<char>(structSize));
2006 22 : if (!intermediate) {
2007 0 : JS_ReportAllocationOverflow(cx);
2008 0 : return false;
2009 : }
2010 :
2011 : jsid id;
2012 22 : size_t i = 0;
2013 26 : while (1) {
2014 48 : if (!JS_NextProperty(cx, iter, &id))
2015 0 : return false;
2016 48 : if (JSID_IS_VOID(id))
2017 : break;
2018 :
2019 34 : if (!JSID_IS_STRING(id)) {
2020 3 : JS_ReportError(cx, "property name is not a string");
2021 3 : return false;
2022 : }
2023 :
2024 31 : JSFlatString *name = JSID_TO_FLAT_STRING(id);
2025 31 : const FieldInfo* field = StructType::LookupField(cx, targetType, name);
2026 31 : if (!field)
2027 3 : return false;
2028 :
2029 56 : js::AutoValueRooter prop(cx);
2030 28 : if (!JS_GetPropertyById(cx, obj, id, prop.jsval_addr()))
2031 0 : return false;
2032 :
2033 : // Convert the field via ImplicitConvert().
2034 28 : char* fieldData = intermediate.get() + field->mOffset;
2035 28 : if (!ImplicitConvert(cx, prop.jsval_value(), field->mType, fieldData, false, NULL))
2036 2 : return false;
2037 :
2038 54 : ++i;
2039 : }
2040 :
2041 28 : const FieldInfoHash* fields = StructType::GetFieldInfo(targetType);
2042 14 : if (i != fields->count()) {
2043 2 : JS_ReportError(cx, "missing fields");
2044 2 : return false;
2045 : }
2046 :
2047 12 : memcpy(buffer, intermediate.get(), structSize);
2048 22 : break;
2049 : }
2050 :
2051 8 : return TypeError(cx, "struct", val);
2052 : }
2053 : case TYPE_void_t:
2054 : case TYPE_function:
2055 0 : JS_NOT_REACHED("invalid type");
2056 : return false;
2057 : }
2058 :
2059 36777 : return true;
2060 : }
2061 :
2062 : // Convert jsval 'val' to a C binary representation of CType 'targetType',
2063 : // storing the result in 'buffer'. This function is more forceful than
2064 : // ImplicitConvert.
2065 : JSBool
2066 494 : ExplicitConvert(JSContext* cx, jsval val, JSObject* targetType, void* buffer)
2067 : {
2068 : // If ImplicitConvert succeeds, use that result.
2069 494 : if (ImplicitConvert(cx, val, targetType, buffer, false, NULL))
2070 470 : return true;
2071 :
2072 : // If ImplicitConvert failed, and there is no pending exception, then assume
2073 : // hard failure (out of memory, or some other similarly serious condition).
2074 : // We store any pending exception in case we need to re-throw it.
2075 48 : js::AutoValueRooter ex(cx);
2076 24 : if (!JS_GetPendingException(cx, ex.jsval_addr()))
2077 0 : return false;
2078 :
2079 : // Otherwise, assume soft failure. Clear the pending exception so that we
2080 : // can throw a different one as required.
2081 24 : JS_ClearPendingException(cx);
2082 :
2083 24 : TypeCode type = CType::GetTypeCode(targetType);
2084 :
2085 24 : switch (type) {
2086 : case TYPE_bool: {
2087 : // Convert according to the ECMAScript ToBoolean() function.
2088 : JSBool result;
2089 0 : ASSERT_OK(JS_ValueToBoolean(cx, val, &result));
2090 0 : *static_cast<bool*>(buffer) = result != JS_FALSE;
2091 0 : break;
2092 : }
2093 : #define DEFINE_INT_TYPE(name, type, ffiType) \
2094 : case TYPE_##name: { \
2095 : /* Convert numeric values with a C-style cast, and */ \
2096 : /* allow conversion from a base-10 or base-16 string. */ \
2097 : type result; \
2098 : if (!jsvalToIntegerExplicit(val, &result) && \
2099 : (!JSVAL_IS_STRING(val) || \
2100 : !StringToInteger(cx, JSVAL_TO_STRING(val), &result))) \
2101 : return TypeError(cx, #name, val); \
2102 : *static_cast<type*>(buffer) = result; \
2103 : break; \
2104 : }
2105 : #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
2106 : #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
2107 : #define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_CHAR_TYPE(x, y, z)
2108 : #include "typedefs.h"
2109 : case TYPE_pointer: {
2110 : // Convert a number, Int64 object, or UInt64 object to a pointer.
2111 : uintptr_t result;
2112 13 : if (!jsvalToPtrExplicit(cx, val, &result))
2113 5 : return TypeError(cx, "pointer", val);
2114 8 : *static_cast<uintptr_t*>(buffer) = result;
2115 8 : break;
2116 : }
2117 : case TYPE_float32_t:
2118 : case TYPE_float64_t:
2119 : case TYPE_float:
2120 : case TYPE_double:
2121 : case TYPE_array:
2122 : case TYPE_struct:
2123 : // ImplicitConvert is sufficient. Re-throw the exception it generated.
2124 11 : JS_SetPendingException(cx, ex.jsval_value());
2125 11 : return false;
2126 : case TYPE_void_t:
2127 : case TYPE_function:
2128 0 : JS_NOT_REACHED("invalid type");
2129 : return false;
2130 : }
2131 8 : return true;
2132 : }
2133 :
2134 : // Given a CType 'typeObj', generate a string describing the C type declaration
2135 : // corresponding to 'typeObj'. For instance, the CType constructed from
2136 : // 'ctypes.int32_t.ptr.array(4).ptr.ptr' will result in the type string
2137 : // 'int32_t*(**)[4]'.
2138 : static JSString*
2139 99 : BuildTypeName(JSContext* cx, JSObject* typeObj)
2140 : {
2141 198 : AutoString result;
2142 :
2143 : // Walk the hierarchy of types, outermost to innermost, building up the type
2144 : // string. This consists of the base type, which goes on the left.
2145 : // Derived type modifiers (* and []) build from the inside outward, with
2146 : // pointers on the left and arrays on the right. An excellent description
2147 : // of the rules for building C type declarations can be found at:
2148 : // http://unixwiz.net/techtips/reading-cdecl.html
2149 99 : TypeCode prevGrouping = CType::GetTypeCode(typeObj), currentGrouping;
2150 135 : while (1) {
2151 234 : currentGrouping = CType::GetTypeCode(typeObj);
2152 234 : switch (currentGrouping) {
2153 : case TYPE_pointer: {
2154 : // Pointer types go on the left.
2155 31 : PrependString(result, "*");
2156 :
2157 31 : typeObj = PointerType::GetBaseType(typeObj);
2158 31 : prevGrouping = currentGrouping;
2159 31 : continue;
2160 : }
2161 : case TYPE_array: {
2162 88 : if (prevGrouping == TYPE_pointer) {
2163 : // Outer type is pointer, inner type is array. Grouping is required.
2164 5 : PrependString(result, "(");
2165 5 : AppendString(result, ")");
2166 : }
2167 :
2168 : // Array types go on the right.
2169 88 : AppendString(result, "[");
2170 : size_t length;
2171 88 : if (ArrayType::GetSafeLength(typeObj, &length))
2172 43 : IntegerToString(length, 10, result);
2173 :
2174 88 : AppendString(result, "]");
2175 :
2176 88 : typeObj = ArrayType::GetBaseType(typeObj);
2177 88 : prevGrouping = currentGrouping;
2178 88 : continue;
2179 : }
2180 : case TYPE_function: {
2181 16 : FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
2182 :
2183 : // Add in the calling convention, if it's not cdecl.
2184 : // There's no trailing or leading space needed here, as none of the
2185 : // modifiers can produce a string beginning with an identifier ---
2186 : // except for TYPE_function itself, which is fine because functions
2187 : // can't return functions.
2188 16 : ABICode abi = GetABICode(fninfo->mABI);
2189 16 : if (abi == ABI_STDCALL)
2190 0 : PrependString(result, "__stdcall");
2191 16 : else if (abi == ABI_WINAPI)
2192 0 : PrependString(result, "WINAPI");
2193 :
2194 : // Function application binds more tightly than dereferencing, so
2195 : // wrap pointer types in parens. Functions can't return functions
2196 : // (only pointers to them), and arrays can't hold functions
2197 : // (similarly), so we don't need to address those cases.
2198 16 : if (prevGrouping == TYPE_pointer) {
2199 8 : PrependString(result, "(");
2200 8 : AppendString(result, ")");
2201 : }
2202 :
2203 : // Argument list goes on the right.
2204 16 : AppendString(result, "(");
2205 23 : for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
2206 7 : JSString* argName = CType::GetName(cx, fninfo->mArgTypes[i]);
2207 7 : AppendString(result, argName);
2208 7 : if (i != fninfo->mArgTypes.length() - 1 ||
2209 : fninfo->mIsVariadic)
2210 3 : AppendString(result, ", ");
2211 : }
2212 16 : if (fninfo->mIsVariadic)
2213 1 : AppendString(result, "...");
2214 16 : AppendString(result, ")");
2215 :
2216 : // Set 'typeObj' to the return type, and let the loop process it.
2217 : // 'prevGrouping' doesn't matter here, because functions cannot return
2218 : // arrays -- thus the parenthetical rules don't get tickled.
2219 16 : typeObj = fninfo->mReturnType;
2220 16 : continue;
2221 : }
2222 : default:
2223 : // Either a basic or struct type. Use the type's name as the base type.
2224 : break;
2225 : }
2226 : break;
2227 : }
2228 :
2229 : // If prepending the base type name directly would splice two
2230 : // identifiers, insert a space.
2231 468 : if (('a' <= result[0] && result[0] <= 'z') ||
2232 171 : ('A' <= result[0] && result[0] <= 'Z') ||
2233 99 : (result[0] == '_'))
2234 0 : PrependString(result, " ");
2235 :
2236 : // Stick the base type and derived type parts together.
2237 99 : JSString* baseName = CType::GetName(cx, typeObj);
2238 99 : PrependString(result, baseName);
2239 99 : return NewUCString(cx, result);
2240 : }
2241 :
2242 : // Given a CType 'typeObj', generate a string 'result' such that 'eval(result)'
2243 : // would construct the same CType. If 'makeShort' is true, assume that any
2244 : // StructType 't' is bound to an in-scope variable of name 't.name', and use
2245 : // that variable in place of generating a string to construct the type 't'.
2246 : // (This means the type comparison function CType::TypesEqual will return true
2247 : // when comparing the input and output of BuildTypeSource, since struct
2248 : // equality is determined by strict JSObject pointer equality.)
2249 : static void
2250 276 : BuildTypeSource(JSContext* cx,
2251 : JSObject* typeObj,
2252 : bool makeShort,
2253 : AutoString& result)
2254 : {
2255 : // Walk the types, building up the toSource() string.
2256 276 : switch (CType::GetTypeCode(typeObj)) {
2257 : case TYPE_void_t:
2258 : #define DEFINE_TYPE(name, type, ffiType) \
2259 : case TYPE_##name:
2260 : #include "typedefs.h"
2261 : {
2262 206 : AppendString(result, "ctypes.");
2263 206 : JSString* nameStr = CType::GetName(cx, typeObj);
2264 206 : AppendString(result, nameStr);
2265 206 : break;
2266 : }
2267 : case TYPE_pointer: {
2268 26 : JSObject* baseType = PointerType::GetBaseType(typeObj);
2269 :
2270 : // Specialcase ctypes.voidptr_t.
2271 26 : if (CType::GetTypeCode(baseType) == TYPE_void_t) {
2272 1 : AppendString(result, "ctypes.voidptr_t");
2273 1 : break;
2274 : }
2275 :
2276 : // Recursively build the source string, and append '.ptr'.
2277 25 : BuildTypeSource(cx, baseType, makeShort, result);
2278 25 : AppendString(result, ".ptr");
2279 25 : break;
2280 : }
2281 : case TYPE_function: {
2282 9 : FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
2283 :
2284 9 : AppendString(result, "ctypes.FunctionType(");
2285 :
2286 9 : switch (GetABICode(fninfo->mABI)) {
2287 : case ABI_DEFAULT:
2288 9 : AppendString(result, "ctypes.default_abi, ");
2289 9 : break;
2290 : case ABI_STDCALL:
2291 0 : AppendString(result, "ctypes.stdcall_abi, ");
2292 0 : break;
2293 : case ABI_WINAPI:
2294 0 : AppendString(result, "ctypes.winapi_abi, ");
2295 0 : break;
2296 : case INVALID_ABI:
2297 0 : JS_NOT_REACHED("invalid abi");
2298 : break;
2299 : }
2300 :
2301 : // Recursively build the source string describing the function return and
2302 : // argument types.
2303 9 : BuildTypeSource(cx, fninfo->mReturnType, true, result);
2304 :
2305 9 : if (fninfo->mArgTypes.length() > 0) {
2306 5 : AppendString(result, ", [");
2307 10 : for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
2308 5 : BuildTypeSource(cx, fninfo->mArgTypes[i], true, result);
2309 5 : if (i != fninfo->mArgTypes.length() - 1 ||
2310 : fninfo->mIsVariadic)
2311 1 : AppendString(result, ", ");
2312 : }
2313 5 : if (fninfo->mIsVariadic)
2314 1 : AppendString(result, "\"...\"");
2315 5 : AppendString(result, "]");
2316 : }
2317 :
2318 9 : AppendString(result, ")");
2319 9 : break;
2320 : }
2321 : case TYPE_array: {
2322 : // Recursively build the source string, and append '.array(n)',
2323 : // where n is the array length, or the empty string if the array length
2324 : // is undefined.
2325 6 : JSObject* baseType = ArrayType::GetBaseType(typeObj);
2326 6 : BuildTypeSource(cx, baseType, makeShort, result);
2327 6 : AppendString(result, ".array(");
2328 :
2329 : size_t length;
2330 6 : if (ArrayType::GetSafeLength(typeObj, &length))
2331 6 : IntegerToString(length, 10, result);
2332 :
2333 6 : AppendString(result, ")");
2334 6 : break;
2335 : }
2336 : case TYPE_struct: {
2337 29 : JSString* name = CType::GetName(cx, typeObj);
2338 :
2339 29 : if (makeShort) {
2340 : // Shorten the type declaration by assuming that StructType 't' is bound
2341 : // to an in-scope variable of name 't.name'.
2342 22 : AppendString(result, name);
2343 22 : break;
2344 : }
2345 :
2346 : // Write the full struct declaration.
2347 7 : AppendString(result, "ctypes.StructType(\"");
2348 7 : AppendString(result, name);
2349 7 : AppendString(result, "\"");
2350 :
2351 : // If it's an opaque struct, we're done.
2352 7 : if (!CType::IsSizeDefined(typeObj)) {
2353 3 : AppendString(result, ")");
2354 3 : break;
2355 : }
2356 :
2357 4 : AppendString(result, ", [");
2358 :
2359 4 : const FieldInfoHash* fields = StructType::GetFieldInfo(typeObj);
2360 4 : size_t length = fields->count();
2361 8 : Array<const FieldInfoHash::Entry*, 64> fieldsArray;
2362 4 : if (!fieldsArray.resize(length))
2363 : break;
2364 :
2365 12 : for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront())
2366 8 : fieldsArray[r.front().value.mIndex] = &r.front();
2367 :
2368 12 : for (size_t i = 0; i < length; ++i) {
2369 8 : const FieldInfoHash::Entry* entry = fieldsArray[i];
2370 8 : AppendString(result, "{ \"");
2371 8 : AppendString(result, entry->key);
2372 8 : AppendString(result, "\": ");
2373 8 : BuildTypeSource(cx, entry->value.mType, true, result);
2374 8 : AppendString(result, " }");
2375 8 : if (i != length - 1)
2376 4 : AppendString(result, ", ");
2377 : }
2378 :
2379 4 : AppendString(result, "])");
2380 : break;
2381 : }
2382 : }
2383 276 : }
2384 :
2385 : // Given a CData object of CType 'typeObj' with binary value 'data', generate a
2386 : // string 'result' such that 'eval(result)' would construct a CData object with
2387 : // the same CType and containing the same binary value. This assumes that any
2388 : // StructType 't' is bound to an in-scope variable of name 't.name'. (This means
2389 : // the type comparison function CType::TypesEqual will return true when
2390 : // comparing the types, since struct equality is determined by strict JSObject
2391 : // pointer equality.) Further, if 'isImplicit' is true, ensure that the
2392 : // resulting string can ImplicitConvert successfully if passed to another data
2393 : // constructor. (This is important when called recursively, since fields of
2394 : // structs and arrays are converted with ImplicitConvert.)
2395 : static JSBool
2396 221 : BuildDataSource(JSContext* cx,
2397 : JSObject* typeObj,
2398 : void* data,
2399 : bool isImplicit,
2400 : AutoString& result)
2401 : {
2402 221 : TypeCode type = CType::GetTypeCode(typeObj);
2403 221 : switch (type) {
2404 : case TYPE_bool:
2405 3 : if (*static_cast<bool*>(data))
2406 3 : AppendString(result, "true");
2407 : else
2408 0 : AppendString(result, "false");
2409 3 : break;
2410 : #define DEFINE_INT_TYPE(name, type, ffiType) \
2411 : case TYPE_##name: \
2412 : /* Serialize as a primitive decimal integer. */ \
2413 : IntegerToString(*static_cast<type*>(data), 10, result); \
2414 : break;
2415 : #define DEFINE_WRAPPED_INT_TYPE(name, type, ffiType) \
2416 : case TYPE_##name: \
2417 : /* Serialize as a wrapped decimal integer. */ \
2418 : if (!numeric_limits<type>::is_signed) \
2419 : AppendString(result, "ctypes.UInt64(\""); \
2420 : else \
2421 : AppendString(result, "ctypes.Int64(\""); \
2422 : \
2423 : IntegerToString(*static_cast<type*>(data), 10, result); \
2424 : AppendString(result, "\")"); \
2425 : break;
2426 : #define DEFINE_FLOAT_TYPE(name, type, ffiType) \
2427 : case TYPE_##name: { \
2428 : /* Serialize as a primitive double. */ \
2429 : double fp = *static_cast<type*>(data); \
2430 : ToCStringBuf cbuf; \
2431 : char* str = NumberToCString(cx, &cbuf, fp); \
2432 : if (!str) { \
2433 : JS_ReportOutOfMemory(cx); \
2434 : return false; \
2435 : } \
2436 : \
2437 : result.append(str, strlen(str)); \
2438 : break; \
2439 : }
2440 : #define DEFINE_CHAR_TYPE(name, type, ffiType) \
2441 : case TYPE_##name: \
2442 : /* Serialize as an integer. */ \
2443 : IntegerToString(*static_cast<type*>(data), 10, result); \
2444 : break;
2445 : #include "typedefs.h"
2446 : case TYPE_jschar: {
2447 : // Serialize as a 1-character JS string.
2448 3 : JSString* str = JS_NewUCStringCopyN(cx, static_cast<jschar*>(data), 1);
2449 3 : if (!str)
2450 0 : return false;
2451 :
2452 : // Escape characters, and quote as necessary.
2453 3 : JSString* src = JS_ValueToSource(cx, STRING_TO_JSVAL(str));
2454 3 : if (!src)
2455 0 : return false;
2456 :
2457 3 : AppendString(result, src);
2458 3 : break;
2459 : }
2460 : case TYPE_pointer:
2461 : case TYPE_function: {
2462 16 : if (isImplicit) {
2463 : // The result must be able to ImplicitConvert successfully.
2464 : // Wrap in a type constructor, then serialize for ExplicitConvert.
2465 2 : BuildTypeSource(cx, typeObj, true, result);
2466 2 : AppendString(result, "(");
2467 : }
2468 :
2469 : // Serialize the pointer value as a wrapped hexadecimal integer.
2470 16 : uintptr_t ptr = *static_cast<uintptr_t*>(data);
2471 16 : AppendString(result, "ctypes.UInt64(\"0x");
2472 16 : IntegerToString(ptr, 16, result);
2473 16 : AppendString(result, "\")");
2474 :
2475 16 : if (isImplicit)
2476 2 : AppendString(result, ")");
2477 :
2478 16 : break;
2479 : }
2480 : case TYPE_array: {
2481 : // Serialize each element of the array recursively. Each element must
2482 : // be able to ImplicitConvert successfully.
2483 4 : JSObject* baseType = ArrayType::GetBaseType(typeObj);
2484 4 : AppendString(result, "[");
2485 :
2486 4 : size_t length = ArrayType::GetLength(typeObj);
2487 4 : size_t elementSize = CType::GetSize(baseType);
2488 28 : for (size_t i = 0; i < length; ++i) {
2489 24 : char* element = static_cast<char*>(data) + elementSize * i;
2490 24 : if (!BuildDataSource(cx, baseType, element, true, result))
2491 0 : return false;
2492 :
2493 24 : if (i + 1 < length)
2494 20 : AppendString(result, ", ");
2495 : }
2496 4 : AppendString(result, "]");
2497 4 : break;
2498 : }
2499 : case TYPE_struct: {
2500 10 : if (isImplicit) {
2501 : // The result must be able to ImplicitConvert successfully.
2502 : // Serialize the data as an object with properties, rather than
2503 : // a sequence of arguments to the StructType constructor.
2504 4 : AppendString(result, "{");
2505 : }
2506 :
2507 : // Serialize each field of the struct recursively. Each field must
2508 : // be able to ImplicitConvert successfully.
2509 10 : const FieldInfoHash* fields = StructType::GetFieldInfo(typeObj);
2510 10 : size_t length = fields->count();
2511 20 : Array<const FieldInfoHash::Entry*, 64> fieldsArray;
2512 10 : if (!fieldsArray.resize(length))
2513 0 : return false;
2514 :
2515 32 : for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront())
2516 22 : fieldsArray[r.front().value.mIndex] = &r.front();
2517 :
2518 32 : for (size_t i = 0; i < length; ++i) {
2519 22 : const FieldInfoHash::Entry* entry = fieldsArray[i];
2520 :
2521 22 : if (isImplicit) {
2522 8 : AppendString(result, "\"");
2523 8 : AppendString(result, entry->key);
2524 8 : AppendString(result, "\": ");
2525 : }
2526 :
2527 22 : char* fieldData = static_cast<char*>(data) + entry->value.mOffset;
2528 22 : if (!BuildDataSource(cx, entry->value.mType, fieldData, true, result))
2529 0 : return false;
2530 :
2531 22 : if (i + 1 != length)
2532 12 : AppendString(result, ", ");
2533 : }
2534 :
2535 10 : if (isImplicit)
2536 4 : AppendString(result, "}");
2537 :
2538 10 : break;
2539 : }
2540 : case TYPE_void_t:
2541 0 : JS_NOT_REACHED("invalid type");
2542 : break;
2543 : }
2544 :
2545 221 : return true;
2546 : }
2547 :
2548 : /*******************************************************************************
2549 : ** JSAPI callback function implementations
2550 : *******************************************************************************/
2551 :
2552 : JSBool
2553 6 : ConstructAbstract(JSContext* cx,
2554 : unsigned argc,
2555 : jsval* vp)
2556 : {
2557 : // Calling an abstract base class constructor is disallowed.
2558 6 : JS_ReportError(cx, "cannot construct from abstract type");
2559 6 : return JS_FALSE;
2560 : }
2561 :
2562 : /*******************************************************************************
2563 : ** CType implementation
2564 : *******************************************************************************/
2565 :
2566 : JSBool
2567 1291 : CType::ConstructData(JSContext* cx,
2568 : unsigned argc,
2569 : jsval* vp)
2570 : {
2571 : // get the callee object...
2572 1291 : JSObject* obj = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
2573 1291 : if (!CType::IsCType(obj)) {
2574 0 : JS_ReportError(cx, "not a CType");
2575 0 : return JS_FALSE;
2576 : }
2577 :
2578 : // How we construct the CData object depends on what type we represent.
2579 : // An instance 'd' of a CData object of type 't' has:
2580 : // * [[Class]] "CData"
2581 : // * __proto__ === t.prototype
2582 1291 : switch (GetTypeCode(obj)) {
2583 : case TYPE_void_t:
2584 0 : JS_ReportError(cx, "cannot construct from void_t");
2585 0 : return JS_FALSE;
2586 : case TYPE_function:
2587 1 : JS_ReportError(cx, "cannot construct from FunctionType; use FunctionType.ptr instead");
2588 1 : return JS_FALSE;
2589 : case TYPE_pointer:
2590 43 : return PointerType::ConstructData(cx, obj, argc, vp);
2591 : case TYPE_array:
2592 481 : return ArrayType::ConstructData(cx, obj, argc, vp);
2593 : case TYPE_struct:
2594 243 : return StructType::ConstructData(cx, obj, argc, vp);
2595 : default:
2596 523 : return ConstructBasic(cx, obj, argc, vp);
2597 : }
2598 : }
2599 :
2600 : JSBool
2601 523 : CType::ConstructBasic(JSContext* cx,
2602 : JSObject* obj,
2603 : unsigned argc,
2604 : jsval* vp)
2605 : {
2606 523 : if (argc > 1) {
2607 0 : JS_ReportError(cx, "CType constructor takes zero or one argument");
2608 0 : return JS_FALSE;
2609 : }
2610 :
2611 : // construct a CData object
2612 523 : JSObject* result = CData::Create(cx, obj, NULL, NULL, true);
2613 523 : if (!result)
2614 0 : return JS_FALSE;
2615 :
2616 523 : if (argc == 1) {
2617 192 : if (!ExplicitConvert(cx, JS_ARGV(cx, vp)[0], obj, CData::GetData(result)))
2618 0 : return JS_FALSE;
2619 : }
2620 :
2621 523 : JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
2622 523 : return JS_TRUE;
2623 : }
2624 :
2625 : JSObject*
2626 723593 : CType::Create(JSContext* cx,
2627 : JSObject* typeProto,
2628 : JSObject* dataProto,
2629 : TypeCode type,
2630 : JSString* name,
2631 : jsval size,
2632 : jsval align,
2633 : ffi_type* ffiType)
2634 : {
2635 723593 : JSObject* parent = JS_GetParent(typeProto);
2636 723593 : JS_ASSERT(parent);
2637 :
2638 : // Create a CType object with the properties and slots common to all CTypes.
2639 : // Each type object 't' has:
2640 : // * [[Class]] "CType"
2641 : // * __proto__ === 'typeProto'; one of ctypes.{CType,PointerType,ArrayType,
2642 : // StructType}.prototype
2643 : // * A constructor which creates and returns a CData object, containing
2644 : // binary data of the given type.
2645 : // * 'prototype' property:
2646 : // * [[Class]] "CDataProto"
2647 : // * __proto__ === 'dataProto'; an object containing properties and
2648 : // functions common to all CData objects of types derived from
2649 : // 'typeProto'. (For instance, this could be ctypes.CData.prototype
2650 : // for simple types, or something representing structs for StructTypes.)
2651 : // * 'constructor' property === 't'
2652 : // * Additional properties specified by 'ps', as appropriate for the
2653 : // specific type instance 't'.
2654 723593 : JSObject* typeObj = JS_NewObject(cx, &sCTypeClass, typeProto, parent);
2655 723593 : if (!typeObj)
2656 0 : return NULL;
2657 1447186 : js::AutoObjectRooter root(cx, typeObj);
2658 :
2659 : // Set up the reserved slots.
2660 723593 : JS_SetReservedSlot(typeObj, SLOT_TYPECODE, INT_TO_JSVAL(type));
2661 723593 : if (ffiType)
2662 719159 : JS_SetReservedSlot(typeObj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(ffiType));
2663 723593 : if (name)
2664 693172 : JS_SetReservedSlot(typeObj, SLOT_NAME, STRING_TO_JSVAL(name));
2665 723593 : JS_SetReservedSlot(typeObj, SLOT_SIZE, size);
2666 723593 : JS_SetReservedSlot(typeObj, SLOT_ALIGN, align);
2667 :
2668 723593 : if (dataProto) {
2669 : // Set up the 'prototype' and 'prototype.constructor' properties.
2670 723361 : JSObject* prototype = JS_NewObject(cx, &sCDataProtoClass, dataProto, parent);
2671 723361 : if (!prototype)
2672 0 : return NULL;
2673 1446722 : js::AutoObjectRooter protoroot(cx, prototype);
2674 :
2675 723361 : if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(typeObj),
2676 723361 : NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT))
2677 0 : return NULL;
2678 :
2679 : // Set the 'prototype' object.
2680 : //if (!JS_FreezeObject(cx, prototype)) // XXX fixme - see bug 541212!
2681 : // return NULL;
2682 1446722 : JS_SetReservedSlot(typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype));
2683 : }
2684 :
2685 723593 : if (!JS_FreezeObject(cx, typeObj))
2686 0 : return NULL;
2687 :
2688 : // Assert a sanity check on size and alignment: size % alignment should always
2689 : // be zero.
2690 1422012 : JS_ASSERT_IF(IsSizeDefined(typeObj),
2691 1422012 : GetSize(typeObj) % GetAlignment(typeObj) == 0);
2692 :
2693 723593 : return typeObj;
2694 : }
2695 :
2696 : JSObject*
2697 692940 : CType::DefineBuiltin(JSContext* cx,
2698 : JSObject* parent,
2699 : const char* propName,
2700 : JSObject* typeProto,
2701 : JSObject* dataProto,
2702 : const char* name,
2703 : TypeCode type,
2704 : jsval size,
2705 : jsval align,
2706 : ffi_type* ffiType)
2707 : {
2708 692940 : JSString* nameStr = JS_NewStringCopyZ(cx, name);
2709 692940 : if (!nameStr)
2710 0 : return NULL;
2711 1385880 : js::AutoStringRooter nameRoot(cx, nameStr);
2712 :
2713 : // Create a new CType object with the common properties and slots.
2714 : JSObject* typeObj = Create(cx, typeProto, dataProto, type, nameStr, size,
2715 692940 : align, ffiType);
2716 692940 : if (!typeObj)
2717 0 : return NULL;
2718 :
2719 : // Define the CType as a 'propName' property on 'parent'.
2720 692940 : if (!JS_DefineProperty(cx, parent, propName, OBJECT_TO_JSVAL(typeObj),
2721 692940 : NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
2722 0 : return NULL;
2723 :
2724 692940 : return typeObj;
2725 : }
2726 :
2727 : void
2728 723593 : CType::Finalize(JSContext* cx, JSObject* obj)
2729 : {
2730 : // Make sure our TypeCode slot is legit. If it's not, bail.
2731 723593 : jsval slot = JS_GetReservedSlot(obj, SLOT_TYPECODE);
2732 723593 : if (JSVAL_IS_VOID(slot))
2733 0 : return;
2734 :
2735 : // The contents of our slots depends on what kind of type we are.
2736 723593 : switch (TypeCode(JSVAL_TO_INT(slot))) {
2737 : case TYPE_function: {
2738 : // Free the FunctionInfo.
2739 1495 : slot = JS_GetReservedSlot(obj, SLOT_FNINFO);
2740 1495 : if (!JSVAL_IS_VOID(slot))
2741 1495 : cx->delete_(static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot)));
2742 1495 : break;
2743 : }
2744 :
2745 : case TYPE_struct: {
2746 : // Free the FieldInfoHash table.
2747 232 : slot = JS_GetReservedSlot(obj, SLOT_FIELDINFO);
2748 232 : if (!JSVAL_IS_VOID(slot)) {
2749 218 : void* info = JSVAL_TO_PRIVATE(slot);
2750 218 : cx->delete_(static_cast<FieldInfoHash*>(info));
2751 : }
2752 : }
2753 :
2754 : // Fall through.
2755 : case TYPE_array: {
2756 : // Free the ffi_type info.
2757 2939 : slot = JS_GetReservedSlot(obj, SLOT_FFITYPE);
2758 2939 : if (!JSVAL_IS_VOID(slot)) {
2759 13 : ffi_type* ffiType = static_cast<ffi_type*>(JSVAL_TO_PRIVATE(slot));
2760 13 : cx->array_delete(ffiType->elements);
2761 13 : cx->delete_(ffiType);
2762 : }
2763 :
2764 2939 : break;
2765 : }
2766 : default:
2767 : // Nothing to do here.
2768 719159 : break;
2769 : }
2770 : }
2771 :
2772 : void
2773 115490 : CType::FinalizeProtoClass(JSContext* cx, JSObject* obj)
2774 : {
2775 : // Finalize the CTypeProto class. The only important bit here is our
2776 : // SLOT_CLOSURECX -- it contains the JSContext that was (lazily) instantiated
2777 : // for use with FunctionType closures. And if we're here, in this finalizer,
2778 : // we're guaranteed to not need it anymore. Note that this slot will only
2779 : // be set for the object (of class CTypeProto) ctypes.FunctionType.prototype.
2780 115490 : jsval slot = JS_GetReservedSlot(obj, SLOT_CLOSURECX);
2781 115490 : if (JSVAL_IS_VOID(slot))
2782 115489 : return;
2783 :
2784 1 : JSContext* closureCx = static_cast<JSContext*>(JSVAL_TO_PRIVATE(slot));
2785 1 : JS_DestroyContextNoGC(closureCx);
2786 : }
2787 :
2788 : void
2789 1571920 : CType::Trace(JSTracer* trc, JSObject* obj)
2790 : {
2791 : // Make sure our TypeCode slot is legit. If it's not, bail.
2792 1571920 : jsval slot = obj->getSlot(SLOT_TYPECODE);
2793 1571920 : if (JSVAL_IS_VOID(slot))
2794 0 : return;
2795 :
2796 : // The contents of our slots depends on what kind of type we are.
2797 1571920 : switch (TypeCode(JSVAL_TO_INT(slot))) {
2798 : case TYPE_struct: {
2799 3256 : slot = obj->getReservedSlot(SLOT_FIELDINFO);
2800 3256 : if (JSVAL_IS_VOID(slot))
2801 0 : return;
2802 :
2803 : FieldInfoHash* fields =
2804 3256 : static_cast<FieldInfoHash*>(JSVAL_TO_PRIVATE(slot));
2805 11396 : for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
2806 8140 : JS_CALL_TRACER(trc, r.front().key, JSTRACE_STRING, "fieldName");
2807 8140 : JS_CALL_TRACER(trc, r.front().value.mType, JSTRACE_OBJECT, "fieldType");
2808 : }
2809 :
2810 3256 : break;
2811 : }
2812 : case TYPE_function: {
2813 : // Check if we have a FunctionInfo.
2814 39049 : slot = obj->getReservedSlot(SLOT_FNINFO);
2815 39049 : if (JSVAL_IS_VOID(slot))
2816 0 : return;
2817 :
2818 39049 : FunctionInfo* fninfo = static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot));
2819 39049 : JS_ASSERT(fninfo);
2820 :
2821 : // Identify our objects to the tracer.
2822 39049 : JS_CALL_TRACER(trc, fninfo->mABI, JSTRACE_OBJECT, "abi");
2823 39049 : JS_CALL_TRACER(trc, fninfo->mReturnType, JSTRACE_OBJECT, "returnType");
2824 136671 : for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i)
2825 97622 : JS_CALL_TRACER(trc, fninfo->mArgTypes[i], JSTRACE_OBJECT, "argType");
2826 :
2827 39049 : break;
2828 : }
2829 : default:
2830 : // Nothing to do here.
2831 1529615 : break;
2832 : }
2833 : }
2834 :
2835 : bool
2836 3660994 : CType::IsCType(JSObject* obj)
2837 : {
2838 3660994 : return JS_GetClass(obj) == &sCTypeClass;
2839 : }
2840 :
2841 : bool
2842 64012 : CType::IsCTypeProto(JSObject* obj)
2843 : {
2844 64012 : return JS_GetClass(obj) == &sCTypeProtoClass;
2845 : }
2846 :
2847 : TypeCode
2848 674369 : CType::GetTypeCode(JSObject* typeObj)
2849 : {
2850 674369 : JS_ASSERT(IsCType(typeObj));
2851 :
2852 674369 : jsval result = JS_GetReservedSlot(typeObj, SLOT_TYPECODE);
2853 674369 : return TypeCode(JSVAL_TO_INT(result));
2854 : }
2855 :
2856 : bool
2857 7882 : CType::TypesEqual(JSObject* t1, JSObject* t2)
2858 : {
2859 7882 : JS_ASSERT(IsCType(t1) && IsCType(t2));
2860 :
2861 : // Fast path: check for object equality.
2862 7882 : if (t1 == t2)
2863 6781 : return true;
2864 :
2865 : // First, perform shallow comparison.
2866 1101 : TypeCode c1 = GetTypeCode(t1);
2867 1101 : TypeCode c2 = GetTypeCode(t2);
2868 1101 : if (c1 != c2)
2869 1085 : return false;
2870 :
2871 : // Determine whether the types require shallow or deep comparison.
2872 16 : switch (c1) {
2873 : case TYPE_pointer: {
2874 : // Compare base types.
2875 9 : JSObject* b1 = PointerType::GetBaseType(t1);
2876 9 : JSObject* b2 = PointerType::GetBaseType(t2);
2877 9 : return TypesEqual(b1, b2);
2878 : }
2879 : case TYPE_function: {
2880 3 : FunctionInfo* f1 = FunctionType::GetFunctionInfo(t1);
2881 3 : FunctionInfo* f2 = FunctionType::GetFunctionInfo(t2);
2882 :
2883 : // Compare abi, return type, and argument types.
2884 3 : if (f1->mABI != f2->mABI)
2885 0 : return false;
2886 :
2887 3 : if (!TypesEqual(f1->mReturnType, f2->mReturnType))
2888 0 : return false;
2889 :
2890 3 : if (f1->mArgTypes.length() != f2->mArgTypes.length())
2891 2 : return false;
2892 :
2893 1 : if (f1->mIsVariadic != f2->mIsVariadic)
2894 1 : return false;
2895 :
2896 0 : for (size_t i = 0; i < f1->mArgTypes.length(); ++i) {
2897 0 : if (!TypesEqual(f1->mArgTypes[i], f2->mArgTypes[i]))
2898 0 : return false;
2899 : }
2900 :
2901 0 : return true;
2902 : }
2903 : case TYPE_array: {
2904 : // Compare length, then base types.
2905 : // An undefined length array matches other undefined length arrays.
2906 0 : size_t s1 = 0, s2 = 0;
2907 0 : bool d1 = ArrayType::GetSafeLength(t1, &s1);
2908 0 : bool d2 = ArrayType::GetSafeLength(t2, &s2);
2909 0 : if (d1 != d2 || (d1 && s1 != s2))
2910 0 : return false;
2911 :
2912 0 : JSObject* b1 = ArrayType::GetBaseType(t1);
2913 0 : JSObject* b2 = ArrayType::GetBaseType(t2);
2914 0 : return TypesEqual(b1, b2);
2915 : }
2916 : case TYPE_struct:
2917 : // Require exact type object equality.
2918 4 : return false;
2919 : default:
2920 : // Shallow comparison is sufficient.
2921 0 : return true;
2922 : }
2923 : }
2924 :
2925 : bool
2926 6429 : CType::GetSafeSize(JSObject* obj, size_t* result)
2927 : {
2928 6429 : JS_ASSERT(CType::IsCType(obj));
2929 :
2930 6429 : jsval size = JS_GetReservedSlot(obj, SLOT_SIZE);
2931 :
2932 : // The "size" property can be an int, a double, or JSVAL_VOID
2933 : // (for arrays of undefined length), and must always fit in a size_t.
2934 6429 : if (JSVAL_IS_INT(size)) {
2935 6417 : *result = JSVAL_TO_INT(size);
2936 6417 : return true;
2937 : }
2938 12 : if (JSVAL_IS_DOUBLE(size)) {
2939 5 : *result = Convert<size_t>(JSVAL_TO_DOUBLE(size));
2940 5 : return true;
2941 : }
2942 :
2943 7 : JS_ASSERT(JSVAL_IS_VOID(size));
2944 7 : return false;
2945 : }
2946 :
2947 : size_t
2948 890312 : CType::GetSize(JSObject* obj)
2949 : {
2950 890312 : JS_ASSERT(CType::IsCType(obj));
2951 :
2952 890312 : jsval size = JS_GetReservedSlot(obj, SLOT_SIZE);
2953 :
2954 890312 : JS_ASSERT(!JSVAL_IS_VOID(size));
2955 :
2956 : // The "size" property can be an int, a double, or JSVAL_VOID
2957 : // (for arrays of undefined length), and must always fit in a size_t.
2958 : // For callers who know it can never be JSVAL_VOID, return a size_t directly.
2959 890312 : if (JSVAL_IS_INT(size))
2960 890305 : return JSVAL_TO_INT(size);
2961 7 : return Convert<size_t>(JSVAL_TO_DOUBLE(size));
2962 : }
2963 :
2964 : bool
2965 787319 : CType::IsSizeDefined(JSObject* obj)
2966 : {
2967 787319 : JS_ASSERT(CType::IsCType(obj));
2968 :
2969 787319 : jsval size = JS_GetReservedSlot(obj, SLOT_SIZE);
2970 :
2971 : // The "size" property can be an int, a double, or JSVAL_VOID
2972 : // (for arrays of undefined length), and must always fit in a size_t.
2973 787319 : JS_ASSERT(JSVAL_IS_INT(size) || JSVAL_IS_DOUBLE(size) || JSVAL_IS_VOID(size));
2974 787319 : return !JSVAL_IS_VOID(size);
2975 : }
2976 :
2977 : size_t
2978 701687 : CType::GetAlignment(JSObject* obj)
2979 : {
2980 701687 : JS_ASSERT(CType::IsCType(obj));
2981 :
2982 701687 : jsval slot = JS_GetReservedSlot(obj, SLOT_ALIGN);
2983 701687 : return static_cast<size_t>(JSVAL_TO_INT(slot));
2984 : }
2985 :
2986 : ffi_type*
2987 5792 : CType::GetFFIType(JSContext* cx, JSObject* obj)
2988 : {
2989 5792 : JS_ASSERT(CType::IsCType(obj));
2990 :
2991 5792 : jsval slot = JS_GetReservedSlot(obj, SLOT_FFITYPE);
2992 :
2993 5792 : if (!JSVAL_IS_VOID(slot)) {
2994 5779 : return static_cast<ffi_type*>(JSVAL_TO_PRIVATE(slot));
2995 : }
2996 :
2997 26 : AutoPtr<ffi_type> result;
2998 13 : switch (CType::GetTypeCode(obj)) {
2999 : case TYPE_array:
3000 0 : result = ArrayType::BuildFFIType(cx, obj);
3001 0 : break;
3002 :
3003 : case TYPE_struct:
3004 13 : result = StructType::BuildFFIType(cx, obj);
3005 13 : break;
3006 :
3007 : default:
3008 0 : JS_NOT_REACHED("simple types must have an ffi_type");
3009 : }
3010 :
3011 13 : if (!result)
3012 0 : return NULL;
3013 13 : JS_SetReservedSlot(obj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(result.get()));
3014 13 : return result.forget();
3015 : }
3016 :
3017 : JSString*
3018 578 : CType::GetName(JSContext* cx, JSObject* obj)
3019 : {
3020 578 : JS_ASSERT(CType::IsCType(obj));
3021 :
3022 578 : jsval string = JS_GetReservedSlot(obj, SLOT_NAME);
3023 578 : if (JSVAL_IS_VOID(string)) {
3024 : // Build the type name lazily.
3025 99 : JSString* name = BuildTypeName(cx, obj);
3026 99 : if (!name)
3027 0 : return NULL;
3028 99 : JS_SetReservedSlot(obj, SLOT_NAME, STRING_TO_JSVAL(name));
3029 99 : return name;
3030 : }
3031 :
3032 479 : return JSVAL_TO_STRING(string);
3033 : }
3034 :
3035 : JSObject*
3036 232 : CType::GetProtoFromCtor(JSObject* obj, CTypeProtoSlot slot)
3037 : {
3038 : // Get ctypes.{Pointer,Array,Struct}Type.prototype from a reserved slot
3039 : // on the type constructor.
3040 232 : jsval protoslot = js::GetFunctionNativeReserved(obj, SLOT_FN_CTORPROTO);
3041 232 : JSObject* proto = JSVAL_TO_OBJECT(protoslot);
3042 232 : JS_ASSERT(proto);
3043 232 : JS_ASSERT(CType::IsCTypeProto(proto));
3044 :
3045 : // Get the desired prototype.
3046 232 : jsval result = JS_GetReservedSlot(proto, slot);
3047 232 : return JSVAL_TO_OBJECT(result);
3048 : }
3049 :
3050 : JSObject*
3051 63326 : CType::GetProtoFromType(JSObject* obj, CTypeProtoSlot slot)
3052 : {
3053 63326 : JS_ASSERT(IsCType(obj));
3054 :
3055 : // Get the prototype of the type object.
3056 63326 : JSObject* proto = JS_GetPrototype(obj);
3057 63326 : JS_ASSERT(proto);
3058 63326 : JS_ASSERT(CType::IsCTypeProto(proto));
3059 :
3060 : // Get the requested ctypes.{Pointer,Array,Struct,Function}Type.prototype.
3061 63326 : jsval result = JS_GetReservedSlot(proto, slot);
3062 63326 : return JSVAL_TO_OBJECT(result);
3063 : }
3064 :
3065 : JSBool
3066 436 : CType::PrototypeGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
3067 : {
3068 436 : if (!(CType::IsCType(obj) || CType::IsCTypeProto(obj))) {
3069 0 : JS_ReportError(cx, "not a CType or CTypeProto");
3070 0 : return JS_FALSE;
3071 : }
3072 :
3073 436 : unsigned slot = CType::IsCTypeProto(obj) ? (unsigned) SLOT_OURDATAPROTO
3074 436 : : (unsigned) SLOT_PROTO;
3075 436 : *vp = JS_GetReservedSlot(obj, slot);
3076 436 : JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp) || JSVAL_IS_VOID(*vp));
3077 436 : return JS_TRUE;
3078 : }
3079 :
3080 : JSBool
3081 155 : CType::NameGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
3082 : {
3083 155 : if (!CType::IsCType(obj)) {
3084 1 : JS_ReportError(cx, "not a CType");
3085 1 : return JS_FALSE;
3086 : }
3087 :
3088 154 : JSString* name = CType::GetName(cx, obj);
3089 154 : if (!name)
3090 0 : return JS_FALSE;
3091 :
3092 154 : *vp = STRING_TO_JSVAL(name);
3093 154 : return JS_TRUE;
3094 : }
3095 :
3096 : JSBool
3097 434 : CType::SizeGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
3098 : {
3099 434 : if (!CType::IsCType(obj)) {
3100 1 : JS_ReportError(cx, "not a CType");
3101 1 : return JS_FALSE;
3102 : }
3103 :
3104 433 : *vp = JS_GetReservedSlot(obj, SLOT_SIZE);
3105 433 : JS_ASSERT(JSVAL_IS_NUMBER(*vp) || JSVAL_IS_VOID(*vp));
3106 433 : return JS_TRUE;
3107 : }
3108 :
3109 : JSBool
3110 3536 : CType::PtrGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
3111 : {
3112 3536 : if (!CType::IsCType(obj)) {
3113 1 : JS_ReportError(cx, "not a CType");
3114 1 : return JS_FALSE;
3115 : }
3116 :
3117 3535 : JSObject* pointerType = PointerType::CreateInternal(cx, obj);
3118 3535 : if (!pointerType)
3119 0 : return JS_FALSE;
3120 :
3121 3535 : *vp = OBJECT_TO_JSVAL(pointerType);
3122 3535 : return JS_TRUE;
3123 : }
3124 :
3125 : JSBool
3126 2025 : CType::CreateArray(JSContext* cx, unsigned argc, jsval* vp)
3127 : {
3128 2025 : JSObject* baseType = JS_THIS_OBJECT(cx, vp);
3129 2025 : if (!baseType || !CType::IsCType(baseType)) {
3130 1 : JS_ReportError(cx, "not a CType");
3131 1 : return JS_FALSE;
3132 : }
3133 :
3134 : // Construct and return a new ArrayType object.
3135 2024 : if (argc > 1) {
3136 0 : JS_ReportError(cx, "array takes zero or one argument");
3137 0 : return JS_FALSE;
3138 : }
3139 :
3140 : // Convert the length argument to a size_t.
3141 2024 : jsval* argv = JS_ARGV(cx, vp);
3142 2024 : size_t length = 0;
3143 2024 : if (argc == 1 && !jsvalToSize(cx, argv[0], false, &length)) {
3144 0 : JS_ReportError(cx, "argument must be a nonnegative integer");
3145 0 : return JS_FALSE;
3146 : }
3147 :
3148 2024 : JSObject* result = ArrayType::CreateInternal(cx, baseType, length, argc == 1);
3149 2024 : if (!result)
3150 2 : return JS_FALSE;
3151 :
3152 2022 : JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
3153 2022 : return JS_TRUE;
3154 : }
3155 :
3156 : JSBool
3157 84 : CType::ToString(JSContext* cx, unsigned argc, jsval* vp)
3158 : {
3159 84 : JSObject* obj = JS_THIS_OBJECT(cx, vp);
3160 84 : if (!obj || !(CType::IsCType(obj) || CType::IsCTypeProto(obj))) {
3161 0 : JS_ReportError(cx, "not a CType");
3162 0 : return JS_FALSE;
3163 : }
3164 :
3165 : // Create the appropriate string depending on whether we're sCTypeClass or
3166 : // sCTypeProtoClass.
3167 : JSString* result;
3168 84 : if (CType::IsCType(obj)) {
3169 166 : AutoString type;
3170 83 : AppendString(type, "type ");
3171 83 : AppendString(type, GetName(cx, obj));
3172 83 : result = NewUCString(cx, type);
3173 : }
3174 : else {
3175 1 : result = JS_NewStringCopyZ(cx, "[CType proto object]");
3176 : }
3177 84 : if (!result)
3178 0 : return JS_FALSE;
3179 :
3180 84 : JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
3181 84 : return JS_TRUE;
3182 : }
3183 :
3184 : JSBool
3185 47 : CType::ToSource(JSContext* cx, unsigned argc, jsval* vp)
3186 : {
3187 47 : JSObject* obj = JS_THIS_OBJECT(cx, vp);
3188 94 : if (!obj ||
3189 48 : !(CType::IsCType(obj) || CType::IsCTypeProto(obj)))
3190 : {
3191 0 : JS_ReportError(cx, "not a CType");
3192 0 : return JS_FALSE;
3193 : }
3194 :
3195 : // Create the appropriate string depending on whether we're sCTypeClass or
3196 : // sCTypeProtoClass.
3197 : JSString* result;
3198 47 : if (CType::IsCType(obj)) {
3199 92 : AutoString source;
3200 46 : BuildTypeSource(cx, obj, false, source);
3201 46 : result = NewUCString(cx, source);
3202 : } else {
3203 1 : result = JS_NewStringCopyZ(cx, "[CType proto object]");
3204 : }
3205 47 : if (!result)
3206 0 : return JS_FALSE;
3207 :
3208 47 : JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
3209 47 : return JS_TRUE;
3210 : }
3211 :
3212 : JSBool
3213 33 : CType::HasInstance(JSContext* cx, JSObject* obj, const jsval* v, JSBool* bp)
3214 : {
3215 33 : JS_ASSERT(CType::IsCType(obj));
3216 :
3217 33 : jsval slot = JS_GetReservedSlot(obj, SLOT_PROTO);
3218 33 : JSObject* prototype = JSVAL_TO_OBJECT(slot);
3219 33 : JS_ASSERT(prototype);
3220 33 : JS_ASSERT(CData::IsCDataProto(prototype));
3221 :
3222 33 : *bp = JS_FALSE;
3223 33 : if (JSVAL_IS_PRIMITIVE(*v))
3224 0 : return JS_TRUE;
3225 :
3226 33 : JSObject* proto = JSVAL_TO_OBJECT(*v);
3227 66 : while ((proto = JS_GetPrototype(proto))) {
3228 33 : if (proto == prototype) {
3229 33 : *bp = JS_TRUE;
3230 33 : break;
3231 : }
3232 : }
3233 33 : return JS_TRUE;
3234 : }
3235 :
3236 : /*******************************************************************************
3237 : ** PointerType implementation
3238 : *******************************************************************************/
3239 :
3240 : JSBool
3241 45 : PointerType::Create(JSContext* cx, unsigned argc, jsval* vp)
3242 : {
3243 : // Construct and return a new PointerType object.
3244 45 : if (argc != 1) {
3245 2 : JS_ReportError(cx, "PointerType takes one argument");
3246 2 : return JS_FALSE;
3247 : }
3248 :
3249 43 : jsval arg = JS_ARGV(cx, vp)[0];
3250 43 : if (JSVAL_IS_PRIMITIVE(arg) || !CType::IsCType(JSVAL_TO_OBJECT(arg))) {
3251 3 : JS_ReportError(cx, "first argument must be a CType");
3252 3 : return JS_FALSE;
3253 : }
3254 :
3255 40 : JSObject* result = CreateInternal(cx, JSVAL_TO_OBJECT(arg));
3256 40 : if (!result)
3257 0 : return JS_FALSE;
3258 :
3259 40 : JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
3260 40 : return JS_TRUE;
3261 : }
3262 :
3263 : JSObject*
3264 30700 : PointerType::CreateInternal(JSContext* cx, JSObject* baseType)
3265 : {
3266 : // check if we have a cached PointerType on our base CType.
3267 30700 : jsval slot = JS_GetReservedSlot(baseType, SLOT_PTR);
3268 30700 : if (!JSVAL_IS_VOID(slot))
3269 4481 : return JSVAL_TO_OBJECT(slot);
3270 :
3271 : // Get ctypes.PointerType.prototype and the common prototype for CData objects
3272 : // of this type.
3273 : JSObject* typeProto;
3274 : JSObject* dataProto;
3275 26219 : typeProto = CType::GetProtoFromType(baseType, SLOT_POINTERPROTO);
3276 26219 : dataProto = CType::GetProtoFromType(baseType, SLOT_POINTERDATAPROTO);
3277 :
3278 : // Create a new CType object with the common properties and slots.
3279 : JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_pointer,
3280 : NULL, INT_TO_JSVAL(sizeof(void*)),
3281 26219 : INT_TO_JSVAL(ffi_type_pointer.alignment),
3282 26219 : &ffi_type_pointer);
3283 26219 : if (!typeObj)
3284 0 : return NULL;
3285 :
3286 : // Set the target type. (This will be 'null' for an opaque pointer type.)
3287 26219 : JS_SetReservedSlot(typeObj, SLOT_TARGET_T, OBJECT_TO_JSVAL(baseType));
3288 :
3289 : // Finally, cache our newly-created PointerType on our pointed-to CType.
3290 26219 : JS_SetReservedSlot(baseType, SLOT_PTR, OBJECT_TO_JSVAL(typeObj));
3291 :
3292 26219 : return typeObj;
3293 : }
3294 :
3295 : JSBool
3296 43 : PointerType::ConstructData(JSContext* cx,
3297 : JSObject* obj,
3298 : unsigned argc,
3299 : jsval* vp)
3300 : {
3301 43 : if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_pointer) {
3302 0 : JS_ReportError(cx, "not a PointerType");
3303 0 : return JS_FALSE;
3304 : }
3305 :
3306 43 : if (argc > 3) {
3307 0 : JS_ReportError(cx, "constructor takes 0, 1, 2, or 3 arguments");
3308 0 : return JS_FALSE;
3309 : }
3310 :
3311 43 : JSObject* result = CData::Create(cx, obj, NULL, NULL, true);
3312 43 : if (!result)
3313 0 : return JS_FALSE;
3314 :
3315 : // Set return value early, must not observe *vp after
3316 43 : JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
3317 :
3318 : // There are 3 things that we might be creating here:
3319 : // 1 - A null pointer (no arguments)
3320 : // 2 - An initialized pointer (1 argument)
3321 : // 3 - A closure (1-3 arguments)
3322 : //
3323 : // The API doesn't give us a perfect way to distinguish 2 and 3, but the
3324 : // heuristics we use should be fine.
3325 :
3326 : //
3327 : // Case 1 - Null pointer
3328 : //
3329 43 : if (argc == 0)
3330 16 : return JS_TRUE;
3331 :
3332 : // Analyze the arguments a bit to decide what to do next.
3333 27 : jsval* argv = JS_ARGV(cx, vp);
3334 27 : JSObject* baseObj = PointerType::GetBaseType(obj);
3335 27 : bool looksLikeClosure = CType::GetTypeCode(baseObj) == TYPE_function &&
3336 11 : JSVAL_IS_OBJECT(argv[0]) &&
3337 38 : JS_ObjectIsCallable(cx, JSVAL_TO_OBJECT(argv[0]));
3338 :
3339 : //
3340 : // Case 2 - Initialized pointer
3341 : //
3342 27 : if (!looksLikeClosure) {
3343 19 : if (argc != 1) {
3344 0 : JS_ReportError(cx, "first argument must be a function");
3345 0 : return JS_FALSE;
3346 : }
3347 19 : return ExplicitConvert(cx, argv[0], obj, CData::GetData(result));
3348 : }
3349 :
3350 : //
3351 : // Case 3 - Closure
3352 : //
3353 :
3354 : // The second argument is an optional 'this' parameter with which to invoke
3355 : // the given js function. Callers may leave this blank, or pass null if they
3356 : // wish to pass the third argument.
3357 8 : JSObject* thisObj = NULL;
3358 8 : if (argc >= 2) {
3359 4 : if (JSVAL_IS_OBJECT(argv[1])) {
3360 4 : thisObj = JSVAL_TO_OBJECT(argv[1]);
3361 0 : } else if (!JS_ValueToObject(cx, argv[1], &thisObj)) {
3362 0 : return JS_FALSE;
3363 : }
3364 : }
3365 :
3366 : // The third argument is an optional error sentinel that js-ctypes will return
3367 : // if an exception is raised while executing the closure. The type must match
3368 : // the return type of the callback.
3369 8 : jsval errVal = JSVAL_VOID;
3370 8 : if (argc == 3)
3371 3 : errVal = argv[2];
3372 :
3373 8 : JSObject* fnObj = JSVAL_TO_OBJECT(argv[0]);
3374 8 : return FunctionType::ConstructData(cx, baseObj, result, fnObj, thisObj, errVal);
3375 : }
3376 :
3377 : JSObject*
3378 9095 : PointerType::GetBaseType(JSObject* obj)
3379 : {
3380 9095 : JS_ASSERT(CType::GetTypeCode(obj) == TYPE_pointer);
3381 :
3382 9095 : jsval type = JS_GetReservedSlot(obj, SLOT_TARGET_T);
3383 9095 : JS_ASSERT(!JSVAL_IS_NULL(type));
3384 9095 : return JSVAL_TO_OBJECT(type);
3385 : }
3386 :
3387 : JSBool
3388 8 : PointerType::TargetTypeGetter(JSContext* cx,
3389 : JSObject* obj,
3390 : jsid idval,
3391 : jsval* vp)
3392 : {
3393 8 : if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_pointer) {
3394 1 : JS_ReportError(cx, "not a PointerType");
3395 1 : return JS_FALSE;
3396 : }
3397 :
3398 7 : *vp = JS_GetReservedSlot(obj, SLOT_TARGET_T);
3399 7 : JS_ASSERT(JSVAL_IS_OBJECT(*vp));
3400 7 : return JS_TRUE;
3401 : }
3402 :
3403 : JSBool
3404 3442 : PointerType::IsNull(JSContext* cx, unsigned argc, jsval* vp)
3405 : {
3406 3442 : JSObject* obj = JS_THIS_OBJECT(cx, vp);
3407 3442 : if (!obj || !CData::IsCData(obj)) {
3408 2 : JS_ReportError(cx, "not a CData");
3409 2 : return JS_FALSE;
3410 : }
3411 :
3412 : // Get pointer type and base type.
3413 3440 : JSObject* typeObj = CData::GetCType(obj);
3414 3440 : if (CType::GetTypeCode(typeObj) != TYPE_pointer) {
3415 0 : JS_ReportError(cx, "not a PointerType");
3416 0 : return JS_FALSE;
3417 : }
3418 :
3419 3440 : void* data = *static_cast<void**>(CData::GetData(obj));
3420 3440 : jsval result = BOOLEAN_TO_JSVAL(data == NULL);
3421 3440 : JS_SET_RVAL(cx, vp, result);
3422 3440 : return JS_TRUE;
3423 : }
3424 :
3425 : JSBool
3426 6 : PointerType::OffsetBy(JSContext* cx, int offset, jsval* vp)
3427 : {
3428 6 : JSObject* obj = JS_THIS_OBJECT(cx, vp);
3429 6 : if (!obj || !CData::IsCData(obj)) {
3430 4 : JS_ReportError(cx, "not a CData");
3431 4 : return JS_FALSE;
3432 : }
3433 :
3434 2 : JSObject* typeObj = CData::GetCType(obj);
3435 2 : if (CType::GetTypeCode(typeObj) != TYPE_pointer) {
3436 0 : JS_ReportError(cx, "not a PointerType");
3437 0 : return JS_FALSE;
3438 : }
3439 :
3440 2 : JSObject* baseType = PointerType::GetBaseType(typeObj);
3441 2 : if (!CType::IsSizeDefined(baseType)) {
3442 0 : JS_ReportError(cx, "cannot modify pointer of undefined size");
3443 0 : return JS_FALSE;
3444 : }
3445 :
3446 2 : size_t elementSize = CType::GetSize(baseType);
3447 2 : char* data = static_cast<char*>(*static_cast<void**>(CData::GetData(obj)));
3448 2 : void* address = data + offset * elementSize;
3449 :
3450 : // Create a PointerType CData object containing the new address.
3451 2 : JSObject* result = CData::Create(cx, typeObj, NULL, &address, true);
3452 2 : if (!result)
3453 0 : return JS_FALSE;
3454 :
3455 2 : JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
3456 2 : return JS_TRUE;
3457 : }
3458 :
3459 : JSBool
3460 3 : PointerType::Increment(JSContext* cx, unsigned argc, jsval* vp)
3461 : {
3462 3 : return OffsetBy(cx, 1, vp);
3463 : }
3464 :
3465 : JSBool
3466 3 : PointerType::Decrement(JSContext* cx, unsigned argc, jsval* vp)
3467 : {
3468 3 : return OffsetBy(cx, -1, vp);
3469 : }
3470 :
3471 : JSBool
3472 1701 : PointerType::ContentsGetter(JSContext* cx,
3473 : JSObject* obj,
3474 : jsid idval,
3475 : jsval* vp)
3476 : {
3477 1701 : if (!CData::IsCData(obj)) {
3478 2 : JS_ReportError(cx, "not a CData");
3479 2 : return JS_FALSE;
3480 : }
3481 :
3482 : // Get pointer type and base type.
3483 1699 : JSObject* typeObj = CData::GetCType(obj);
3484 1699 : if (CType::GetTypeCode(typeObj) != TYPE_pointer) {
3485 0 : JS_ReportError(cx, "not a PointerType");
3486 0 : return JS_FALSE;
3487 : }
3488 :
3489 1699 : JSObject* baseType = GetBaseType(typeObj);
3490 1699 : if (!CType::IsSizeDefined(baseType)) {
3491 2 : JS_ReportError(cx, "cannot get contents of undefined size");
3492 2 : return JS_FALSE;
3493 : }
3494 :
3495 1697 : void* data = *static_cast<void**>(CData::GetData(obj));
3496 1697 : if (data == NULL) {
3497 1 : JS_ReportError(cx, "cannot read contents of null pointer");
3498 1 : return JS_FALSE;
3499 : }
3500 :
3501 : jsval result;
3502 1696 : if (!ConvertToJS(cx, baseType, NULL, data, false, false, &result))
3503 0 : return JS_FALSE;
3504 :
3505 1696 : JS_SET_RVAL(cx, vp, result);
3506 1696 : return JS_TRUE;
3507 : }
3508 :
3509 : JSBool
3510 4 : PointerType::ContentsSetter(JSContext* cx,
3511 : JSObject* obj,
3512 : jsid idval,
3513 : JSBool strict,
3514 : jsval* vp)
3515 : {
3516 4 : if (!CData::IsCData(obj)) {
3517 0 : JS_ReportError(cx, "not a CData");
3518 0 : return JS_FALSE;
3519 : }
3520 :
3521 : // Get pointer type and base type.
3522 4 : JSObject* typeObj = CData::GetCType(obj);
3523 4 : if (CType::GetTypeCode(typeObj) != TYPE_pointer) {
3524 0 : JS_ReportError(cx, "not a PointerType");
3525 0 : return JS_FALSE;
3526 : }
3527 :
3528 4 : JSObject* baseType = GetBaseType(typeObj);
3529 4 : if (!CType::IsSizeDefined(baseType)) {
3530 2 : JS_ReportError(cx, "cannot set contents of undefined size");
3531 2 : return JS_FALSE;
3532 : }
3533 :
3534 2 : void* data = *static_cast<void**>(CData::GetData(obj));
3535 2 : if (data == NULL) {
3536 1 : JS_ReportError(cx, "cannot write contents to null pointer");
3537 1 : return JS_FALSE;
3538 : }
3539 :
3540 1 : return ImplicitConvert(cx, *vp, baseType, data, false, NULL);
3541 : }
3542 :
3543 : /*******************************************************************************
3544 : ** ArrayType implementation
3545 : *******************************************************************************/
3546 :
3547 : JSBool
3548 436 : ArrayType::Create(JSContext* cx, unsigned argc, jsval* vp)
3549 : {
3550 : // Construct and return a new ArrayType object.
3551 436 : if (argc < 1 || argc > 2) {
3552 2 : JS_ReportError(cx, "ArrayType takes one or two arguments");
3553 2 : return JS_FALSE;
3554 : }
3555 :
3556 434 : jsval* argv = JS_ARGV(cx, vp);
3557 867 : if (JSVAL_IS_PRIMITIVE(argv[0]) ||
3558 433 : !CType::IsCType(JSVAL_TO_OBJECT(argv[0]))) {
3559 1 : JS_ReportError(cx, "first argument must be a CType");
3560 1 : return JS_FALSE;
3561 : }
3562 :
3563 : // Convert the length argument to a size_t.
3564 433 : size_t length = 0;
3565 433 : if (argc == 2 && !jsvalToSize(cx, argv[1], false, &length)) {
3566 2 : JS_ReportError(cx, "second argument must be a nonnegative integer");
3567 2 : return JS_FALSE;
3568 : }
3569 :
3570 431 : JSObject* baseType = JSVAL_TO_OBJECT(argv[0]);
3571 431 : JSObject* result = CreateInternal(cx, baseType, length, argc == 2);
3572 431 : if (!result)
3573 3 : return JS_FALSE;
3574 :
3575 428 : JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
3576 428 : return JS_TRUE;
3577 : }
3578 :
3579 : JSObject*
3580 2712 : ArrayType::CreateInternal(JSContext* cx,
3581 : JSObject* baseType,
3582 : size_t length,
3583 : bool lengthDefined)
3584 : {
3585 : // Get ctypes.ArrayType.prototype and the common prototype for CData objects
3586 : // of this type, from ctypes.CType.prototype.
3587 2712 : JSObject* typeProto = CType::GetProtoFromType(baseType, SLOT_ARRAYPROTO);
3588 2712 : JSObject* dataProto = CType::GetProtoFromType(baseType, SLOT_ARRAYDATAPROTO);
3589 :
3590 : // Determine the size of the array from the base type, if possible.
3591 : // The size of the base type must be defined.
3592 : // If our length is undefined, both our size and length will be undefined.
3593 : size_t baseSize;
3594 2712 : if (!CType::GetSafeSize(baseType, &baseSize)) {
3595 3 : JS_ReportError(cx, "base size must be defined");
3596 3 : return NULL;
3597 : }
3598 :
3599 2709 : jsval sizeVal = JSVAL_VOID;
3600 2709 : jsval lengthVal = JSVAL_VOID;
3601 2709 : if (lengthDefined) {
3602 : // Check for overflow, and convert to an int or double as required.
3603 2360 : size_t size = length * baseSize;
3604 2360 : if (length > 0 && size / length != baseSize) {
3605 2 : JS_ReportError(cx, "size overflow");
3606 2 : return NULL;
3607 : }
3608 4716 : if (!SizeTojsval(cx, size, &sizeVal) ||
3609 2358 : !SizeTojsval(cx, length, &lengthVal))
3610 0 : return NULL;
3611 : }
3612 :
3613 2707 : size_t align = CType::GetAlignment(baseType);
3614 :
3615 : // Create a new CType object with the common properties and slots.
3616 : JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_array, NULL,
3617 2707 : sizeVal, INT_TO_JSVAL(align), NULL);
3618 2707 : if (!typeObj)
3619 0 : return NULL;
3620 :
3621 : // Set the element type.
3622 2707 : JS_SetReservedSlot(typeObj, SLOT_ELEMENT_T, OBJECT_TO_JSVAL(baseType));
3623 :
3624 : // Set the length.
3625 2707 : JS_SetReservedSlot(typeObj, SLOT_LENGTH, lengthVal);
3626 :
3627 2707 : return typeObj;
3628 : }
3629 :
3630 : JSBool
3631 481 : ArrayType::ConstructData(JSContext* cx,
3632 : JSObject* obj,
3633 : unsigned argc,
3634 : jsval* vp)
3635 : {
3636 481 : if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_array) {
3637 0 : JS_ReportError(cx, "not an ArrayType");
3638 0 : return JS_FALSE;
3639 : }
3640 :
3641 : // Decide whether we have an object to initialize from. We'll override this
3642 : // if we get a length argument instead.
3643 481 : bool convertObject = argc == 1;
3644 :
3645 : // Check if we're an array of undefined length. If we are, allow construction
3646 : // with a length argument, or with an actual JS array.
3647 481 : if (CType::IsSizeDefined(obj)) {
3648 223 : if (argc > 1) {
3649 0 : JS_ReportError(cx, "constructor takes zero or one argument");
3650 0 : return JS_FALSE;
3651 : }
3652 :
3653 : } else {
3654 258 : if (argc != 1) {
3655 1 : JS_ReportError(cx, "constructor takes one argument");
3656 1 : return JS_FALSE;
3657 : }
3658 :
3659 257 : JSObject* baseType = GetBaseType(obj);
3660 :
3661 257 : jsval* argv = JS_ARGV(cx, vp);
3662 : size_t length;
3663 257 : if (jsvalToSize(cx, argv[0], false, &length)) {
3664 : // Have a length, rather than an object to initialize from.
3665 1 : convertObject = false;
3666 :
3667 256 : } else if (!JSVAL_IS_PRIMITIVE(argv[0])) {
3668 : // We were given an object with a .length property.
3669 : // This could be a JS array, or a CData array.
3670 0 : JSObject* arg = JSVAL_TO_OBJECT(argv[0]);
3671 0 : js::AutoValueRooter lengthVal(cx);
3672 0 : if (!JS_GetProperty(cx, arg, "length", lengthVal.jsval_addr()) ||
3673 0 : !jsvalToSize(cx, lengthVal.jsval_value(), false, &length)) {
3674 0 : JS_ReportError(cx, "argument must be an array object or length");
3675 0 : return JS_FALSE;
3676 : }
3677 :
3678 256 : } else if (JSVAL_IS_STRING(argv[0])) {
3679 : // We were given a string. Size the array to the appropriate length,
3680 : // including space for the terminator.
3681 256 : JSString* sourceString = JSVAL_TO_STRING(argv[0]);
3682 256 : size_t sourceLength = sourceString->length();
3683 256 : const jschar* sourceChars = sourceString->getChars(cx);
3684 256 : if (!sourceChars)
3685 0 : return false;
3686 :
3687 256 : switch (CType::GetTypeCode(baseType)) {
3688 : case TYPE_char:
3689 : case TYPE_signed_char:
3690 : case TYPE_unsigned_char: {
3691 : // Determine the UTF-8 length.
3692 255 : length = GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength);
3693 255 : if (length == (size_t) -1)
3694 0 : return false;
3695 :
3696 255 : ++length;
3697 255 : break;
3698 : }
3699 : case TYPE_jschar:
3700 1 : length = sourceLength + 1;
3701 1 : break;
3702 : default:
3703 0 : return TypeError(cx, "array", argv[0]);
3704 : }
3705 :
3706 : } else {
3707 0 : JS_ReportError(cx, "argument must be an array object or length");
3708 0 : return JS_FALSE;
3709 : }
3710 :
3711 : // Construct a new ArrayType of defined length, for the new CData object.
3712 257 : obj = CreateInternal(cx, baseType, length, true);
3713 257 : if (!obj)
3714 0 : return JS_FALSE;
3715 : }
3716 :
3717 : // Root the CType object, in case we created one above.
3718 960 : js::AutoObjectRooter root(cx, obj);
3719 :
3720 480 : JSObject* result = CData::Create(cx, obj, NULL, NULL, true);
3721 480 : if (!result)
3722 0 : return JS_FALSE;
3723 :
3724 480 : JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
3725 :
3726 480 : if (convertObject) {
3727 272 : if (!ExplicitConvert(cx, JS_ARGV(cx, vp)[0], obj, CData::GetData(result)))
3728 4 : return JS_FALSE;
3729 : }
3730 :
3731 476 : return JS_TRUE;
3732 : }
3733 :
3734 : JSObject*
3735 149703 : ArrayType::GetBaseType(JSObject* obj)
3736 : {
3737 149703 : JS_ASSERT(CType::IsCType(obj));
3738 149703 : JS_ASSERT(CType::GetTypeCode(obj) == TYPE_array);
3739 :
3740 149703 : jsval type = JS_GetReservedSlot(obj, SLOT_ELEMENT_T);
3741 149703 : JS_ASSERT(!JSVAL_IS_NULL(type));
3742 149703 : return JSVAL_TO_OBJECT(type);
3743 : }
3744 :
3745 : bool
3746 94 : ArrayType::GetSafeLength(JSObject* obj, size_t* result)
3747 : {
3748 94 : JS_ASSERT(CType::IsCType(obj));
3749 94 : JS_ASSERT(CType::GetTypeCode(obj) == TYPE_array);
3750 :
3751 94 : jsval length = JS_GetReservedSlot(obj, SLOT_LENGTH);
3752 :
3753 : // The "length" property can be an int, a double, or JSVAL_VOID
3754 : // (for arrays of undefined length), and must always fit in a size_t.
3755 94 : if (JSVAL_IS_INT(length)) {
3756 49 : *result = JSVAL_TO_INT(length);
3757 49 : return true;
3758 : }
3759 45 : if (JSVAL_IS_DOUBLE(length)) {
3760 0 : *result = Convert<size_t>(JSVAL_TO_DOUBLE(length));
3761 0 : return true;
3762 : }
3763 :
3764 45 : JS_ASSERT(JSVAL_IS_VOID(length));
3765 45 : return false;
3766 : }
3767 :
3768 : size_t
3769 148229 : ArrayType::GetLength(JSObject* obj)
3770 : {
3771 148229 : JS_ASSERT(CType::IsCType(obj));
3772 148229 : JS_ASSERT(CType::GetTypeCode(obj) == TYPE_array);
3773 :
3774 148229 : jsval length = JS_GetReservedSlot(obj, SLOT_LENGTH);
3775 :
3776 148229 : JS_ASSERT(!JSVAL_IS_VOID(length));
3777 :
3778 : // The "length" property can be an int, a double, or JSVAL_VOID
3779 : // (for arrays of undefined length), and must always fit in a size_t.
3780 : // For callers who know it can never be JSVAL_VOID, return a size_t directly.
3781 148229 : if (JSVAL_IS_INT(length))
3782 148229 : return JSVAL_TO_INT(length);
3783 0 : return Convert<size_t>(JSVAL_TO_DOUBLE(length));
3784 : }
3785 :
3786 : ffi_type*
3787 0 : ArrayType::BuildFFIType(JSContext* cx, JSObject* obj)
3788 : {
3789 0 : JS_ASSERT(CType::IsCType(obj));
3790 0 : JS_ASSERT(CType::GetTypeCode(obj) == TYPE_array);
3791 0 : JS_ASSERT(CType::IsSizeDefined(obj));
3792 :
3793 0 : JSObject* baseType = ArrayType::GetBaseType(obj);
3794 0 : ffi_type* ffiBaseType = CType::GetFFIType(cx, baseType);
3795 0 : if (!ffiBaseType)
3796 0 : return NULL;
3797 :
3798 0 : size_t length = ArrayType::GetLength(obj);
3799 :
3800 : // Create an ffi_type to represent the array. This is necessary for the case
3801 : // where the array is part of a struct. Since libffi has no intrinsic
3802 : // support for array types, we approximate it by creating a struct type
3803 : // with elements of type 'baseType' and with appropriate size and alignment
3804 : // values. It would be nice to not do all the work of setting up 'elements',
3805 : // but some libffi platforms currently require that it be meaningful. I'm
3806 : // looking at you, x86_64.
3807 0 : AutoPtr<ffi_type> ffiType(cx->new_<ffi_type>());
3808 0 : if (!ffiType) {
3809 0 : JS_ReportOutOfMemory(cx);
3810 0 : return NULL;
3811 : }
3812 :
3813 0 : ffiType->type = FFI_TYPE_STRUCT;
3814 0 : ffiType->size = CType::GetSize(obj);
3815 0 : ffiType->alignment = CType::GetAlignment(obj);
3816 0 : ffiType->elements = cx->array_new<ffi_type*>(length + 1);
3817 0 : if (!ffiType->elements) {
3818 0 : JS_ReportAllocationOverflow(cx);
3819 0 : return NULL;
3820 : }
3821 :
3822 0 : for (size_t i = 0; i < length; ++i)
3823 0 : ffiType->elements[i] = ffiBaseType;
3824 0 : ffiType->elements[length] = NULL;
3825 :
3826 0 : return ffiType.forget();
3827 : }
3828 :
3829 : JSBool
3830 2 : ArrayType::ElementTypeGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
3831 : {
3832 2 : if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_array) {
3833 1 : JS_ReportError(cx, "not an ArrayType");
3834 1 : return JS_FALSE;
3835 : }
3836 :
3837 1 : *vp = JS_GetReservedSlot(obj, SLOT_ELEMENT_T);
3838 1 : JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
3839 1 : return JS_TRUE;
3840 : }
3841 :
3842 : JSBool
3843 516 : ArrayType::LengthGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
3844 : {
3845 : // This getter exists for both CTypes and CDatas of the ArrayType persuasion.
3846 : // If we're dealing with a CData, get the CType from it.
3847 516 : if (CData::IsCData(obj))
3848 504 : obj = CData::GetCType(obj);
3849 :
3850 516 : if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_array) {
3851 3 : JS_ReportError(cx, "not an ArrayType");
3852 3 : return JS_FALSE;
3853 : }
3854 :
3855 513 : *vp = JS_GetReservedSlot(obj, SLOT_LENGTH);
3856 513 : JS_ASSERT(JSVAL_IS_NUMBER(*vp) || JSVAL_IS_VOID(*vp));
3857 513 : return JS_TRUE;
3858 : }
3859 :
3860 : JSBool
3861 122212 : ArrayType::Getter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
3862 : {
3863 : // This should never happen, but we'll check to be safe.
3864 122212 : if (!CData::IsCData(obj)) {
3865 0 : JS_ReportError(cx, "not a CData");
3866 0 : return JS_FALSE;
3867 : }
3868 :
3869 : // Bail early if we're not an ArrayType. (This setter is present for all
3870 : // CData, regardless of CType.)
3871 122212 : JSObject* typeObj = CData::GetCType(obj);
3872 122212 : if (CType::GetTypeCode(typeObj) != TYPE_array)
3873 1 : return JS_TRUE;
3874 :
3875 : // Convert the index to a size_t and bounds-check it.
3876 : size_t index;
3877 122211 : size_t length = GetLength(typeObj);
3878 122211 : bool ok = jsidToSize(cx, idval, true, &index);
3879 122211 : if (!ok && JSID_IS_STRING(idval)) {
3880 : // String either isn't a number, or doesn't fit in size_t.
3881 : // Chances are it's a regular property lookup, so return.
3882 0 : return JS_TRUE;
3883 : }
3884 122211 : if (!ok || index >= length) {
3885 2 : JS_ReportError(cx, "invalid index");
3886 2 : return JS_FALSE;
3887 : }
3888 :
3889 122209 : JSObject* baseType = GetBaseType(typeObj);
3890 122209 : size_t elementSize = CType::GetSize(baseType);
3891 122209 : char* data = static_cast<char*>(CData::GetData(obj)) + elementSize * index;
3892 122209 : return ConvertToJS(cx, baseType, obj, data, false, false, vp);
3893 : }
3894 :
3895 : JSBool
3896 24146 : ArrayType::Setter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict, jsval* vp)
3897 : {
3898 : // This should never happen, but we'll check to be safe.
3899 24146 : if (!CData::IsCData(obj)) {
3900 0 : JS_ReportError(cx, "not a CData");
3901 0 : return JS_FALSE;
3902 : }
3903 :
3904 : // Bail early if we're not an ArrayType. (This setter is present for all
3905 : // CData, regardless of CType.)
3906 24146 : JSObject* typeObj = CData::GetCType(obj);
3907 24146 : if (CType::GetTypeCode(typeObj) != TYPE_array)
3908 0 : return JS_TRUE;
3909 :
3910 : // Convert the index to a size_t and bounds-check it.
3911 : size_t index;
3912 24146 : size_t length = GetLength(typeObj);
3913 24146 : bool ok = jsidToSize(cx, idval, true, &index);
3914 24146 : if (!ok && JSID_IS_STRING(idval)) {
3915 : // String either isn't a number, or doesn't fit in size_t.
3916 : // Chances are it's a regular property lookup, so return.
3917 0 : return JS_TRUE;
3918 : }
3919 24146 : if (!ok || index >= length) {
3920 0 : JS_ReportError(cx, "invalid index");
3921 0 : return JS_FALSE;
3922 : }
3923 :
3924 24146 : JSObject* baseType = GetBaseType(typeObj);
3925 24146 : size_t elementSize = CType::GetSize(baseType);
3926 24146 : char* data = static_cast<char*>(CData::GetData(obj)) + elementSize * index;
3927 24146 : return ImplicitConvert(cx, *vp, baseType, data, false, NULL);
3928 : }
3929 :
3930 : JSBool
3931 1462 : ArrayType::AddressOfElement(JSContext* cx, unsigned argc, jsval* vp)
3932 : {
3933 1462 : JSObject* obj = JS_THIS_OBJECT(cx, vp);
3934 1462 : if (!obj || !CData::IsCData(obj)) {
3935 2 : JS_ReportError(cx, "not a CData");
3936 2 : return JS_FALSE;
3937 : }
3938 :
3939 1460 : JSObject* typeObj = CData::GetCType(obj);
3940 1460 : if (CType::GetTypeCode(typeObj) != TYPE_array) {
3941 0 : JS_ReportError(cx, "not an ArrayType");
3942 0 : return JS_FALSE;
3943 : }
3944 :
3945 1460 : if (argc != 1) {
3946 0 : JS_ReportError(cx, "addressOfElement takes one argument");
3947 0 : return JS_FALSE;
3948 : }
3949 :
3950 1460 : JSObject* baseType = GetBaseType(typeObj);
3951 1460 : JSObject* pointerType = PointerType::CreateInternal(cx, baseType);
3952 1460 : if (!pointerType)
3953 0 : return JS_FALSE;
3954 2920 : js::AutoObjectRooter root(cx, pointerType);
3955 :
3956 : // Create a PointerType CData object containing null.
3957 1460 : JSObject* result = CData::Create(cx, pointerType, NULL, NULL, true);
3958 1460 : if (!result)
3959 0 : return JS_FALSE;
3960 :
3961 1460 : JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
3962 :
3963 : // Convert the index to a size_t and bounds-check it.
3964 : size_t index;
3965 1460 : size_t length = GetLength(typeObj);
3966 1460 : if (!jsvalToSize(cx, JS_ARGV(cx, vp)[0], false, &index) ||
3967 : index >= length) {
3968 0 : JS_ReportError(cx, "invalid index");
3969 0 : return JS_FALSE;
3970 : }
3971 :
3972 : // Manually set the pointer inside the object, so we skip the conversion step.
3973 1460 : void** data = static_cast<void**>(CData::GetData(result));
3974 1460 : size_t elementSize = CType::GetSize(baseType);
3975 1460 : *data = static_cast<char*>(CData::GetData(obj)) + elementSize * index;
3976 1460 : return JS_TRUE;
3977 : }
3978 :
3979 : /*******************************************************************************
3980 : ** StructType implementation
3981 : *******************************************************************************/
3982 :
3983 : // For a struct field descriptor 'val' of the form { name : type }, extract
3984 : // 'name' and 'type'.
3985 : static JSFlatString*
3986 562 : ExtractStructField(JSContext* cx, jsval val, JSObject** typeObj)
3987 : {
3988 562 : if (JSVAL_IS_PRIMITIVE(val)) {
3989 1 : JS_ReportError(cx, "struct field descriptors require a valid name and type");
3990 1 : return NULL;
3991 : }
3992 :
3993 561 : JSObject* obj = JSVAL_TO_OBJECT(val);
3994 561 : JSObject* iter = JS_NewPropertyIterator(cx, obj);
3995 561 : if (!iter)
3996 0 : return NULL;
3997 1122 : js::AutoObjectRooter iterroot(cx, iter);
3998 :
3999 : jsid nameid;
4000 561 : if (!JS_NextProperty(cx, iter, &nameid))
4001 0 : return NULL;
4002 561 : if (JSID_IS_VOID(nameid)) {
4003 2 : JS_ReportError(cx, "struct field descriptors require a valid name and type");
4004 2 : return NULL;
4005 : }
4006 :
4007 559 : if (!JSID_IS_STRING(nameid)) {
4008 2 : JS_ReportError(cx, "struct field descriptors require a valid name and type");
4009 2 : return NULL;
4010 : }
4011 :
4012 : // make sure we have one, and only one, property
4013 : jsid id;
4014 557 : if (!JS_NextProperty(cx, iter, &id))
4015 0 : return NULL;
4016 557 : if (!JSID_IS_VOID(id)) {
4017 1 : JS_ReportError(cx, "struct field descriptors must contain one property");
4018 1 : return NULL;
4019 : }
4020 :
4021 1112 : js::AutoValueRooter propVal(cx);
4022 556 : if (!JS_GetPropertyById(cx, obj, nameid, propVal.jsval_addr()))
4023 0 : return NULL;
4024 :
4025 1109 : if (propVal.value().isPrimitive() ||
4026 553 : !CType::IsCType(JSVAL_TO_OBJECT(propVal.jsval_value()))) {
4027 4 : JS_ReportError(cx, "struct field descriptors require a valid name and type");
4028 4 : return NULL;
4029 : }
4030 :
4031 : // Undefined size or zero size struct members are illegal.
4032 : // (Zero-size arrays are legal as struct members in C++, but libffi will
4033 : // choke on a zero-size struct, so we disallow them.)
4034 552 : *typeObj = JSVAL_TO_OBJECT(propVal.jsval_value());
4035 : size_t size;
4036 552 : if (!CType::GetSafeSize(*typeObj, &size) || size == 0) {
4037 3 : JS_ReportError(cx, "struct field types must have defined and nonzero size");
4038 3 : return NULL;
4039 : }
4040 :
4041 549 : return JSID_TO_FLAT_STRING(nameid);
4042 : }
4043 :
4044 : // For a struct field with 'name' and 'type', add an element of the form
4045 : // { name : type }.
4046 : static JSBool
4047 4 : AddFieldToArray(JSContext* cx,
4048 : jsval* element,
4049 : JSFlatString* name,
4050 : JSObject* typeObj)
4051 : {
4052 4 : JSObject* fieldObj = JS_NewObject(cx, NULL, NULL, NULL);
4053 4 : if (!fieldObj)
4054 0 : return false;
4055 :
4056 4 : *element = OBJECT_TO_JSVAL(fieldObj);
4057 :
4058 4 : if (!JS_DefineUCProperty(cx, fieldObj,
4059 : name->chars(), name->length(),
4060 : OBJECT_TO_JSVAL(typeObj), NULL, NULL,
4061 4 : JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
4062 0 : return false;
4063 :
4064 4 : return JS_FreezeObject(cx, fieldObj);
4065 : }
4066 :
4067 : JSBool
4068 235 : StructType::Create(JSContext* cx, unsigned argc, jsval* vp)
4069 : {
4070 : // Construct and return a new StructType object.
4071 235 : if (argc < 1 || argc > 2) {
4072 2 : JS_ReportError(cx, "StructType takes one or two arguments");
4073 2 : return JS_FALSE;
4074 : }
4075 :
4076 233 : jsval* argv = JS_ARGV(cx, vp);
4077 233 : jsval name = argv[0];
4078 233 : if (!JSVAL_IS_STRING(name)) {
4079 1 : JS_ReportError(cx, "first argument must be a string");
4080 1 : return JS_FALSE;
4081 : }
4082 :
4083 : // Get ctypes.StructType.prototype from the ctypes.StructType constructor.
4084 232 : JSObject* callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
4085 232 : JSObject* typeProto = CType::GetProtoFromCtor(callee, SLOT_STRUCTPROTO);
4086 :
4087 : // Create a simple StructType with no defined fields. The result will be
4088 : // non-instantiable as CData, will have no 'prototype' property, and will
4089 : // have undefined size and alignment and no ffi_type.
4090 : JSObject* result = CType::Create(cx, typeProto, NULL, TYPE_struct,
4091 232 : JSVAL_TO_STRING(name), JSVAL_VOID, JSVAL_VOID, NULL);
4092 232 : if (!result)
4093 0 : return JS_FALSE;
4094 464 : js::AutoObjectRooter root(cx, result);
4095 :
4096 232 : if (argc == 2) {
4097 457 : if (JSVAL_IS_PRIMITIVE(argv[1]) ||
4098 228 : !JS_IsArrayObject(cx, JSVAL_TO_OBJECT(argv[1]))) {
4099 1 : JS_ReportError(cx, "second argument must be an array");
4100 1 : return JS_FALSE;
4101 : }
4102 :
4103 : // Define the struct fields.
4104 228 : if (!DefineInternal(cx, result, JSVAL_TO_OBJECT(argv[1])))
4105 11 : return JS_FALSE;
4106 : }
4107 :
4108 220 : JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
4109 220 : return JS_TRUE;
4110 : }
4111 :
4112 : JSBool
4113 234 : StructType::DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj)
4114 : {
4115 : uint32_t len;
4116 234 : ASSERT_OK(JS_GetArrayLength(cx, fieldsObj, &len));
4117 :
4118 : // Get the common prototype for CData objects of this type from
4119 : // ctypes.CType.prototype.
4120 234 : JSObject* dataProto = CType::GetProtoFromType(typeObj, SLOT_STRUCTDATAPROTO);
4121 :
4122 : // Set up the 'prototype' and 'prototype.constructor' properties.
4123 : // The prototype will reflect the struct fields as properties on CData objects
4124 : // created from this type.
4125 234 : JSObject* prototype = JS_NewObject(cx, &sCDataProtoClass, dataProto, NULL);
4126 234 : if (!prototype)
4127 0 : return JS_FALSE;
4128 468 : js::AutoObjectRooter protoroot(cx, prototype);
4129 :
4130 234 : if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(typeObj),
4131 234 : NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT))
4132 0 : return JS_FALSE;
4133 :
4134 : // Create a FieldInfoHash to stash on the type object, and an array to root
4135 : // its constituents. (We cannot simply stash the hash in a reserved slot now
4136 : // to get GC safety for free, since if anything in this function fails we
4137 : // do not want to mutate 'typeObj'.)
4138 468 : AutoPtr<FieldInfoHash> fields(cx->new_<FieldInfoHash>());
4139 468 : Array<jsval, 16> fieldRootsArray;
4140 234 : if (!fields || !fields->init(len) || !fieldRootsArray.appendN(JSVAL_VOID, len)) {
4141 0 : JS_ReportOutOfMemory(cx);
4142 0 : return JS_FALSE;
4143 : }
4144 : js::AutoArrayRooter fieldRoots(cx, fieldRootsArray.length(),
4145 468 : fieldRootsArray.begin());
4146 :
4147 : // Process the field types.
4148 : size_t structSize, structAlign;
4149 234 : if (len != 0) {
4150 233 : structSize = 0;
4151 233 : structAlign = 0;
4152 :
4153 780 : for (uint32_t i = 0; i < len; ++i) {
4154 1124 : js::AutoValueRooter item(cx);
4155 562 : if (!JS_GetElement(cx, fieldsObj, i, item.jsval_addr()))
4156 0 : return JS_FALSE;
4157 :
4158 562 : JSObject* fieldType = NULL;
4159 562 : JSFlatString* name = ExtractStructField(cx, item.jsval_value(), &fieldType);
4160 562 : if (!name)
4161 13 : return JS_FALSE;
4162 549 : fieldRootsArray[i] = OBJECT_TO_JSVAL(fieldType);
4163 :
4164 : // Make sure each field name is unique, and add it to the hash.
4165 549 : FieldInfoHash::AddPtr entryPtr = fields->lookupForAdd(name);
4166 549 : if (entryPtr) {
4167 1 : JS_ReportError(cx, "struct fields must have unique names");
4168 1 : return JS_FALSE;
4169 : }
4170 548 : ASSERT_OK(fields->add(entryPtr, name, FieldInfo()));
4171 548 : FieldInfo& info = entryPtr->value;
4172 548 : info.mType = fieldType;
4173 548 : info.mIndex = i;
4174 :
4175 : // Add the field to the StructType's 'prototype' property.
4176 548 : if (!JS_DefineUCProperty(cx, prototype,
4177 : name->chars(), name->length(), JSVAL_VOID,
4178 : StructType::FieldGetter, StructType::FieldSetter,
4179 548 : JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_PERMANENT))
4180 0 : return JS_FALSE;
4181 :
4182 548 : size_t fieldSize = CType::GetSize(fieldType);
4183 548 : size_t fieldAlign = CType::GetAlignment(fieldType);
4184 548 : size_t fieldOffset = Align(structSize, fieldAlign);
4185 : // Check for overflow. Since we hold invariant that fieldSize % fieldAlign
4186 : // be zero, we can safely check fieldOffset + fieldSize without first
4187 : // checking fieldOffset for overflow.
4188 548 : if (fieldOffset + fieldSize < structSize) {
4189 1 : JS_ReportError(cx, "size overflow");
4190 1 : return JS_FALSE;
4191 : }
4192 547 : info.mOffset = fieldOffset;
4193 547 : structSize = fieldOffset + fieldSize;
4194 :
4195 547 : if (fieldAlign > structAlign)
4196 288 : structAlign = fieldAlign;
4197 : }
4198 :
4199 : // Pad the struct tail according to struct alignment.
4200 218 : size_t structTail = Align(structSize, structAlign);
4201 218 : if (structTail < structSize) {
4202 1 : JS_ReportError(cx, "size overflow");
4203 1 : return JS_FALSE;
4204 : }
4205 217 : structSize = structTail;
4206 :
4207 : } else {
4208 : // Empty structs are illegal in C, but are legal and have a size of
4209 : // 1 byte in C++. We're going to allow them, and trick libffi into
4210 : // believing this by adding a char member. The resulting struct will have
4211 : // no getters or setters, and will be initialized to zero.
4212 1 : structSize = 1;
4213 1 : structAlign = 1;
4214 : }
4215 :
4216 : jsval sizeVal;
4217 218 : if (!SizeTojsval(cx, structSize, &sizeVal))
4218 0 : return JS_FALSE;
4219 :
4220 218 : JS_SetReservedSlot(typeObj, SLOT_FIELDINFO, PRIVATE_TO_JSVAL(fields.forget()));
4221 :
4222 218 : JS_SetReservedSlot(typeObj, SLOT_SIZE, sizeVal);
4223 218 : JS_SetReservedSlot(typeObj, SLOT_ALIGN, INT_TO_JSVAL(structAlign));
4224 : //if (!JS_FreezeObject(cx, prototype)0 // XXX fixme - see bug 541212!
4225 : // return false;
4226 218 : JS_SetReservedSlot(typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype));
4227 218 : return JS_TRUE;
4228 : }
4229 :
4230 : ffi_type*
4231 13 : StructType::BuildFFIType(JSContext* cx, JSObject* obj)
4232 : {
4233 13 : JS_ASSERT(CType::IsCType(obj));
4234 13 : JS_ASSERT(CType::GetTypeCode(obj) == TYPE_struct);
4235 13 : JS_ASSERT(CType::IsSizeDefined(obj));
4236 :
4237 13 : const FieldInfoHash* fields = GetFieldInfo(obj);
4238 13 : size_t len = fields->count();
4239 :
4240 13 : size_t structSize = CType::GetSize(obj);
4241 13 : size_t structAlign = CType::GetAlignment(obj);
4242 :
4243 26 : AutoPtr<ffi_type> ffiType(cx->new_<ffi_type>());
4244 13 : if (!ffiType) {
4245 0 : JS_ReportOutOfMemory(cx);
4246 0 : return NULL;
4247 : }
4248 13 : ffiType->type = FFI_TYPE_STRUCT;
4249 :
4250 26 : AutoPtr<ffi_type*>::Array elements;
4251 13 : if (len != 0) {
4252 13 : elements = cx->array_new<ffi_type*>(len + 1);
4253 13 : if (!elements) {
4254 0 : JS_ReportOutOfMemory(cx);
4255 0 : return NULL;
4256 : }
4257 13 : elements[len] = NULL;
4258 :
4259 58 : for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
4260 45 : const FieldInfoHash::Entry& entry = r.front();
4261 45 : ffi_type* fieldType = CType::GetFFIType(cx, entry.value.mType);
4262 45 : if (!fieldType)
4263 0 : return NULL;
4264 45 : elements[entry.value.mIndex] = fieldType;
4265 : }
4266 :
4267 : } else {
4268 : // Represent an empty struct as having a size of 1 byte, just like C++.
4269 0 : JS_ASSERT(structSize == 1);
4270 0 : JS_ASSERT(structAlign == 1);
4271 0 : elements = cx->array_new<ffi_type*>(2);
4272 0 : if (!elements) {
4273 0 : JS_ReportOutOfMemory(cx);
4274 0 : return NULL;
4275 : }
4276 0 : elements[0] = &ffi_type_uint8;
4277 0 : elements[1] = NULL;
4278 : }
4279 :
4280 13 : ffiType->elements = elements.get();
4281 :
4282 : #ifdef DEBUG
4283 : // Perform a sanity check: the result of our struct size and alignment
4284 : // calculations should match libffi's. We force it to do this calculation
4285 : // by calling ffi_prep_cif.
4286 : ffi_cif cif;
4287 13 : ffiType->size = 0;
4288 13 : ffiType->alignment = 0;
4289 13 : ffi_status status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 0, ffiType.get(), NULL);
4290 13 : JS_ASSERT(status == FFI_OK);
4291 13 : JS_ASSERT(structSize == ffiType->size);
4292 13 : JS_ASSERT(structAlign == ffiType->alignment);
4293 : #else
4294 : // Fill in the ffi_type's size and align fields. This makes libffi treat the
4295 : // type as initialized; it will not recompute the values. (We assume
4296 : // everything agrees; if it doesn't, we really want to know about it, which
4297 : // is the purpose of the above debug-only check.)
4298 : ffiType->size = structSize;
4299 : ffiType->alignment = structAlign;
4300 : #endif
4301 :
4302 13 : elements.forget();
4303 13 : return ffiType.forget();
4304 : }
4305 :
4306 : JSBool
4307 10 : StructType::Define(JSContext* cx, unsigned argc, jsval* vp)
4308 : {
4309 10 : JSObject* obj = JS_THIS_OBJECT(cx, vp);
4310 29 : if (!obj ||
4311 10 : !CType::IsCType(obj) ||
4312 9 : CType::GetTypeCode(obj) != TYPE_struct) {
4313 1 : JS_ReportError(cx, "not a StructType");
4314 1 : return JS_FALSE;
4315 : }
4316 :
4317 9 : if (CType::IsSizeDefined(obj)) {
4318 1 : JS_ReportError(cx, "StructType has already been defined");
4319 1 : return JS_FALSE;
4320 : }
4321 :
4322 8 : if (argc != 1) {
4323 2 : JS_ReportError(cx, "define takes one argument");
4324 2 : return JS_FALSE;
4325 : }
4326 :
4327 6 : jsval arg = JS_ARGV(cx, vp)[0];
4328 12 : if (JSVAL_IS_PRIMITIVE(arg) ||
4329 6 : !JS_IsArrayObject(cx, JSVAL_TO_OBJECT(arg))) {
4330 0 : JS_ReportError(cx, "argument must be an array");
4331 0 : return JS_FALSE;
4332 : }
4333 :
4334 6 : return DefineInternal(cx, obj, JSVAL_TO_OBJECT(arg));
4335 : }
4336 :
4337 : JSBool
4338 243 : StructType::ConstructData(JSContext* cx,
4339 : JSObject* obj,
4340 : unsigned argc,
4341 : jsval* vp)
4342 : {
4343 243 : if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_struct) {
4344 0 : JS_ReportError(cx, "not a StructType");
4345 0 : return JS_FALSE;
4346 : }
4347 :
4348 243 : if (!CType::IsSizeDefined(obj)) {
4349 1 : JS_ReportError(cx, "cannot construct an opaque StructType");
4350 1 : return JS_FALSE;
4351 : }
4352 :
4353 242 : JSObject* result = CData::Create(cx, obj, NULL, NULL, true);
4354 242 : if (!result)
4355 0 : return JS_FALSE;
4356 :
4357 242 : JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
4358 :
4359 242 : if (argc == 0)
4360 208 : return JS_TRUE;
4361 :
4362 34 : char* buffer = static_cast<char*>(CData::GetData(result));
4363 34 : const FieldInfoHash* fields = GetFieldInfo(obj);
4364 :
4365 34 : jsval* argv = JS_ARGV(cx, vp);
4366 34 : if (argc == 1) {
4367 : // There are two possible interpretations of the argument:
4368 : // 1) It may be an object '{ ... }' with properties representing the
4369 : // struct fields intended to ExplicitConvert wholesale to our StructType.
4370 : // 2) If the struct contains one field, the arg may be intended to
4371 : // ImplicitConvert directly to that arg's CType.
4372 : // Thankfully, the conditions for these two possibilities to succeed
4373 : // are mutually exclusive, so we can pick the right one.
4374 :
4375 : // Try option 1) first.
4376 11 : if (ExplicitConvert(cx, argv[0], obj, buffer))
4377 4 : return JS_TRUE;
4378 :
4379 7 : if (fields->count() != 1)
4380 1 : return JS_FALSE;
4381 :
4382 : // If ExplicitConvert failed, and there is no pending exception, then assume
4383 : // hard failure (out of memory, or some other similarly serious condition).
4384 6 : if (!JS_IsExceptionPending(cx))
4385 0 : return JS_FALSE;
4386 :
4387 : // Otherwise, assume soft failure, and clear the pending exception so that we
4388 : // can throw a different one as required.
4389 6 : JS_ClearPendingException(cx);
4390 :
4391 : // Fall through to try option 2).
4392 : }
4393 :
4394 : // We have a type constructor of the form 'ctypes.StructType(a, b, c, ...)'.
4395 : // ImplicitConvert each field.
4396 29 : if (argc == fields->count()) {
4397 103 : for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
4398 76 : const FieldInfo& field = r.front().value;
4399 : STATIC_ASSUME(field.mIndex < fields->count()); /* Quantified invariant */
4400 152 : if (!ImplicitConvert(cx, argv[field.mIndex], field.mType,
4401 76 : buffer + field.mOffset,
4402 228 : false, NULL))
4403 1 : return JS_FALSE;
4404 : }
4405 :
4406 27 : return JS_TRUE;
4407 : }
4408 :
4409 : JS_ReportError(cx, "constructor takes 0, 1, or %u arguments",
4410 1 : fields->count());
4411 1 : return JS_FALSE;
4412 : }
4413 :
4414 : const FieldInfoHash*
4415 1109 : StructType::GetFieldInfo(JSObject* obj)
4416 : {
4417 1109 : JS_ASSERT(CType::IsCType(obj));
4418 1109 : JS_ASSERT(CType::GetTypeCode(obj) == TYPE_struct);
4419 :
4420 1109 : jsval slot = JS_GetReservedSlot(obj, SLOT_FIELDINFO);
4421 1109 : JS_ASSERT(!JSVAL_IS_VOID(slot) && JSVAL_TO_PRIVATE(slot));
4422 :
4423 1109 : return static_cast<const FieldInfoHash*>(JSVAL_TO_PRIVATE(slot));
4424 : }
4425 :
4426 : const FieldInfo*
4427 1032 : StructType::LookupField(JSContext* cx, JSObject* obj, JSFlatString *name)
4428 : {
4429 1032 : JS_ASSERT(CType::IsCType(obj));
4430 1032 : JS_ASSERT(CType::GetTypeCode(obj) == TYPE_struct);
4431 :
4432 1032 : FieldInfoHash::Ptr ptr = GetFieldInfo(obj)->lookup(name);
4433 1032 : if (ptr)
4434 1028 : return &ptr->value;
4435 :
4436 8 : JSAutoByteString bytes(cx, name);
4437 4 : if (!bytes)
4438 0 : return NULL;
4439 :
4440 4 : JS_ReportError(cx, "%s does not name a field", bytes.ptr());
4441 4 : return NULL;
4442 : }
4443 :
4444 : JSObject*
4445 2 : StructType::BuildFieldsArray(JSContext* cx, JSObject* obj)
4446 : {
4447 2 : JS_ASSERT(CType::IsCType(obj));
4448 2 : JS_ASSERT(CType::GetTypeCode(obj) == TYPE_struct);
4449 2 : JS_ASSERT(CType::IsSizeDefined(obj));
4450 :
4451 2 : const FieldInfoHash* fields = GetFieldInfo(obj);
4452 2 : size_t len = fields->count();
4453 :
4454 : // Prepare a new array for the 'fields' property of the StructType.
4455 4 : Array<jsval, 16> fieldsVec;
4456 2 : if (!fieldsVec.appendN(JSVAL_VOID, len))
4457 0 : return NULL;
4458 4 : js::AutoArrayRooter root(cx, fieldsVec.length(), fieldsVec.begin());
4459 :
4460 6 : for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
4461 4 : const FieldInfoHash::Entry& entry = r.front();
4462 : // Add the field descriptor to the array.
4463 8 : if (!AddFieldToArray(cx, &fieldsVec[entry.value.mIndex],
4464 8 : entry.key, entry.value.mType))
4465 0 : return NULL;
4466 : }
4467 :
4468 2 : JSObject* fieldsProp = JS_NewArrayObject(cx, len, fieldsVec.begin());
4469 2 : if (!fieldsProp)
4470 0 : return NULL;
4471 :
4472 : // Seal the fields array.
4473 2 : if (!JS_FreezeObject(cx, fieldsProp))
4474 0 : return NULL;
4475 :
4476 2 : return fieldsProp;
4477 : }
4478 :
4479 : JSBool
4480 8 : StructType::FieldsArrayGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
4481 : {
4482 8 : if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_struct) {
4483 1 : JS_ReportError(cx, "not a StructType");
4484 1 : return JS_FALSE;
4485 : }
4486 :
4487 7 : *vp = JS_GetReservedSlot(obj, SLOT_FIELDS);
4488 :
4489 7 : if (!CType::IsSizeDefined(obj)) {
4490 1 : JS_ASSERT(JSVAL_IS_VOID(*vp));
4491 1 : return JS_TRUE;
4492 : }
4493 :
4494 6 : if (JSVAL_IS_VOID(*vp)) {
4495 : // Build the 'fields' array lazily.
4496 2 : JSObject* fields = BuildFieldsArray(cx, obj);
4497 2 : if (!fields)
4498 0 : return JS_FALSE;
4499 2 : JS_SetReservedSlot(obj, SLOT_FIELDS, OBJECT_TO_JSVAL(fields));
4500 :
4501 2 : *vp = OBJECT_TO_JSVAL(fields);
4502 : }
4503 :
4504 12 : JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp) &&
4505 12 : JS_IsArrayObject(cx, JSVAL_TO_OBJECT(*vp)));
4506 6 : return JS_TRUE;
4507 : }
4508 :
4509 : JSBool
4510 788 : StructType::FieldGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
4511 : {
4512 788 : if (!CData::IsCData(obj)) {
4513 2 : JS_ReportError(cx, "not a CData");
4514 2 : return JS_FALSE;
4515 : }
4516 :
4517 786 : JSObject* typeObj = CData::GetCType(obj);
4518 786 : if (CType::GetTypeCode(typeObj) != TYPE_struct) {
4519 0 : JS_ReportError(cx, "not a StructType");
4520 0 : return JS_FALSE;
4521 : }
4522 :
4523 786 : const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_FLAT_STRING(idval));
4524 786 : if (!field)
4525 0 : return JS_FALSE;
4526 :
4527 786 : char* data = static_cast<char*>(CData::GetData(obj)) + field->mOffset;
4528 786 : return ConvertToJS(cx, field->mType, obj, data, false, false, vp);
4529 : }
4530 :
4531 : JSBool
4532 8 : StructType::FieldSetter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict, jsval* vp)
4533 : {
4534 8 : if (!CData::IsCData(obj)) {
4535 0 : JS_ReportError(cx, "not a CData");
4536 0 : return JS_FALSE;
4537 : }
4538 :
4539 8 : JSObject* typeObj = CData::GetCType(obj);
4540 8 : if (CType::GetTypeCode(typeObj) != TYPE_struct) {
4541 0 : JS_ReportError(cx, "not a StructType");
4542 0 : return JS_FALSE;
4543 : }
4544 :
4545 8 : const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_FLAT_STRING(idval));
4546 8 : if (!field)
4547 0 : return JS_FALSE;
4548 :
4549 8 : char* data = static_cast<char*>(CData::GetData(obj)) + field->mOffset;
4550 8 : return ImplicitConvert(cx, *vp, field->mType, data, false, NULL);
4551 : }
4552 :
4553 : JSBool
4554 211 : StructType::AddressOfField(JSContext* cx, unsigned argc, jsval* vp)
4555 : {
4556 211 : JSObject* obj = JS_THIS_OBJECT(cx, vp);
4557 211 : if (!obj || !CData::IsCData(obj)) {
4558 2 : JS_ReportError(cx, "not a CData");
4559 2 : return JS_FALSE;
4560 : }
4561 :
4562 209 : JSObject* typeObj = CData::GetCType(obj);
4563 209 : if (CType::GetTypeCode(typeObj) != TYPE_struct) {
4564 0 : JS_ReportError(cx, "not a StructType");
4565 0 : return JS_FALSE;
4566 : }
4567 :
4568 209 : if (argc != 1) {
4569 2 : JS_ReportError(cx, "addressOfField takes one argument");
4570 2 : return JS_FALSE;
4571 : }
4572 :
4573 207 : JSFlatString *str = JS_FlattenString(cx, JSVAL_TO_STRING(JS_ARGV(cx, vp)[0]));
4574 207 : if (!str)
4575 0 : return JS_FALSE;
4576 :
4577 207 : const FieldInfo* field = LookupField(cx, typeObj, str);
4578 207 : if (!field)
4579 1 : return JS_FALSE;
4580 :
4581 206 : JSObject* baseType = field->mType;
4582 206 : JSObject* pointerType = PointerType::CreateInternal(cx, baseType);
4583 206 : if (!pointerType)
4584 0 : return JS_FALSE;
4585 412 : js::AutoObjectRooter root(cx, pointerType);
4586 :
4587 : // Create a PointerType CData object containing null.
4588 206 : JSObject* result = CData::Create(cx, pointerType, NULL, NULL, true);
4589 206 : if (!result)
4590 0 : return JS_FALSE;
4591 :
4592 206 : JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
4593 :
4594 : // Manually set the pointer inside the object, so we skip the conversion step.
4595 206 : void** data = static_cast<void**>(CData::GetData(result));
4596 206 : *data = static_cast<char*>(CData::GetData(obj)) + field->mOffset;
4597 206 : return JS_TRUE;
4598 : }
4599 :
4600 : /*******************************************************************************
4601 : ** FunctionType implementation
4602 : *******************************************************************************/
4603 :
4604 : // Helper class for handling allocation of function arguments.
4605 : struct AutoValue
4606 : {
4607 23385 : AutoValue() : mData(NULL) { }
4608 :
4609 23385 : ~AutoValue()
4610 : {
4611 23385 : UnwantedForeground::array_delete(static_cast<char*>(mData));
4612 23385 : }
4613 :
4614 21823 : bool SizeToType(JSContext* cx, JSObject* type)
4615 : {
4616 : // Allocate a minimum of sizeof(ffi_arg) to handle small integers.
4617 21823 : size_t size = Align(CType::GetSize(type), sizeof(ffi_arg));
4618 21823 : mData = cx->array_new<char>(size);
4619 21823 : if (mData)
4620 21823 : memset(mData, 0, size);
4621 21823 : return mData != NULL;
4622 : }
4623 :
4624 : void* mData;
4625 : };
4626 :
4627 : static bool
4628 2998 : GetABI(JSContext* cx, jsval abiType, ffi_abi* result)
4629 : {
4630 2998 : if (JSVAL_IS_PRIMITIVE(abiType))
4631 0 : return false;
4632 :
4633 2998 : ABICode abi = GetABICode(JSVAL_TO_OBJECT(abiType));
4634 :
4635 : // determine the ABI from the subset of those available on the
4636 : // given platform. ABI_DEFAULT specifies the default
4637 : // C calling convention (cdecl) on each platform.
4638 2998 : switch (abi) {
4639 : case ABI_DEFAULT:
4640 2995 : *result = FFI_DEFAULT_ABI;
4641 2995 : return true;
4642 : case ABI_STDCALL:
4643 : case ABI_WINAPI:
4644 : #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
4645 : *result = FFI_STDCALL;
4646 : return true;
4647 : #elif (defined(_WIN64))
4648 : // We'd like the same code to work across Win32 and Win64, so stdcall_api
4649 : // and winapi_abi become aliases to the lone Win64 ABI.
4650 : *result = FFI_WIN64;
4651 : return true;
4652 : #endif
4653 : case INVALID_ABI:
4654 : break;
4655 : }
4656 3 : return false;
4657 : }
4658 :
4659 : static JSObject*
4660 4256 : PrepareType(JSContext* cx, jsval type)
4661 : {
4662 8512 : if (JSVAL_IS_PRIMITIVE(type) ||
4663 4256 : !CType::IsCType(JSVAL_TO_OBJECT(type))) {
4664 0 : JS_ReportError(cx, "not a ctypes type");
4665 0 : return NULL;
4666 : }
4667 :
4668 4256 : JSObject* result = JSVAL_TO_OBJECT(type);
4669 4256 : TypeCode typeCode = CType::GetTypeCode(result);
4670 :
4671 4256 : if (typeCode == TYPE_array) {
4672 : // convert array argument types to pointers, just like C.
4673 : // ImplicitConvert will do the same, when passing an array as data.
4674 44 : JSObject* baseType = ArrayType::GetBaseType(result);
4675 44 : result = PointerType::CreateInternal(cx, baseType);
4676 44 : if (!result)
4677 0 : return NULL;
4678 :
4679 4212 : } else if (typeCode == TYPE_void_t || typeCode == TYPE_function) {
4680 : // disallow void or function argument types
4681 3 : JS_ReportError(cx, "Cannot have void or function argument type");
4682 3 : return NULL;
4683 : }
4684 :
4685 4253 : if (!CType::IsSizeDefined(result)) {
4686 0 : JS_ReportError(cx, "Argument type must have defined size");
4687 0 : return NULL;
4688 : }
4689 :
4690 : // libffi cannot pass types of zero size by value.
4691 4253 : JS_ASSERT(CType::GetSize(result) != 0);
4692 :
4693 4253 : return result;
4694 : }
4695 :
4696 : static JSObject*
4697 1501 : PrepareReturnType(JSContext* cx, jsval type)
4698 : {
4699 3002 : if (JSVAL_IS_PRIMITIVE(type) ||
4700 1501 : !CType::IsCType(JSVAL_TO_OBJECT(type))) {
4701 1 : JS_ReportError(cx, "not a ctypes type");
4702 1 : return NULL;
4703 : }
4704 :
4705 1500 : JSObject* result = JSVAL_TO_OBJECT(type);
4706 1500 : TypeCode typeCode = CType::GetTypeCode(result);
4707 :
4708 : // Arrays and functions can never be return types.
4709 1500 : if (typeCode == TYPE_array || typeCode == TYPE_function) {
4710 0 : JS_ReportError(cx, "Return type cannot be an array or function");
4711 0 : return NULL;
4712 : }
4713 :
4714 1500 : if (typeCode != TYPE_void_t && !CType::IsSizeDefined(result)) {
4715 0 : JS_ReportError(cx, "Return type must have defined size");
4716 0 : return NULL;
4717 : }
4718 :
4719 : // libffi cannot pass types of zero size by value.
4720 1500 : JS_ASSERT(typeCode == TYPE_void_t || CType::GetSize(result) != 0);
4721 :
4722 1500 : return result;
4723 : }
4724 :
4725 : static JS_ALWAYS_INLINE JSBool
4726 4246 : IsEllipsis(JSContext* cx, jsval v, bool* isEllipsis)
4727 : {
4728 4246 : *isEllipsis = false;
4729 4246 : if (!JSVAL_IS_STRING(v))
4730 4239 : return true;
4731 7 : JSString* str = JSVAL_TO_STRING(v);
4732 7 : if (str->length() != 3)
4733 0 : return true;
4734 7 : const jschar* chars = str->getChars(cx);
4735 7 : if (!chars)
4736 0 : return false;
4737 7 : jschar dot = '.';
4738 7 : *isEllipsis = (chars[0] == dot &&
4739 7 : chars[1] == dot &&
4740 14 : chars[2] == dot);
4741 7 : return true;
4742 : }
4743 :
4744 : static JSBool
4745 1494 : PrepareCIF(JSContext* cx,
4746 : FunctionInfo* fninfo)
4747 : {
4748 : ffi_abi abi;
4749 1494 : if (!GetABI(cx, OBJECT_TO_JSVAL(fninfo->mABI), &abi)) {
4750 0 : JS_ReportError(cx, "Invalid ABI specification");
4751 0 : return false;
4752 : }
4753 :
4754 1494 : ffi_type* rtype = CType::GetFFIType(cx, fninfo->mReturnType);
4755 1494 : if (!rtype)
4756 0 : return false;
4757 :
4758 : ffi_status status =
4759 : ffi_prep_cif(&fninfo->mCIF,
4760 : abi,
4761 : fninfo->mFFITypes.length(),
4762 : rtype,
4763 1494 : fninfo->mFFITypes.begin());
4764 :
4765 1494 : switch (status) {
4766 : case FFI_OK:
4767 1494 : return true;
4768 : case FFI_BAD_ABI:
4769 0 : JS_ReportError(cx, "Invalid ABI specification");
4770 0 : return false;
4771 : case FFI_BAD_TYPEDEF:
4772 0 : JS_ReportError(cx, "Invalid type specification");
4773 0 : return false;
4774 : default:
4775 0 : JS_ReportError(cx, "Unknown libffi error");
4776 0 : return false;
4777 : }
4778 : }
4779 :
4780 : void
4781 1474 : FunctionType::BuildSymbolName(JSString* name,
4782 : JSObject* typeObj,
4783 : AutoCString& result)
4784 : {
4785 1474 : FunctionInfo* fninfo = GetFunctionInfo(typeObj);
4786 :
4787 1474 : switch (GetABICode(fninfo->mABI)) {
4788 : case ABI_DEFAULT:
4789 : case ABI_WINAPI:
4790 : // For cdecl or WINAPI functions, no mangling is necessary.
4791 1474 : AppendString(result, name);
4792 1474 : break;
4793 :
4794 : case ABI_STDCALL: {
4795 : #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
4796 : // On WIN32, stdcall functions look like:
4797 : // _foo@40
4798 : // where 'foo' is the function name, and '40' is the aligned size of the
4799 : // arguments.
4800 : AppendString(result, "_");
4801 : AppendString(result, name);
4802 : AppendString(result, "@");
4803 :
4804 : // Compute the suffix by aligning each argument to sizeof(ffi_arg).
4805 : size_t size = 0;
4806 : for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
4807 : JSObject* argType = fninfo->mArgTypes[i];
4808 : size += Align(CType::GetSize(argType), sizeof(ffi_arg));
4809 : }
4810 :
4811 : IntegerToString(size, 10, result);
4812 : #elif defined(_WIN64)
4813 : // On Win64, stdcall is an alias to the default ABI for compatibility, so no
4814 : // mangling is done.
4815 : AppendString(result, name);
4816 : #endif
4817 0 : break;
4818 : }
4819 :
4820 : case INVALID_ABI:
4821 0 : JS_NOT_REACHED("invalid abi");
4822 : break;
4823 : }
4824 1474 : }
4825 :
4826 : static FunctionInfo*
4827 1504 : NewFunctionInfo(JSContext* cx,
4828 : jsval abiType,
4829 : jsval returnType,
4830 : jsval* argTypes,
4831 : unsigned argLength)
4832 : {
4833 3008 : AutoPtr<FunctionInfo> fninfo(cx->new_<FunctionInfo>());
4834 1504 : if (!fninfo) {
4835 0 : JS_ReportOutOfMemory(cx);
4836 0 : return NULL;
4837 : }
4838 :
4839 : ffi_abi abi;
4840 1504 : if (!GetABI(cx, abiType, &abi)) {
4841 3 : JS_ReportError(cx, "Invalid ABI specification");
4842 3 : return NULL;
4843 : }
4844 1501 : fninfo->mABI = JSVAL_TO_OBJECT(abiType);
4845 :
4846 : // prepare the result type
4847 1501 : fninfo->mReturnType = PrepareReturnType(cx, returnType);
4848 1501 : if (!fninfo->mReturnType)
4849 1 : return NULL;
4850 :
4851 : // prepare the argument types
4852 3000 : if (!fninfo->mArgTypes.reserve(argLength) ||
4853 1500 : !fninfo->mFFITypes.reserve(argLength)) {
4854 0 : JS_ReportOutOfMemory(cx);
4855 0 : return NULL;
4856 : }
4857 :
4858 1500 : fninfo->mIsVariadic = false;
4859 :
4860 5736 : for (uint32_t i = 0; i < argLength; ++i) {
4861 : bool isEllipsis;
4862 4246 : if (!IsEllipsis(cx, argTypes[i], &isEllipsis))
4863 0 : return NULL;
4864 4246 : if (isEllipsis) {
4865 7 : fninfo->mIsVariadic = true;
4866 7 : if (i < 1) {
4867 : JS_ReportError(cx, "\"...\" may not be the first and only parameter "
4868 1 : "type of a variadic function declaration");
4869 1 : return NULL;
4870 : }
4871 6 : if (i < argLength - 1) {
4872 : JS_ReportError(cx, "\"...\" must be the last parameter type of a "
4873 1 : "variadic function declaration");
4874 1 : return NULL;
4875 : }
4876 5 : if (GetABICode(fninfo->mABI) != ABI_DEFAULT) {
4877 : JS_ReportError(cx, "Variadic functions must use the __cdecl calling "
4878 0 : "convention");
4879 0 : return NULL;
4880 : }
4881 5 : break;
4882 : }
4883 :
4884 4239 : JSObject* argType = PrepareType(cx, argTypes[i]);
4885 4239 : if (!argType)
4886 3 : return NULL;
4887 :
4888 4236 : ffi_type* ffiType = CType::GetFFIType(cx, argType);
4889 4236 : if (!ffiType)
4890 0 : return NULL;
4891 :
4892 4236 : fninfo->mArgTypes.infallibleAppend(argType);
4893 4236 : fninfo->mFFITypes.infallibleAppend(ffiType);
4894 : }
4895 :
4896 1495 : if (fninfo->mIsVariadic)
4897 : // wait to PrepareCIF until function is called
4898 5 : return fninfo.forget();
4899 :
4900 1490 : if (!PrepareCIF(cx, fninfo.get()))
4901 0 : return NULL;
4902 :
4903 1490 : return fninfo.forget();
4904 : }
4905 :
4906 : JSBool
4907 213 : FunctionType::Create(JSContext* cx, unsigned argc, jsval* vp)
4908 : {
4909 : // Construct and return a new FunctionType object.
4910 213 : if (argc < 2 || argc > 3) {
4911 2 : JS_ReportError(cx, "FunctionType takes two or three arguments");
4912 2 : return JS_FALSE;
4913 : }
4914 :
4915 211 : jsval* argv = JS_ARGV(cx, vp);
4916 422 : Array<jsval, 16> argTypes;
4917 211 : JSObject* arrayObj = NULL;
4918 :
4919 211 : if (argc == 3) {
4920 : // Prepare an array of jsvals for the arguments.
4921 333 : if (JSVAL_IS_PRIMITIVE(argv[2]) ||
4922 166 : !JS_IsArrayObject(cx, JSVAL_TO_OBJECT(argv[2]))) {
4923 2 : JS_ReportError(cx, "third argument must be an array");
4924 2 : return JS_FALSE;
4925 : }
4926 :
4927 165 : arrayObj = JSVAL_TO_OBJECT(argv[2]);
4928 : uint32_t len;
4929 165 : ASSERT_OK(JS_GetArrayLength(cx, arrayObj, &len));
4930 :
4931 165 : if (!argTypes.appendN(JSVAL_VOID, len)) {
4932 0 : JS_ReportOutOfMemory(cx);
4933 0 : return JS_FALSE;
4934 : }
4935 : }
4936 :
4937 : // Pull out the argument types from the array, if any.
4938 209 : JS_ASSERT(!argTypes.length() || arrayObj);
4939 418 : js::AutoArrayRooter items(cx, argTypes.length(), argTypes.begin());
4940 1158 : for (uint32_t i = 0; i < argTypes.length(); ++i) {
4941 949 : if (!JS_GetElement(cx, arrayObj, i, &argTypes[i]))
4942 0 : return JS_FALSE;
4943 : }
4944 :
4945 209 : JSObject* result = CreateInternal(cx, argv[0], argv[1],
4946 418 : argTypes.begin(), argTypes.length());
4947 209 : if (!result)
4948 7 : return JS_FALSE;
4949 :
4950 202 : JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
4951 202 : return JS_TRUE;
4952 : }
4953 :
4954 : JSObject*
4955 1504 : FunctionType::CreateInternal(JSContext* cx,
4956 : jsval abi,
4957 : jsval rtype,
4958 : jsval* argtypes,
4959 : unsigned arglen)
4960 : {
4961 : // Determine and check the types, and prepare the function CIF.
4962 3008 : AutoPtr<FunctionInfo> fninfo(NewFunctionInfo(cx, abi, rtype, argtypes, arglen));
4963 1504 : if (!fninfo)
4964 9 : return NULL;
4965 :
4966 : // Get ctypes.FunctionType.prototype and the common prototype for CData objects
4967 : // of this type, from ctypes.CType.prototype.
4968 1495 : JSObject* typeProto = CType::GetProtoFromType(fninfo->mReturnType,
4969 1495 : SLOT_FUNCTIONPROTO);
4970 1495 : JSObject* dataProto = CType::GetProtoFromType(fninfo->mReturnType,
4971 1495 : SLOT_FUNCTIONDATAPROTO);
4972 :
4973 : // Create a new CType object with the common properties and slots.
4974 : JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_function,
4975 1495 : NULL, JSVAL_VOID, JSVAL_VOID, NULL);
4976 1495 : if (!typeObj)
4977 0 : return NULL;
4978 2990 : js::AutoObjectRooter root(cx, typeObj);
4979 :
4980 : // Stash the FunctionInfo in a reserved slot.
4981 1495 : JS_SetReservedSlot(typeObj, SLOT_FNINFO, PRIVATE_TO_JSVAL(fninfo.forget()));
4982 :
4983 1495 : return typeObj;
4984 : }
4985 :
4986 : // Construct a function pointer to a JS function (see CClosure::Create()).
4987 : // Regular function pointers are constructed directly in
4988 : // PointerType::ConstructData().
4989 : JSBool
4990 8 : FunctionType::ConstructData(JSContext* cx,
4991 : JSObject* typeObj,
4992 : JSObject* dataObj,
4993 : JSObject* fnObj,
4994 : JSObject* thisObj,
4995 : jsval errVal)
4996 : {
4997 8 : JS_ASSERT(CType::GetTypeCode(typeObj) == TYPE_function);
4998 :
4999 8 : PRFuncPtr* data = static_cast<PRFuncPtr*>(CData::GetData(dataObj));
5000 :
5001 8 : FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
5002 8 : if (fninfo->mIsVariadic) {
5003 1 : JS_ReportError(cx, "Can't declare a variadic callback function");
5004 1 : return JS_FALSE;
5005 : }
5006 7 : if (GetABICode(fninfo->mABI) == ABI_WINAPI) {
5007 : JS_ReportError(cx, "Can't declare a ctypes.winapi_abi callback function, "
5008 0 : "use ctypes.stdcall_abi instead");
5009 0 : return JS_FALSE;
5010 : }
5011 :
5012 7 : JSObject* closureObj = CClosure::Create(cx, typeObj, fnObj, thisObj, errVal, data);
5013 7 : if (!closureObj)
5014 1 : return JS_FALSE;
5015 12 : js::AutoObjectRooter root(cx, closureObj);
5016 :
5017 : // Set the closure object as the referent of the new CData object.
5018 6 : JS_SetReservedSlot(dataObj, SLOT_REFERENT, OBJECT_TO_JSVAL(closureObj));
5019 :
5020 : // Seal the CData object, to prevent modification of the function pointer.
5021 : // This permanently associates this object with the closure, and avoids
5022 : // having to do things like reset SLOT_REFERENT when someone tries to
5023 : // change the pointer value.
5024 : // XXX This will need to change when bug 541212 is fixed -- CData::ValueSetter
5025 : // could be called on a frozen object.
5026 6 : return JS_FreezeObject(cx, dataObj);
5027 : }
5028 :
5029 : typedef Array<AutoValue, 16> AutoValueAutoArray;
5030 :
5031 : static JSBool
5032 17404 : ConvertArgument(JSContext* cx,
5033 : jsval arg,
5034 : JSObject* type,
5035 : AutoValue* value,
5036 : AutoValueAutoArray* strings)
5037 : {
5038 17404 : if (!value->SizeToType(cx, type)) {
5039 0 : JS_ReportAllocationOverflow(cx);
5040 0 : return false;
5041 : }
5042 :
5043 17404 : bool freePointer = false;
5044 17404 : if (!ImplicitConvert(cx, arg, type, value->mData, true, &freePointer))
5045 7 : return false;
5046 :
5047 17397 : if (freePointer) {
5048 : // ImplicitConvert converted a string for us, which we have to free.
5049 : // Keep track of it.
5050 4 : if (!strings->growBy(1)) {
5051 0 : JS_ReportOutOfMemory(cx);
5052 0 : return false;
5053 : }
5054 4 : strings->back().mData = *static_cast<char**>(value->mData);
5055 : }
5056 :
5057 17397 : return true;
5058 : }
5059 :
5060 : JSBool
5061 5985 : FunctionType::Call(JSContext* cx,
5062 : unsigned argc,
5063 : jsval* vp)
5064 : {
5065 : // get the callee object...
5066 5985 : JSObject* obj = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
5067 5985 : if (!CData::IsCData(obj)) {
5068 0 : JS_ReportError(cx, "not a CData");
5069 0 : return false;
5070 : }
5071 :
5072 5985 : JSObject* typeObj = CData::GetCType(obj);
5073 5985 : if (CType::GetTypeCode(typeObj) != TYPE_pointer) {
5074 0 : JS_ReportError(cx, "not a FunctionType.ptr");
5075 0 : return false;
5076 : }
5077 :
5078 5985 : typeObj = PointerType::GetBaseType(typeObj);
5079 5985 : if (CType::GetTypeCode(typeObj) != TYPE_function) {
5080 0 : JS_ReportError(cx, "not a FunctionType.ptr");
5081 0 : return false;
5082 : }
5083 :
5084 5985 : FunctionInfo* fninfo = GetFunctionInfo(typeObj);
5085 5985 : uint32_t argcFixed = fninfo->mArgTypes.length();
5086 :
5087 5985 : if ((!fninfo->mIsVariadic && argc != argcFixed) ||
5088 : (fninfo->mIsVariadic && argc < argcFixed)) {
5089 0 : JS_ReportError(cx, "Number of arguments does not match declaration");
5090 0 : return false;
5091 : }
5092 :
5093 : // Check if we have a Library object. If we do, make sure it's open.
5094 5985 : jsval slot = JS_GetReservedSlot(obj, SLOT_REFERENT);
5095 5985 : if (!JSVAL_IS_VOID(slot) && Library::IsLibrary(JSVAL_TO_OBJECT(slot))) {
5096 5977 : PRLibrary* library = Library::GetLibrary(JSVAL_TO_OBJECT(slot));
5097 5977 : if (!library) {
5098 1 : JS_ReportError(cx, "library is not open");
5099 1 : return false;
5100 : }
5101 : }
5102 :
5103 : // prepare the values for each argument
5104 11968 : AutoValueAutoArray values;
5105 11968 : AutoValueAutoArray strings;
5106 5984 : if (!values.resize(argc)) {
5107 0 : JS_ReportOutOfMemory(cx);
5108 0 : return false;
5109 : }
5110 :
5111 5984 : jsval* argv = JS_ARGV(cx, vp);
5112 23364 : for (unsigned i = 0; i < argcFixed; ++i)
5113 17387 : if (!ConvertArgument(cx, argv[i], fninfo->mArgTypes[i], &values[i], &strings))
5114 7 : return false;
5115 :
5116 5977 : if (fninfo->mIsVariadic) {
5117 4 : if (!fninfo->mFFITypes.resize(argc)) {
5118 0 : JS_ReportOutOfMemory(cx);
5119 0 : return false;
5120 : }
5121 :
5122 : JSObject* obj; // Could reuse obj instead of declaring a second
5123 : JSObject* type; // JSObject*, but readability would suffer.
5124 :
5125 21 : for (uint32_t i = argcFixed; i < argc; ++i) {
5126 51 : if (JSVAL_IS_PRIMITIVE(argv[i]) ||
5127 34 : !CData::IsCData(obj = JSVAL_TO_OBJECT(argv[i]))) {
5128 : // Since we know nothing about the CTypes of the ... arguments,
5129 : // they absolutely must be CData objects already.
5130 : JS_ReportError(cx, "argument %d of type %s is not a CData object",
5131 0 : i, JS_GetTypeName(cx, JS_TypeOfValue(cx, argv[i])));
5132 0 : return false;
5133 : }
5134 85 : if (!(type = CData::GetCType(obj)) ||
5135 17 : !(type = PrepareType(cx, OBJECT_TO_JSVAL(type))) ||
5136 : // Relying on ImplicitConvert only for the limited purpose of
5137 : // converting one CType to another (e.g., T[] to T*).
5138 17 : !ConvertArgument(cx, argv[i], type, &values[i], &strings) ||
5139 34 : !(fninfo->mFFITypes[i] = CType::GetFFIType(cx, type))) {
5140 : // These functions report their own errors.
5141 0 : return false;
5142 : }
5143 : }
5144 4 : if (!PrepareCIF(cx, fninfo))
5145 0 : return false;
5146 : }
5147 :
5148 : // initialize a pointer to an appropriate location, for storing the result
5149 11954 : AutoValue returnValue;
5150 5977 : TypeCode typeCode = CType::GetTypeCode(fninfo->mReturnType);
5151 10396 : if (typeCode != TYPE_void_t &&
5152 4419 : !returnValue.SizeToType(cx, fninfo->mReturnType)) {
5153 0 : JS_ReportAllocationOverflow(cx);
5154 0 : return false;
5155 : }
5156 :
5157 5977 : uintptr_t fn = *reinterpret_cast<uintptr_t*>(CData::GetData(obj));
5158 :
5159 : // suspend the request before we call into the function, since the call
5160 : // may block or otherwise take a long time to return.
5161 : {
5162 11954 : JSAutoSuspendRequest suspend(cx);
5163 : ffi_call(&fninfo->mCIF, FFI_FN(fn), returnValue.mData,
5164 5977 : reinterpret_cast<void**>(values.begin()));
5165 : }
5166 :
5167 : // Small integer types get returned as a word-sized ffi_arg. Coerce it back
5168 : // into the correct size for ConvertToJS.
5169 5977 : switch (typeCode) {
5170 : #define DEFINE_INT_TYPE(name, type, ffiType) \
5171 : case TYPE_##name: \
5172 : if (sizeof(type) < sizeof(ffi_arg)) { \
5173 : ffi_arg data = *static_cast<ffi_arg*>(returnValue.mData); \
5174 : *static_cast<type*>(returnValue.mData) = static_cast<type>(data); \
5175 : } \
5176 : break;
5177 : #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
5178 : #define DEFINE_BOOL_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
5179 : #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
5180 : #define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
5181 : #include "typedefs.h"
5182 : default:
5183 3621 : break;
5184 : }
5185 :
5186 : // prepare a JS object from the result
5187 : return ConvertToJS(cx, fninfo->mReturnType, NULL, returnValue.mData,
5188 5977 : false, true, vp);
5189 : }
5190 :
5191 : FunctionInfo*
5192 7522 : FunctionType::GetFunctionInfo(JSObject* obj)
5193 : {
5194 7522 : JS_ASSERT(CType::IsCType(obj));
5195 7522 : JS_ASSERT(CType::GetTypeCode(obj) == TYPE_function);
5196 :
5197 7522 : jsval slot = JS_GetReservedSlot(obj, SLOT_FNINFO);
5198 7522 : JS_ASSERT(!JSVAL_IS_VOID(slot) && JSVAL_TO_PRIVATE(slot));
5199 :
5200 7522 : return static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot));
5201 : }
5202 :
5203 : static JSBool
5204 13 : CheckFunctionType(JSContext* cx, JSObject* obj)
5205 : {
5206 13 : if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_function) {
5207 4 : JS_ReportError(cx, "not a FunctionType");
5208 4 : return JS_FALSE;
5209 : }
5210 9 : return JS_TRUE;
5211 : }
5212 :
5213 : JSBool
5214 5 : FunctionType::ArgTypesGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
5215 : {
5216 5 : if (!CheckFunctionType(cx, obj))
5217 1 : return JS_FALSE;
5218 :
5219 : // Check if we have a cached argTypes array.
5220 4 : *vp = JS_GetReservedSlot(obj, SLOT_ARGS_T);
5221 4 : if (!JSVAL_IS_VOID(*vp))
5222 2 : return JS_TRUE;
5223 :
5224 2 : FunctionInfo* fninfo = GetFunctionInfo(obj);
5225 2 : size_t len = fninfo->mArgTypes.length();
5226 :
5227 : // Prepare a new array.
5228 4 : Array<jsval, 16> vec;
5229 2 : if (!vec.resize(len))
5230 0 : return JS_FALSE;
5231 :
5232 4 : for (size_t i = 0; i < len; ++i)
5233 2 : vec[i] = OBJECT_TO_JSVAL(fninfo->mArgTypes[i]);
5234 :
5235 2 : JSObject* argTypes = JS_NewArrayObject(cx, len, vec.begin());
5236 2 : if (!argTypes)
5237 0 : return JS_FALSE;
5238 :
5239 : // Seal and cache it.
5240 2 : if (!JS_FreezeObject(cx, argTypes))
5241 0 : return JS_FALSE;
5242 2 : JS_SetReservedSlot(obj, SLOT_ARGS_T, OBJECT_TO_JSVAL(argTypes));
5243 :
5244 2 : *vp = OBJECT_TO_JSVAL(argTypes);
5245 2 : return JS_TRUE;
5246 : }
5247 :
5248 : JSBool
5249 3 : FunctionType::ReturnTypeGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
5250 : {
5251 3 : if (!CheckFunctionType(cx, obj))
5252 1 : return JS_FALSE;
5253 :
5254 : // Get the returnType object from the FunctionInfo.
5255 2 : *vp = OBJECT_TO_JSVAL(GetFunctionInfo(obj)->mReturnType);
5256 2 : return JS_TRUE;
5257 : }
5258 :
5259 : JSBool
5260 3 : FunctionType::ABIGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
5261 : {
5262 3 : if (!CheckFunctionType(cx, obj))
5263 1 : return JS_FALSE;
5264 :
5265 : // Get the abi object from the FunctionInfo.
5266 2 : *vp = OBJECT_TO_JSVAL(GetFunctionInfo(obj)->mABI);
5267 2 : return JS_TRUE;
5268 : }
5269 :
5270 : JSBool
5271 2 : FunctionType::IsVariadicGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
5272 : {
5273 2 : if (!CheckFunctionType(cx, obj))
5274 1 : return JS_FALSE;
5275 :
5276 1 : *vp = BOOLEAN_TO_JSVAL(GetFunctionInfo(obj)->mIsVariadic);
5277 1 : return JS_TRUE;
5278 : }
5279 :
5280 : /*******************************************************************************
5281 : ** CClosure implementation
5282 : *******************************************************************************/
5283 :
5284 : JSObject*
5285 7 : CClosure::Create(JSContext* cx,
5286 : JSObject* typeObj,
5287 : JSObject* fnObj,
5288 : JSObject* thisObj,
5289 : jsval errVal,
5290 : PRFuncPtr* fnptr)
5291 : {
5292 7 : JS_ASSERT(fnObj);
5293 :
5294 7 : JSObject* result = JS_NewObject(cx, &sCClosureClass, NULL, NULL);
5295 7 : if (!result)
5296 0 : return NULL;
5297 14 : js::AutoObjectRooter root(cx, result);
5298 :
5299 : // Get the FunctionInfo from the FunctionType.
5300 7 : FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
5301 7 : JS_ASSERT(!fninfo->mIsVariadic);
5302 7 : JS_ASSERT(GetABICode(fninfo->mABI) != ABI_WINAPI);
5303 :
5304 14 : AutoPtr<ClosureInfo> cinfo(cx->new_<ClosureInfo>(JS_GetRuntime(cx)));
5305 7 : if (!cinfo) {
5306 0 : JS_ReportOutOfMemory(cx);
5307 0 : return NULL;
5308 : }
5309 :
5310 : // Get the prototype of the FunctionType object, of class CTypeProto,
5311 : // which stores our JSContext for use with the closure.
5312 7 : JSObject* proto = JS_GetPrototype(typeObj);
5313 7 : JS_ASSERT(proto);
5314 7 : JS_ASSERT(CType::IsCTypeProto(proto));
5315 :
5316 : // Get a JSContext for use with the closure.
5317 7 : jsval slot = JS_GetReservedSlot(proto, SLOT_CLOSURECX);
5318 7 : if (!JSVAL_IS_VOID(slot)) {
5319 : // Use the existing JSContext.
5320 6 : cinfo->cx = static_cast<JSContext*>(JSVAL_TO_PRIVATE(slot));
5321 6 : JS_ASSERT(cinfo->cx);
5322 : } else {
5323 : // Lazily instantiate a new JSContext, and stash it on
5324 : // ctypes.FunctionType.prototype.
5325 1 : JSRuntime* runtime = JS_GetRuntime(cx);
5326 1 : cinfo->cx = JS_NewContext(runtime, 8192);
5327 1 : if (!cinfo->cx) {
5328 0 : JS_ReportOutOfMemory(cx);
5329 0 : return NULL;
5330 : }
5331 :
5332 1 : JS_SetReservedSlot(proto, SLOT_CLOSURECX, PRIVATE_TO_JSVAL(cinfo->cx));
5333 : }
5334 :
5335 : // Prepare the error sentinel value. It's important to do this now, because
5336 : // we might be unable to convert the value to the proper type. If so, we want
5337 : // the caller to know about it _now_, rather than some uncertain time in the
5338 : // future when the error sentinel is actually needed.
5339 7 : if (!JSVAL_IS_VOID(errVal)) {
5340 :
5341 : // Make sure the callback returns something.
5342 3 : if (CType::GetTypeCode(fninfo->mReturnType) == TYPE_void_t) {
5343 1 : JS_ReportError(cx, "A void callback can't pass an error sentinel");
5344 1 : return NULL;
5345 : }
5346 :
5347 : // With the exception of void, the FunctionType constructor ensures that
5348 : // the return type has a defined size.
5349 2 : JS_ASSERT(CType::IsSizeDefined(fninfo->mReturnType));
5350 :
5351 : // Allocate a buffer for the return value.
5352 2 : size_t rvSize = CType::GetSize(fninfo->mReturnType);
5353 2 : cinfo->errResult = cx->malloc_(rvSize);
5354 2 : if (!cinfo->errResult)
5355 0 : return NULL;
5356 :
5357 : // Do the value conversion. This might fail, in which case we throw.
5358 4 : if (!ImplicitConvert(cx, errVal, fninfo->mReturnType, cinfo->errResult,
5359 2 : false, NULL))
5360 0 : return NULL;
5361 : } else {
5362 4 : cinfo->errResult = NULL;
5363 : }
5364 :
5365 : // Copy the important bits of context into cinfo.
5366 6 : cinfo->closureObj = result;
5367 6 : cinfo->typeObj = typeObj;
5368 6 : cinfo->thisObj = thisObj;
5369 6 : cinfo->jsfnObj = fnObj;
5370 :
5371 : // Create an ffi_closure object and initialize it.
5372 : void* code;
5373 6 : cinfo->closure =
5374 6 : static_cast<ffi_closure*>(ffi_closure_alloc(sizeof(ffi_closure), &code));
5375 6 : if (!cinfo->closure || !code) {
5376 0 : JS_ReportError(cx, "couldn't create closure - libffi error");
5377 0 : return NULL;
5378 : }
5379 :
5380 6 : ffi_status status = ffi_prep_closure_loc(cinfo->closure, &fninfo->mCIF,
5381 12 : CClosure::ClosureStub, cinfo.get(), code);
5382 6 : if (status != FFI_OK) {
5383 0 : JS_ReportError(cx, "couldn't create closure - libffi error");
5384 0 : return NULL;
5385 : }
5386 :
5387 : // Stash the ClosureInfo struct on our new object.
5388 6 : JS_SetReservedSlot(result, SLOT_CLOSUREINFO, PRIVATE_TO_JSVAL(cinfo.forget()));
5389 :
5390 : // Casting between void* and a function pointer is forbidden in C and C++.
5391 : // Do it via an integral type.
5392 6 : *fnptr = reinterpret_cast<PRFuncPtr>(reinterpret_cast<uintptr_t>(code));
5393 6 : return result;
5394 : }
5395 :
5396 : void
5397 0 : CClosure::Trace(JSTracer* trc, JSObject* obj)
5398 : {
5399 : // Make sure our ClosureInfo slot is legit. If it's not, bail.
5400 0 : jsval slot = JS_GetReservedSlot(obj, SLOT_CLOSUREINFO);
5401 0 : if (JSVAL_IS_VOID(slot))
5402 0 : return;
5403 :
5404 0 : ClosureInfo* cinfo = static_cast<ClosureInfo*>(JSVAL_TO_PRIVATE(slot));
5405 :
5406 : // Identify our objects to the tracer. (There's no need to identify
5407 : // 'closureObj', since that's us.)
5408 0 : JS_CALL_OBJECT_TRACER(trc, cinfo->typeObj, "typeObj");
5409 0 : JS_CALL_OBJECT_TRACER(trc, cinfo->jsfnObj, "jsfnObj");
5410 0 : if (cinfo->thisObj)
5411 0 : JS_CALL_OBJECT_TRACER(trc, cinfo->thisObj, "thisObj");
5412 : }
5413 :
5414 : void
5415 7 : CClosure::Finalize(JSContext* cx, JSObject* obj)
5416 : {
5417 : // Make sure our ClosureInfo slot is legit. If it's not, bail.
5418 7 : jsval slot = JS_GetReservedSlot(obj, SLOT_CLOSUREINFO);
5419 7 : if (JSVAL_IS_VOID(slot))
5420 1 : return;
5421 :
5422 6 : ClosureInfo* cinfo = static_cast<ClosureInfo*>(JSVAL_TO_PRIVATE(slot));
5423 6 : cx->delete_(cinfo);
5424 : }
5425 :
5426 : void
5427 10 : CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData)
5428 : {
5429 10 : JS_ASSERT(cif);
5430 10 : JS_ASSERT(result);
5431 10 : JS_ASSERT(args);
5432 10 : JS_ASSERT(userData);
5433 :
5434 : // Retrieve the essentials from our closure object.
5435 10 : ClosureInfo* cinfo = static_cast<ClosureInfo*>(userData);
5436 10 : JSContext* cx = cinfo->cx;
5437 10 : JSObject* typeObj = cinfo->typeObj;
5438 10 : JSObject* thisObj = cinfo->thisObj;
5439 10 : JSObject* jsfnObj = cinfo->jsfnObj;
5440 :
5441 10 : JS_AbortIfWrongThread(JS_GetRuntime(cx));
5442 :
5443 20 : JSAutoRequest ar(cx);
5444 :
5445 20 : JSAutoEnterCompartment ac;
5446 10 : if (!ac.enter(cx, jsfnObj))
5447 : return;
5448 :
5449 : // Assert that our CIFs agree.
5450 10 : FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
5451 10 : JS_ASSERT(cif == &fninfo->mCIF);
5452 :
5453 10 : TypeCode typeCode = CType::GetTypeCode(fninfo->mReturnType);
5454 :
5455 : // Initialize the result to zero, in case something fails. Small integer types
5456 : // are promoted to a word-sized ffi_arg, so we must be careful to zero the
5457 : // whole word.
5458 10 : size_t rvSize = 0;
5459 10 : if (cif->rtype != &ffi_type_void) {
5460 9 : rvSize = cif->rtype->size;
5461 9 : switch (typeCode) {
5462 : #define DEFINE_INT_TYPE(name, type, ffiType) \
5463 : case TYPE_##name:
5464 : #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
5465 : #define DEFINE_BOOL_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
5466 : #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
5467 : #define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
5468 : #include "typedefs.h"
5469 9 : rvSize = Align(rvSize, sizeof(ffi_arg));
5470 9 : break;
5471 : default:
5472 0 : break;
5473 : }
5474 9 : memset(result, 0, rvSize);
5475 : }
5476 :
5477 : // Get a death grip on 'closureObj'.
5478 20 : js::AutoObjectRooter root(cx, cinfo->closureObj);
5479 :
5480 : // Set up an array for converted arguments.
5481 20 : Array<jsval, 16> argv;
5482 10 : if (!argv.appendN(JSVAL_VOID, cif->nargs)) {
5483 0 : JS_ReportOutOfMemory(cx);
5484 : return;
5485 : }
5486 :
5487 20 : js::AutoArrayRooter roots(cx, argv.length(), argv.begin());
5488 19 : for (uint32_t i = 0; i < cif->nargs; ++i) {
5489 : // Convert each argument, and have any CData objects created depend on
5490 : // the existing buffers.
5491 27 : if (!ConvertToJS(cx, fninfo->mArgTypes[i], NULL, args[i], false, false,
5492 27 : &argv[i]))
5493 : return;
5494 : }
5495 :
5496 : // Call the JS function. 'thisObj' may be NULL, in which case the JS engine
5497 : // will find an appropriate object to use.
5498 : jsval rval;
5499 : JSBool success = JS_CallFunctionValue(cx, thisObj, OBJECT_TO_JSVAL(jsfnObj),
5500 10 : cif->nargs, argv.begin(), &rval);
5501 :
5502 : // Convert the result. Note that we pass 'isArgument = false', such that
5503 : // ImplicitConvert will *not* autoconvert a JS string into a pointer-to-char
5504 : // type, which would require an allocation that we can't track. The JS
5505 : // function must perform this conversion itself and return a PointerType
5506 : // CData; thusly, the burden of freeing the data is left to the user.
5507 10 : if (success && cif->rtype != &ffi_type_void)
5508 : success = ImplicitConvert(cx, rval, fninfo->mReturnType, result, false,
5509 6 : NULL);
5510 :
5511 10 : if (!success) {
5512 : // Something failed. The callee may have thrown, or it may not have
5513 : // returned a value that ImplicitConvert() was happy with. Depending on how
5514 : // prudent the consumer has been, we may or may not have a recovery plan.
5515 :
5516 : // In any case, a JS exception cannot be passed to C code, so report the
5517 : // exception if any and clear it from the cx.
5518 3 : if (JS_IsExceptionPending(cx))
5519 0 : JS_ReportPendingException(cx);
5520 :
5521 3 : if (cinfo->errResult) {
5522 : // Good case: we have a sentinel that we can return. Copy it in place of
5523 : // the actual return value, and then proceed.
5524 :
5525 : // The buffer we're returning might be larger than the size of the return
5526 : // type, due to libffi alignment issues (see above). But it should never
5527 : // be smaller.
5528 3 : size_t copySize = CType::GetSize(fninfo->mReturnType);
5529 3 : JS_ASSERT(copySize <= rvSize);
5530 3 : memcpy(result, cinfo->errResult, copySize);
5531 : } else {
5532 : // Bad case: not much we can do here. The rv is already zeroed out, so we
5533 : // just report (another) error and hope for the best. JS_ReportError will
5534 : // actually throw an exception here, so then we have to report it. Again.
5535 : // Ugh.
5536 : JS_ReportError(cx, "JavaScript callback failed, and an error sentinel "
5537 0 : "was not specified.");
5538 0 : if (JS_IsExceptionPending(cx))
5539 0 : JS_ReportPendingException(cx);
5540 :
5541 : return;
5542 : }
5543 : }
5544 :
5545 : // Small integer types must be returned as a word-sized ffi_arg. Coerce it
5546 : // back into the size libffi expects.
5547 10 : switch (typeCode) {
5548 : #define DEFINE_INT_TYPE(name, type, ffiType) \
5549 : case TYPE_##name: \
5550 : if (sizeof(type) < sizeof(ffi_arg)) { \
5551 : ffi_arg data = *static_cast<type*>(result); \
5552 : *static_cast<ffi_arg*>(result) = data; \
5553 : } \
5554 : break;
5555 : #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
5556 : #define DEFINE_BOOL_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
5557 : #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
5558 : #define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
5559 : #include "typedefs.h"
5560 : default:
5561 1 : break;
5562 : }
5563 : }
5564 :
5565 : /*******************************************************************************
5566 : ** CData implementation
5567 : *******************************************************************************/
5568 :
5569 : // Create a new CData object of type 'typeObj' containing binary data supplied
5570 : // in 'source', optionally with a referent object 'refObj'.
5571 : //
5572 : // * 'typeObj' must be a CType of defined (but possibly zero) size.
5573 : //
5574 : // * If an object 'refObj' is supplied, the new CData object stores the
5575 : // referent object in a reserved slot for GC safety, such that 'refObj' will
5576 : // be held alive by the resulting CData object. 'refObj' may or may not be
5577 : // a CData object; merely an object we want to keep alive.
5578 : // * If 'refObj' is a CData object, 'ownResult' must be false.
5579 : // * Otherwise, 'refObj' is a Library or CClosure object, and 'ownResult'
5580 : // may be true or false.
5581 : // * Otherwise 'refObj' is NULL. In this case, 'ownResult' may be true or false.
5582 : //
5583 : // * If 'ownResult' is true, the CData object will allocate an appropriately
5584 : // sized buffer, and free it upon finalization. If 'source' data is
5585 : // supplied, the data will be copied from 'source' into the buffer;
5586 : // otherwise, the entirety of the new buffer will be initialized to zero.
5587 : // * If 'ownResult' is false, the new CData's buffer refers to a slice of
5588 : // another buffer kept alive by 'refObj'. 'source' data must be provided,
5589 : // and the new CData's buffer will refer to 'source'.
5590 : JSObject*
5591 12694 : CData::Create(JSContext* cx,
5592 : JSObject* typeObj,
5593 : JSObject* refObj,
5594 : void* source,
5595 : bool ownResult)
5596 : {
5597 12694 : JS_ASSERT(typeObj);
5598 12694 : JS_ASSERT(CType::IsCType(typeObj));
5599 12694 : JS_ASSERT(CType::IsSizeDefined(typeObj));
5600 12694 : JS_ASSERT(ownResult || source);
5601 12694 : JS_ASSERT_IF(refObj && CData::IsCData(refObj), !ownResult);
5602 :
5603 : // Get the 'prototype' property from the type.
5604 12694 : jsval slot = JS_GetReservedSlot(typeObj, SLOT_PROTO);
5605 12694 : JS_ASSERT(!JSVAL_IS_PRIMITIVE(slot));
5606 :
5607 12694 : JSObject* proto = JSVAL_TO_OBJECT(slot);
5608 12694 : JSObject* parent = JS_GetParent(typeObj);
5609 12694 : JS_ASSERT(parent);
5610 :
5611 12694 : JSObject* dataObj = JS_NewObject(cx, &sCDataClass, proto, parent);
5612 12694 : if (!dataObj)
5613 0 : return NULL;
5614 25388 : js::AutoObjectRooter root(cx, dataObj);
5615 :
5616 : // set the CData's associated type
5617 12694 : JS_SetReservedSlot(dataObj, SLOT_CTYPE, OBJECT_TO_JSVAL(typeObj));
5618 :
5619 : // Stash the referent object, if any, for GC safety.
5620 12694 : if (refObj)
5621 5112 : JS_SetReservedSlot(dataObj, SLOT_REFERENT, OBJECT_TO_JSVAL(refObj));
5622 :
5623 : // Set our ownership flag.
5624 12694 : JS_SetReservedSlot(dataObj, SLOT_OWNS, BOOLEAN_TO_JSVAL(ownResult));
5625 :
5626 : // attach the buffer. since it might not be 2-byte aligned, we need to
5627 : // allocate an aligned space for it and store it there. :(
5628 12694 : char** buffer = cx->new_<char*>();
5629 12694 : if (!buffer) {
5630 0 : JS_ReportOutOfMemory(cx);
5631 0 : return NULL;
5632 : }
5633 :
5634 : char* data;
5635 12694 : if (!ownResult) {
5636 5301 : data = static_cast<char*>(source);
5637 : } else {
5638 : // Initialize our own buffer.
5639 7393 : size_t size = CType::GetSize(typeObj);
5640 7393 : data = cx->array_new<char>(size);
5641 7393 : if (!data) {
5642 : // Report a catchable allocation error.
5643 0 : JS_ReportAllocationOverflow(cx);
5644 0 : Foreground::delete_(buffer);
5645 0 : return NULL;
5646 : }
5647 :
5648 7393 : if (!source)
5649 3978 : memset(data, 0, size);
5650 : else
5651 3415 : memcpy(data, source, size);
5652 : }
5653 :
5654 12694 : *buffer = data;
5655 12694 : JS_SetReservedSlot(dataObj, SLOT_DATA, PRIVATE_TO_JSVAL(buffer));
5656 :
5657 12694 : return dataObj;
5658 : }
5659 :
5660 : void
5661 12694 : CData::Finalize(JSContext* cx, JSObject* obj)
5662 : {
5663 : // Delete our buffer, and the data it contains if we own it.
5664 12694 : jsval slot = JS_GetReservedSlot(obj, SLOT_OWNS);
5665 12694 : if (JSVAL_IS_VOID(slot))
5666 0 : return;
5667 :
5668 12694 : JSBool owns = JSVAL_TO_BOOLEAN(slot);
5669 :
5670 12694 : slot = JS_GetReservedSlot(obj, SLOT_DATA);
5671 12694 : if (JSVAL_IS_VOID(slot))
5672 0 : return;
5673 12694 : char** buffer = static_cast<char**>(JSVAL_TO_PRIVATE(slot));
5674 :
5675 12694 : if (owns)
5676 7393 : cx->array_delete(*buffer);
5677 12694 : cx->delete_(buffer);
5678 : }
5679 :
5680 : JSObject*
5681 175392 : CData::GetCType(JSObject* dataObj)
5682 : {
5683 175392 : JS_ASSERT(CData::IsCData(dataObj));
5684 :
5685 175392 : jsval slot = JS_GetReservedSlot(dataObj, SLOT_CTYPE);
5686 175392 : JSObject* typeObj = JSVAL_TO_OBJECT(slot);
5687 175392 : JS_ASSERT(CType::IsCType(typeObj));
5688 175392 : return typeObj;
5689 : }
5690 :
5691 : void*
5692 178063 : CData::GetData(JSObject* dataObj)
5693 : {
5694 178063 : JS_ASSERT(CData::IsCData(dataObj));
5695 :
5696 178063 : jsval slot = JS_GetReservedSlot(dataObj, SLOT_DATA);
5697 :
5698 178063 : void** buffer = static_cast<void**>(JSVAL_TO_PRIVATE(slot));
5699 178063 : JS_ASSERT(buffer);
5700 178063 : JS_ASSERT(*buffer);
5701 178063 : return *buffer;
5702 : }
5703 :
5704 : bool
5705 660143 : CData::IsCData(JSObject* obj)
5706 : {
5707 660143 : return JS_GetClass(obj) == &sCDataClass;
5708 : }
5709 :
5710 : bool
5711 95 : CData::IsCDataProto(JSObject* obj)
5712 : {
5713 95 : return JS_GetClass(obj) == &sCDataProtoClass;
5714 : }
5715 :
5716 : JSBool
5717 2882 : CData::ValueGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
5718 : {
5719 2882 : if (!IsCData(obj)) {
5720 31 : JS_ReportError(cx, "not a CData");
5721 31 : return JS_FALSE;
5722 : }
5723 :
5724 : // Convert the value to a primitive; do not create a new CData object.
5725 2851 : if (!ConvertToJS(cx, GetCType(obj), NULL, GetData(obj), true, false, vp))
5726 4 : return JS_FALSE;
5727 :
5728 2847 : return JS_TRUE;
5729 : }
5730 :
5731 : JSBool
5732 811 : CData::ValueSetter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict, jsval* vp)
5733 : {
5734 811 : if (!IsCData(obj)) {
5735 31 : JS_ReportError(cx, "not a CData");
5736 31 : return JS_FALSE;
5737 : }
5738 :
5739 780 : return ImplicitConvert(cx, *vp, GetCType(obj), GetData(obj), false, NULL);
5740 : }
5741 :
5742 : JSBool
5743 1055 : CData::Address(JSContext* cx, unsigned argc, jsval* vp)
5744 : {
5745 1055 : if (argc != 0) {
5746 0 : JS_ReportError(cx, "address takes zero arguments");
5747 0 : return JS_FALSE;
5748 : }
5749 :
5750 1055 : JSObject* obj = JS_THIS_OBJECT(cx, vp);
5751 1055 : if (!obj || !IsCData(obj)) {
5752 31 : JS_ReportError(cx, "not a CData");
5753 31 : return JS_FALSE;
5754 : }
5755 :
5756 1024 : JSObject* typeObj = CData::GetCType(obj);
5757 1024 : JSObject* pointerType = PointerType::CreateInternal(cx, typeObj);
5758 1024 : if (!pointerType)
5759 0 : return JS_FALSE;
5760 2048 : js::AutoObjectRooter root(cx, pointerType);
5761 :
5762 : // Create a PointerType CData object containing null.
5763 1024 : JSObject* result = CData::Create(cx, pointerType, NULL, NULL, true);
5764 1024 : if (!result)
5765 0 : return JS_FALSE;
5766 :
5767 1024 : JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
5768 :
5769 : // Manually set the pointer inside the object, so we skip the conversion step.
5770 1024 : void** data = static_cast<void**>(GetData(result));
5771 1024 : *data = GetData(obj);
5772 1024 : return JS_TRUE;
5773 : }
5774 :
5775 : JSBool
5776 3165 : CData::Cast(JSContext* cx, unsigned argc, jsval* vp)
5777 : {
5778 3165 : if (argc != 2) {
5779 0 : JS_ReportError(cx, "cast takes two arguments");
5780 0 : return JS_FALSE;
5781 : }
5782 :
5783 3165 : jsval* argv = JS_ARGV(cx, vp);
5784 6330 : if (JSVAL_IS_PRIMITIVE(argv[0]) ||
5785 3165 : !CData::IsCData(JSVAL_TO_OBJECT(argv[0]))) {
5786 0 : JS_ReportError(cx, "first argument must be a CData");
5787 0 : return JS_FALSE;
5788 : }
5789 3165 : JSObject* sourceData = JSVAL_TO_OBJECT(argv[0]);
5790 3165 : JSObject* sourceType = CData::GetCType(sourceData);
5791 :
5792 6330 : if (JSVAL_IS_PRIMITIVE(argv[1]) ||
5793 3165 : !CType::IsCType(JSVAL_TO_OBJECT(argv[1]))) {
5794 0 : JS_ReportError(cx, "second argument must be a CType");
5795 0 : return JS_FALSE;
5796 : }
5797 :
5798 3165 : JSObject* targetType = JSVAL_TO_OBJECT(argv[1]);
5799 : size_t targetSize;
5800 6328 : if (!CType::GetSafeSize(targetType, &targetSize) ||
5801 3163 : targetSize > CType::GetSize(sourceType)) {
5802 : JS_ReportError(cx,
5803 3 : "target CType has undefined or larger size than source CType");
5804 3 : return JS_FALSE;
5805 : }
5806 :
5807 : // Construct a new CData object with a type of 'targetType' and a referent
5808 : // of 'sourceData'.
5809 3162 : void* data = CData::GetData(sourceData);
5810 3162 : JSObject* result = CData::Create(cx, targetType, sourceData, data, false);
5811 3162 : if (!result)
5812 0 : return JS_FALSE;
5813 :
5814 3162 : JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
5815 3162 : return JS_TRUE;
5816 : }
5817 :
5818 : JSBool
5819 0 : CData::GetRuntime(JSContext* cx, unsigned argc, jsval* vp)
5820 : {
5821 0 : if (argc != 1) {
5822 0 : JS_ReportError(cx, "getRuntime takes one argument");
5823 0 : return JS_FALSE;
5824 : }
5825 :
5826 0 : jsval* argv = JS_ARGV(cx, vp);
5827 0 : if (JSVAL_IS_PRIMITIVE(argv[0]) ||
5828 0 : !CType::IsCType(JSVAL_TO_OBJECT(argv[0]))) {
5829 0 : JS_ReportError(cx, "first argument must be a CType");
5830 0 : return JS_FALSE;
5831 : }
5832 :
5833 0 : JSObject* targetType = JSVAL_TO_OBJECT(argv[0]);
5834 : size_t targetSize;
5835 0 : if (!CType::GetSafeSize(targetType, &targetSize) ||
5836 : targetSize != sizeof(void*)) {
5837 0 : JS_ReportError(cx, "target CType has non-pointer size");
5838 0 : return JS_FALSE;
5839 : }
5840 :
5841 0 : void* data = static_cast<void*>(cx->runtime);
5842 0 : JSObject* result = CData::Create(cx, targetType, NULL, &data, true);
5843 0 : if (!result)
5844 0 : return JS_FALSE;
5845 :
5846 0 : JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
5847 0 : return JS_TRUE;
5848 : }
5849 :
5850 : JSBool
5851 166 : CData::ReadString(JSContext* cx, unsigned argc, jsval* vp)
5852 : {
5853 166 : if (argc != 0) {
5854 0 : JS_ReportError(cx, "readString takes zero arguments");
5855 0 : return JS_FALSE;
5856 : }
5857 :
5858 166 : JSObject* obj = JS_THIS_OBJECT(cx, vp);
5859 166 : if (!obj || !IsCData(obj)) {
5860 31 : JS_ReportError(cx, "not a CData");
5861 31 : return JS_FALSE;
5862 : }
5863 :
5864 : // Make sure we are a pointer to, or an array of, an 8-bit or 16-bit
5865 : // character or integer type.
5866 : JSObject* baseType;
5867 135 : JSObject* typeObj = GetCType(obj);
5868 135 : TypeCode typeCode = CType::GetTypeCode(typeObj);
5869 : void* data;
5870 135 : size_t maxLength = -1;
5871 135 : switch (typeCode) {
5872 : case TYPE_pointer:
5873 7 : baseType = PointerType::GetBaseType(typeObj);
5874 7 : data = *static_cast<void**>(GetData(obj));
5875 7 : if (data == NULL) {
5876 0 : JS_ReportError(cx, "cannot read contents of null pointer");
5877 0 : return JS_FALSE;
5878 : }
5879 7 : break;
5880 : case TYPE_array:
5881 128 : baseType = ArrayType::GetBaseType(typeObj);
5882 128 : data = GetData(obj);
5883 128 : maxLength = ArrayType::GetLength(typeObj);
5884 128 : break;
5885 : default:
5886 0 : JS_ReportError(cx, "not a PointerType or ArrayType");
5887 0 : return JS_FALSE;
5888 : }
5889 :
5890 : // Convert the string buffer, taking care to determine the correct string
5891 : // length in the case of arrays (which may contain embedded nulls).
5892 : JSString* result;
5893 135 : switch (CType::GetTypeCode(baseType)) {
5894 : case TYPE_int8_t:
5895 : case TYPE_uint8_t:
5896 : case TYPE_char:
5897 : case TYPE_signed_char:
5898 : case TYPE_unsigned_char: {
5899 131 : char* bytes = static_cast<char*>(data);
5900 131 : size_t length = strnlen(bytes, maxLength);
5901 :
5902 : // Determine the length.
5903 : size_t dstlen;
5904 131 : if (!InflateUTF8StringToBuffer(cx, bytes, length, NULL, &dstlen))
5905 1 : return JS_FALSE;
5906 :
5907 : jschar* dst =
5908 130 : static_cast<jschar*>(JS_malloc(cx, (dstlen + 1) * sizeof(jschar)));
5909 130 : if (!dst)
5910 0 : return JS_FALSE;
5911 :
5912 130 : ASSERT_OK(InflateUTF8StringToBuffer(cx, bytes, length, dst, &dstlen));
5913 130 : dst[dstlen] = 0;
5914 :
5915 130 : result = JS_NewUCString(cx, dst, dstlen);
5916 130 : break;
5917 : }
5918 : case TYPE_int16_t:
5919 : case TYPE_uint16_t:
5920 : case TYPE_short:
5921 : case TYPE_unsigned_short:
5922 : case TYPE_jschar: {
5923 4 : jschar* chars = static_cast<jschar*>(data);
5924 4 : size_t length = strnlen(chars, maxLength);
5925 4 : result = JS_NewUCStringCopyN(cx, chars, length);
5926 4 : break;
5927 : }
5928 : default:
5929 : JS_ReportError(cx,
5930 0 : "base type is not an 8-bit or 16-bit integer or character type");
5931 0 : return JS_FALSE;
5932 : }
5933 :
5934 134 : if (!result)
5935 0 : return JS_FALSE;
5936 :
5937 134 : JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
5938 134 : return JS_TRUE;
5939 : }
5940 :
5941 : JSBool
5942 237 : CData::ToSource(JSContext* cx, unsigned argc, jsval* vp)
5943 : {
5944 237 : if (argc != 0) {
5945 0 : JS_ReportError(cx, "toSource takes zero arguments");
5946 0 : return JS_FALSE;
5947 : }
5948 :
5949 237 : JSObject* obj = JS_THIS_OBJECT(cx, vp);
5950 474 : if (!obj ||
5951 299 : !(CData::IsCData(obj) || CData::IsCDataProto(obj))) {
5952 0 : JS_ReportError(cx, "not a CData");
5953 0 : return JS_FALSE;
5954 : }
5955 :
5956 : JSString* result;
5957 237 : if (CData::IsCData(obj)) {
5958 175 : JSObject* typeObj = CData::GetCType(obj);
5959 175 : void* data = CData::GetData(obj);
5960 :
5961 : // Walk the types, building up the toSource() string.
5962 : // First, we build up the type expression:
5963 : // 't.ptr' for pointers;
5964 : // 't.array([n])' for arrays;
5965 : // 'n' for structs, where n = t.name, the struct's name. (We assume this is
5966 : // bound to a variable in the current scope.)
5967 350 : AutoString source;
5968 175 : BuildTypeSource(cx, typeObj, true, source);
5969 175 : AppendString(source, "(");
5970 175 : if (!BuildDataSource(cx, typeObj, data, false, source))
5971 0 : return JS_FALSE;
5972 :
5973 175 : AppendString(source, ")");
5974 :
5975 350 : result = NewUCString(cx, source);
5976 : }
5977 : else
5978 62 : result = JS_NewStringCopyZ(cx, "[CData proto object]");
5979 237 : if (!result)
5980 0 : return JS_FALSE;
5981 :
5982 237 : JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
5983 237 : return JS_TRUE;
5984 : }
5985 :
5986 : /*******************************************************************************
5987 : ** Int64 and UInt64 implementation
5988 : *******************************************************************************/
5989 :
5990 : JSObject*
5991 2482 : Int64Base::Construct(JSContext* cx,
5992 : JSObject* proto,
5993 : uint64_t data,
5994 : bool isUnsigned)
5995 : {
5996 2482 : JSClass* clasp = isUnsigned ? &sUInt64Class : &sInt64Class;
5997 2482 : JSObject* result = JS_NewObject(cx, clasp, proto, JS_GetParent(proto));
5998 2482 : if (!result)
5999 0 : return NULL;
6000 4964 : js::AutoObjectRooter root(cx, result);
6001 :
6002 : // attach the Int64's data
6003 2482 : uint64_t* buffer = cx->new_<uint64_t>(data);
6004 2482 : if (!buffer) {
6005 0 : JS_ReportOutOfMemory(cx);
6006 0 : return NULL;
6007 : }
6008 :
6009 2482 : JS_SetReservedSlot(result, SLOT_INT64, PRIVATE_TO_JSVAL(buffer));
6010 :
6011 2482 : if (!JS_FreezeObject(cx, result))
6012 0 : return NULL;
6013 :
6014 2482 : return result;
6015 : }
6016 :
6017 : void
6018 2482 : Int64Base::Finalize(JSContext* cx, JSObject* obj)
6019 : {
6020 2482 : jsval slot = JS_GetReservedSlot(obj, SLOT_INT64);
6021 2482 : if (JSVAL_IS_VOID(slot))
6022 0 : return;
6023 :
6024 2482 : cx->delete_(static_cast<uint64_t*>(JSVAL_TO_PRIVATE(slot)));
6025 : }
6026 :
6027 : uint64_t
6028 4193 : Int64Base::GetInt(JSObject* obj) {
6029 4193 : JS_ASSERT(Int64::IsInt64(obj) || UInt64::IsUInt64(obj));
6030 :
6031 4193 : jsval slot = JS_GetReservedSlot(obj, SLOT_INT64);
6032 4193 : return *static_cast<uint64_t*>(JSVAL_TO_PRIVATE(slot));
6033 : }
6034 :
6035 : JSBool
6036 2925 : Int64Base::ToString(JSContext* cx,
6037 : JSObject* obj,
6038 : unsigned argc,
6039 : jsval* vp,
6040 : bool isUnsigned)
6041 : {
6042 2925 : if (argc > 1) {
6043 2 : JS_ReportError(cx, "toString takes zero or one argument");
6044 2 : return JS_FALSE;
6045 : }
6046 :
6047 2923 : int radix = 10;
6048 2923 : if (argc == 1) {
6049 169 : jsval arg = JS_ARGV(cx, vp)[0];
6050 169 : if (JSVAL_IS_INT(arg))
6051 169 : radix = JSVAL_TO_INT(arg);
6052 169 : if (!JSVAL_IS_INT(arg) || radix < 2 || radix > 36) {
6053 6 : JS_ReportError(cx, "radix argument must be an integer between 2 and 36");
6054 6 : return JS_FALSE;
6055 : }
6056 : }
6057 :
6058 5834 : AutoString intString;
6059 2917 : if (isUnsigned) {
6060 2431 : IntegerToString(GetInt(obj), radix, intString);
6061 : } else {
6062 486 : IntegerToString(static_cast<int64_t>(GetInt(obj)), radix, intString);
6063 : }
6064 :
6065 2917 : JSString *result = NewUCString(cx, intString);
6066 2917 : if (!result)
6067 0 : return JS_FALSE;
6068 :
6069 2917 : JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
6070 2917 : return JS_TRUE;
6071 : }
6072 :
6073 : JSBool
6074 30 : Int64Base::ToSource(JSContext* cx,
6075 : JSObject* obj,
6076 : unsigned argc,
6077 : jsval* vp,
6078 : bool isUnsigned)
6079 : {
6080 30 : if (argc != 0) {
6081 2 : JS_ReportError(cx, "toSource takes zero arguments");
6082 2 : return JS_FALSE;
6083 : }
6084 :
6085 : // Return a decimal string suitable for constructing the number.
6086 56 : AutoString source;
6087 28 : if (isUnsigned) {
6088 11 : AppendString(source, "ctypes.UInt64(\"");
6089 11 : IntegerToString(GetInt(obj), 10, source);
6090 : } else {
6091 17 : AppendString(source, "ctypes.Int64(\"");
6092 17 : IntegerToString(static_cast<int64_t>(GetInt(obj)), 10, source);
6093 : }
6094 28 : AppendString(source, "\")");
6095 :
6096 28 : JSString *result = NewUCString(cx, source);
6097 28 : if (!result)
6098 0 : return JS_FALSE;
6099 :
6100 28 : JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
6101 28 : return JS_TRUE;
6102 : }
6103 :
6104 : JSBool
6105 144 : Int64::Construct(JSContext* cx,
6106 : unsigned argc,
6107 : jsval* vp)
6108 : {
6109 : // Construct and return a new Int64 object.
6110 144 : if (argc != 1) {
6111 1 : JS_ReportError(cx, "Int64 takes one argument");
6112 1 : return JS_FALSE;
6113 : }
6114 :
6115 143 : jsval* argv = JS_ARGV(cx, vp);
6116 143 : int64_t i = 0;
6117 143 : if (!jsvalToBigInteger(cx, argv[0], true, &i))
6118 18 : return TypeError(cx, "int64", argv[0]);
6119 :
6120 : // Get ctypes.Int64.prototype from the 'prototype' property of the ctor.
6121 : jsval slot;
6122 : ASSERT_OK(JS_GetProperty(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)),
6123 125 : "prototype", &slot));
6124 125 : JSObject* proto = JSVAL_TO_OBJECT(slot);
6125 125 : JS_ASSERT(JS_GetClass(proto) == &sInt64ProtoClass);
6126 :
6127 125 : JSObject* result = Int64Base::Construct(cx, proto, i, false);
6128 125 : if (!result)
6129 0 : return JS_FALSE;
6130 :
6131 125 : JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
6132 125 : return JS_TRUE;
6133 : }
6134 :
6135 : bool
6136 6064 : Int64::IsInt64(JSObject* obj)
6137 : {
6138 6064 : return JS_GetClass(obj) == &sInt64Class;
6139 : }
6140 :
6141 : JSBool
6142 491 : Int64::ToString(JSContext* cx, unsigned argc, jsval* vp)
6143 : {
6144 491 : JSObject* obj = JS_THIS_OBJECT(cx, vp);
6145 491 : if (!obj || !Int64::IsInt64(obj)) {
6146 1 : JS_ReportError(cx, "not an Int64");
6147 1 : return JS_FALSE;
6148 : }
6149 :
6150 490 : return Int64Base::ToString(cx, obj, argc, vp, false);
6151 : }
6152 :
6153 : JSBool
6154 19 : Int64::ToSource(JSContext* cx, unsigned argc, jsval* vp)
6155 : {
6156 19 : JSObject* obj = JS_THIS_OBJECT(cx, vp);
6157 19 : if (!obj || !Int64::IsInt64(obj)) {
6158 1 : JS_ReportError(cx, "not an Int64");
6159 1 : return JS_FALSE;
6160 : }
6161 :
6162 18 : return Int64Base::ToSource(cx, obj, argc, vp, false);
6163 : }
6164 :
6165 : JSBool
6166 13 : Int64::Compare(JSContext* cx, unsigned argc, jsval* vp)
6167 : {
6168 13 : jsval* argv = JS_ARGV(cx, vp);
6169 62 : if (argc != 2 ||
6170 13 : JSVAL_IS_PRIMITIVE(argv[0]) ||
6171 12 : JSVAL_IS_PRIMITIVE(argv[1]) ||
6172 12 : !Int64::IsInt64(JSVAL_TO_OBJECT(argv[0])) ||
6173 12 : !Int64::IsInt64(JSVAL_TO_OBJECT(argv[1]))) {
6174 2 : JS_ReportError(cx, "compare takes two Int64 arguments");
6175 2 : return JS_FALSE;
6176 : }
6177 :
6178 11 : JSObject* obj1 = JSVAL_TO_OBJECT(argv[0]);
6179 11 : JSObject* obj2 = JSVAL_TO_OBJECT(argv[1]);
6180 :
6181 11 : int64_t i1 = Int64Base::GetInt(obj1);
6182 11 : int64_t i2 = Int64Base::GetInt(obj2);
6183 :
6184 11 : if (i1 == i2)
6185 2 : JS_SET_RVAL(cx, vp, INT_TO_JSVAL(0));
6186 9 : else if (i1 < i2)
6187 5 : JS_SET_RVAL(cx, vp, INT_TO_JSVAL(-1));
6188 : else
6189 4 : JS_SET_RVAL(cx, vp, INT_TO_JSVAL(1));
6190 :
6191 11 : return JS_TRUE;
6192 : }
6193 :
6194 : #define LO_MASK ((uint64_t(1) << 32) - 1)
6195 : #define INT64_LO(i) ((i) & LO_MASK)
6196 : #define INT64_HI(i) ((i) >> 32)
6197 :
6198 : JSBool
6199 4 : Int64::Lo(JSContext* cx, unsigned argc, jsval* vp)
6200 : {
6201 4 : jsval* argv = JS_ARGV(cx, vp);
6202 7 : if (argc != 1 || JSVAL_IS_PRIMITIVE(argv[0]) ||
6203 3 : !Int64::IsInt64(JSVAL_TO_OBJECT(argv[0]))) {
6204 2 : JS_ReportError(cx, "lo takes one Int64 argument");
6205 2 : return JS_FALSE;
6206 : }
6207 :
6208 2 : JSObject* obj = JSVAL_TO_OBJECT(argv[0]);
6209 2 : int64_t u = Int64Base::GetInt(obj);
6210 2 : double d = uint32_t(INT64_LO(u));
6211 :
6212 : jsval result;
6213 2 : if (!JS_NewNumberValue(cx, d, &result))
6214 0 : return JS_FALSE;
6215 :
6216 2 : JS_SET_RVAL(cx, vp, result);
6217 2 : return JS_TRUE;
6218 : }
6219 :
6220 : JSBool
6221 4 : Int64::Hi(JSContext* cx, unsigned argc, jsval* vp)
6222 : {
6223 4 : jsval* argv = JS_ARGV(cx, vp);
6224 7 : if (argc != 1 || JSVAL_IS_PRIMITIVE(argv[0]) ||
6225 3 : !Int64::IsInt64(JSVAL_TO_OBJECT(argv[0]))) {
6226 2 : JS_ReportError(cx, "hi takes one Int64 argument");
6227 2 : return JS_FALSE;
6228 : }
6229 :
6230 2 : JSObject* obj = JSVAL_TO_OBJECT(argv[0]);
6231 2 : int64_t u = Int64Base::GetInt(obj);
6232 2 : double d = int32_t(INT64_HI(u));
6233 :
6234 : jsval result;
6235 2 : if (!JS_NewNumberValue(cx, d, &result))
6236 0 : return JS_FALSE;
6237 :
6238 2 : JS_SET_RVAL(cx, vp, result);
6239 2 : return JS_TRUE;
6240 : }
6241 :
6242 : JSBool
6243 12 : Int64::Join(JSContext* cx, unsigned argc, jsval* vp)
6244 : {
6245 12 : if (argc != 2) {
6246 0 : JS_ReportError(cx, "join takes two arguments");
6247 0 : return JS_FALSE;
6248 : }
6249 :
6250 12 : jsval* argv = JS_ARGV(cx, vp);
6251 : int32_t hi;
6252 : uint32_t lo;
6253 12 : if (!jsvalToInteger(cx, argv[0], &hi))
6254 2 : return TypeError(cx, "int32", argv[0]);
6255 10 : if (!jsvalToInteger(cx, argv[1], &lo))
6256 2 : return TypeError(cx, "uint32", argv[1]);
6257 :
6258 8 : int64_t i = (int64_t(hi) << 32) + int64_t(lo);
6259 :
6260 : // Get Int64.prototype from the function's reserved slot.
6261 8 : JSObject* callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
6262 :
6263 8 : jsval slot = js::GetFunctionNativeReserved(callee, SLOT_FN_INT64PROTO);
6264 8 : JSObject* proto = JSVAL_TO_OBJECT(slot);
6265 8 : JS_ASSERT(JS_GetClass(proto) == &sInt64ProtoClass);
6266 :
6267 8 : JSObject* result = Int64Base::Construct(cx, proto, i, false);
6268 8 : if (!result)
6269 0 : return JS_FALSE;
6270 :
6271 8 : JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
6272 8 : return JS_TRUE;
6273 : }
6274 :
6275 : JSBool
6276 119 : UInt64::Construct(JSContext* cx,
6277 : unsigned argc,
6278 : jsval* vp)
6279 : {
6280 : // Construct and return a new UInt64 object.
6281 119 : if (argc != 1) {
6282 1 : JS_ReportError(cx, "UInt64 takes one argument");
6283 1 : return JS_FALSE;
6284 : }
6285 :
6286 118 : jsval* argv = JS_ARGV(cx, vp);
6287 118 : uint64_t u = 0;
6288 118 : if (!jsvalToBigInteger(cx, argv[0], true, &u))
6289 19 : return TypeError(cx, "uint64", argv[0]);
6290 :
6291 : // Get ctypes.UInt64.prototype from the 'prototype' property of the ctor.
6292 : jsval slot;
6293 : ASSERT_OK(JS_GetProperty(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)),
6294 99 : "prototype", &slot));
6295 99 : JSObject* proto = JSVAL_TO_OBJECT(slot);
6296 99 : JS_ASSERT(JS_GetClass(proto) == &sUInt64ProtoClass);
6297 :
6298 99 : JSObject* result = Int64Base::Construct(cx, proto, u, true);
6299 99 : if (!result)
6300 0 : return JS_FALSE;
6301 :
6302 99 : JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
6303 99 : return JS_TRUE;
6304 : }
6305 :
6306 : bool
6307 7302 : UInt64::IsUInt64(JSObject* obj)
6308 : {
6309 7302 : return JS_GetClass(obj) == &sUInt64Class;
6310 : }
6311 :
6312 : JSBool
6313 2436 : UInt64::ToString(JSContext* cx, unsigned argc, jsval* vp)
6314 : {
6315 2436 : JSObject* obj = JS_THIS_OBJECT(cx, vp);
6316 2436 : if (!obj || !UInt64::IsUInt64(obj)) {
6317 1 : JS_ReportError(cx, "not a UInt64");
6318 1 : return JS_FALSE;
6319 : }
6320 :
6321 2435 : return Int64Base::ToString(cx, obj, argc, vp, true);
6322 : }
6323 :
6324 : JSBool
6325 13 : UInt64::ToSource(JSContext* cx, unsigned argc, jsval* vp)
6326 : {
6327 13 : JSObject* obj = JS_THIS_OBJECT(cx, vp);
6328 13 : if (!obj || !UInt64::IsUInt64(obj)) {
6329 1 : JS_ReportError(cx, "not a UInt64");
6330 1 : return JS_FALSE;
6331 : }
6332 :
6333 12 : return Int64Base::ToSource(cx, obj, argc, vp, true);
6334 : }
6335 :
6336 : JSBool
6337 10 : UInt64::Compare(JSContext* cx, unsigned argc, jsval* vp)
6338 : {
6339 10 : jsval* argv = JS_ARGV(cx, vp);
6340 47 : if (argc != 2 ||
6341 10 : JSVAL_IS_PRIMITIVE(argv[0]) ||
6342 9 : JSVAL_IS_PRIMITIVE(argv[1]) ||
6343 9 : !UInt64::IsUInt64(JSVAL_TO_OBJECT(argv[0])) ||
6344 9 : !UInt64::IsUInt64(JSVAL_TO_OBJECT(argv[1]))) {
6345 2 : JS_ReportError(cx, "compare takes two UInt64 arguments");
6346 2 : return JS_FALSE;
6347 : }
6348 :
6349 8 : JSObject* obj1 = JSVAL_TO_OBJECT(argv[0]);
6350 8 : JSObject* obj2 = JSVAL_TO_OBJECT(argv[1]);
6351 :
6352 8 : uint64_t u1 = Int64Base::GetInt(obj1);
6353 8 : uint64_t u2 = Int64Base::GetInt(obj2);
6354 :
6355 8 : if (u1 == u2)
6356 3 : JS_SET_RVAL(cx, vp, INT_TO_JSVAL(0));
6357 5 : else if (u1 < u2)
6358 4 : JS_SET_RVAL(cx, vp, INT_TO_JSVAL(-1));
6359 : else
6360 1 : JS_SET_RVAL(cx, vp, INT_TO_JSVAL(1));
6361 :
6362 8 : return JS_TRUE;
6363 : }
6364 :
6365 : JSBool
6366 4 : UInt64::Lo(JSContext* cx, unsigned argc, jsval* vp)
6367 : {
6368 4 : jsval* argv = JS_ARGV(cx, vp);
6369 7 : if (argc != 1 || JSVAL_IS_PRIMITIVE(argv[0]) ||
6370 3 : !UInt64::IsUInt64(JSVAL_TO_OBJECT(argv[0]))) {
6371 2 : JS_ReportError(cx, "lo takes one UInt64 argument");
6372 2 : return JS_FALSE;
6373 : }
6374 :
6375 2 : JSObject* obj = JSVAL_TO_OBJECT(argv[0]);
6376 2 : uint64_t u = Int64Base::GetInt(obj);
6377 2 : double d = uint32_t(INT64_LO(u));
6378 :
6379 : jsval result;
6380 2 : if (!JS_NewNumberValue(cx, d, &result))
6381 0 : return JS_FALSE;
6382 :
6383 2 : JS_SET_RVAL(cx, vp, result);
6384 2 : return JS_TRUE;
6385 : }
6386 :
6387 : JSBool
6388 4 : UInt64::Hi(JSContext* cx, unsigned argc, jsval* vp)
6389 : {
6390 4 : jsval* argv = JS_ARGV(cx, vp);
6391 7 : if (argc != 1 || JSVAL_IS_PRIMITIVE(argv[0]) ||
6392 3 : !UInt64::IsUInt64(JSVAL_TO_OBJECT(argv[0]))) {
6393 2 : JS_ReportError(cx, "hi takes one UInt64 argument");
6394 2 : return JS_FALSE;
6395 : }
6396 :
6397 2 : JSObject* obj = JSVAL_TO_OBJECT(argv[0]);
6398 2 : uint64_t u = Int64Base::GetInt(obj);
6399 2 : double d = uint32_t(INT64_HI(u));
6400 :
6401 : jsval result;
6402 2 : if (!JS_NewNumberValue(cx, d, &result))
6403 0 : return JS_FALSE;
6404 :
6405 2 : JS_SET_RVAL(cx, vp, result);
6406 2 : return JS_TRUE;
6407 : }
6408 :
6409 : JSBool
6410 14 : UInt64::Join(JSContext* cx, unsigned argc, jsval* vp)
6411 : {
6412 14 : if (argc != 2) {
6413 0 : JS_ReportError(cx, "join takes two arguments");
6414 0 : return JS_FALSE;
6415 : }
6416 :
6417 14 : jsval* argv = JS_ARGV(cx, vp);
6418 : uint32_t hi;
6419 : uint32_t lo;
6420 14 : if (!jsvalToInteger(cx, argv[0], &hi))
6421 2 : return TypeError(cx, "uint32_t", argv[0]);
6422 12 : if (!jsvalToInteger(cx, argv[1], &lo))
6423 2 : return TypeError(cx, "uint32_t", argv[1]);
6424 :
6425 10 : uint64_t u = (uint64_t(hi) << 32) + uint64_t(lo);
6426 :
6427 : // Get UInt64.prototype from the function's reserved slot.
6428 10 : JSObject* callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
6429 :
6430 10 : jsval slot = js::GetFunctionNativeReserved(callee, SLOT_FN_INT64PROTO);
6431 10 : JSObject* proto = JSVAL_TO_OBJECT(slot);
6432 10 : JS_ASSERT(JS_GetClass(proto) == &sUInt64ProtoClass);
6433 :
6434 10 : JSObject* result = Int64Base::Construct(cx, proto, u, true);
6435 10 : if (!result)
6436 0 : return JS_FALSE;
6437 :
6438 10 : JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
6439 10 : return JS_TRUE;
6440 : }
6441 :
6442 : }
6443 : }
6444 :
|