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