Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ctypes/CTypes.h"
9 #include "mozilla/FloatingPoint.h"
10 #include "mozilla/MemoryReporting.h"
11 #include "mozilla/NumericLimits.h"
13 #include <math.h>
14 #include <stdint.h>
16 #if defined(XP_WIN)
17 #include <float.h>
18 #endif
20 #if defined(SOLARIS)
21 #include <ieeefp.h>
22 #endif
24 #ifdef HAVE_SSIZE_T
25 #include <sys/types.h>
26 #endif
28 #if defined(XP_UNIX)
29 #include <errno.h>
30 #elif defined(XP_WIN)
31 #include <windows.h>
32 #endif
34 #include "jscntxt.h"
35 #include "jsfun.h"
36 #include "jsnum.h"
37 #include "jsprf.h"
39 #include "builtin/TypedObject.h"
40 #include "ctypes/Library.h"
42 using namespace std;
43 using mozilla::NumericLimits;
45 namespace js {
46 namespace ctypes {
48 size_t
49 GetDeflatedUTF8StringLength(JSContext *maybecx, const jschar *chars,
50 size_t nchars)
51 {
52 size_t nbytes;
53 const jschar *end;
54 unsigned c, c2;
55 char buffer[10];
57 nbytes = nchars;
58 for (end = chars + nchars; chars != end; chars++) {
59 c = *chars;
60 if (c < 0x80)
61 continue;
62 if (0xD800 <= c && c <= 0xDFFF) {
63 /* Surrogate pair. */
64 chars++;
66 /* nbytes sets 1 length since this is surrogate pair. */
67 nbytes--;
68 if (c >= 0xDC00 || chars == end)
69 goto bad_surrogate;
70 c2 = *chars;
71 if (c2 < 0xDC00 || c2 > 0xDFFF)
72 goto bad_surrogate;
73 c = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
74 }
75 c >>= 11;
76 nbytes++;
77 while (c) {
78 c >>= 5;
79 nbytes++;
80 }
81 }
82 return nbytes;
84 bad_surrogate:
85 if (maybecx) {
86 JS_snprintf(buffer, 10, "0x%x", c);
87 JS_ReportErrorFlagsAndNumber(maybecx, JSREPORT_ERROR, js_GetErrorMessage,
88 nullptr, JSMSG_BAD_SURROGATE_CHAR, buffer);
89 }
90 return (size_t) -1;
91 }
93 bool
94 DeflateStringToUTF8Buffer(JSContext *maybecx, const jschar *src, size_t srclen,
95 char *dst, size_t *dstlenp)
96 {
97 size_t i, utf8Len;
98 jschar c, c2;
99 uint32_t v;
100 uint8_t utf8buf[6];
102 size_t dstlen = *dstlenp;
103 size_t origDstlen = dstlen;
105 while (srclen) {
106 c = *src++;
107 srclen--;
108 if (c >= 0xDC00 && c <= 0xDFFF)
109 goto badSurrogate;
110 if (c < 0xD800 || c > 0xDBFF) {
111 v = c;
112 } else {
113 if (srclen < 1)
114 goto badSurrogate;
115 c2 = *src;
116 if ((c2 < 0xDC00) || (c2 > 0xDFFF))
117 goto badSurrogate;
118 src++;
119 srclen--;
120 v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
121 }
122 if (v < 0x0080) {
123 /* no encoding necessary - performance hack */
124 if (dstlen == 0)
125 goto bufferTooSmall;
126 *dst++ = (char) v;
127 utf8Len = 1;
128 } else {
129 utf8Len = js_OneUcs4ToUtf8Char(utf8buf, v);
130 if (utf8Len > dstlen)
131 goto bufferTooSmall;
132 for (i = 0; i < utf8Len; i++)
133 *dst++ = (char) utf8buf[i];
134 }
135 dstlen -= utf8Len;
136 }
137 *dstlenp = (origDstlen - dstlen);
138 return true;
140 badSurrogate:
141 *dstlenp = (origDstlen - dstlen);
142 /* Delegate error reporting to the measurement function. */
143 if (maybecx)
144 GetDeflatedUTF8StringLength(maybecx, src - 1, srclen + 1);
145 return false;
147 bufferTooSmall:
148 *dstlenp = (origDstlen - dstlen);
149 if (maybecx) {
150 JS_ReportErrorNumber(maybecx, js_GetErrorMessage, nullptr,
151 JSMSG_BUFFER_TOO_SMALL);
152 }
153 return false;
154 }
156 /*******************************************************************************
157 ** JSAPI function prototypes
158 *******************************************************************************/
160 // We use an enclosing struct here out of paranoia about the ability of gcc 4.4
161 // (and maybe 4.5) to correctly compile this if it were a template function.
162 // See also the comments in dom/workers/Events.cpp (and other adjacent files) by
163 // the |struct Property| there.
164 template<JS::IsAcceptableThis Test, JS::NativeImpl Impl>
165 struct Property
166 {
167 static bool
168 Fun(JSContext* cx, unsigned argc, JS::Value* vp)
169 {
170 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
171 return JS::CallNonGenericMethod<Test, Impl>(cx, args);
172 }
173 };
175 static bool ConstructAbstract(JSContext* cx, unsigned argc, jsval* vp);
177 namespace CType {
178 static bool ConstructData(JSContext* cx, unsigned argc, jsval* vp);
179 static bool ConstructBasic(JSContext* cx, HandleObject obj, const CallArgs& args);
181 static void Trace(JSTracer* trc, JSObject* obj);
182 static void Finalize(JSFreeOp *fop, JSObject* obj);
184 bool IsCType(HandleValue v);
185 bool IsCTypeOrProto(HandleValue v);
187 bool PrototypeGetter(JSContext* cx, JS::CallArgs args);
188 bool NameGetter(JSContext* cx, JS::CallArgs args);
189 bool SizeGetter(JSContext* cx, JS::CallArgs args);
190 bool PtrGetter(JSContext* cx, JS::CallArgs args);
192 static bool CreateArray(JSContext* cx, unsigned argc, jsval* vp);
193 static bool ToString(JSContext* cx, unsigned argc, jsval* vp);
194 static bool ToSource(JSContext* cx, unsigned argc, jsval* vp);
195 static bool HasInstance(JSContext* cx, HandleObject obj, MutableHandleValue v, bool* bp);
198 /*
199 * Get the global "ctypes" object.
200 *
201 * |obj| must be a CType object.
202 *
203 * This function never returns nullptr.
204 */
205 static JSObject* GetGlobalCTypes(JSContext* cx, JSObject* obj);
207 }
209 namespace ABI {
210 bool IsABI(JSObject* obj);
211 static bool ToSource(JSContext* cx, unsigned argc, jsval* vp);
212 }
214 namespace PointerType {
215 static bool Create(JSContext* cx, unsigned argc, jsval* vp);
216 static bool ConstructData(JSContext* cx, HandleObject obj, const CallArgs& args);
218 bool IsPointerType(HandleValue v);
219 bool IsPointer(HandleValue v);
221 bool TargetTypeGetter(JSContext* cx, JS::CallArgs args);
222 bool ContentsGetter(JSContext* cx, JS::CallArgs args);
223 bool ContentsSetter(JSContext* cx, JS::CallArgs args);
225 static bool IsNull(JSContext* cx, unsigned argc, jsval* vp);
226 static bool Increment(JSContext* cx, unsigned argc, jsval* vp);
227 static bool Decrement(JSContext* cx, unsigned argc, jsval* vp);
228 // The following is not an instance function, since we don't want to expose arbitrary
229 // pointer arithmetic at this moment.
230 static bool OffsetBy(JSContext* cx, const CallArgs& args, int offset);
231 }
233 namespace ArrayType {
234 bool IsArrayType(HandleValue v);
235 bool IsArrayOrArrayType(HandleValue v);
237 static bool Create(JSContext* cx, unsigned argc, jsval* vp);
238 static bool ConstructData(JSContext* cx, HandleObject obj, const CallArgs& args);
240 bool ElementTypeGetter(JSContext* cx, JS::CallArgs args);
241 bool LengthGetter(JSContext* cx, JS::CallArgs args);
243 static bool Getter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp);
244 static bool Setter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, MutableHandleValue vp);
245 static bool AddressOfElement(JSContext* cx, unsigned argc, jsval* vp);
246 }
248 namespace StructType {
249 bool IsStruct(HandleValue v);
251 static bool Create(JSContext* cx, unsigned argc, jsval* vp);
252 static bool ConstructData(JSContext* cx, HandleObject obj, const CallArgs& args);
254 bool FieldsArrayGetter(JSContext* cx, JS::CallArgs args);
256 static bool FieldGetter(JSContext* cx, HandleObject obj, HandleId idval,
257 MutableHandleValue vp);
258 static bool FieldSetter(JSContext* cx, HandleObject obj, HandleId idval, bool strict,
259 MutableHandleValue vp);
260 static bool AddressOfField(JSContext* cx, unsigned argc, jsval* vp);
261 static bool Define(JSContext* cx, unsigned argc, jsval* vp);
262 }
264 namespace FunctionType {
265 static bool Create(JSContext* cx, unsigned argc, jsval* vp);
266 static bool ConstructData(JSContext* cx, HandleObject typeObj,
267 HandleObject dataObj, HandleObject fnObj, HandleObject thisObj, jsval errVal);
269 static bool Call(JSContext* cx, unsigned argc, jsval* vp);
271 bool IsFunctionType(HandleValue v);
273 bool ArgTypesGetter(JSContext* cx, JS::CallArgs args);
274 bool ReturnTypeGetter(JSContext* cx, JS::CallArgs args);
275 bool ABIGetter(JSContext* cx, JS::CallArgs args);
276 bool IsVariadicGetter(JSContext* cx, JS::CallArgs args);
277 }
279 namespace CClosure {
280 static void Trace(JSTracer* trc, JSObject* obj);
281 static void Finalize(JSFreeOp *fop, JSObject* obj);
283 // libffi callback
284 static void ClosureStub(ffi_cif* cif, void* result, void** args,
285 void* userData);
286 }
288 namespace CData {
289 static void Finalize(JSFreeOp *fop, JSObject* obj);
291 bool ValueGetter(JSContext* cx, JS::CallArgs args);
292 bool ValueSetter(JSContext* cx, JS::CallArgs args);
294 static bool Address(JSContext* cx, unsigned argc, jsval* vp);
295 static bool ReadString(JSContext* cx, unsigned argc, jsval* vp);
296 static bool ReadStringReplaceMalformed(JSContext* cx, unsigned argc, jsval* vp);
297 static bool ToSource(JSContext* cx, unsigned argc, jsval* vp);
298 static JSString *GetSourceString(JSContext *cx, HandleObject typeObj,
299 void *data);
301 bool ErrnoGetter(JSContext* cx, JS::CallArgs args);
303 #if defined(XP_WIN)
304 bool LastErrorGetter(JSContext* cx, JS::CallArgs args);
305 #endif // defined(XP_WIN)
306 }
308 namespace CDataFinalizer {
309 /*
310 * Attach a C function as a finalizer to a JS object.
311 *
312 * This function is available from JS as |ctypes.withFinalizer|.
313 *
314 * JavaScript signature:
315 * function(CData, CData): CDataFinalizer
316 * value finalizer finalizable
317 *
318 * Where |finalizer| is a one-argument function taking a value
319 * with the same type as |value|.
320 */
321 static bool Construct(JSContext* cx, unsigned argc, jsval *vp);
323 /*
324 * Private data held by |CDataFinalizer|.
325 *
326 * See also |enum CDataFinalizerSlot| for the slots of
327 * |CDataFinalizer|.
328 *
329 * Note: the private data may be nullptr, if |dispose|, |forget| or the
330 * finalizer has already been called.
331 */
332 struct Private {
333 /*
334 * The C data to pass to the code.
335 * Finalization/|dispose|/|forget| release this memory.
336 */
337 void *cargs;
339 /*
340 * The total size of the buffer pointed by |cargs|
341 */
342 size_t cargs_size;
344 /*
345 * Low-level signature information.
346 * Finalization/|dispose|/|forget| release this memory.
347 */
348 ffi_cif CIF;
350 /*
351 * The C function to invoke during finalization.
352 * Do not deallocate this.
353 */
354 uintptr_t code;
356 /*
357 * A buffer for holding the return value.
358 * Finalization/|dispose|/|forget| release this memory.
359 */
360 void *rvalue;
361 };
363 /*
364 * Methods of instances of |CDataFinalizer|
365 */
366 namespace Methods {
367 static bool Dispose(JSContext* cx, unsigned argc, jsval *vp);
368 static bool Forget(JSContext* cx, unsigned argc, jsval *vp);
369 static bool ToSource(JSContext* cx, unsigned argc, jsval *vp);
370 static bool ToString(JSContext* cx, unsigned argc, jsval *vp);
371 }
373 /*
374 * Utility functions
375 *
376 * @return true if |obj| is a CDataFinalizer, false otherwise.
377 */
378 static bool IsCDataFinalizer(JSObject *obj);
380 /*
381 * Clean up the finalization information of a CDataFinalizer.
382 *
383 * Used by |Finalize|, |Dispose| and |Forget|.
384 *
385 * @param p The private information of the CDataFinalizer. If nullptr,
386 * this function does nothing.
387 * @param obj Either nullptr, if the object should not be cleaned up (i.e.
388 * during finalization) or a CDataFinalizer JSObject. Always use nullptr
389 * if you are calling from a finalizer.
390 */
391 static void Cleanup(Private *p, JSObject *obj);
393 /*
394 * Perform the actual call to the finalizer code.
395 */
396 static void CallFinalizer(CDataFinalizer::Private *p,
397 int* errnoStatus,
398 int32_t* lastErrorStatus);
400 /*
401 * Return the CType of a CDataFinalizer object, or nullptr if the object
402 * has been cleaned-up already.
403 */
404 static JSObject *GetCType(JSContext *cx, JSObject *obj);
406 /*
407 * Perform finalization of a |CDataFinalizer|
408 */
409 static void Finalize(JSFreeOp *fop, JSObject *obj);
411 /*
412 * Return the jsval contained by this finalizer.
413 *
414 * Note that the jsval is actually not recorded, but converted back from C.
415 */
416 static bool GetValue(JSContext *cx, JSObject *obj, jsval *result);
418 static JSObject* GetCData(JSContext *cx, JSObject *obj);
419 }
422 // Int64Base provides functions common to Int64 and UInt64.
423 namespace Int64Base {
424 JSObject* Construct(JSContext* cx, HandleObject proto, uint64_t data,
425 bool isUnsigned);
427 uint64_t GetInt(JSObject* obj);
429 bool ToString(JSContext* cx, JSObject* obj, const CallArgs& args,
430 bool isUnsigned);
432 bool ToSource(JSContext* cx, JSObject* obj, const CallArgs& args,
433 bool isUnsigned);
435 static void Finalize(JSFreeOp *fop, JSObject* obj);
436 }
438 namespace Int64 {
439 static bool Construct(JSContext* cx, unsigned argc, jsval* vp);
441 static bool ToString(JSContext* cx, unsigned argc, jsval* vp);
442 static bool ToSource(JSContext* cx, unsigned argc, jsval* vp);
444 static bool Compare(JSContext* cx, unsigned argc, jsval* vp);
445 static bool Lo(JSContext* cx, unsigned argc, jsval* vp);
446 static bool Hi(JSContext* cx, unsigned argc, jsval* vp);
447 static bool Join(JSContext* cx, unsigned argc, jsval* vp);
448 }
450 namespace UInt64 {
451 static bool Construct(JSContext* cx, unsigned argc, jsval* vp);
453 static bool ToString(JSContext* cx, unsigned argc, jsval* vp);
454 static bool ToSource(JSContext* cx, unsigned argc, jsval* vp);
456 static bool Compare(JSContext* cx, unsigned argc, jsval* vp);
457 static bool Lo(JSContext* cx, unsigned argc, jsval* vp);
458 static bool Hi(JSContext* cx, unsigned argc, jsval* vp);
459 static bool Join(JSContext* cx, unsigned argc, jsval* vp);
460 }
462 /*******************************************************************************
463 ** JSClass definitions and initialization functions
464 *******************************************************************************/
466 // Class representing the 'ctypes' object itself. This exists to contain the
467 // JSCTypesCallbacks set of function pointers.
468 static const JSClass sCTypesGlobalClass = {
469 "ctypes",
470 JSCLASS_HAS_RESERVED_SLOTS(CTYPESGLOBAL_SLOTS),
471 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
472 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
473 };
475 static const JSClass sCABIClass = {
476 "CABI",
477 JSCLASS_HAS_RESERVED_SLOTS(CABI_SLOTS),
478 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
479 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
480 };
482 // Class representing ctypes.{C,Pointer,Array,Struct,Function}Type.prototype.
483 // This exists to give said prototypes a class of "CType", and to provide
484 // reserved slots for stashing various other prototype objects.
485 static const JSClass sCTypeProtoClass = {
486 "CType",
487 JSCLASS_HAS_RESERVED_SLOTS(CTYPEPROTO_SLOTS),
488 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
489 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, nullptr,
490 ConstructAbstract, nullptr, ConstructAbstract
491 };
493 // Class representing ctypes.CData.prototype and the 'prototype' properties
494 // of CTypes. This exists to give said prototypes a class of "CData".
495 static const JSClass sCDataProtoClass = {
496 "CData",
497 0,
498 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
499 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
500 };
502 static const JSClass sCTypeClass = {
503 "CType",
504 JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS),
505 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
506 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CType::Finalize,
507 CType::ConstructData, CType::HasInstance, CType::ConstructData,
508 CType::Trace
509 };
511 static const JSClass sCDataClass = {
512 "CData",
513 JSCLASS_HAS_RESERVED_SLOTS(CDATA_SLOTS),
514 JS_PropertyStub, JS_DeletePropertyStub, ArrayType::Getter, ArrayType::Setter,
515 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CData::Finalize,
516 FunctionType::Call, nullptr, FunctionType::Call
517 };
519 static const JSClass sCClosureClass = {
520 "CClosure",
521 JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS),
522 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
523 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CClosure::Finalize,
524 nullptr, nullptr, nullptr, CClosure::Trace
525 };
527 /*
528 * Class representing the prototype of CDataFinalizer.
529 */
530 static const JSClass sCDataFinalizerProtoClass = {
531 "CDataFinalizer",
532 0,
533 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
534 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
535 };
537 /*
538 * Class representing instances of CDataFinalizer.
539 *
540 * Instances of CDataFinalizer have both private data (with type
541 * |CDataFinalizer::Private|) and slots (see |CDataFinalizerSlots|).
542 */
543 static const JSClass sCDataFinalizerClass = {
544 "CDataFinalizer",
545 JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(CDATAFINALIZER_SLOTS),
546 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
547 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CDataFinalizer::Finalize,
548 };
551 #define CTYPESFN_FLAGS \
552 (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
554 #define CTYPESCTOR_FLAGS \
555 (CTYPESFN_FLAGS | JSFUN_CONSTRUCTOR)
557 #define CTYPESACC_FLAGS \
558 (JSPROP_ENUMERATE | JSPROP_PERMANENT)
560 #define CABIFN_FLAGS \
561 (JSPROP_READONLY | JSPROP_PERMANENT)
563 #define CDATAFN_FLAGS \
564 (JSPROP_READONLY | JSPROP_PERMANENT)
566 #define CDATAFINALIZERFN_FLAGS \
567 (JSPROP_READONLY | JSPROP_PERMANENT)
569 static const JSPropertySpec sCTypeProps[] = {
570 JS_PSG("name",
571 (Property<CType::IsCType, CType::NameGetter>::Fun),
572 CTYPESACC_FLAGS),
573 JS_PSG("size",
574 (Property<CType::IsCType, CType::SizeGetter>::Fun),
575 CTYPESACC_FLAGS),
576 JS_PSG("ptr",
577 (Property<CType::IsCType, CType::PtrGetter>::Fun),
578 CTYPESACC_FLAGS),
579 JS_PSG("prototype",
580 (Property<CType::IsCTypeOrProto, CType::PrototypeGetter>::Fun),
581 CTYPESACC_FLAGS),
582 JS_PS_END
583 };
585 static const JSFunctionSpec sCTypeFunctions[] = {
586 JS_FN("array", CType::CreateArray, 0, CTYPESFN_FLAGS),
587 JS_FN("toString", CType::ToString, 0, CTYPESFN_FLAGS),
588 JS_FN("toSource", CType::ToSource, 0, CTYPESFN_FLAGS),
589 JS_FS_END
590 };
592 static const JSFunctionSpec sCABIFunctions[] = {
593 JS_FN("toSource", ABI::ToSource, 0, CABIFN_FLAGS),
594 JS_FN("toString", ABI::ToSource, 0, CABIFN_FLAGS),
595 JS_FS_END
596 };
598 static const JSPropertySpec sCDataProps[] = {
599 JS_PSGS("value",
600 (Property<CData::IsCData, CData::ValueGetter>::Fun),
601 (Property<CData::IsCData, CData::ValueSetter>::Fun),
602 JSPROP_PERMANENT),
603 JS_PS_END
604 };
606 static const JSFunctionSpec sCDataFunctions[] = {
607 JS_FN("address", CData::Address, 0, CDATAFN_FLAGS),
608 JS_FN("readString", CData::ReadString, 0, CDATAFN_FLAGS),
609 JS_FN("readStringReplaceMalformed", CData::ReadStringReplaceMalformed, 0, CDATAFN_FLAGS),
610 JS_FN("toSource", CData::ToSource, 0, CDATAFN_FLAGS),
611 JS_FN("toString", CData::ToSource, 0, CDATAFN_FLAGS),
612 JS_FS_END
613 };
615 static const JSFunctionSpec sCDataFinalizerFunctions[] = {
616 JS_FN("dispose", CDataFinalizer::Methods::Dispose, 0, CDATAFINALIZERFN_FLAGS),
617 JS_FN("forget", CDataFinalizer::Methods::Forget, 0, CDATAFINALIZERFN_FLAGS),
618 JS_FN("readString",CData::ReadString, 0, CDATAFINALIZERFN_FLAGS),
619 JS_FN("toString", CDataFinalizer::Methods::ToString, 0, CDATAFINALIZERFN_FLAGS),
620 JS_FN("toSource", CDataFinalizer::Methods::ToSource, 0, CDATAFINALIZERFN_FLAGS),
621 JS_FS_END
622 };
624 static const JSFunctionSpec sPointerFunction =
625 JS_FN("PointerType", PointerType::Create, 1, CTYPESCTOR_FLAGS);
627 static const JSPropertySpec sPointerProps[] = {
628 JS_PSG("targetType",
629 (Property<PointerType::IsPointerType, PointerType::TargetTypeGetter>::Fun),
630 CTYPESACC_FLAGS),
631 JS_PS_END
632 };
634 static const JSFunctionSpec sPointerInstanceFunctions[] = {
635 JS_FN("isNull", PointerType::IsNull, 0, CTYPESFN_FLAGS),
636 JS_FN("increment", PointerType::Increment, 0, CTYPESFN_FLAGS),
637 JS_FN("decrement", PointerType::Decrement, 0, CTYPESFN_FLAGS),
638 JS_FS_END
639 };
641 static const JSPropertySpec sPointerInstanceProps[] = {
642 JS_PSGS("contents",
643 (Property<PointerType::IsPointer, PointerType::ContentsGetter>::Fun),
644 (Property<PointerType::IsPointer, PointerType::ContentsSetter>::Fun),
645 JSPROP_PERMANENT),
646 JS_PS_END
647 };
649 static const JSFunctionSpec sArrayFunction =
650 JS_FN("ArrayType", ArrayType::Create, 1, CTYPESCTOR_FLAGS);
652 static const JSPropertySpec sArrayProps[] = {
653 JS_PSG("elementType",
654 (Property<ArrayType::IsArrayType, ArrayType::ElementTypeGetter>::Fun),
655 CTYPESACC_FLAGS),
656 JS_PSG("length",
657 (Property<ArrayType::IsArrayOrArrayType, ArrayType::LengthGetter>::Fun),
658 CTYPESACC_FLAGS),
659 JS_PS_END
660 };
662 static const JSFunctionSpec sArrayInstanceFunctions[] = {
663 JS_FN("addressOfElement", ArrayType::AddressOfElement, 1, CDATAFN_FLAGS),
664 JS_FS_END
665 };
667 static const JSPropertySpec sArrayInstanceProps[] = {
668 JS_PSG("length",
669 (Property<ArrayType::IsArrayOrArrayType, ArrayType::LengthGetter>::Fun),
670 JSPROP_PERMANENT),
671 JS_PS_END
672 };
674 static const JSFunctionSpec sStructFunction =
675 JS_FN("StructType", StructType::Create, 2, CTYPESCTOR_FLAGS);
677 static const JSPropertySpec sStructProps[] = {
678 JS_PSG("fields",
679 (Property<StructType::IsStruct, StructType::FieldsArrayGetter>::Fun),
680 CTYPESACC_FLAGS),
681 JS_PS_END
682 };
684 static const JSFunctionSpec sStructFunctions[] = {
685 JS_FN("define", StructType::Define, 1, CDATAFN_FLAGS),
686 JS_FS_END
687 };
689 static const JSFunctionSpec sStructInstanceFunctions[] = {
690 JS_FN("addressOfField", StructType::AddressOfField, 1, CDATAFN_FLAGS),
691 JS_FS_END
692 };
694 static const JSFunctionSpec sFunctionFunction =
695 JS_FN("FunctionType", FunctionType::Create, 2, CTYPESCTOR_FLAGS);
697 static const JSPropertySpec sFunctionProps[] = {
698 JS_PSG("argTypes",
699 (Property<FunctionType::IsFunctionType, FunctionType::ArgTypesGetter>::Fun),
700 CTYPESACC_FLAGS),
701 JS_PSG("returnType",
702 (Property<FunctionType::IsFunctionType, FunctionType::ReturnTypeGetter>::Fun),
703 CTYPESACC_FLAGS),
704 JS_PSG("abi",
705 (Property<FunctionType::IsFunctionType, FunctionType::ABIGetter>::Fun),
706 CTYPESACC_FLAGS),
707 JS_PSG("isVariadic",
708 (Property<FunctionType::IsFunctionType, FunctionType::IsVariadicGetter>::Fun),
709 CTYPESACC_FLAGS),
710 JS_PS_END
711 };
713 static const JSFunctionSpec sFunctionInstanceFunctions[] = {
714 JS_FN("call", js_fun_call, 1, CDATAFN_FLAGS),
715 JS_FN("apply", js_fun_apply, 2, CDATAFN_FLAGS),
716 JS_FS_END
717 };
719 static const JSClass sInt64ProtoClass = {
720 "Int64",
721 0,
722 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
723 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
724 };
726 static const JSClass sUInt64ProtoClass = {
727 "UInt64",
728 0,
729 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
730 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
731 };
733 static const JSClass sInt64Class = {
734 "Int64",
735 JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS),
736 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
737 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Int64Base::Finalize
738 };
740 static const JSClass sUInt64Class = {
741 "UInt64",
742 JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS),
743 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
744 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Int64Base::Finalize
745 };
747 static const JSFunctionSpec sInt64StaticFunctions[] = {
748 JS_FN("compare", Int64::Compare, 2, CTYPESFN_FLAGS),
749 JS_FN("lo", Int64::Lo, 1, CTYPESFN_FLAGS),
750 JS_FN("hi", Int64::Hi, 1, CTYPESFN_FLAGS),
751 JS_FN("join", Int64::Join, 2, CTYPESFN_FLAGS),
752 JS_FS_END
753 };
755 static const JSFunctionSpec sUInt64StaticFunctions[] = {
756 JS_FN("compare", UInt64::Compare, 2, CTYPESFN_FLAGS),
757 JS_FN("lo", UInt64::Lo, 1, CTYPESFN_FLAGS),
758 JS_FN("hi", UInt64::Hi, 1, CTYPESFN_FLAGS),
759 JS_FN("join", UInt64::Join, 2, CTYPESFN_FLAGS),
760 JS_FS_END
761 };
763 static const JSFunctionSpec sInt64Functions[] = {
764 JS_FN("toString", Int64::ToString, 0, CTYPESFN_FLAGS),
765 JS_FN("toSource", Int64::ToSource, 0, CTYPESFN_FLAGS),
766 JS_FS_END
767 };
769 static const JSFunctionSpec sUInt64Functions[] = {
770 JS_FN("toString", UInt64::ToString, 0, CTYPESFN_FLAGS),
771 JS_FN("toSource", UInt64::ToSource, 0, CTYPESFN_FLAGS),
772 JS_FS_END
773 };
775 static const JSPropertySpec sModuleProps[] = {
776 JS_PSG("errno",
777 (Property<IsCTypesGlobal, CData::ErrnoGetter>::Fun),
778 JSPROP_PERMANENT),
779 #if defined(XP_WIN)
780 JS_PSG("winLastError",
781 (Property<IsCTypesGlobal, CData::LastErrorGetter>::Fun),
782 JSPROP_PERMANENT),
783 #endif // defined(XP_WIN)
784 JS_PS_END
785 };
787 static const JSFunctionSpec sModuleFunctions[] = {
788 JS_FN("CDataFinalizer", CDataFinalizer::Construct, 2, CTYPESFN_FLAGS),
789 JS_FN("open", Library::Open, 1, CTYPESFN_FLAGS),
790 JS_FN("cast", CData::Cast, 2, CTYPESFN_FLAGS),
791 JS_FN("getRuntime", CData::GetRuntime, 1, CTYPESFN_FLAGS),
792 JS_FN("libraryName", Library::Name, 1, CTYPESFN_FLAGS),
793 JS_FS_END
794 };
796 static MOZ_ALWAYS_INLINE JSString*
797 NewUCString(JSContext* cx, const AutoString& from)
798 {
799 return JS_NewUCStringCopyN(cx, from.begin(), from.length());
800 }
802 /*
803 * Return a size rounded up to a multiple of a power of two.
804 *
805 * Note: |align| must be a power of 2.
806 */
807 static MOZ_ALWAYS_INLINE size_t
808 Align(size_t val, size_t align)
809 {
810 // Ensure that align is a power of two.
811 MOZ_ASSERT(align != 0 && (align & (align - 1)) == 0);
812 return ((val - 1) | (align - 1)) + 1;
813 }
815 static ABICode
816 GetABICode(JSObject* obj)
817 {
818 // make sure we have an object representing a CABI class,
819 // and extract the enumerated class type from the reserved slot.
820 if (JS_GetClass(obj) != &sCABIClass)
821 return INVALID_ABI;
823 jsval result = JS_GetReservedSlot(obj, SLOT_ABICODE);
824 return ABICode(JSVAL_TO_INT(result));
825 }
827 static const JSErrorFormatString ErrorFormatString[CTYPESERR_LIMIT] = {
828 #define MSG_DEF(name, number, count, exception, format) \
829 { format, count, exception } ,
830 #include "ctypes/ctypes.msg"
831 #undef MSG_DEF
832 };
834 static const JSErrorFormatString*
835 GetErrorMessage(void* userRef, const char* locale, const unsigned errorNumber)
836 {
837 if (0 < errorNumber && errorNumber < CTYPESERR_LIMIT)
838 return &ErrorFormatString[errorNumber];
839 return nullptr;
840 }
842 static bool
843 TypeError(JSContext* cx, const char* expected, HandleValue actual)
844 {
845 JSString* str = JS_ValueToSource(cx, actual);
846 JSAutoByteString bytes;
848 const char* src;
849 if (str) {
850 src = bytes.encodeLatin1(cx, str);
851 if (!src)
852 return false;
853 } else {
854 JS_ClearPendingException(cx);
855 src = "<<error converting value to string>>";
856 }
857 JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
858 CTYPESMSG_TYPE_ERROR, expected, src);
859 return false;
860 }
862 static JSObject*
863 InitCTypeClass(JSContext* cx, HandleObject parent)
864 {
865 JSFunction *fun = JS_DefineFunction(cx, parent, "CType", ConstructAbstract, 0,
866 CTYPESCTOR_FLAGS);
867 if (!fun)
868 return nullptr;
870 RootedObject ctor(cx, JS_GetFunctionObject(fun));
871 RootedObject fnproto(cx);
872 if (!JS_GetPrototype(cx, ctor, &fnproto))
873 return nullptr;
874 JS_ASSERT(ctor);
875 JS_ASSERT(fnproto);
877 // Set up ctypes.CType.prototype.
878 RootedObject prototype(cx, JS_NewObject(cx, &sCTypeProtoClass, fnproto, parent));
879 if (!prototype)
880 return nullptr;
882 if (!JS_DefineProperty(cx, ctor, "prototype", prototype,
883 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
884 return nullptr;
886 if (!JS_DefineProperty(cx, prototype, "constructor", ctor,
887 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
888 return nullptr;
890 // Define properties and functions common to all CTypes.
891 if (!JS_DefineProperties(cx, prototype, sCTypeProps) ||
892 !JS_DefineFunctions(cx, prototype, sCTypeFunctions))
893 return nullptr;
895 if (!JS_FreezeObject(cx, ctor) || !JS_FreezeObject(cx, prototype))
896 return nullptr;
898 return prototype;
899 }
901 static JSObject*
902 InitABIClass(JSContext* cx, JSObject* parent)
903 {
904 RootedObject obj(cx, JS_NewObject(cx, nullptr, NullPtr(), NullPtr()));
906 if (!obj)
907 return nullptr;
909 if (!JS_DefineFunctions(cx, obj, sCABIFunctions))
910 return nullptr;
912 return obj;
913 }
916 static JSObject*
917 InitCDataClass(JSContext* cx, HandleObject parent, HandleObject CTypeProto)
918 {
919 JSFunction* fun = JS_DefineFunction(cx, parent, "CData", ConstructAbstract, 0,
920 CTYPESCTOR_FLAGS);
921 if (!fun)
922 return nullptr;
924 RootedObject ctor(cx, JS_GetFunctionObject(fun));
925 JS_ASSERT(ctor);
927 // Set up ctypes.CData.__proto__ === ctypes.CType.prototype.
928 // (Note that 'ctypes.CData instanceof Function' is still true, thanks to the
929 // prototype chain.)
930 if (!JS_SetPrototype(cx, ctor, CTypeProto))
931 return nullptr;
933 // Set up ctypes.CData.prototype.
934 RootedObject prototype(cx, JS_NewObject(cx, &sCDataProtoClass, NullPtr(), parent));
935 if (!prototype)
936 return nullptr;
938 if (!JS_DefineProperty(cx, ctor, "prototype", prototype,
939 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
940 return nullptr;
942 if (!JS_DefineProperty(cx, prototype, "constructor", ctor,
943 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
944 return nullptr;
946 // Define properties and functions common to all CDatas.
947 if (!JS_DefineProperties(cx, prototype, sCDataProps) ||
948 !JS_DefineFunctions(cx, prototype, sCDataFunctions))
949 return nullptr;
951 if (//!JS_FreezeObject(cx, prototype) || // XXX fixme - see bug 541212!
952 !JS_FreezeObject(cx, ctor))
953 return nullptr;
955 return prototype;
956 }
958 static bool
959 DefineABIConstant(JSContext* cx,
960 HandleObject parent,
961 const char* name,
962 ABICode code,
963 HandleObject prototype)
964 {
965 RootedObject obj(cx, JS_DefineObject(cx, parent, name, &sCABIClass, prototype,
966 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT));
967 if (!obj)
968 return false;
969 JS_SetReservedSlot(obj, SLOT_ABICODE, INT_TO_JSVAL(code));
970 return JS_FreezeObject(cx, obj);
971 }
973 // Set up a single type constructor for
974 // ctypes.{Pointer,Array,Struct,Function}Type.
975 static bool
976 InitTypeConstructor(JSContext* cx,
977 HandleObject parent,
978 HandleObject CTypeProto,
979 HandleObject CDataProto,
980 const JSFunctionSpec spec,
981 const JSFunctionSpec* fns,
982 const JSPropertySpec* props,
983 const JSFunctionSpec* instanceFns,
984 const JSPropertySpec* instanceProps,
985 MutableHandleObject typeProto,
986 MutableHandleObject dataProto)
987 {
988 JSFunction* fun = js::DefineFunctionWithReserved(cx, parent, spec.name, spec.call.op,
989 spec.nargs, spec.flags);
990 if (!fun)
991 return false;
993 RootedObject obj(cx, JS_GetFunctionObject(fun));
994 if (!obj)
995 return false;
997 // Set up the .prototype and .prototype.constructor properties.
998 typeProto.set(JS_NewObject(cx, &sCTypeProtoClass, CTypeProto, parent));
999 if (!typeProto)
1000 return false;
1002 // Define property before proceeding, for GC safety.
1003 if (!JS_DefineProperty(cx, obj, "prototype", typeProto,
1004 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
1005 return false;
1007 if (fns && !JS_DefineFunctions(cx, typeProto, fns))
1008 return false;
1010 if (!JS_DefineProperties(cx, typeProto, props))
1011 return false;
1013 if (!JS_DefineProperty(cx, typeProto, "constructor", obj,
1014 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
1015 return false;
1017 // Stash ctypes.{Pointer,Array,Struct}Type.prototype on a reserved slot of
1018 // the type constructor, for faster lookup.
1019 js::SetFunctionNativeReserved(obj, SLOT_FN_CTORPROTO, OBJECT_TO_JSVAL(typeProto));
1021 // Create an object to serve as the common ancestor for all CData objects
1022 // created from the given type constructor. This has ctypes.CData.prototype
1023 // as its prototype, such that it inherits the properties and functions
1024 // common to all CDatas.
1025 dataProto.set(JS_NewObject(cx, &sCDataProtoClass, CDataProto, parent));
1026 if (!dataProto)
1027 return false;
1029 // Define functions and properties on the 'dataProto' object that are common
1030 // to all CData objects created from this type constructor. (These will
1031 // become functions and properties on CData objects created from this type.)
1032 if (instanceFns && !JS_DefineFunctions(cx, dataProto, instanceFns))
1033 return false;
1035 if (instanceProps && !JS_DefineProperties(cx, dataProto, instanceProps))
1036 return false;
1038 // Link the type prototype to the data prototype.
1039 JS_SetReservedSlot(typeProto, SLOT_OURDATAPROTO, OBJECT_TO_JSVAL(dataProto));
1041 if (!JS_FreezeObject(cx, obj) ||
1042 //!JS_FreezeObject(cx, dataProto) || // XXX fixme - see bug 541212!
1043 !JS_FreezeObject(cx, typeProto))
1044 return false;
1046 return true;
1047 }
1049 static JSObject*
1050 InitInt64Class(JSContext* cx,
1051 HandleObject parent,
1052 const JSClass* clasp,
1053 JSNative construct,
1054 const JSFunctionSpec* fs,
1055 const JSFunctionSpec* static_fs)
1056 {
1057 // Init type class and constructor
1058 RootedObject prototype(cx, JS_InitClass(cx, parent, js::NullPtr(), clasp, construct,
1059 0, nullptr, fs, nullptr, static_fs));
1060 if (!prototype)
1061 return nullptr;
1063 RootedObject ctor(cx, JS_GetConstructor(cx, prototype));
1064 if (!ctor)
1065 return nullptr;
1066 if (!JS_FreezeObject(cx, ctor))
1067 return nullptr;
1069 // Redefine the 'join' function as an extended native and stash
1070 // ctypes.{Int64,UInt64}.prototype in a reserved slot of the new function.
1071 JS_ASSERT(clasp == &sInt64ProtoClass || clasp == &sUInt64ProtoClass);
1072 JSNative native = (clasp == &sInt64ProtoClass) ? Int64::Join : UInt64::Join;
1073 JSFunction* fun = js::DefineFunctionWithReserved(cx, ctor, "join", native,
1074 2, CTYPESFN_FLAGS);
1075 if (!fun)
1076 return nullptr;
1078 js::SetFunctionNativeReserved(fun, SLOT_FN_INT64PROTO,
1079 OBJECT_TO_JSVAL(prototype));
1081 if (!JS_FreezeObject(cx, prototype))
1082 return nullptr;
1084 return prototype;
1085 }
1087 static void
1088 AttachProtos(JSObject* proto, const AutoObjectVector& protos)
1089 {
1090 // For a given 'proto' of [[Class]] "CTypeProto", attach each of the 'protos'
1091 // to the appropriate CTypeProtoSlot. (SLOT_CTYPES is the last slot
1092 // of [[Class]] "CTypeProto" that we fill in this automated manner.)
1093 for (uint32_t i = 0; i <= SLOT_CTYPES; ++i)
1094 JS_SetReservedSlot(proto, i, OBJECT_TO_JSVAL(protos[i]));
1095 }
1097 static bool
1098 InitTypeClasses(JSContext* cx, HandleObject parent)
1099 {
1100 // Initialize the ctypes.CType class. This acts as an abstract base class for
1101 // the various types, and provides the common API functions. It has:
1102 // * [[Class]] "Function"
1103 // * __proto__ === Function.prototype
1104 // * A constructor that throws a TypeError. (You can't construct an
1105 // abstract type!)
1106 // * 'prototype' property:
1107 // * [[Class]] "CTypeProto"
1108 // * __proto__ === Function.prototype
1109 // * A constructor that throws a TypeError. (You can't construct an
1110 // abstract type instance!)
1111 // * 'constructor' property === ctypes.CType
1112 // * Provides properties and functions common to all CTypes.
1113 RootedObject CTypeProto(cx, InitCTypeClass(cx, parent));
1114 if (!CTypeProto)
1115 return false;
1117 // Initialize the ctypes.CData class. This acts as an abstract base class for
1118 // instances of the various types, and provides the common API functions.
1119 // It has:
1120 // * [[Class]] "Function"
1121 // * __proto__ === Function.prototype
1122 // * A constructor that throws a TypeError. (You can't construct an
1123 // abstract type instance!)
1124 // * 'prototype' property:
1125 // * [[Class]] "CDataProto"
1126 // * 'constructor' property === ctypes.CData
1127 // * Provides properties and functions common to all CDatas.
1128 RootedObject CDataProto(cx, InitCDataClass(cx, parent, CTypeProto));
1129 if (!CDataProto)
1130 return false;
1132 // Link CTypeProto to CDataProto.
1133 JS_SetReservedSlot(CTypeProto, SLOT_OURDATAPROTO, OBJECT_TO_JSVAL(CDataProto));
1135 // Create and attach the special class constructors: ctypes.PointerType,
1136 // ctypes.ArrayType, ctypes.StructType, and ctypes.FunctionType.
1137 // Each of these constructors 'c' has, respectively:
1138 // * [[Class]] "Function"
1139 // * __proto__ === Function.prototype
1140 // * A constructor that creates a user-defined type.
1141 // * 'prototype' property:
1142 // * [[Class]] "CTypeProto"
1143 // * __proto__ === ctypes.CType.prototype
1144 // * 'constructor' property === 'c'
1145 // We also construct an object 'p' to serve, given a type object 't'
1146 // constructed from one of these type constructors, as
1147 // 't.prototype.__proto__'. This object has:
1148 // * [[Class]] "CDataProto"
1149 // * __proto__ === ctypes.CData.prototype
1150 // * Properties and functions common to all CDatas.
1151 // Therefore an instance 't' of ctypes.{Pointer,Array,Struct,Function}Type
1152 // will have, resp.:
1153 // * [[Class]] "CType"
1154 // * __proto__ === ctypes.{Pointer,Array,Struct,Function}Type.prototype
1155 // * A constructor which creates and returns a CData object, containing
1156 // binary data of the given type.
1157 // * 'prototype' property:
1158 // * [[Class]] "CDataProto"
1159 // * __proto__ === 'p', the prototype object from above
1160 // * 'constructor' property === 't'
1161 AutoObjectVector protos(cx);
1162 protos.resize(CTYPEPROTO_SLOTS);
1163 if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
1164 sPointerFunction, nullptr, sPointerProps,
1165 sPointerInstanceFunctions, sPointerInstanceProps,
1166 protos.handleAt(SLOT_POINTERPROTO), protos.handleAt(SLOT_POINTERDATAPROTO)))
1167 return false;
1169 if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
1170 sArrayFunction, nullptr, sArrayProps,
1171 sArrayInstanceFunctions, sArrayInstanceProps,
1172 protos.handleAt(SLOT_ARRAYPROTO), protos.handleAt(SLOT_ARRAYDATAPROTO)))
1173 return false;
1175 if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
1176 sStructFunction, sStructFunctions, sStructProps,
1177 sStructInstanceFunctions, nullptr,
1178 protos.handleAt(SLOT_STRUCTPROTO), protos.handleAt(SLOT_STRUCTDATAPROTO)))
1179 return false;
1181 if (!InitTypeConstructor(cx, parent, CTypeProto, protos.handleAt(SLOT_POINTERDATAPROTO),
1182 sFunctionFunction, nullptr, sFunctionProps, sFunctionInstanceFunctions, nullptr,
1183 protos.handleAt(SLOT_FUNCTIONPROTO), protos.handleAt(SLOT_FUNCTIONDATAPROTO)))
1184 return false;
1186 protos[SLOT_CDATAPROTO] = CDataProto;
1188 // Create and attach the ctypes.{Int64,UInt64} constructors.
1189 // Each of these has, respectively:
1190 // * [[Class]] "Function"
1191 // * __proto__ === Function.prototype
1192 // * A constructor that creates a ctypes.{Int64,UInt64} object, respectively.
1193 // * 'prototype' property:
1194 // * [[Class]] {"Int64Proto","UInt64Proto"}
1195 // * 'constructor' property === ctypes.{Int64,UInt64}
1196 protos[SLOT_INT64PROTO] = InitInt64Class(cx, parent, &sInt64ProtoClass,
1197 Int64::Construct, sInt64Functions, sInt64StaticFunctions);
1198 if (!protos[SLOT_INT64PROTO])
1199 return false;
1200 protos[SLOT_UINT64PROTO] = InitInt64Class(cx, parent, &sUInt64ProtoClass,
1201 UInt64::Construct, sUInt64Functions, sUInt64StaticFunctions);
1202 if (!protos[SLOT_UINT64PROTO])
1203 return false;
1205 // Finally, store a pointer to the global ctypes object.
1206 // Note that there is no other reliable manner of locating this object.
1207 protos[SLOT_CTYPES] = parent;
1209 // Attach the prototypes just created to each of ctypes.CType.prototype,
1210 // and the special type constructors, so we can access them when constructing
1211 // instances of those types.
1212 AttachProtos(CTypeProto, protos);
1213 AttachProtos(protos[SLOT_POINTERPROTO], protos);
1214 AttachProtos(protos[SLOT_ARRAYPROTO], protos);
1215 AttachProtos(protos[SLOT_STRUCTPROTO], protos);
1216 AttachProtos(protos[SLOT_FUNCTIONPROTO], protos);
1218 RootedObject ABIProto(cx, InitABIClass(cx, parent));
1219 if (!ABIProto)
1220 return false;
1222 // Attach objects representing ABI constants.
1223 if (!DefineABIConstant(cx, parent, "default_abi", ABI_DEFAULT, ABIProto) ||
1224 !DefineABIConstant(cx, parent, "stdcall_abi", ABI_STDCALL, ABIProto) ||
1225 !DefineABIConstant(cx, parent, "winapi_abi", ABI_WINAPI, ABIProto))
1226 return false;
1228 // Create objects representing the builtin types, and attach them to the
1229 // ctypes object. Each type object 't' has:
1230 // * [[Class]] "CType"
1231 // * __proto__ === ctypes.CType.prototype
1232 // * A constructor which creates and returns a CData object, containing
1233 // binary data of the given type.
1234 // * 'prototype' property:
1235 // * [[Class]] "CDataProto"
1236 // * __proto__ === ctypes.CData.prototype
1237 // * 'constructor' property === 't'
1238 #define DEFINE_TYPE(name, type, ffiType) \
1239 RootedObject typeObj_##name(cx, \
1240 CType::DefineBuiltin(cx, parent, #name, CTypeProto, CDataProto, #name, \
1241 TYPE_##name, INT_TO_JSVAL(sizeof(type)), \
1242 INT_TO_JSVAL(ffiType.alignment), &ffiType)); \
1243 if (!typeObj_##name) \
1244 return false;
1245 #include "ctypes/typedefs.h"
1247 // Alias 'ctypes.unsigned' as 'ctypes.unsigned_int', since they represent
1248 // the same type in C.
1249 if (!JS_DefineProperty(cx, parent, "unsigned", typeObj_unsigned_int,
1250 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
1251 return false;
1253 // Create objects representing the special types void_t and voidptr_t.
1254 RootedObject typeObj(cx,
1255 CType::DefineBuiltin(cx, parent, "void_t", CTypeProto, CDataProto, "void",
1256 TYPE_void_t, JSVAL_VOID, JSVAL_VOID, &ffi_type_void));
1257 if (!typeObj)
1258 return false;
1260 typeObj = PointerType::CreateInternal(cx, typeObj);
1261 if (!typeObj)
1262 return false;
1263 if (!JS_DefineProperty(cx, parent, "voidptr_t", typeObj,
1264 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
1265 return false;
1267 return true;
1268 }
1270 bool
1271 IsCTypesGlobal(JSObject* obj)
1272 {
1273 return JS_GetClass(obj) == &sCTypesGlobalClass;
1274 }
1276 bool
1277 IsCTypesGlobal(HandleValue v)
1278 {
1279 return v.isObject() && IsCTypesGlobal(&v.toObject());
1280 }
1282 // Get the JSCTypesCallbacks struct from the 'ctypes' object 'obj'.
1283 JSCTypesCallbacks*
1284 GetCallbacks(JSObject* obj)
1285 {
1286 JS_ASSERT(IsCTypesGlobal(obj));
1288 jsval result = JS_GetReservedSlot(obj, SLOT_CALLBACKS);
1289 if (JSVAL_IS_VOID(result))
1290 return nullptr;
1292 return static_cast<JSCTypesCallbacks*>(JSVAL_TO_PRIVATE(result));
1293 }
1295 // Utility function to access a property of an object as an object
1296 // returns false and sets the error if the property does not exist
1297 // or is not an object
1298 static bool GetObjectProperty(JSContext *cx, HandleObject obj,
1299 const char *property, MutableHandleObject result)
1300 {
1301 RootedValue val(cx);
1302 if (!JS_GetProperty(cx, obj, property, &val)) {
1303 return false;
1304 }
1306 if (JSVAL_IS_PRIMITIVE(val)) {
1307 JS_ReportError(cx, "missing or non-object field");
1308 return false;
1309 }
1311 result.set(JSVAL_TO_OBJECT(val));
1312 return true;
1313 }
1315 } /* namespace ctypes */
1316 } /* namespace js */
1318 using namespace js;
1319 using namespace js::ctypes;
1321 JS_PUBLIC_API(bool)
1322 JS_InitCTypesClass(JSContext* cx, HandleObject global)
1323 {
1324 // attach ctypes property to global object
1325 RootedObject ctypes(cx, JS_NewObject(cx, &sCTypesGlobalClass, NullPtr(), NullPtr()));
1326 if (!ctypes)
1327 return false;
1329 if (!JS_DefineProperty(cx, global, "ctypes", ctypes, JSPROP_READONLY | JSPROP_PERMANENT,
1330 JS_PropertyStub, JS_StrictPropertyStub)){
1331 return false;
1332 }
1334 if (!InitTypeClasses(cx, ctypes))
1335 return false;
1337 // attach API functions and properties
1338 if (!JS_DefineFunctions(cx, ctypes, sModuleFunctions) ||
1339 !JS_DefineProperties(cx, ctypes, sModuleProps))
1340 return false;
1342 // Set up ctypes.CDataFinalizer.prototype.
1343 RootedObject ctor(cx);
1344 if (!GetObjectProperty(cx, ctypes, "CDataFinalizer", &ctor))
1345 return false;
1347 RootedObject prototype(cx, JS_NewObject(cx, &sCDataFinalizerProtoClass, NullPtr(), ctypes));
1348 if (!prototype)
1349 return false;
1351 if (!JS_DefineFunctions(cx, prototype, sCDataFinalizerFunctions))
1352 return false;
1354 if (!JS_DefineProperty(cx, ctor, "prototype", prototype,
1355 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
1356 return false;
1358 if (!JS_DefineProperty(cx, prototype, "constructor", ctor,
1359 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
1360 return false;
1363 // Seal the ctypes object, to prevent modification.
1364 return JS_FreezeObject(cx, ctypes);
1365 }
1367 JS_PUBLIC_API(void)
1368 JS_SetCTypesCallbacks(JSObject *ctypesObj, JSCTypesCallbacks* callbacks)
1369 {
1370 JS_ASSERT(callbacks);
1371 JS_ASSERT(IsCTypesGlobal(ctypesObj));
1373 // Set the callbacks on a reserved slot.
1374 JS_SetReservedSlot(ctypesObj, SLOT_CALLBACKS, PRIVATE_TO_JSVAL(callbacks));
1375 }
1377 namespace js {
1379 JS_FRIEND_API(size_t)
1380 SizeOfDataIfCDataObject(mozilla::MallocSizeOf mallocSizeOf, JSObject *obj)
1381 {
1382 if (!CData::IsCData(obj))
1383 return 0;
1385 size_t n = 0;
1386 jsval slot = JS_GetReservedSlot(obj, ctypes::SLOT_OWNS);
1387 if (!JSVAL_IS_VOID(slot)) {
1388 bool owns = JSVAL_TO_BOOLEAN(slot);
1389 slot = JS_GetReservedSlot(obj, ctypes::SLOT_DATA);
1390 if (!JSVAL_IS_VOID(slot)) {
1391 char** buffer = static_cast<char**>(JSVAL_TO_PRIVATE(slot));
1392 n += mallocSizeOf(buffer);
1393 if (owns)
1394 n += mallocSizeOf(*buffer);
1395 }
1396 }
1397 return n;
1398 }
1400 namespace ctypes {
1402 /*******************************************************************************
1403 ** Type conversion functions
1404 *******************************************************************************/
1406 // Enforce some sanity checks on type widths and properties.
1407 // Where the architecture is 64-bit, make sure it's LP64 or LLP64. (ctypes.int
1408 // autoconverts to a primitive JS number; to support ILP64 architectures, it
1409 // would need to autoconvert to an Int64 object instead. Therefore we enforce
1410 // this invariant here.)
1411 JS_STATIC_ASSERT(sizeof(bool) == 1 || sizeof(bool) == 4);
1412 JS_STATIC_ASSERT(sizeof(char) == 1);
1413 JS_STATIC_ASSERT(sizeof(short) == 2);
1414 JS_STATIC_ASSERT(sizeof(int) == 4);
1415 JS_STATIC_ASSERT(sizeof(unsigned) == 4);
1416 JS_STATIC_ASSERT(sizeof(long) == 4 || sizeof(long) == 8);
1417 JS_STATIC_ASSERT(sizeof(long long) == 8);
1418 JS_STATIC_ASSERT(sizeof(size_t) == sizeof(uintptr_t));
1419 JS_STATIC_ASSERT(sizeof(float) == 4);
1420 JS_STATIC_ASSERT(sizeof(PRFuncPtr) == sizeof(void*));
1421 JS_STATIC_ASSERT(NumericLimits<double>::is_signed);
1423 // Templated helper to convert FromType to TargetType, for the default case
1424 // where the trivial POD constructor will do.
1425 template<class TargetType, class FromType>
1426 struct ConvertImpl {
1427 static MOZ_ALWAYS_INLINE TargetType Convert(FromType d) {
1428 return TargetType(d);
1429 }
1430 };
1432 #ifdef _MSC_VER
1433 // MSVC can't perform double to unsigned __int64 conversion when the
1434 // double is greater than 2^63 - 1. Help it along a little.
1435 template<>
1436 struct ConvertImpl<uint64_t, double> {
1437 static MOZ_ALWAYS_INLINE uint64_t Convert(double d) {
1438 return d > 0x7fffffffffffffffui64 ?
1439 uint64_t(d - 0x8000000000000000ui64) + 0x8000000000000000ui64 :
1440 uint64_t(d);
1441 }
1442 };
1443 #endif
1445 // C++ doesn't guarantee that exact values are the only ones that will
1446 // round-trip. In fact, on some platforms, including SPARC, there are pairs of
1447 // values, a uint64_t and a double, such that neither value is exactly
1448 // representable in the other type, but they cast to each other.
1449 #if defined(SPARC) || defined(__powerpc__)
1450 // Simulate x86 overflow behavior
1451 template<>
1452 struct ConvertImpl<uint64_t, double> {
1453 static MOZ_ALWAYS_INLINE uint64_t Convert(double d) {
1454 return d >= 0xffffffffffffffff ?
1455 0x8000000000000000 : uint64_t(d);
1456 }
1457 };
1459 template<>
1460 struct ConvertImpl<int64_t, double> {
1461 static MOZ_ALWAYS_INLINE int64_t Convert(double d) {
1462 return d >= 0x7fffffffffffffff ?
1463 0x8000000000000000 : int64_t(d);
1464 }
1465 };
1466 #endif
1468 template<class TargetType, class FromType>
1469 static MOZ_ALWAYS_INLINE TargetType Convert(FromType d)
1470 {
1471 return ConvertImpl<TargetType, FromType>::Convert(d);
1472 }
1474 template<class TargetType, class FromType>
1475 static MOZ_ALWAYS_INLINE bool IsAlwaysExact()
1476 {
1477 // Return 'true' if TargetType can always exactly represent FromType.
1478 // This means that:
1479 // 1) TargetType must be the same or more bits wide as FromType. For integers
1480 // represented in 'n' bits, unsigned variants will have 'n' digits while
1481 // signed will have 'n - 1'. For floating point types, 'digits' is the
1482 // mantissa width.
1483 // 2) If FromType is signed, TargetType must also be signed. (Floating point
1484 // types are always signed.)
1485 // 3) If TargetType is an exact integral type, FromType must be also.
1486 if (NumericLimits<TargetType>::digits < NumericLimits<FromType>::digits)
1487 return false;
1489 if (NumericLimits<FromType>::is_signed &&
1490 !NumericLimits<TargetType>::is_signed)
1491 return false;
1493 if (!NumericLimits<FromType>::is_exact &&
1494 NumericLimits<TargetType>::is_exact)
1495 return false;
1497 return true;
1498 }
1500 // Templated helper to determine if FromType 'i' converts losslessly to
1501 // TargetType 'j'. Default case where both types are the same signedness.
1502 template<class TargetType, class FromType, bool TargetSigned, bool FromSigned>
1503 struct IsExactImpl {
1504 static MOZ_ALWAYS_INLINE bool Test(FromType i, TargetType j) {
1505 JS_STATIC_ASSERT(NumericLimits<TargetType>::is_exact);
1506 return FromType(j) == i;
1507 }
1508 };
1510 // Specialization where TargetType is unsigned, FromType is signed.
1511 template<class TargetType, class FromType>
1512 struct IsExactImpl<TargetType, FromType, false, true> {
1513 static MOZ_ALWAYS_INLINE bool Test(FromType i, TargetType j) {
1514 JS_STATIC_ASSERT(NumericLimits<TargetType>::is_exact);
1515 return i >= 0 && FromType(j) == i;
1516 }
1517 };
1519 // Specialization where TargetType is signed, FromType is unsigned.
1520 template<class TargetType, class FromType>
1521 struct IsExactImpl<TargetType, FromType, true, false> {
1522 static MOZ_ALWAYS_INLINE bool Test(FromType i, TargetType j) {
1523 JS_STATIC_ASSERT(NumericLimits<TargetType>::is_exact);
1524 return TargetType(i) >= 0 && FromType(j) == i;
1525 }
1526 };
1528 // Convert FromType 'i' to TargetType 'result', returning true iff 'result'
1529 // is an exact representation of 'i'.
1530 template<class TargetType, class FromType>
1531 static MOZ_ALWAYS_INLINE bool ConvertExact(FromType i, TargetType* result)
1532 {
1533 // Require that TargetType is integral, to simplify conversion.
1534 JS_STATIC_ASSERT(NumericLimits<TargetType>::is_exact);
1536 *result = Convert<TargetType>(i);
1538 // See if we can avoid a dynamic check.
1539 if (IsAlwaysExact<TargetType, FromType>())
1540 return true;
1542 // Return 'true' if 'i' is exactly representable in 'TargetType'.
1543 return IsExactImpl<TargetType,
1544 FromType,
1545 NumericLimits<TargetType>::is_signed,
1546 NumericLimits<FromType>::is_signed>::Test(i, *result);
1547 }
1549 // Templated helper to determine if Type 'i' is negative. Default case
1550 // where IntegerType is unsigned.
1551 template<class Type, bool IsSigned>
1552 struct IsNegativeImpl {
1553 static MOZ_ALWAYS_INLINE bool Test(Type i) {
1554 return false;
1555 }
1556 };
1558 // Specialization where Type is signed.
1559 template<class Type>
1560 struct IsNegativeImpl<Type, true> {
1561 static MOZ_ALWAYS_INLINE bool Test(Type i) {
1562 return i < 0;
1563 }
1564 };
1566 // Determine whether Type 'i' is negative.
1567 template<class Type>
1568 static MOZ_ALWAYS_INLINE bool IsNegative(Type i)
1569 {
1570 return IsNegativeImpl<Type, NumericLimits<Type>::is_signed>::Test(i);
1571 }
1573 // Implicitly convert val to bool, allowing bool, int, and double
1574 // arguments numerically equal to 0 or 1.
1575 static bool
1576 jsvalToBool(JSContext* cx, jsval val, bool* result)
1577 {
1578 if (JSVAL_IS_BOOLEAN(val)) {
1579 *result = JSVAL_TO_BOOLEAN(val);
1580 return true;
1581 }
1582 if (JSVAL_IS_INT(val)) {
1583 int32_t i = JSVAL_TO_INT(val);
1584 *result = i != 0;
1585 return i == 0 || i == 1;
1586 }
1587 if (JSVAL_IS_DOUBLE(val)) {
1588 double d = JSVAL_TO_DOUBLE(val);
1589 *result = d != 0;
1590 // Allow -0.
1591 return d == 1 || d == 0;
1592 }
1593 // Don't silently convert null to bool. It's probably a mistake.
1594 return false;
1595 }
1597 // Implicitly convert val to IntegerType, allowing bool, int, double,
1598 // Int64, UInt64, and CData integer types 't' where all values of 't' are
1599 // representable by IntegerType.
1600 template<class IntegerType>
1601 static bool
1602 jsvalToInteger(JSContext* cx, jsval val, IntegerType* result)
1603 {
1604 JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact);
1606 if (JSVAL_IS_INT(val)) {
1607 // Make sure the integer fits in the alotted precision, and has the right
1608 // sign.
1609 int32_t i = JSVAL_TO_INT(val);
1610 return ConvertExact(i, result);
1611 }
1612 if (JSVAL_IS_DOUBLE(val)) {
1613 // Don't silently lose bits here -- check that val really is an
1614 // integer value, and has the right sign.
1615 double d = JSVAL_TO_DOUBLE(val);
1616 return ConvertExact(d, result);
1617 }
1618 if (!JSVAL_IS_PRIMITIVE(val)) {
1619 JSObject* obj = JSVAL_TO_OBJECT(val);
1620 if (CData::IsCData(obj)) {
1621 JSObject* typeObj = CData::GetCType(obj);
1622 void* data = CData::GetData(obj);
1624 // Check whether the source type is always representable, with exact
1625 // precision, by the target type. If it is, convert the value.
1626 switch (CType::GetTypeCode(typeObj)) {
1627 #define DEFINE_INT_TYPE(name, fromType, ffiType) \
1628 case TYPE_##name: \
1629 if (!IsAlwaysExact<IntegerType, fromType>()) \
1630 return false; \
1631 *result = IntegerType(*static_cast<fromType*>(data)); \
1632 return true;
1633 #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
1634 #include "ctypes/typedefs.h"
1635 case TYPE_void_t:
1636 case TYPE_bool:
1637 case TYPE_float:
1638 case TYPE_double:
1639 case TYPE_float32_t:
1640 case TYPE_float64_t:
1641 case TYPE_char:
1642 case TYPE_signed_char:
1643 case TYPE_unsigned_char:
1644 case TYPE_jschar:
1645 case TYPE_pointer:
1646 case TYPE_function:
1647 case TYPE_array:
1648 case TYPE_struct:
1649 // Not a compatible number type.
1650 return false;
1651 }
1652 }
1654 if (Int64::IsInt64(obj)) {
1655 // Make sure the integer fits in IntegerType.
1656 int64_t i = Int64Base::GetInt(obj);
1657 return ConvertExact(i, result);
1658 }
1660 if (UInt64::IsUInt64(obj)) {
1661 // Make sure the integer fits in IntegerType.
1662 uint64_t i = Int64Base::GetInt(obj);
1663 return ConvertExact(i, result);
1664 }
1666 if (CDataFinalizer::IsCDataFinalizer(obj)) {
1667 RootedValue innerData(cx);
1668 if (!CDataFinalizer::GetValue(cx, obj, innerData.address())) {
1669 return false; // Nothing to convert
1670 }
1671 return jsvalToInteger(cx, innerData, result);
1672 }
1674 return false;
1675 }
1676 if (JSVAL_IS_BOOLEAN(val)) {
1677 // Implicitly promote boolean values to 0 or 1, like C.
1678 *result = JSVAL_TO_BOOLEAN(val);
1679 JS_ASSERT(*result == 0 || *result == 1);
1680 return true;
1681 }
1682 // Don't silently convert null to an integer. It's probably a mistake.
1683 return false;
1684 }
1686 // Implicitly convert val to FloatType, allowing int, double,
1687 // Int64, UInt64, and CData numeric types 't' where all values of 't' are
1688 // representable by FloatType.
1689 template<class FloatType>
1690 static bool
1691 jsvalToFloat(JSContext *cx, jsval val, FloatType* result)
1692 {
1693 JS_STATIC_ASSERT(!NumericLimits<FloatType>::is_exact);
1695 // The following casts may silently throw away some bits, but there's
1696 // no good way around it. Sternly requiring that the 64-bit double
1697 // argument be exactly representable as a 32-bit float is
1698 // unrealistic: it would allow 1/2 to pass but not 1/3.
1699 if (JSVAL_IS_INT(val)) {
1700 *result = FloatType(JSVAL_TO_INT(val));
1701 return true;
1702 }
1703 if (JSVAL_IS_DOUBLE(val)) {
1704 *result = FloatType(JSVAL_TO_DOUBLE(val));
1705 return true;
1706 }
1707 if (!JSVAL_IS_PRIMITIVE(val)) {
1708 JSObject* obj = JSVAL_TO_OBJECT(val);
1709 if (CData::IsCData(obj)) {
1710 JSObject* typeObj = CData::GetCType(obj);
1711 void* data = CData::GetData(obj);
1713 // Check whether the source type is always representable, with exact
1714 // precision, by the target type. If it is, convert the value.
1715 switch (CType::GetTypeCode(typeObj)) {
1716 #define DEFINE_FLOAT_TYPE(name, fromType, ffiType) \
1717 case TYPE_##name: \
1718 if (!IsAlwaysExact<FloatType, fromType>()) \
1719 return false; \
1720 *result = FloatType(*static_cast<fromType*>(data)); \
1721 return true;
1722 #define DEFINE_INT_TYPE(x, y, z) DEFINE_FLOAT_TYPE(x, y, z)
1723 #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
1724 #include "ctypes/typedefs.h"
1725 case TYPE_void_t:
1726 case TYPE_bool:
1727 case TYPE_char:
1728 case TYPE_signed_char:
1729 case TYPE_unsigned_char:
1730 case TYPE_jschar:
1731 case TYPE_pointer:
1732 case TYPE_function:
1733 case TYPE_array:
1734 case TYPE_struct:
1735 // Not a compatible number type.
1736 return false;
1737 }
1738 }
1739 }
1740 // Don't silently convert true to 1.0 or false to 0.0, even though C/C++
1741 // does it. It's likely to be a mistake.
1742 return false;
1743 }
1745 template<class IntegerType>
1746 static bool
1747 StringToInteger(JSContext* cx, JSString* string, IntegerType* result)
1748 {
1749 JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact);
1751 const jschar* cp = string->getChars(nullptr);
1752 if (!cp)
1753 return false;
1755 const jschar* end = cp + string->length();
1756 if (cp == end)
1757 return false;
1759 IntegerType sign = 1;
1760 if (cp[0] == '-') {
1761 if (!NumericLimits<IntegerType>::is_signed)
1762 return false;
1764 sign = -1;
1765 ++cp;
1766 }
1768 // Assume base-10, unless the string begins with '0x' or '0X'.
1769 IntegerType base = 10;
1770 if (end - cp > 2 && cp[0] == '0' && (cp[1] == 'x' || cp[1] == 'X')) {
1771 cp += 2;
1772 base = 16;
1773 }
1775 // Scan the string left to right and build the number,
1776 // checking for valid characters 0 - 9, a - f, A - F and overflow.
1777 IntegerType i = 0;
1778 while (cp != end) {
1779 jschar c = *cp++;
1780 if (c >= '0' && c <= '9')
1781 c -= '0';
1782 else if (base == 16 && c >= 'a' && c <= 'f')
1783 c = c - 'a' + 10;
1784 else if (base == 16 && c >= 'A' && c <= 'F')
1785 c = c - 'A' + 10;
1786 else
1787 return false;
1789 IntegerType ii = i;
1790 i = ii * base + sign * c;
1791 if (i / base != ii) // overflow
1792 return false;
1793 }
1795 *result = i;
1796 return true;
1797 }
1799 // Implicitly convert val to IntegerType, allowing int, double,
1800 // Int64, UInt64, and optionally a decimal or hexadecimal string argument.
1801 // (This is common code shared by jsvalToSize and the Int64/UInt64 constructors.)
1802 template<class IntegerType>
1803 static bool
1804 jsvalToBigInteger(JSContext* cx,
1805 jsval val,
1806 bool allowString,
1807 IntegerType* result)
1808 {
1809 JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact);
1811 if (JSVAL_IS_INT(val)) {
1812 // Make sure the integer fits in the alotted precision, and has the right
1813 // sign.
1814 int32_t i = JSVAL_TO_INT(val);
1815 return ConvertExact(i, result);
1816 }
1817 if (JSVAL_IS_DOUBLE(val)) {
1818 // Don't silently lose bits here -- check that val really is an
1819 // integer value, and has the right sign.
1820 double d = JSVAL_TO_DOUBLE(val);
1821 return ConvertExact(d, result);
1822 }
1823 if (allowString && JSVAL_IS_STRING(val)) {
1824 // Allow conversion from base-10 or base-16 strings, provided the result
1825 // fits in IntegerType. (This allows an Int64 or UInt64 object to be passed
1826 // to the JS array element operator, which will automatically call
1827 // toString() on the object for us.)
1828 return StringToInteger(cx, JSVAL_TO_STRING(val), result);
1829 }
1830 if (!JSVAL_IS_PRIMITIVE(val)) {
1831 // Allow conversion from an Int64 or UInt64 object directly.
1832 JSObject* obj = JSVAL_TO_OBJECT(val);
1834 if (UInt64::IsUInt64(obj)) {
1835 // Make sure the integer fits in IntegerType.
1836 uint64_t i = Int64Base::GetInt(obj);
1837 return ConvertExact(i, result);
1838 }
1840 if (Int64::IsInt64(obj)) {
1841 // Make sure the integer fits in IntegerType.
1842 int64_t i = Int64Base::GetInt(obj);
1843 return ConvertExact(i, result);
1844 }
1846 if (CDataFinalizer::IsCDataFinalizer(obj)) {
1847 RootedValue innerData(cx);
1848 if (!CDataFinalizer::GetValue(cx, obj, innerData.address())) {
1849 return false; // Nothing to convert
1850 }
1851 return jsvalToBigInteger(cx, innerData, allowString, result);
1852 }
1854 }
1855 return false;
1856 }
1858 // Implicitly convert val to a size value, where the size value is represented
1859 // by size_t but must also fit in a double.
1860 static bool
1861 jsvalToSize(JSContext* cx, jsval val, bool allowString, size_t* result)
1862 {
1863 if (!jsvalToBigInteger(cx, val, allowString, result))
1864 return false;
1866 // Also check that the result fits in a double.
1867 return Convert<size_t>(double(*result)) == *result;
1868 }
1870 // Implicitly convert val to IntegerType, allowing int, double,
1871 // Int64, UInt64, and optionally a decimal or hexadecimal string argument.
1872 // (This is common code shared by jsvalToSize and the Int64/UInt64 constructors.)
1873 template<class IntegerType>
1874 static bool
1875 jsidToBigInteger(JSContext* cx,
1876 jsid val,
1877 bool allowString,
1878 IntegerType* result)
1879 {
1880 JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact);
1882 if (JSID_IS_INT(val)) {
1883 // Make sure the integer fits in the alotted precision, and has the right
1884 // sign.
1885 int32_t i = JSID_TO_INT(val);
1886 return ConvertExact(i, result);
1887 }
1888 if (allowString && JSID_IS_STRING(val)) {
1889 // Allow conversion from base-10 or base-16 strings, provided the result
1890 // fits in IntegerType. (This allows an Int64 or UInt64 object to be passed
1891 // to the JS array element operator, which will automatically call
1892 // toString() on the object for us.)
1893 return StringToInteger(cx, JSID_TO_STRING(val), result);
1894 }
1895 if (JSID_IS_OBJECT(val)) {
1896 // Allow conversion from an Int64 or UInt64 object directly.
1897 JSObject* obj = JSID_TO_OBJECT(val);
1899 if (UInt64::IsUInt64(obj)) {
1900 // Make sure the integer fits in IntegerType.
1901 uint64_t i = Int64Base::GetInt(obj);
1902 return ConvertExact(i, result);
1903 }
1905 if (Int64::IsInt64(obj)) {
1906 // Make sure the integer fits in IntegerType.
1907 int64_t i = Int64Base::GetInt(obj);
1908 return ConvertExact(i, result);
1909 }
1910 }
1911 return false;
1912 }
1914 // Implicitly convert val to a size value, where the size value is represented
1915 // by size_t but must also fit in a double.
1916 static bool
1917 jsidToSize(JSContext* cx, jsid val, bool allowString, size_t* result)
1918 {
1919 if (!jsidToBigInteger(cx, val, allowString, result))
1920 return false;
1922 // Also check that the result fits in a double.
1923 return Convert<size_t>(double(*result)) == *result;
1924 }
1926 // Implicitly convert a size value to a jsval, ensuring that the size_t value
1927 // fits in a double.
1928 static bool
1929 SizeTojsval(JSContext* cx, size_t size, jsval* result)
1930 {
1931 if (Convert<size_t>(double(size)) != size) {
1932 JS_ReportError(cx, "size overflow");
1933 return false;
1934 }
1936 *result = JS_NumberValue(double(size));
1937 return true;
1938 }
1940 // Forcefully convert val to IntegerType when explicitly requested.
1941 template<class IntegerType>
1942 static bool
1943 jsvalToIntegerExplicit(jsval val, IntegerType* result)
1944 {
1945 JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact);
1947 if (JSVAL_IS_DOUBLE(val)) {
1948 // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
1949 double d = JSVAL_TO_DOUBLE(val);
1950 *result = mozilla::IsFinite(d) ? IntegerType(d) : 0;
1951 return true;
1952 }
1953 if (!JSVAL_IS_PRIMITIVE(val)) {
1954 // Convert Int64 and UInt64 values by C-style cast.
1955 JSObject* obj = JSVAL_TO_OBJECT(val);
1956 if (Int64::IsInt64(obj)) {
1957 int64_t i = Int64Base::GetInt(obj);
1958 *result = IntegerType(i);
1959 return true;
1960 }
1961 if (UInt64::IsUInt64(obj)) {
1962 uint64_t i = Int64Base::GetInt(obj);
1963 *result = IntegerType(i);
1964 return true;
1965 }
1966 }
1967 return false;
1968 }
1970 // Forcefully convert val to a pointer value when explicitly requested.
1971 static bool
1972 jsvalToPtrExplicit(JSContext* cx, jsval val, uintptr_t* result)
1973 {
1974 if (JSVAL_IS_INT(val)) {
1975 // int32_t always fits in intptr_t. If the integer is negative, cast through
1976 // an intptr_t intermediate to sign-extend.
1977 int32_t i = JSVAL_TO_INT(val);
1978 *result = i < 0 ? uintptr_t(intptr_t(i)) : uintptr_t(i);
1979 return true;
1980 }
1981 if (JSVAL_IS_DOUBLE(val)) {
1982 double d = JSVAL_TO_DOUBLE(val);
1983 if (d < 0) {
1984 // Cast through an intptr_t intermediate to sign-extend.
1985 intptr_t i = Convert<intptr_t>(d);
1986 if (double(i) != d)
1987 return false;
1989 *result = uintptr_t(i);
1990 return true;
1991 }
1993 // Don't silently lose bits here -- check that val really is an
1994 // integer value, and has the right sign.
1995 *result = Convert<uintptr_t>(d);
1996 return double(*result) == d;
1997 }
1998 if (!JSVAL_IS_PRIMITIVE(val)) {
1999 JSObject* obj = JSVAL_TO_OBJECT(val);
2000 if (Int64::IsInt64(obj)) {
2001 int64_t i = Int64Base::GetInt(obj);
2002 intptr_t p = intptr_t(i);
2004 // Make sure the integer fits in the alotted precision.
2005 if (int64_t(p) != i)
2006 return false;
2007 *result = uintptr_t(p);
2008 return true;
2009 }
2011 if (UInt64::IsUInt64(obj)) {
2012 uint64_t i = Int64Base::GetInt(obj);
2014 // Make sure the integer fits in the alotted precision.
2015 *result = uintptr_t(i);
2016 return uint64_t(*result) == i;
2017 }
2018 }
2019 return false;
2020 }
2022 template<class IntegerType, class CharType, size_t N, class AP>
2023 void
2024 IntegerToString(IntegerType i, int radix, Vector<CharType, N, AP>& result)
2025 {
2026 JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact);
2028 // The buffer must be big enough for all the bits of IntegerType to fit,
2029 // in base-2, including '-'.
2030 CharType buffer[sizeof(IntegerType) * 8 + 1];
2031 CharType* end = buffer + sizeof(buffer) / sizeof(CharType);
2032 CharType* cp = end;
2034 // Build the string in reverse. We use multiplication and subtraction
2035 // instead of modulus because that's much faster.
2036 const bool isNegative = IsNegative(i);
2037 size_t sign = isNegative ? -1 : 1;
2038 do {
2039 IntegerType ii = i / IntegerType(radix);
2040 size_t index = sign * size_t(i - ii * IntegerType(radix));
2041 *--cp = "0123456789abcdefghijklmnopqrstuvwxyz"[index];
2042 i = ii;
2043 } while (i != 0);
2045 if (isNegative)
2046 *--cp = '-';
2048 JS_ASSERT(cp >= buffer);
2049 result.append(cp, end);
2050 }
2052 template<class CharType>
2053 static size_t
2054 strnlen(const CharType* begin, size_t max)
2055 {
2056 for (const CharType* s = begin; s != begin + max; ++s)
2057 if (*s == 0)
2058 return s - begin;
2060 return max;
2061 }
2063 // Convert C binary value 'data' of CType 'typeObj' to a JS primitive, where
2064 // possible; otherwise, construct and return a CData object. The following
2065 // semantics apply when constructing a CData object for return:
2066 // * If 'wantPrimitive' is true, the caller indicates that 'result' must be
2067 // a JS primitive, and ConvertToJS will fail if 'result' would be a CData
2068 // object. Otherwise:
2069 // * If a CData object 'parentObj' is supplied, the new CData object is
2070 // dependent on the given parent and its buffer refers to a slice of the
2071 // parent's buffer.
2072 // * If 'parentObj' is null, the new CData object may or may not own its
2073 // resulting buffer depending on the 'ownResult' argument.
2074 static bool
2075 ConvertToJS(JSContext* cx,
2076 HandleObject typeObj,
2077 HandleObject parentObj,
2078 void* data,
2079 bool wantPrimitive,
2080 bool ownResult,
2081 jsval* result)
2082 {
2083 JS_ASSERT(!parentObj || CData::IsCData(parentObj));
2084 JS_ASSERT(!parentObj || !ownResult);
2085 JS_ASSERT(!wantPrimitive || !ownResult);
2087 TypeCode typeCode = CType::GetTypeCode(typeObj);
2089 switch (typeCode) {
2090 case TYPE_void_t:
2091 *result = JSVAL_VOID;
2092 break;
2093 case TYPE_bool:
2094 *result = *static_cast<bool*>(data) ? JSVAL_TRUE : JSVAL_FALSE;
2095 break;
2096 #define DEFINE_INT_TYPE(name, type, ffiType) \
2097 case TYPE_##name: { \
2098 type value = *static_cast<type*>(data); \
2099 if (sizeof(type) < 4) \
2100 *result = INT_TO_JSVAL(int32_t(value)); \
2101 else \
2102 *result = JS_NumberValue(double(value)); \
2103 break; \
2104 }
2105 #define DEFINE_WRAPPED_INT_TYPE(name, type, ffiType) \
2106 case TYPE_##name: { \
2107 /* Return an Int64 or UInt64 object - do not convert to a JS number. */ \
2108 uint64_t value; \
2109 RootedObject proto(cx); \
2110 if (!NumericLimits<type>::is_signed) { \
2111 value = *static_cast<type*>(data); \
2112 /* Get ctypes.UInt64.prototype from ctypes.CType.prototype. */ \
2113 proto = CType::GetProtoFromType(cx, typeObj, SLOT_UINT64PROTO); \
2114 if (!proto) \
2115 return false; \
2116 } else { \
2117 value = int64_t(*static_cast<type*>(data)); \
2118 /* Get ctypes.Int64.prototype from ctypes.CType.prototype. */ \
2119 proto = CType::GetProtoFromType(cx, typeObj, SLOT_INT64PROTO); \
2120 if (!proto) \
2121 return false; \
2122 } \
2123 \
2124 JSObject* obj = Int64Base::Construct(cx, proto, value, \
2125 !NumericLimits<type>::is_signed); \
2126 if (!obj) \
2127 return false; \
2128 *result = OBJECT_TO_JSVAL(obj); \
2129 break; \
2130 }
2131 #define DEFINE_FLOAT_TYPE(name, type, ffiType) \
2132 case TYPE_##name: { \
2133 type value = *static_cast<type*>(data); \
2134 *result = JS_NumberValue(double(value)); \
2135 break; \
2136 }
2137 #define DEFINE_CHAR_TYPE(name, type, ffiType) \
2138 case TYPE_##name: \
2139 /* Convert to an integer. We have no idea what character encoding to */ \
2140 /* use, if any. */ \
2141 *result = INT_TO_JSVAL(*static_cast<type*>(data)); \
2142 break;
2143 #include "ctypes/typedefs.h"
2144 case TYPE_jschar: {
2145 // Convert the jschar to a 1-character string.
2146 JSString* str = JS_NewUCStringCopyN(cx, static_cast<jschar*>(data), 1);
2147 if (!str)
2148 return false;
2150 *result = STRING_TO_JSVAL(str);
2151 break;
2152 }
2153 case TYPE_pointer:
2154 case TYPE_array:
2155 case TYPE_struct: {
2156 // We're about to create a new CData object to return. If the caller doesn't
2157 // want this, return early.
2158 if (wantPrimitive) {
2159 JS_ReportError(cx, "cannot convert to primitive value");
2160 return false;
2161 }
2163 JSObject* obj = CData::Create(cx, typeObj, parentObj, data, ownResult);
2164 if (!obj)
2165 return false;
2167 *result = OBJECT_TO_JSVAL(obj);
2168 break;
2169 }
2170 case TYPE_function:
2171 MOZ_ASSUME_UNREACHABLE("cannot return a FunctionType");
2172 }
2174 return true;
2175 }
2177 // Determine if the contents of a typed array can be converted without
2178 // ambiguity to a C type. Elements of a Int8Array are converted to
2179 // ctypes.int8_t, UInt8Array to ctypes.uint8_t, etc.
2180 bool CanConvertTypedArrayItemTo(JSObject *baseType, JSObject *valObj, JSContext *cx) {
2181 TypeCode baseTypeCode = CType::GetTypeCode(baseType);
2182 if (baseTypeCode == TYPE_void_t) {
2183 return true;
2184 }
2185 TypeCode elementTypeCode;
2186 switch (JS_GetArrayBufferViewType(valObj)) {
2187 case ScalarTypeDescr::TYPE_INT8:
2188 elementTypeCode = TYPE_int8_t;
2189 break;
2190 case ScalarTypeDescr::TYPE_UINT8:
2191 case ScalarTypeDescr::TYPE_UINT8_CLAMPED:
2192 elementTypeCode = TYPE_uint8_t;
2193 break;
2194 case ScalarTypeDescr::TYPE_INT16:
2195 elementTypeCode = TYPE_int16_t;
2196 break;
2197 case ScalarTypeDescr::TYPE_UINT16:
2198 elementTypeCode = TYPE_uint16_t;
2199 break;
2200 case ScalarTypeDescr::TYPE_INT32:
2201 elementTypeCode = TYPE_int32_t;
2202 break;
2203 case ScalarTypeDescr::TYPE_UINT32:
2204 elementTypeCode = TYPE_uint32_t;
2205 break;
2206 case ScalarTypeDescr::TYPE_FLOAT32:
2207 elementTypeCode = TYPE_float32_t;
2208 break;
2209 case ScalarTypeDescr::TYPE_FLOAT64:
2210 elementTypeCode = TYPE_float64_t;
2211 break;
2212 default:
2213 return false;
2214 }
2215 return elementTypeCode == baseTypeCode;
2216 }
2218 // Implicitly convert jsval 'val' to a C binary representation of CType
2219 // 'targetType', storing the result in 'buffer'. Adequate space must be
2220 // provided in 'buffer' by the caller. This function generally does minimal
2221 // coercion between types. There are two cases in which this function is used:
2222 // 1) The target buffer is internal to a CData object; we simply write data
2223 // into it.
2224 // 2) We are converting an argument for an ffi call, in which case 'isArgument'
2225 // will be true. This allows us to handle a special case: if necessary,
2226 // we can autoconvert a JS string primitive to a pointer-to-character type.
2227 // In this case, ownership of the allocated string is handed off to the
2228 // caller; 'freePointer' will be set to indicate this.
2229 static bool
2230 ImplicitConvert(JSContext* cx,
2231 HandleValue val,
2232 JSObject* targetType_,
2233 void* buffer,
2234 bool isArgument,
2235 bool* freePointer)
2236 {
2237 RootedObject targetType(cx, targetType_);
2238 JS_ASSERT(CType::IsSizeDefined(targetType));
2240 // First, check if val is either a CData object or a CDataFinalizer
2241 // of type targetType.
2242 JSObject* sourceData = nullptr;
2243 JSObject* sourceType = nullptr;
2244 RootedObject valObj(cx, nullptr);
2245 if (!JSVAL_IS_PRIMITIVE(val)) {
2246 valObj = JSVAL_TO_OBJECT(val);
2247 if (CData::IsCData(valObj)) {
2248 sourceData = valObj;
2249 sourceType = CData::GetCType(sourceData);
2251 // If the types are equal, copy the buffer contained within the CData.
2252 // (Note that the buffers may overlap partially or completely.)
2253 if (CType::TypesEqual(sourceType, targetType)) {
2254 size_t size = CType::GetSize(sourceType);
2255 memmove(buffer, CData::GetData(sourceData), size);
2256 return true;
2257 }
2258 } else if (CDataFinalizer::IsCDataFinalizer(valObj)) {
2259 sourceData = valObj;
2260 sourceType = CDataFinalizer::GetCType(cx, sourceData);
2262 CDataFinalizer::Private *p = (CDataFinalizer::Private *)
2263 JS_GetPrivate(sourceData);
2265 if (!p) {
2266 // We have called |dispose| or |forget| already.
2267 JS_ReportError(cx, "Attempting to convert an empty CDataFinalizer");
2268 return false;
2269 }
2271 // If the types are equal, copy the buffer contained within the CData.
2272 if (CType::TypesEqual(sourceType, targetType)) {
2273 memmove(buffer, p->cargs, p->cargs_size);
2274 return true;
2275 }
2276 }
2277 }
2279 TypeCode targetCode = CType::GetTypeCode(targetType);
2281 switch (targetCode) {
2282 case TYPE_bool: {
2283 // Do not implicitly lose bits, but allow the values 0, 1, and -0.
2284 // Programs can convert explicitly, if needed, using `Boolean(v)` or `!!v`.
2285 bool result;
2286 if (!jsvalToBool(cx, val, &result))
2287 return TypeError(cx, "boolean", val);
2288 *static_cast<bool*>(buffer) = result;
2289 break;
2290 }
2291 #define DEFINE_INT_TYPE(name, type, ffiType) \
2292 case TYPE_##name: { \
2293 /* Do not implicitly lose bits. */ \
2294 type result; \
2295 if (!jsvalToInteger(cx, val, &result)) \
2296 return TypeError(cx, #name, val); \
2297 *static_cast<type*>(buffer) = result; \
2298 break; \
2299 }
2300 #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
2301 #define DEFINE_FLOAT_TYPE(name, type, ffiType) \
2302 case TYPE_##name: { \
2303 type result; \
2304 if (!jsvalToFloat(cx, val, &result)) \
2305 return TypeError(cx, #name, val); \
2306 *static_cast<type*>(buffer) = result; \
2307 break; \
2308 }
2309 #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
2310 #define DEFINE_JSCHAR_TYPE(name, type, ffiType) \
2311 case TYPE_##name: { \
2312 /* Convert from a 1-character string, regardless of encoding, */ \
2313 /* or from an integer, provided the result fits in 'type'. */ \
2314 type result; \
2315 if (JSVAL_IS_STRING(val)) { \
2316 JSString* str = JSVAL_TO_STRING(val); \
2317 if (str->length() != 1) \
2318 return TypeError(cx, #name, val); \
2319 const jschar *chars = str->getChars(cx); \
2320 if (!chars) \
2321 return false; \
2322 result = chars[0]; \
2323 } else if (!jsvalToInteger(cx, val, &result)) { \
2324 return TypeError(cx, #name, val); \
2325 } \
2326 *static_cast<type*>(buffer) = result; \
2327 break; \
2328 }
2329 #include "ctypes/typedefs.h"
2330 case TYPE_pointer: {
2331 if (JSVAL_IS_NULL(val)) {
2332 // Convert to a null pointer.
2333 *static_cast<void**>(buffer) = nullptr;
2334 break;
2335 }
2337 JS::Rooted<JSObject*> baseType(cx, PointerType::GetBaseType(targetType));
2338 if (sourceData) {
2339 // First, determine if the targetType is ctypes.void_t.ptr.
2340 TypeCode sourceCode = CType::GetTypeCode(sourceType);
2341 void* sourceBuffer = CData::GetData(sourceData);
2342 bool voidptrTarget = CType::GetTypeCode(baseType) == TYPE_void_t;
2344 if (sourceCode == TYPE_pointer && voidptrTarget) {
2345 // Autoconvert if targetType is ctypes.voidptr_t.
2346 *static_cast<void**>(buffer) = *static_cast<void**>(sourceBuffer);
2347 break;
2348 }
2349 if (sourceCode == TYPE_array) {
2350 // Autoconvert an array to a ctypes.void_t.ptr or to
2351 // sourceType.elementType.ptr, just like C.
2352 JSObject* elementType = ArrayType::GetBaseType(sourceType);
2353 if (voidptrTarget || CType::TypesEqual(baseType, elementType)) {
2354 *static_cast<void**>(buffer) = sourceBuffer;
2355 break;
2356 }
2357 }
2359 } else if (isArgument && JSVAL_IS_STRING(val)) {
2360 // Convert the string for the ffi call. This requires allocating space
2361 // which the caller assumes ownership of.
2362 // TODO: Extend this so we can safely convert strings at other times also.
2363 JSString* sourceString = JSVAL_TO_STRING(val);
2364 size_t sourceLength = sourceString->length();
2365 const jschar* sourceChars = sourceString->getChars(cx);
2366 if (!sourceChars)
2367 return false;
2369 switch (CType::GetTypeCode(baseType)) {
2370 case TYPE_char:
2371 case TYPE_signed_char:
2372 case TYPE_unsigned_char: {
2373 // Convert from UTF-16 to UTF-8.
2374 size_t nbytes =
2375 GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength);
2376 if (nbytes == (size_t) -1)
2377 return false;
2379 char** charBuffer = static_cast<char**>(buffer);
2380 *charBuffer = cx->pod_malloc<char>(nbytes + 1);
2381 if (!*charBuffer) {
2382 JS_ReportAllocationOverflow(cx);
2383 return false;
2384 }
2386 ASSERT_OK(DeflateStringToUTF8Buffer(cx, sourceChars, sourceLength,
2387 *charBuffer, &nbytes));
2388 (*charBuffer)[nbytes] = 0;
2389 *freePointer = true;
2390 break;
2391 }
2392 case TYPE_jschar: {
2393 // Copy the jschar string data. (We could provide direct access to the
2394 // JSString's buffer, but this approach is safer if the caller happens
2395 // to modify the string.)
2396 jschar** jscharBuffer = static_cast<jschar**>(buffer);
2397 *jscharBuffer = cx->pod_malloc<jschar>(sourceLength + 1);
2398 if (!*jscharBuffer) {
2399 JS_ReportAllocationOverflow(cx);
2400 return false;
2401 }
2403 *freePointer = true;
2404 memcpy(*jscharBuffer, sourceChars, sourceLength * sizeof(jschar));
2405 (*jscharBuffer)[sourceLength] = 0;
2406 break;
2407 }
2408 default:
2409 return TypeError(cx, "string pointer", val);
2410 }
2411 break;
2412 } else if (!JSVAL_IS_PRIMITIVE(val) && JS_IsArrayBufferObject(valObj)) {
2413 // Convert ArrayBuffer to pointer without any copy.
2414 // Just as with C arrays, we make no effort to
2415 // keep the ArrayBuffer alive.
2416 void* p = JS_GetStableArrayBufferData(cx, valObj);
2417 if (!p)
2418 return false;
2419 *static_cast<void**>(buffer) = p;
2420 break;
2421 } if (!JSVAL_IS_PRIMITIVE(val) && JS_IsTypedArrayObject(valObj)) {
2422 if(!CanConvertTypedArrayItemTo(baseType, valObj, cx)) {
2423 return TypeError(cx, "typed array with the appropriate type", val);
2424 }
2426 // Convert TypedArray to pointer without any copy.
2427 // Just as with C arrays, we make no effort to
2428 // keep the TypedArray alive.
2429 *static_cast<void**>(buffer) = JS_GetArrayBufferViewData(valObj);
2430 break;
2431 }
2432 return TypeError(cx, "pointer", val);
2433 }
2434 case TYPE_array: {
2435 RootedObject baseType(cx, ArrayType::GetBaseType(targetType));
2436 size_t targetLength = ArrayType::GetLength(targetType);
2438 if (JSVAL_IS_STRING(val)) {
2439 JSString* sourceString = JSVAL_TO_STRING(val);
2440 size_t sourceLength = sourceString->length();
2441 const jschar* sourceChars = sourceString->getChars(cx);
2442 if (!sourceChars)
2443 return false;
2445 switch (CType::GetTypeCode(baseType)) {
2446 case TYPE_char:
2447 case TYPE_signed_char:
2448 case TYPE_unsigned_char: {
2449 // Convert from UTF-16 to UTF-8.
2450 size_t nbytes =
2451 GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength);
2452 if (nbytes == (size_t) -1)
2453 return false;
2455 if (targetLength < nbytes) {
2456 JS_ReportError(cx, "ArrayType has insufficient length");
2457 return false;
2458 }
2460 char* charBuffer = static_cast<char*>(buffer);
2461 ASSERT_OK(DeflateStringToUTF8Buffer(cx, sourceChars, sourceLength,
2462 charBuffer, &nbytes));
2464 if (targetLength > nbytes)
2465 charBuffer[nbytes] = 0;
2467 break;
2468 }
2469 case TYPE_jschar: {
2470 // Copy the string data, jschar for jschar, including the terminator
2471 // if there's space.
2472 if (targetLength < sourceLength) {
2473 JS_ReportError(cx, "ArrayType has insufficient length");
2474 return false;
2475 }
2477 memcpy(buffer, sourceChars, sourceLength * sizeof(jschar));
2478 if (targetLength > sourceLength)
2479 static_cast<jschar*>(buffer)[sourceLength] = 0;
2481 break;
2482 }
2483 default:
2484 return TypeError(cx, "array", val);
2485 }
2487 } else if (!JSVAL_IS_PRIMITIVE(val) && JS_IsArrayObject(cx, valObj)) {
2488 // Convert each element of the array by calling ImplicitConvert.
2489 uint32_t sourceLength;
2490 if (!JS_GetArrayLength(cx, valObj, &sourceLength) ||
2491 targetLength != size_t(sourceLength)) {
2492 JS_ReportError(cx, "ArrayType length does not match source array length");
2493 return false;
2494 }
2496 // Convert into an intermediate, in case of failure.
2497 size_t elementSize = CType::GetSize(baseType);
2498 size_t arraySize = elementSize * targetLength;
2499 AutoPtr<char> intermediate(cx->pod_malloc<char>(arraySize));
2500 if (!intermediate) {
2501 JS_ReportAllocationOverflow(cx);
2502 return false;
2503 }
2505 for (uint32_t i = 0; i < sourceLength; ++i) {
2506 RootedValue item(cx);
2507 if (!JS_GetElement(cx, valObj, i, &item))
2508 return false;
2510 char* data = intermediate.get() + elementSize * i;
2511 if (!ImplicitConvert(cx, item, baseType, data, false, nullptr))
2512 return false;
2513 }
2515 memcpy(buffer, intermediate.get(), arraySize);
2517 } else if (!JSVAL_IS_PRIMITIVE(val) &&
2518 JS_IsArrayBufferObject(valObj)) {
2519 // Check that array is consistent with type, then
2520 // copy the array.
2521 uint32_t sourceLength = JS_GetArrayBufferByteLength(valObj);
2522 size_t elementSize = CType::GetSize(baseType);
2523 size_t arraySize = elementSize * targetLength;
2524 if (arraySize != size_t(sourceLength)) {
2525 JS_ReportError(cx, "ArrayType length does not match source ArrayBuffer length");
2526 return false;
2527 }
2528 memcpy(buffer, JS_GetArrayBufferData(valObj), sourceLength);
2529 break;
2530 } else if (!JSVAL_IS_PRIMITIVE(val) &&
2531 JS_IsTypedArrayObject(valObj)) {
2532 // Check that array is consistent with type, then
2533 // copy the array.
2534 if(!CanConvertTypedArrayItemTo(baseType, valObj, cx)) {
2535 return TypeError(cx, "typed array with the appropriate type", val);
2536 }
2538 uint32_t sourceLength = JS_GetTypedArrayByteLength(valObj);
2539 size_t elementSize = CType::GetSize(baseType);
2540 size_t arraySize = elementSize * targetLength;
2541 if (arraySize != size_t(sourceLength)) {
2542 JS_ReportError(cx, "typed array length does not match source TypedArray length");
2543 return false;
2544 }
2545 memcpy(buffer, JS_GetArrayBufferViewData(valObj), sourceLength);
2546 break;
2547 } else {
2548 // Don't implicitly convert to string. Users can implicitly convert
2549 // with `String(x)` or `""+x`.
2550 return TypeError(cx, "array", val);
2551 }
2552 break;
2553 }
2554 case TYPE_struct: {
2555 if (!JSVAL_IS_PRIMITIVE(val) && !sourceData) {
2556 // Enumerate the properties of the object; if they match the struct
2557 // specification, convert the fields.
2558 RootedObject iter(cx, JS_NewPropertyIterator(cx, valObj));
2559 if (!iter)
2560 return false;
2562 // Convert into an intermediate, in case of failure.
2563 size_t structSize = CType::GetSize(targetType);
2564 AutoPtr<char> intermediate(cx->pod_malloc<char>(structSize));
2565 if (!intermediate) {
2566 JS_ReportAllocationOverflow(cx);
2567 return false;
2568 }
2570 RootedId id(cx);
2571 size_t i = 0;
2572 while (1) {
2573 if (!JS_NextProperty(cx, iter, id.address()))
2574 return false;
2575 if (JSID_IS_VOID(id))
2576 break;
2578 if (!JSID_IS_STRING(id)) {
2579 JS_ReportError(cx, "property name is not a string");
2580 return false;
2581 }
2583 JSFlatString *name = JSID_TO_FLAT_STRING(id);
2584 const FieldInfo* field = StructType::LookupField(cx, targetType, name);
2585 if (!field)
2586 return false;
2588 RootedValue prop(cx);
2589 if (!JS_GetPropertyById(cx, valObj, id, &prop))
2590 return false;
2592 // Convert the field via ImplicitConvert().
2593 char* fieldData = intermediate.get() + field->mOffset;
2594 if (!ImplicitConvert(cx, prop, field->mType, fieldData, false, nullptr))
2595 return false;
2597 ++i;
2598 }
2600 const FieldInfoHash* fields = StructType::GetFieldInfo(targetType);
2601 if (i != fields->count()) {
2602 JS_ReportError(cx, "missing fields");
2603 return false;
2604 }
2606 memcpy(buffer, intermediate.get(), structSize);
2607 break;
2608 }
2610 return TypeError(cx, "struct", val);
2611 }
2612 case TYPE_void_t:
2613 case TYPE_function:
2614 MOZ_ASSUME_UNREACHABLE("invalid type");
2615 }
2617 return true;
2618 }
2620 // Convert jsval 'val' to a C binary representation of CType 'targetType',
2621 // storing the result in 'buffer'. This function is more forceful than
2622 // ImplicitConvert.
2623 static bool
2624 ExplicitConvert(JSContext* cx, HandleValue val, HandleObject targetType, void* buffer)
2625 {
2626 // If ImplicitConvert succeeds, use that result.
2627 if (ImplicitConvert(cx, val, targetType, buffer, false, nullptr))
2628 return true;
2630 // If ImplicitConvert failed, and there is no pending exception, then assume
2631 // hard failure (out of memory, or some other similarly serious condition).
2632 // We store any pending exception in case we need to re-throw it.
2633 RootedValue ex(cx);
2634 if (!JS_GetPendingException(cx, &ex))
2635 return false;
2637 // Otherwise, assume soft failure. Clear the pending exception so that we
2638 // can throw a different one as required.
2639 JS_ClearPendingException(cx);
2641 TypeCode type = CType::GetTypeCode(targetType);
2643 switch (type) {
2644 case TYPE_bool: {
2645 *static_cast<bool*>(buffer) = ToBoolean(val);
2646 break;
2647 }
2648 #define DEFINE_INT_TYPE(name, type, ffiType) \
2649 case TYPE_##name: { \
2650 /* Convert numeric values with a C-style cast, and */ \
2651 /* allow conversion from a base-10 or base-16 string. */ \
2652 type result; \
2653 if (!jsvalToIntegerExplicit(val, &result) && \
2654 (!JSVAL_IS_STRING(val) || \
2655 !StringToInteger(cx, JSVAL_TO_STRING(val), &result))) \
2656 return TypeError(cx, #name, val); \
2657 *static_cast<type*>(buffer) = result; \
2658 break; \
2659 }
2660 #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
2661 #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
2662 #define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_CHAR_TYPE(x, y, z)
2663 #include "ctypes/typedefs.h"
2664 case TYPE_pointer: {
2665 // Convert a number, Int64 object, or UInt64 object to a pointer.
2666 uintptr_t result;
2667 if (!jsvalToPtrExplicit(cx, val, &result))
2668 return TypeError(cx, "pointer", val);
2669 *static_cast<uintptr_t*>(buffer) = result;
2670 break;
2671 }
2672 case TYPE_float32_t:
2673 case TYPE_float64_t:
2674 case TYPE_float:
2675 case TYPE_double:
2676 case TYPE_array:
2677 case TYPE_struct:
2678 // ImplicitConvert is sufficient. Re-throw the exception it generated.
2679 JS_SetPendingException(cx, ex);
2680 return false;
2681 case TYPE_void_t:
2682 case TYPE_function:
2683 MOZ_ASSUME_UNREACHABLE("invalid type");
2684 }
2685 return true;
2686 }
2688 // Given a CType 'typeObj', generate a string describing the C type declaration
2689 // corresponding to 'typeObj'. For instance, the CType constructed from
2690 // 'ctypes.int32_t.ptr.array(4).ptr.ptr' will result in the type string
2691 // 'int32_t*(**)[4]'.
2692 static JSString*
2693 BuildTypeName(JSContext* cx, JSObject* typeObj_)
2694 {
2695 AutoString result;
2696 RootedObject typeObj(cx, typeObj_);
2698 // Walk the hierarchy of types, outermost to innermost, building up the type
2699 // string. This consists of the base type, which goes on the left.
2700 // Derived type modifiers (* and []) build from the inside outward, with
2701 // pointers on the left and arrays on the right. An excellent description
2702 // of the rules for building C type declarations can be found at:
2703 // http://unixwiz.net/techtips/reading-cdecl.html
2704 TypeCode prevGrouping = CType::GetTypeCode(typeObj), currentGrouping;
2705 while (1) {
2706 currentGrouping = CType::GetTypeCode(typeObj);
2707 switch (currentGrouping) {
2708 case TYPE_pointer: {
2709 // Pointer types go on the left.
2710 PrependString(result, "*");
2712 typeObj = PointerType::GetBaseType(typeObj);
2713 prevGrouping = currentGrouping;
2714 continue;
2715 }
2716 case TYPE_array: {
2717 if (prevGrouping == TYPE_pointer) {
2718 // Outer type is pointer, inner type is array. Grouping is required.
2719 PrependString(result, "(");
2720 AppendString(result, ")");
2721 }
2723 // Array types go on the right.
2724 AppendString(result, "[");
2725 size_t length;
2726 if (ArrayType::GetSafeLength(typeObj, &length))
2727 IntegerToString(length, 10, result);
2729 AppendString(result, "]");
2731 typeObj = ArrayType::GetBaseType(typeObj);
2732 prevGrouping = currentGrouping;
2733 continue;
2734 }
2735 case TYPE_function: {
2736 FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
2738 // Add in the calling convention, if it's not cdecl.
2739 // There's no trailing or leading space needed here, as none of the
2740 // modifiers can produce a string beginning with an identifier ---
2741 // except for TYPE_function itself, which is fine because functions
2742 // can't return functions.
2743 ABICode abi = GetABICode(fninfo->mABI);
2744 if (abi == ABI_STDCALL)
2745 PrependString(result, "__stdcall");
2746 else if (abi == ABI_WINAPI)
2747 PrependString(result, "WINAPI");
2749 // Function application binds more tightly than dereferencing, so
2750 // wrap pointer types in parens. Functions can't return functions
2751 // (only pointers to them), and arrays can't hold functions
2752 // (similarly), so we don't need to address those cases.
2753 if (prevGrouping == TYPE_pointer) {
2754 PrependString(result, "(");
2755 AppendString(result, ")");
2756 }
2758 // Argument list goes on the right.
2759 AppendString(result, "(");
2760 for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
2761 RootedObject argType(cx, fninfo->mArgTypes[i]);
2762 JSString* argName = CType::GetName(cx, argType);
2763 AppendString(result, argName);
2764 if (i != fninfo->mArgTypes.length() - 1 ||
2765 fninfo->mIsVariadic)
2766 AppendString(result, ", ");
2767 }
2768 if (fninfo->mIsVariadic)
2769 AppendString(result, "...");
2770 AppendString(result, ")");
2772 // Set 'typeObj' to the return type, and let the loop process it.
2773 // 'prevGrouping' doesn't matter here, because functions cannot return
2774 // arrays -- thus the parenthetical rules don't get tickled.
2775 typeObj = fninfo->mReturnType;
2776 continue;
2777 }
2778 default:
2779 // Either a basic or struct type. Use the type's name as the base type.
2780 break;
2781 }
2782 break;
2783 }
2785 // If prepending the base type name directly would splice two
2786 // identifiers, insert a space.
2787 if (('a' <= result[0] && result[0] <= 'z') ||
2788 ('A' <= result[0] && result[0] <= 'Z') ||
2789 (result[0] == '_'))
2790 PrependString(result, " ");
2792 // Stick the base type and derived type parts together.
2793 JSString* baseName = CType::GetName(cx, typeObj);
2794 PrependString(result, baseName);
2795 return NewUCString(cx, result);
2796 }
2798 // Given a CType 'typeObj', generate a string 'result' such that 'eval(result)'
2799 // would construct the same CType. If 'makeShort' is true, assume that any
2800 // StructType 't' is bound to an in-scope variable of name 't.name', and use
2801 // that variable in place of generating a string to construct the type 't'.
2802 // (This means the type comparison function CType::TypesEqual will return true
2803 // when comparing the input and output of BuildTypeSource, since struct
2804 // equality is determined by strict JSObject pointer equality.)
2805 static void
2806 BuildTypeSource(JSContext* cx,
2807 JSObject* typeObj_,
2808 bool makeShort,
2809 AutoString& result)
2810 {
2811 RootedObject typeObj(cx, typeObj_);
2813 // Walk the types, building up the toSource() string.
2814 switch (CType::GetTypeCode(typeObj)) {
2815 case TYPE_void_t:
2816 #define DEFINE_TYPE(name, type, ffiType) \
2817 case TYPE_##name:
2818 #include "ctypes/typedefs.h"
2819 {
2820 AppendString(result, "ctypes.");
2821 JSString* nameStr = CType::GetName(cx, typeObj);
2822 AppendString(result, nameStr);
2823 break;
2824 }
2825 case TYPE_pointer: {
2826 RootedObject baseType(cx, PointerType::GetBaseType(typeObj));
2828 // Specialcase ctypes.voidptr_t.
2829 if (CType::GetTypeCode(baseType) == TYPE_void_t) {
2830 AppendString(result, "ctypes.voidptr_t");
2831 break;
2832 }
2834 // Recursively build the source string, and append '.ptr'.
2835 BuildTypeSource(cx, baseType, makeShort, result);
2836 AppendString(result, ".ptr");
2837 break;
2838 }
2839 case TYPE_function: {
2840 FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
2842 AppendString(result, "ctypes.FunctionType(");
2844 switch (GetABICode(fninfo->mABI)) {
2845 case ABI_DEFAULT:
2846 AppendString(result, "ctypes.default_abi, ");
2847 break;
2848 case ABI_STDCALL:
2849 AppendString(result, "ctypes.stdcall_abi, ");
2850 break;
2851 case ABI_WINAPI:
2852 AppendString(result, "ctypes.winapi_abi, ");
2853 break;
2854 case INVALID_ABI:
2855 MOZ_ASSUME_UNREACHABLE("invalid abi");
2856 }
2858 // Recursively build the source string describing the function return and
2859 // argument types.
2860 BuildTypeSource(cx, fninfo->mReturnType, true, result);
2862 if (fninfo->mArgTypes.length() > 0) {
2863 AppendString(result, ", [");
2864 for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
2865 BuildTypeSource(cx, fninfo->mArgTypes[i], true, result);
2866 if (i != fninfo->mArgTypes.length() - 1 ||
2867 fninfo->mIsVariadic)
2868 AppendString(result, ", ");
2869 }
2870 if (fninfo->mIsVariadic)
2871 AppendString(result, "\"...\"");
2872 AppendString(result, "]");
2873 }
2875 AppendString(result, ")");
2876 break;
2877 }
2878 case TYPE_array: {
2879 // Recursively build the source string, and append '.array(n)',
2880 // where n is the array length, or the empty string if the array length
2881 // is undefined.
2882 JSObject* baseType = ArrayType::GetBaseType(typeObj);
2883 BuildTypeSource(cx, baseType, makeShort, result);
2884 AppendString(result, ".array(");
2886 size_t length;
2887 if (ArrayType::GetSafeLength(typeObj, &length))
2888 IntegerToString(length, 10, result);
2890 AppendString(result, ")");
2891 break;
2892 }
2893 case TYPE_struct: {
2894 JSString* name = CType::GetName(cx, typeObj);
2896 if (makeShort) {
2897 // Shorten the type declaration by assuming that StructType 't' is bound
2898 // to an in-scope variable of name 't.name'.
2899 AppendString(result, name);
2900 break;
2901 }
2903 // Write the full struct declaration.
2904 AppendString(result, "ctypes.StructType(\"");
2905 AppendString(result, name);
2906 AppendString(result, "\"");
2908 // If it's an opaque struct, we're done.
2909 if (!CType::IsSizeDefined(typeObj)) {
2910 AppendString(result, ")");
2911 break;
2912 }
2914 AppendString(result, ", [");
2916 const FieldInfoHash* fields = StructType::GetFieldInfo(typeObj);
2917 size_t length = fields->count();
2918 Array<const FieldInfoHash::Entry*, 64> fieldsArray;
2919 if (!fieldsArray.resize(length))
2920 break;
2922 for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront())
2923 fieldsArray[r.front().value().mIndex] = &r.front();
2925 for (size_t i = 0; i < length; ++i) {
2926 const FieldInfoHash::Entry* entry = fieldsArray[i];
2927 AppendString(result, "{ \"");
2928 AppendString(result, entry->key());
2929 AppendString(result, "\": ");
2930 BuildTypeSource(cx, entry->value().mType, true, result);
2931 AppendString(result, " }");
2932 if (i != length - 1)
2933 AppendString(result, ", ");
2934 }
2936 AppendString(result, "])");
2937 break;
2938 }
2939 }
2940 }
2942 // Given a CData object of CType 'typeObj' with binary value 'data', generate a
2943 // string 'result' such that 'eval(result)' would construct a CData object with
2944 // the same CType and containing the same binary value. This assumes that any
2945 // StructType 't' is bound to an in-scope variable of name 't.name'. (This means
2946 // the type comparison function CType::TypesEqual will return true when
2947 // comparing the types, since struct equality is determined by strict JSObject
2948 // pointer equality.) Further, if 'isImplicit' is true, ensure that the
2949 // resulting string can ImplicitConvert successfully if passed to another data
2950 // constructor. (This is important when called recursively, since fields of
2951 // structs and arrays are converted with ImplicitConvert.)
2952 static bool
2953 BuildDataSource(JSContext* cx,
2954 HandleObject typeObj,
2955 void* data,
2956 bool isImplicit,
2957 AutoString& result)
2958 {
2959 TypeCode type = CType::GetTypeCode(typeObj);
2960 switch (type) {
2961 case TYPE_bool:
2962 if (*static_cast<bool*>(data))
2963 AppendString(result, "true");
2964 else
2965 AppendString(result, "false");
2966 break;
2967 #define DEFINE_INT_TYPE(name, type, ffiType) \
2968 case TYPE_##name: \
2969 /* Serialize as a primitive decimal integer. */ \
2970 IntegerToString(*static_cast<type*>(data), 10, result); \
2971 break;
2972 #define DEFINE_WRAPPED_INT_TYPE(name, type, ffiType) \
2973 case TYPE_##name: \
2974 /* Serialize as a wrapped decimal integer. */ \
2975 if (!NumericLimits<type>::is_signed) \
2976 AppendString(result, "ctypes.UInt64(\""); \
2977 else \
2978 AppendString(result, "ctypes.Int64(\""); \
2979 \
2980 IntegerToString(*static_cast<type*>(data), 10, result); \
2981 AppendString(result, "\")"); \
2982 break;
2983 #define DEFINE_FLOAT_TYPE(name, type, ffiType) \
2984 case TYPE_##name: { \
2985 /* Serialize as a primitive double. */ \
2986 double fp = *static_cast<type*>(data); \
2987 ToCStringBuf cbuf; \
2988 char* str = NumberToCString(cx, &cbuf, fp); \
2989 if (!str) { \
2990 JS_ReportOutOfMemory(cx); \
2991 return false; \
2992 } \
2993 \
2994 result.append(str, strlen(str)); \
2995 break; \
2996 }
2997 #define DEFINE_CHAR_TYPE(name, type, ffiType) \
2998 case TYPE_##name: \
2999 /* Serialize as an integer. */ \
3000 IntegerToString(*static_cast<type*>(data), 10, result); \
3001 break;
3002 #include "ctypes/typedefs.h"
3003 case TYPE_jschar: {
3004 // Serialize as a 1-character JS string.
3005 JSString* str = JS_NewUCStringCopyN(cx, static_cast<jschar*>(data), 1);
3006 if (!str)
3007 return false;
3009 // Escape characters, and quote as necessary.
3010 RootedValue valStr(cx, StringValue(str));
3011 JSString* src = JS_ValueToSource(cx, valStr);
3012 if (!src)
3013 return false;
3015 AppendString(result, src);
3016 break;
3017 }
3018 case TYPE_pointer:
3019 case TYPE_function: {
3020 if (isImplicit) {
3021 // The result must be able to ImplicitConvert successfully.
3022 // Wrap in a type constructor, then serialize for ExplicitConvert.
3023 BuildTypeSource(cx, typeObj, true, result);
3024 AppendString(result, "(");
3025 }
3027 // Serialize the pointer value as a wrapped hexadecimal integer.
3028 uintptr_t ptr = *static_cast<uintptr_t*>(data);
3029 AppendString(result, "ctypes.UInt64(\"0x");
3030 IntegerToString(ptr, 16, result);
3031 AppendString(result, "\")");
3033 if (isImplicit)
3034 AppendString(result, ")");
3036 break;
3037 }
3038 case TYPE_array: {
3039 // Serialize each element of the array recursively. Each element must
3040 // be able to ImplicitConvert successfully.
3041 RootedObject baseType(cx, ArrayType::GetBaseType(typeObj));
3042 AppendString(result, "[");
3044 size_t length = ArrayType::GetLength(typeObj);
3045 size_t elementSize = CType::GetSize(baseType);
3046 for (size_t i = 0; i < length; ++i) {
3047 char* element = static_cast<char*>(data) + elementSize * i;
3048 if (!BuildDataSource(cx, baseType, element, true, result))
3049 return false;
3051 if (i + 1 < length)
3052 AppendString(result, ", ");
3053 }
3054 AppendString(result, "]");
3055 break;
3056 }
3057 case TYPE_struct: {
3058 if (isImplicit) {
3059 // The result must be able to ImplicitConvert successfully.
3060 // Serialize the data as an object with properties, rather than
3061 // a sequence of arguments to the StructType constructor.
3062 AppendString(result, "{");
3063 }
3065 // Serialize each field of the struct recursively. Each field must
3066 // be able to ImplicitConvert successfully.
3067 const FieldInfoHash* fields = StructType::GetFieldInfo(typeObj);
3068 size_t length = fields->count();
3069 Array<const FieldInfoHash::Entry*, 64> fieldsArray;
3070 if (!fieldsArray.resize(length))
3071 return false;
3073 for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront())
3074 fieldsArray[r.front().value().mIndex] = &r.front();
3076 for (size_t i = 0; i < length; ++i) {
3077 const FieldInfoHash::Entry* entry = fieldsArray[i];
3079 if (isImplicit) {
3080 AppendString(result, "\"");
3081 AppendString(result, entry->key());
3082 AppendString(result, "\": ");
3083 }
3085 char* fieldData = static_cast<char*>(data) + entry->value().mOffset;
3086 RootedObject entryType(cx, entry->value().mType);
3087 if (!BuildDataSource(cx, entryType, fieldData, true, result))
3088 return false;
3090 if (i + 1 != length)
3091 AppendString(result, ", ");
3092 }
3094 if (isImplicit)
3095 AppendString(result, "}");
3097 break;
3098 }
3099 case TYPE_void_t:
3100 MOZ_ASSUME_UNREACHABLE("invalid type");
3101 }
3103 return true;
3104 }
3106 /*******************************************************************************
3107 ** JSAPI callback function implementations
3108 *******************************************************************************/
3110 bool
3111 ConstructAbstract(JSContext* cx,
3112 unsigned argc,
3113 jsval* vp)
3114 {
3115 // Calling an abstract base class constructor is disallowed.
3116 JS_ReportError(cx, "cannot construct from abstract type");
3117 return false;
3118 }
3120 /*******************************************************************************
3121 ** CType implementation
3122 *******************************************************************************/
3124 bool
3125 CType::ConstructData(JSContext* cx,
3126 unsigned argc,
3127 jsval* vp)
3128 {
3129 CallArgs args = CallArgsFromVp(argc, vp);
3130 // get the callee object...
3131 RootedObject obj(cx, &args.callee());
3132 if (!CType::IsCType(obj)) {
3133 JS_ReportError(cx, "not a CType");
3134 return false;
3135 }
3137 // How we construct the CData object depends on what type we represent.
3138 // An instance 'd' of a CData object of type 't' has:
3139 // * [[Class]] "CData"
3140 // * __proto__ === t.prototype
3141 switch (GetTypeCode(obj)) {
3142 case TYPE_void_t:
3143 JS_ReportError(cx, "cannot construct from void_t");
3144 return false;
3145 case TYPE_function:
3146 JS_ReportError(cx, "cannot construct from FunctionType; use FunctionType.ptr instead");
3147 return false;
3148 case TYPE_pointer:
3149 return PointerType::ConstructData(cx, obj, args);
3150 case TYPE_array:
3151 return ArrayType::ConstructData(cx, obj, args);
3152 case TYPE_struct:
3153 return StructType::ConstructData(cx, obj, args);
3154 default:
3155 return ConstructBasic(cx, obj, args);
3156 }
3157 }
3159 bool
3160 CType::ConstructBasic(JSContext* cx,
3161 HandleObject obj,
3162 const CallArgs& args)
3163 {
3164 if (args.length() > 1) {
3165 JS_ReportError(cx, "CType constructor takes zero or one argument");
3166 return false;
3167 }
3169 // construct a CData object
3170 RootedObject result(cx, CData::Create(cx, obj, NullPtr(), nullptr, true));
3171 if (!result)
3172 return false;
3174 if (args.length() == 1) {
3175 if (!ExplicitConvert(cx, args[0], obj, CData::GetData(result)))
3176 return false;
3177 }
3179 args.rval().setObject(*result);
3180 return true;
3181 }
3183 JSObject*
3184 CType::Create(JSContext* cx,
3185 HandleObject typeProto,
3186 HandleObject dataProto,
3187 TypeCode type,
3188 JSString* name_,
3189 jsval size_,
3190 jsval align_,
3191 ffi_type* ffiType)
3192 {
3193 RootedString name(cx, name_);
3194 RootedValue size(cx, size_);
3195 RootedValue align(cx, align_);
3196 RootedObject parent(cx, JS_GetParent(typeProto));
3197 JS_ASSERT(parent);
3199 // Create a CType object with the properties and slots common to all CTypes.
3200 // Each type object 't' has:
3201 // * [[Class]] "CType"
3202 // * __proto__ === 'typeProto'; one of ctypes.{CType,PointerType,ArrayType,
3203 // StructType}.prototype
3204 // * A constructor which creates and returns a CData object, containing
3205 // binary data of the given type.
3206 // * 'prototype' property:
3207 // * [[Class]] "CDataProto"
3208 // * __proto__ === 'dataProto'; an object containing properties and
3209 // functions common to all CData objects of types derived from
3210 // 'typeProto'. (For instance, this could be ctypes.CData.prototype
3211 // for simple types, or something representing structs for StructTypes.)
3212 // * 'constructor' property === 't'
3213 // * Additional properties specified by 'ps', as appropriate for the
3214 // specific type instance 't'.
3215 RootedObject typeObj(cx, JS_NewObject(cx, &sCTypeClass, typeProto, parent));
3216 if (!typeObj)
3217 return nullptr;
3219 // Set up the reserved slots.
3220 JS_SetReservedSlot(typeObj, SLOT_TYPECODE, INT_TO_JSVAL(type));
3221 if (ffiType)
3222 JS_SetReservedSlot(typeObj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(ffiType));
3223 if (name)
3224 JS_SetReservedSlot(typeObj, SLOT_NAME, STRING_TO_JSVAL(name));
3225 JS_SetReservedSlot(typeObj, SLOT_SIZE, size);
3226 JS_SetReservedSlot(typeObj, SLOT_ALIGN, align);
3228 if (dataProto) {
3229 // Set up the 'prototype' and 'prototype.constructor' properties.
3230 RootedObject prototype(cx, JS_NewObject(cx, &sCDataProtoClass, dataProto, parent));
3231 if (!prototype)
3232 return nullptr;
3234 if (!JS_DefineProperty(cx, prototype, "constructor", typeObj,
3235 JSPROP_READONLY | JSPROP_PERMANENT))
3236 return nullptr;
3238 // Set the 'prototype' object.
3239 //if (!JS_FreezeObject(cx, prototype)) // XXX fixme - see bug 541212!
3240 // return nullptr;
3241 JS_SetReservedSlot(typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype));
3242 }
3244 if (!JS_FreezeObject(cx, typeObj))
3245 return nullptr;
3247 // Assert a sanity check on size and alignment: size % alignment should always
3248 // be zero.
3249 JS_ASSERT_IF(IsSizeDefined(typeObj),
3250 GetSize(typeObj) % GetAlignment(typeObj) == 0);
3252 return typeObj;
3253 }
3255 JSObject*
3256 CType::DefineBuiltin(JSContext* cx,
3257 JSObject* parent_,
3258 const char* propName,
3259 JSObject* typeProto_,
3260 JSObject* dataProto_,
3261 const char* name,
3262 TypeCode type,
3263 jsval size_,
3264 jsval align_,
3265 ffi_type* ffiType)
3266 {
3267 RootedObject parent(cx, parent_);
3268 RootedObject typeProto(cx, typeProto_);
3269 RootedObject dataProto(cx, dataProto_);
3270 RootedValue size(cx, size_);
3271 RootedValue align(cx, align_);
3273 RootedString nameStr(cx, JS_NewStringCopyZ(cx, name));
3274 if (!nameStr)
3275 return nullptr;
3277 // Create a new CType object with the common properties and slots.
3278 RootedObject typeObj(cx, Create(cx, typeProto, dataProto, type, nameStr, size, align, ffiType));
3279 if (!typeObj)
3280 return nullptr;
3282 // Define the CType as a 'propName' property on 'parent'.
3283 if (!JS_DefineProperty(cx, parent, propName, typeObj,
3284 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
3285 return nullptr;
3287 return typeObj;
3288 }
3290 void
3291 CType::Finalize(JSFreeOp *fop, JSObject* obj)
3292 {
3293 // Make sure our TypeCode slot is legit. If it's not, bail.
3294 jsval slot = JS_GetReservedSlot(obj, SLOT_TYPECODE);
3295 if (JSVAL_IS_VOID(slot))
3296 return;
3298 // The contents of our slots depends on what kind of type we are.
3299 switch (TypeCode(JSVAL_TO_INT(slot))) {
3300 case TYPE_function: {
3301 // Free the FunctionInfo.
3302 slot = JS_GetReservedSlot(obj, SLOT_FNINFO);
3303 if (!JSVAL_IS_VOID(slot))
3304 FreeOp::get(fop)->delete_(static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot)));
3305 break;
3306 }
3308 case TYPE_struct: {
3309 // Free the FieldInfoHash table.
3310 slot = JS_GetReservedSlot(obj, SLOT_FIELDINFO);
3311 if (!JSVAL_IS_VOID(slot)) {
3312 void* info = JSVAL_TO_PRIVATE(slot);
3313 FreeOp::get(fop)->delete_(static_cast<FieldInfoHash*>(info));
3314 }
3315 }
3317 // Fall through.
3318 case TYPE_array: {
3319 // Free the ffi_type info.
3320 slot = JS_GetReservedSlot(obj, SLOT_FFITYPE);
3321 if (!JSVAL_IS_VOID(slot)) {
3322 ffi_type* ffiType = static_cast<ffi_type*>(JSVAL_TO_PRIVATE(slot));
3323 FreeOp::get(fop)->free_(ffiType->elements);
3324 FreeOp::get(fop)->delete_(ffiType);
3325 }
3327 break;
3328 }
3329 default:
3330 // Nothing to do here.
3331 break;
3332 }
3333 }
3335 void
3336 CType::Trace(JSTracer* trc, JSObject* obj)
3337 {
3338 // Make sure our TypeCode slot is legit. If it's not, bail.
3339 jsval slot = obj->getSlot(SLOT_TYPECODE);
3340 if (JSVAL_IS_VOID(slot))
3341 return;
3343 // The contents of our slots depends on what kind of type we are.
3344 switch (TypeCode(JSVAL_TO_INT(slot))) {
3345 case TYPE_struct: {
3346 slot = obj->getReservedSlot(SLOT_FIELDINFO);
3347 if (JSVAL_IS_VOID(slot))
3348 return;
3350 FieldInfoHash* fields =
3351 static_cast<FieldInfoHash*>(JSVAL_TO_PRIVATE(slot));
3352 for (FieldInfoHash::Enum e(*fields); !e.empty(); e.popFront()) {
3353 JSString *key = e.front().key();
3354 JS_CallStringTracer(trc, &key, "fieldName");
3355 if (key != e.front().key())
3356 e.rekeyFront(JS_ASSERT_STRING_IS_FLAT(key));
3357 JS_CallHeapObjectTracer(trc, &e.front().value().mType, "fieldType");
3358 }
3360 break;
3361 }
3362 case TYPE_function: {
3363 // Check if we have a FunctionInfo.
3364 slot = obj->getReservedSlot(SLOT_FNINFO);
3365 if (JSVAL_IS_VOID(slot))
3366 return;
3368 FunctionInfo* fninfo = static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot));
3369 JS_ASSERT(fninfo);
3371 // Identify our objects to the tracer.
3372 JS_CallHeapObjectTracer(trc, &fninfo->mABI, "abi");
3373 JS_CallHeapObjectTracer(trc, &fninfo->mReturnType, "returnType");
3374 for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i)
3375 JS_CallHeapObjectTracer(trc, &fninfo->mArgTypes[i], "argType");
3377 break;
3378 }
3379 default:
3380 // Nothing to do here.
3381 break;
3382 }
3383 }
3385 bool
3386 CType::IsCType(JSObject* obj)
3387 {
3388 return JS_GetClass(obj) == &sCTypeClass;
3389 }
3391 bool
3392 CType::IsCTypeProto(JSObject* obj)
3393 {
3394 return JS_GetClass(obj) == &sCTypeProtoClass;
3395 }
3397 TypeCode
3398 CType::GetTypeCode(JSObject* typeObj)
3399 {
3400 JS_ASSERT(IsCType(typeObj));
3402 jsval result = JS_GetReservedSlot(typeObj, SLOT_TYPECODE);
3403 return TypeCode(JSVAL_TO_INT(result));
3404 }
3406 bool
3407 CType::TypesEqual(JSObject* t1, JSObject* t2)
3408 {
3409 JS_ASSERT(IsCType(t1) && IsCType(t2));
3411 // Fast path: check for object equality.
3412 if (t1 == t2)
3413 return true;
3415 // First, perform shallow comparison.
3416 TypeCode c1 = GetTypeCode(t1);
3417 TypeCode c2 = GetTypeCode(t2);
3418 if (c1 != c2)
3419 return false;
3421 // Determine whether the types require shallow or deep comparison.
3422 switch (c1) {
3423 case TYPE_pointer: {
3424 // Compare base types.
3425 JSObject* b1 = PointerType::GetBaseType(t1);
3426 JSObject* b2 = PointerType::GetBaseType(t2);
3427 return TypesEqual(b1, b2);
3428 }
3429 case TYPE_function: {
3430 FunctionInfo* f1 = FunctionType::GetFunctionInfo(t1);
3431 FunctionInfo* f2 = FunctionType::GetFunctionInfo(t2);
3433 // Compare abi, return type, and argument types.
3434 if (f1->mABI != f2->mABI)
3435 return false;
3437 if (!TypesEqual(f1->mReturnType, f2->mReturnType))
3438 return false;
3440 if (f1->mArgTypes.length() != f2->mArgTypes.length())
3441 return false;
3443 if (f1->mIsVariadic != f2->mIsVariadic)
3444 return false;
3446 for (size_t i = 0; i < f1->mArgTypes.length(); ++i) {
3447 if (!TypesEqual(f1->mArgTypes[i], f2->mArgTypes[i]))
3448 return false;
3449 }
3451 return true;
3452 }
3453 case TYPE_array: {
3454 // Compare length, then base types.
3455 // An undefined length array matches other undefined length arrays.
3456 size_t s1 = 0, s2 = 0;
3457 bool d1 = ArrayType::GetSafeLength(t1, &s1);
3458 bool d2 = ArrayType::GetSafeLength(t2, &s2);
3459 if (d1 != d2 || (d1 && s1 != s2))
3460 return false;
3462 JSObject* b1 = ArrayType::GetBaseType(t1);
3463 JSObject* b2 = ArrayType::GetBaseType(t2);
3464 return TypesEqual(b1, b2);
3465 }
3466 case TYPE_struct:
3467 // Require exact type object equality.
3468 return false;
3469 default:
3470 // Shallow comparison is sufficient.
3471 return true;
3472 }
3473 }
3475 bool
3476 CType::GetSafeSize(JSObject* obj, size_t* result)
3477 {
3478 JS_ASSERT(CType::IsCType(obj));
3480 jsval size = JS_GetReservedSlot(obj, SLOT_SIZE);
3482 // The "size" property can be an int, a double, or JSVAL_VOID
3483 // (for arrays of undefined length), and must always fit in a size_t.
3484 if (JSVAL_IS_INT(size)) {
3485 *result = JSVAL_TO_INT(size);
3486 return true;
3487 }
3488 if (JSVAL_IS_DOUBLE(size)) {
3489 *result = Convert<size_t>(JSVAL_TO_DOUBLE(size));
3490 return true;
3491 }
3493 JS_ASSERT(JSVAL_IS_VOID(size));
3494 return false;
3495 }
3497 size_t
3498 CType::GetSize(JSObject* obj)
3499 {
3500 JS_ASSERT(CType::IsCType(obj));
3502 jsval size = JS_GetReservedSlot(obj, SLOT_SIZE);
3504 JS_ASSERT(!JSVAL_IS_VOID(size));
3506 // The "size" property can be an int, a double, or JSVAL_VOID
3507 // (for arrays of undefined length), and must always fit in a size_t.
3508 // For callers who know it can never be JSVAL_VOID, return a size_t directly.
3509 if (JSVAL_IS_INT(size))
3510 return JSVAL_TO_INT(size);
3511 return Convert<size_t>(JSVAL_TO_DOUBLE(size));
3512 }
3514 bool
3515 CType::IsSizeDefined(JSObject* obj)
3516 {
3517 JS_ASSERT(CType::IsCType(obj));
3519 jsval size = JS_GetReservedSlot(obj, SLOT_SIZE);
3521 // The "size" property can be an int, a double, or JSVAL_VOID
3522 // (for arrays of undefined length), and must always fit in a size_t.
3523 JS_ASSERT(JSVAL_IS_INT(size) || JSVAL_IS_DOUBLE(size) || JSVAL_IS_VOID(size));
3524 return !JSVAL_IS_VOID(size);
3525 }
3527 size_t
3528 CType::GetAlignment(JSObject* obj)
3529 {
3530 JS_ASSERT(CType::IsCType(obj));
3532 jsval slot = JS_GetReservedSlot(obj, SLOT_ALIGN);
3533 return static_cast<size_t>(JSVAL_TO_INT(slot));
3534 }
3536 ffi_type*
3537 CType::GetFFIType(JSContext* cx, JSObject* obj)
3538 {
3539 JS_ASSERT(CType::IsCType(obj));
3541 jsval slot = JS_GetReservedSlot(obj, SLOT_FFITYPE);
3543 if (!JSVAL_IS_VOID(slot)) {
3544 return static_cast<ffi_type*>(JSVAL_TO_PRIVATE(slot));
3545 }
3547 AutoPtr<ffi_type> result;
3548 switch (CType::GetTypeCode(obj)) {
3549 case TYPE_array:
3550 result = ArrayType::BuildFFIType(cx, obj);
3551 break;
3553 case TYPE_struct:
3554 result = StructType::BuildFFIType(cx, obj);
3555 break;
3557 default:
3558 MOZ_ASSUME_UNREACHABLE("simple types must have an ffi_type");
3559 }
3561 if (!result)
3562 return nullptr;
3563 JS_SetReservedSlot(obj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(result.get()));
3564 return result.forget();
3565 }
3567 JSString*
3568 CType::GetName(JSContext* cx, HandleObject obj)
3569 {
3570 JS_ASSERT(CType::IsCType(obj));
3572 jsval string = JS_GetReservedSlot(obj, SLOT_NAME);
3573 if (!JSVAL_IS_VOID(string))
3574 return JSVAL_TO_STRING(string);
3576 // Build the type name lazily.
3577 JSString* name = BuildTypeName(cx, obj);
3578 if (!name)
3579 return nullptr;
3580 JS_SetReservedSlot(obj, SLOT_NAME, STRING_TO_JSVAL(name));
3581 return name;
3582 }
3584 JSObject*
3585 CType::GetProtoFromCtor(JSObject* obj, CTypeProtoSlot slot)
3586 {
3587 // Get ctypes.{Pointer,Array,Struct}Type.prototype from a reserved slot
3588 // on the type constructor.
3589 jsval protoslot = js::GetFunctionNativeReserved(obj, SLOT_FN_CTORPROTO);
3590 JSObject* proto = &protoslot.toObject();
3591 JS_ASSERT(proto);
3592 JS_ASSERT(CType::IsCTypeProto(proto));
3594 // Get the desired prototype.
3595 jsval result = JS_GetReservedSlot(proto, slot);
3596 return &result.toObject();
3597 }
3599 JSObject*
3600 CType::GetProtoFromType(JSContext* cx, JSObject* objArg, CTypeProtoSlot slot)
3601 {
3602 JS_ASSERT(IsCType(objArg));
3603 RootedObject obj(cx, objArg);
3605 // Get the prototype of the type object.
3606 RootedObject proto(cx);
3607 if (!JS_GetPrototype(cx, obj, &proto))
3608 return nullptr;
3609 JS_ASSERT(proto);
3610 JS_ASSERT(CType::IsCTypeProto(proto));
3612 // Get the requested ctypes.{Pointer,Array,Struct,Function}Type.prototype.
3613 jsval result = JS_GetReservedSlot(proto, slot);
3614 JS_ASSERT(!JSVAL_IS_PRIMITIVE(result));
3615 return JSVAL_TO_OBJECT(result);
3616 }
3618 bool
3619 CType::IsCTypeOrProto(HandleValue v)
3620 {
3621 if (!v.isObject())
3622 return false;
3623 JSObject* obj = &v.toObject();
3624 return CType::IsCType(obj) || CType::IsCTypeProto(obj);
3625 }
3627 bool
3628 CType::PrototypeGetter(JSContext* cx, JS::CallArgs args)
3629 {
3630 RootedObject obj(cx, &args.thisv().toObject());
3631 unsigned slot = CType::IsCTypeProto(obj) ? (unsigned) SLOT_OURDATAPROTO
3632 : (unsigned) SLOT_PROTO;
3633 args.rval().set(JS_GetReservedSlot(obj, slot));
3634 MOZ_ASSERT(args.rval().isObject() || args.rval().isUndefined());
3635 return true;
3636 }
3638 bool
3639 CType::IsCType(HandleValue v)
3640 {
3641 return v.isObject() && CType::IsCType(&v.toObject());
3642 }
3644 bool
3645 CType::NameGetter(JSContext* cx, JS::CallArgs args)
3646 {
3647 RootedObject obj(cx, &args.thisv().toObject());
3648 JSString* name = CType::GetName(cx, obj);
3649 if (!name)
3650 return false;
3652 args.rval().setString(name);
3653 return true;
3654 }
3656 bool
3657 CType::SizeGetter(JSContext* cx, JS::CallArgs args)
3658 {
3659 RootedObject obj(cx, &args.thisv().toObject());
3660 args.rval().set(JS_GetReservedSlot(obj, SLOT_SIZE));
3661 MOZ_ASSERT(args.rval().isNumber() || args.rval().isUndefined());
3662 return true;
3663 }
3665 bool
3666 CType::PtrGetter(JSContext* cx, JS::CallArgs args)
3667 {
3668 RootedObject obj(cx, &args.thisv().toObject());
3669 JSObject* pointerType = PointerType::CreateInternal(cx, obj);
3670 if (!pointerType)
3671 return false;
3673 args.rval().setObject(*pointerType);
3674 return true;
3675 }
3677 bool
3678 CType::CreateArray(JSContext* cx, unsigned argc, jsval* vp)
3679 {
3680 CallArgs args = CallArgsFromVp(argc, vp);
3681 RootedObject baseType(cx, JS_THIS_OBJECT(cx, vp));
3682 if (!baseType)
3683 return false;
3684 if (!CType::IsCType(baseType)) {
3685 JS_ReportError(cx, "not a CType");
3686 return false;
3687 }
3689 // Construct and return a new ArrayType object.
3690 if (args.length() > 1) {
3691 JS_ReportError(cx, "array takes zero or one argument");
3692 return false;
3693 }
3695 // Convert the length argument to a size_t.
3696 size_t length = 0;
3697 if (args.length() == 1 && !jsvalToSize(cx, args[0], false, &length)) {
3698 JS_ReportError(cx, "argument must be a nonnegative integer");
3699 return false;
3700 }
3702 JSObject* result = ArrayType::CreateInternal(cx, baseType, length, args.length() == 1);
3703 if (!result)
3704 return false;
3706 args.rval().setObject(*result);
3707 return true;
3708 }
3710 bool
3711 CType::ToString(JSContext* cx, unsigned argc, jsval* vp)
3712 {
3713 CallArgs args = CallArgsFromVp(argc, vp);
3714 RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
3715 if (!obj)
3716 return false;
3717 if (!CType::IsCType(obj) && !CType::IsCTypeProto(obj)) {
3718 JS_ReportError(cx, "not a CType");
3719 return false;
3720 }
3722 // Create the appropriate string depending on whether we're sCTypeClass or
3723 // sCTypeProtoClass.
3724 JSString* result;
3725 if (CType::IsCType(obj)) {
3726 AutoString type;
3727 AppendString(type, "type ");
3728 AppendString(type, GetName(cx, obj));
3729 result = NewUCString(cx, type);
3730 }
3731 else {
3732 result = JS_NewStringCopyZ(cx, "[CType proto object]");
3733 }
3734 if (!result)
3735 return false;
3737 args.rval().setString(result);
3738 return true;
3739 }
3741 bool
3742 CType::ToSource(JSContext* cx, unsigned argc, jsval* vp)
3743 {
3744 CallArgs args = CallArgsFromVp(argc, vp);
3745 JSObject* obj = JS_THIS_OBJECT(cx, vp);
3746 if (!obj)
3747 return false;
3748 if (!CType::IsCType(obj) && !CType::IsCTypeProto(obj))
3749 {
3750 JS_ReportError(cx, "not a CType");
3751 return false;
3752 }
3754 // Create the appropriate string depending on whether we're sCTypeClass or
3755 // sCTypeProtoClass.
3756 JSString* result;
3757 if (CType::IsCType(obj)) {
3758 AutoString source;
3759 BuildTypeSource(cx, obj, false, source);
3760 result = NewUCString(cx, source);
3761 } else {
3762 result = JS_NewStringCopyZ(cx, "[CType proto object]");
3763 }
3764 if (!result)
3765 return false;
3767 args.rval().setString(result);
3768 return true;
3769 }
3771 bool
3772 CType::HasInstance(JSContext* cx, HandleObject obj, MutableHandleValue v, bool* bp)
3773 {
3774 JS_ASSERT(CType::IsCType(obj));
3776 jsval slot = JS_GetReservedSlot(obj, SLOT_PROTO);
3777 JS::Rooted<JSObject*> prototype(cx, &slot.toObject());
3778 JS_ASSERT(prototype);
3779 JS_ASSERT(CData::IsCDataProto(prototype));
3781 *bp = false;
3782 if (JSVAL_IS_PRIMITIVE(v))
3783 return true;
3785 RootedObject proto(cx, &v.toObject());
3786 for (;;) {
3787 if (!JS_GetPrototype(cx, proto, &proto))
3788 return false;
3789 if (!proto)
3790 break;
3791 if (proto == prototype) {
3792 *bp = true;
3793 break;
3794 }
3795 }
3796 return true;
3797 }
3799 static JSObject*
3800 CType::GetGlobalCTypes(JSContext* cx, JSObject* objArg)
3801 {
3802 JS_ASSERT(CType::IsCType(objArg));
3804 RootedObject obj(cx, objArg);
3805 RootedObject objTypeProto(cx);
3806 if (!JS_GetPrototype(cx, obj, &objTypeProto))
3807 return nullptr;
3808 JS_ASSERT(objTypeProto);
3809 JS_ASSERT(CType::IsCTypeProto(objTypeProto));
3811 jsval valCTypes = JS_GetReservedSlot(objTypeProto, SLOT_CTYPES);
3812 JS_ASSERT(!JSVAL_IS_PRIMITIVE(valCTypes));
3814 JS_ASSERT(!JSVAL_IS_PRIMITIVE(valCTypes));
3815 return &valCTypes.toObject();
3816 }
3818 /*******************************************************************************
3819 ** ABI implementation
3820 *******************************************************************************/
3822 bool
3823 ABI::IsABI(JSObject* obj)
3824 {
3825 return JS_GetClass(obj) == &sCABIClass;
3826 }
3828 bool
3829 ABI::ToSource(JSContext* cx, unsigned argc, jsval* vp)
3830 {
3831 CallArgs args = CallArgsFromVp(argc, vp);
3832 if (args.length() != 0) {
3833 JS_ReportError(cx, "toSource takes zero arguments");
3834 return false;
3835 }
3837 JSObject* obj = JS_THIS_OBJECT(cx, vp);
3838 if (!obj)
3839 return false;
3840 if (!ABI::IsABI(obj)) {
3841 JS_ReportError(cx, "not an ABI");
3842 return false;
3843 }
3845 JSString* result;
3846 switch (GetABICode(obj)) {
3847 case ABI_DEFAULT:
3848 result = JS_NewStringCopyZ(cx, "ctypes.default_abi");
3849 break;
3850 case ABI_STDCALL:
3851 result = JS_NewStringCopyZ(cx, "ctypes.stdcall_abi");
3852 break;
3853 case ABI_WINAPI:
3854 result = JS_NewStringCopyZ(cx, "ctypes.winapi_abi");
3855 break;
3856 default:
3857 JS_ReportError(cx, "not a valid ABICode");
3858 return false;
3859 }
3860 if (!result)
3861 return false;
3863 args.rval().setString(result);
3864 return true;
3865 }
3868 /*******************************************************************************
3869 ** PointerType implementation
3870 *******************************************************************************/
3872 bool
3873 PointerType::Create(JSContext* cx, unsigned argc, jsval* vp)
3874 {
3875 CallArgs args = CallArgsFromVp(argc, vp);
3876 // Construct and return a new PointerType object.
3877 if (args.length() != 1) {
3878 JS_ReportError(cx, "PointerType takes one argument");
3879 return false;
3880 }
3882 jsval arg = args[0];
3883 RootedObject obj(cx);
3884 if (JSVAL_IS_PRIMITIVE(arg) || !CType::IsCType(obj = &arg.toObject())) {
3885 JS_ReportError(cx, "first argument must be a CType");
3886 return false;
3887 }
3889 JSObject* result = CreateInternal(cx, obj);
3890 if (!result)
3891 return false;
3893 args.rval().setObject(*result);
3894 return true;
3895 }
3897 JSObject*
3898 PointerType::CreateInternal(JSContext* cx, HandleObject baseType)
3899 {
3900 // check if we have a cached PointerType on our base CType.
3901 jsval slot = JS_GetReservedSlot(baseType, SLOT_PTR);
3902 if (!slot.isUndefined())
3903 return &slot.toObject();
3905 // Get ctypes.PointerType.prototype and the common prototype for CData objects
3906 // of this type, or ctypes.FunctionType.prototype for function pointers.
3907 CTypeProtoSlot slotId = CType::GetTypeCode(baseType) == TYPE_function ?
3908 SLOT_FUNCTIONDATAPROTO : SLOT_POINTERDATAPROTO;
3909 RootedObject dataProto(cx, CType::GetProtoFromType(cx, baseType, slotId));
3910 if (!dataProto)
3911 return nullptr;
3912 RootedObject typeProto(cx, CType::GetProtoFromType(cx, baseType, SLOT_POINTERPROTO));
3913 if (!typeProto)
3914 return nullptr;
3916 // Create a new CType object with the common properties and slots.
3917 JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_pointer,
3918 nullptr, INT_TO_JSVAL(sizeof(void*)),
3919 INT_TO_JSVAL(ffi_type_pointer.alignment),
3920 &ffi_type_pointer);
3921 if (!typeObj)
3922 return nullptr;
3924 // Set the target type. (This will be 'null' for an opaque pointer type.)
3925 JS_SetReservedSlot(typeObj, SLOT_TARGET_T, OBJECT_TO_JSVAL(baseType));
3927 // Finally, cache our newly-created PointerType on our pointed-to CType.
3928 JS_SetReservedSlot(baseType, SLOT_PTR, OBJECT_TO_JSVAL(typeObj));
3930 return typeObj;
3931 }
3933 bool
3934 PointerType::ConstructData(JSContext* cx,
3935 HandleObject obj,
3936 const CallArgs& args)
3937 {
3938 if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_pointer) {
3939 JS_ReportError(cx, "not a PointerType");
3940 return false;
3941 }
3943 if (args.length() > 3) {
3944 JS_ReportError(cx, "constructor takes 0, 1, 2, or 3 arguments");
3945 return false;
3946 }
3948 RootedObject result(cx, CData::Create(cx, obj, NullPtr(), nullptr, true));
3949 if (!result)
3950 return false;
3952 // Set return value early, must not observe *vp after
3953 args.rval().setObject(*result);
3955 // There are 3 things that we might be creating here:
3956 // 1 - A null pointer (no arguments)
3957 // 2 - An initialized pointer (1 argument)
3958 // 3 - A closure (1-3 arguments)
3959 //
3960 // The API doesn't give us a perfect way to distinguish 2 and 3, but the
3961 // heuristics we use should be fine.
3963 //
3964 // Case 1 - Null pointer
3965 //
3966 if (args.length() == 0)
3967 return true;
3969 // Analyze the arguments a bit to decide what to do next.
3970 RootedObject baseObj(cx, PointerType::GetBaseType(obj));
3971 bool looksLikeClosure = CType::GetTypeCode(baseObj) == TYPE_function &&
3972 args[0].isObject() &&
3973 JS_ObjectIsCallable(cx, &args[0].toObject());
3975 //
3976 // Case 2 - Initialized pointer
3977 //
3978 if (!looksLikeClosure) {
3979 if (args.length() != 1) {
3980 JS_ReportError(cx, "first argument must be a function");
3981 return false;
3982 }
3983 return ExplicitConvert(cx, args[0], obj, CData::GetData(result));
3984 }
3986 //
3987 // Case 3 - Closure
3988 //
3990 // The second argument is an optional 'this' parameter with which to invoke
3991 // the given js function. Callers may leave this blank, or pass null if they
3992 // wish to pass the third argument.
3993 RootedObject thisObj(cx, nullptr);
3994 if (args.length() >= 2) {
3995 if (args[1].isNull()) {
3996 thisObj = nullptr;
3997 } else if (!JSVAL_IS_PRIMITIVE(args[1])) {
3998 thisObj = &args[1].toObject();
3999 } else if (!JS_ValueToObject(cx, args[1], &thisObj)) {
4000 return false;
4001 }
4002 }
4004 // The third argument is an optional error sentinel that js-ctypes will return
4005 // if an exception is raised while executing the closure. The type must match
4006 // the return type of the callback.
4007 jsval errVal = JSVAL_VOID;
4008 if (args.length() == 3)
4009 errVal = args[2];
4011 RootedObject fnObj(cx, &args[0].toObject());
4012 return FunctionType::ConstructData(cx, baseObj, result, fnObj, thisObj, errVal);
4013 }
4015 JSObject*
4016 PointerType::GetBaseType(JSObject* obj)
4017 {
4018 JS_ASSERT(CType::GetTypeCode(obj) == TYPE_pointer);
4020 jsval type = JS_GetReservedSlot(obj, SLOT_TARGET_T);
4021 JS_ASSERT(!type.isNull());
4022 return &type.toObject();
4023 }
4025 bool
4026 PointerType::IsPointerType(HandleValue v)
4027 {
4028 if (!v.isObject())
4029 return false;
4030 JSObject* obj = &v.toObject();
4031 return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_pointer;
4032 }
4034 bool
4035 PointerType::IsPointer(HandleValue v)
4036 {
4037 if (!v.isObject())
4038 return false;
4039 JSObject* obj = &v.toObject();
4040 return CData::IsCData(obj) && CType::GetTypeCode(CData::GetCType(obj)) == TYPE_pointer;
4041 }
4043 bool
4044 PointerType::TargetTypeGetter(JSContext* cx, JS::CallArgs args)
4045 {
4046 RootedObject obj(cx, &args.thisv().toObject());
4047 args.rval().set(JS_GetReservedSlot(obj, SLOT_TARGET_T));
4048 MOZ_ASSERT(args.rval().isObject());
4049 return true;
4050 }
4052 bool
4053 PointerType::IsNull(JSContext* cx, unsigned argc, jsval* vp)
4054 {
4055 CallArgs args = CallArgsFromVp(argc, vp);
4056 JSObject* obj = JS_THIS_OBJECT(cx, vp);
4057 if (!obj)
4058 return false;
4059 if (!CData::IsCData(obj)) {
4060 JS_ReportError(cx, "not a CData");
4061 return false;
4062 }
4064 // Get pointer type and base type.
4065 JSObject* typeObj = CData::GetCType(obj);
4066 if (CType::GetTypeCode(typeObj) != TYPE_pointer) {
4067 JS_ReportError(cx, "not a PointerType");
4068 return false;
4069 }
4071 void* data = *static_cast<void**>(CData::GetData(obj));
4072 args.rval().setBoolean(data == nullptr);
4073 return true;
4074 }
4076 bool
4077 PointerType::OffsetBy(JSContext* cx, const CallArgs& args, int offset)
4078 {
4079 JSObject* obj = JS_THIS_OBJECT(cx, args.base());
4080 if (!obj)
4081 return false;
4082 if (!CData::IsCData(obj)) {
4083 JS_ReportError(cx, "not a CData");
4084 return false;
4085 }
4087 RootedObject typeObj(cx, CData::GetCType(obj));
4088 if (CType::GetTypeCode(typeObj) != TYPE_pointer) {
4089 JS_ReportError(cx, "not a PointerType");
4090 return false;
4091 }
4093 RootedObject baseType(cx, PointerType::GetBaseType(typeObj));
4094 if (!CType::IsSizeDefined(baseType)) {
4095 JS_ReportError(cx, "cannot modify pointer of undefined size");
4096 return false;
4097 }
4099 size_t elementSize = CType::GetSize(baseType);
4100 char* data = static_cast<char*>(*static_cast<void**>(CData::GetData(obj)));
4101 void* address = data + offset * elementSize;
4103 // Create a PointerType CData object containing the new address.
4104 JSObject* result = CData::Create(cx, typeObj, NullPtr(), &address, true);
4105 if (!result)
4106 return false;
4108 args.rval().setObject(*result);
4109 return true;
4110 }
4112 bool
4113 PointerType::Increment(JSContext* cx, unsigned argc, jsval* vp)
4114 {
4115 CallArgs args = CallArgsFromVp(argc, vp);
4116 return OffsetBy(cx, args, 1);
4117 }
4119 bool
4120 PointerType::Decrement(JSContext* cx, unsigned argc, jsval* vp)
4121 {
4122 CallArgs args = CallArgsFromVp(argc, vp);
4123 return OffsetBy(cx, args, -1);
4124 }
4126 bool
4127 PointerType::ContentsGetter(JSContext* cx, JS::CallArgs args)
4128 {
4129 RootedObject obj(cx, &args.thisv().toObject());
4130 RootedObject baseType(cx, GetBaseType(CData::GetCType(obj)));
4131 if (!CType::IsSizeDefined(baseType)) {
4132 JS_ReportError(cx, "cannot get contents of undefined size");
4133 return false;
4134 }
4136 void* data = *static_cast<void**>(CData::GetData(obj));
4137 if (data == nullptr) {
4138 JS_ReportError(cx, "cannot read contents of null pointer");
4139 return false;
4140 }
4142 RootedValue result(cx);
4143 if (!ConvertToJS(cx, baseType, NullPtr(), data, false, false, result.address()))
4144 return false;
4146 args.rval().set(result);
4147 return true;
4148 }
4150 bool
4151 PointerType::ContentsSetter(JSContext* cx, JS::CallArgs args)
4152 {
4153 RootedObject obj(cx, &args.thisv().toObject());
4154 RootedObject baseType(cx, GetBaseType(CData::GetCType(obj)));
4155 if (!CType::IsSizeDefined(baseType)) {
4156 JS_ReportError(cx, "cannot set contents of undefined size");
4157 return false;
4158 }
4160 void* data = *static_cast<void**>(CData::GetData(obj));
4161 if (data == nullptr) {
4162 JS_ReportError(cx, "cannot write contents to null pointer");
4163 return false;
4164 }
4166 args.rval().setUndefined();
4167 return ImplicitConvert(cx, args.get(0), baseType, data, false, nullptr);
4168 }
4170 /*******************************************************************************
4171 ** ArrayType implementation
4172 *******************************************************************************/
4174 bool
4175 ArrayType::Create(JSContext* cx, unsigned argc, jsval* vp)
4176 {
4177 CallArgs args = CallArgsFromVp(argc, vp);
4178 // Construct and return a new ArrayType object.
4179 if (args.length() < 1 || args.length() > 2) {
4180 JS_ReportError(cx, "ArrayType takes one or two arguments");
4181 return false;
4182 }
4184 if (JSVAL_IS_PRIMITIVE(args[0]) ||
4185 !CType::IsCType(&args[0].toObject())) {
4186 JS_ReportError(cx, "first argument must be a CType");
4187 return false;
4188 }
4190 // Convert the length argument to a size_t.
4191 size_t length = 0;
4192 if (args.length() == 2 && !jsvalToSize(cx, args[1], false, &length)) {
4193 JS_ReportError(cx, "second argument must be a nonnegative integer");
4194 return false;
4195 }
4197 RootedObject baseType(cx, &args[0].toObject());
4198 JSObject* result = CreateInternal(cx, baseType, length, args.length() == 2);
4199 if (!result)
4200 return false;
4202 args.rval().setObject(*result);
4203 return true;
4204 }
4206 JSObject*
4207 ArrayType::CreateInternal(JSContext* cx,
4208 HandleObject baseType,
4209 size_t length,
4210 bool lengthDefined)
4211 {
4212 // Get ctypes.ArrayType.prototype and the common prototype for CData objects
4213 // of this type, from ctypes.CType.prototype.
4214 RootedObject typeProto(cx, CType::GetProtoFromType(cx, baseType, SLOT_ARRAYPROTO));
4215 if (!typeProto)
4216 return nullptr;
4217 RootedObject dataProto(cx, CType::GetProtoFromType(cx, baseType, SLOT_ARRAYDATAPROTO));
4218 if (!dataProto)
4219 return nullptr;
4221 // Determine the size of the array from the base type, if possible.
4222 // The size of the base type must be defined.
4223 // If our length is undefined, both our size and length will be undefined.
4224 size_t baseSize;
4225 if (!CType::GetSafeSize(baseType, &baseSize)) {
4226 JS_ReportError(cx, "base size must be defined");
4227 return nullptr;
4228 }
4230 RootedValue sizeVal(cx, JSVAL_VOID);
4231 RootedValue lengthVal(cx, JSVAL_VOID);
4232 if (lengthDefined) {
4233 // Check for overflow, and convert to an int or double as required.
4234 size_t size = length * baseSize;
4235 if (length > 0 && size / length != baseSize) {
4236 JS_ReportError(cx, "size overflow");
4237 return nullptr;
4238 }
4239 if (!SizeTojsval(cx, size, sizeVal.address()) ||
4240 !SizeTojsval(cx, length, lengthVal.address()))
4241 return nullptr;
4242 }
4244 size_t align = CType::GetAlignment(baseType);
4246 // Create a new CType object with the common properties and slots.
4247 JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_array, nullptr,
4248 sizeVal, INT_TO_JSVAL(align), nullptr);
4249 if (!typeObj)
4250 return nullptr;
4252 // Set the element type.
4253 JS_SetReservedSlot(typeObj, SLOT_ELEMENT_T, OBJECT_TO_JSVAL(baseType));
4255 // Set the length.
4256 JS_SetReservedSlot(typeObj, SLOT_LENGTH, lengthVal);
4258 return typeObj;
4259 }
4261 bool
4262 ArrayType::ConstructData(JSContext* cx,
4263 HandleObject obj_,
4264 const CallArgs& args)
4265 {
4266 RootedObject obj(cx, obj_); // Make a mutable version
4268 if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_array) {
4269 JS_ReportError(cx, "not an ArrayType");
4270 return false;
4271 }
4273 // Decide whether we have an object to initialize from. We'll override this
4274 // if we get a length argument instead.
4275 bool convertObject = args.length() == 1;
4277 // Check if we're an array of undefined length. If we are, allow construction
4278 // with a length argument, or with an actual JS array.
4279 if (CType::IsSizeDefined(obj)) {
4280 if (args.length() > 1) {
4281 JS_ReportError(cx, "constructor takes zero or one argument");
4282 return false;
4283 }
4285 } else {
4286 if (args.length() != 1) {
4287 JS_ReportError(cx, "constructor takes one argument");
4288 return false;
4289 }
4291 RootedObject baseType(cx, GetBaseType(obj));
4293 size_t length;
4294 if (jsvalToSize(cx, args[0], false, &length)) {
4295 // Have a length, rather than an object to initialize from.
4296 convertObject = false;
4298 } else if (!JSVAL_IS_PRIMITIVE(args[0])) {
4299 // We were given an object with a .length property.
4300 // This could be a JS array, or a CData array.
4301 RootedObject arg(cx, &args[0].toObject());
4302 RootedValue lengthVal(cx);
4303 if (!JS_GetProperty(cx, arg, "length", &lengthVal) ||
4304 !jsvalToSize(cx, lengthVal, false, &length)) {
4305 JS_ReportError(cx, "argument must be an array object or length");
4306 return false;
4307 }
4309 } else if (args[0].isString()) {
4310 // We were given a string. Size the array to the appropriate length,
4311 // including space for the terminator.
4312 JSString* sourceString = args[0].toString();
4313 size_t sourceLength = sourceString->length();
4314 const jschar* sourceChars = sourceString->getChars(cx);
4315 if (!sourceChars)
4316 return false;
4318 switch (CType::GetTypeCode(baseType)) {
4319 case TYPE_char:
4320 case TYPE_signed_char:
4321 case TYPE_unsigned_char: {
4322 // Determine the UTF-8 length.
4323 length = GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength);
4324 if (length == (size_t) -1)
4325 return false;
4327 ++length;
4328 break;
4329 }
4330 case TYPE_jschar:
4331 length = sourceLength + 1;
4332 break;
4333 default:
4334 return TypeError(cx, "array", args[0]);
4335 }
4337 } else {
4338 JS_ReportError(cx, "argument must be an array object or length");
4339 return false;
4340 }
4342 // Construct a new ArrayType of defined length, for the new CData object.
4343 obj = CreateInternal(cx, baseType, length, true);
4344 if (!obj)
4345 return false;
4346 }
4348 JSObject* result = CData::Create(cx, obj, NullPtr(), nullptr, true);
4349 if (!result)
4350 return false;
4352 args.rval().setObject(*result);
4354 if (convertObject) {
4355 if (!ExplicitConvert(cx, args[0], obj, CData::GetData(result)))
4356 return false;
4357 }
4359 return true;
4360 }
4362 JSObject*
4363 ArrayType::GetBaseType(JSObject* obj)
4364 {
4365 JS_ASSERT(CType::IsCType(obj));
4366 JS_ASSERT(CType::GetTypeCode(obj) == TYPE_array);
4368 jsval type = JS_GetReservedSlot(obj, SLOT_ELEMENT_T);
4369 JS_ASSERT(!JSVAL_IS_NULL(type));
4370 return &type.toObject();
4371 }
4373 bool
4374 ArrayType::GetSafeLength(JSObject* obj, size_t* result)
4375 {
4376 JS_ASSERT(CType::IsCType(obj));
4377 JS_ASSERT(CType::GetTypeCode(obj) == TYPE_array);
4379 jsval length = JS_GetReservedSlot(obj, SLOT_LENGTH);
4381 // The "length" property can be an int, a double, or JSVAL_VOID
4382 // (for arrays of undefined length), and must always fit in a size_t.
4383 if (length.isInt32()) {
4384 *result = length.toInt32();;
4385 return true;
4386 }
4387 if (length.isDouble()) {
4388 *result = Convert<size_t>(length.toDouble());
4389 return true;
4390 }
4392 JS_ASSERT(length.isUndefined());
4393 return false;
4394 }
4396 size_t
4397 ArrayType::GetLength(JSObject* obj)
4398 {
4399 JS_ASSERT(CType::IsCType(obj));
4400 JS_ASSERT(CType::GetTypeCode(obj) == TYPE_array);
4402 jsval length = JS_GetReservedSlot(obj, SLOT_LENGTH);
4404 JS_ASSERT(!length.isUndefined());
4406 // The "length" property can be an int, a double, or JSVAL_VOID
4407 // (for arrays of undefined length), and must always fit in a size_t.
4408 // For callers who know it can never be JSVAL_VOID, return a size_t directly.
4409 if (length.isInt32())
4410 return length.toInt32();;
4411 return Convert<size_t>(length.toDouble());
4412 }
4414 ffi_type*
4415 ArrayType::BuildFFIType(JSContext* cx, JSObject* obj)
4416 {
4417 JS_ASSERT(CType::IsCType(obj));
4418 JS_ASSERT(CType::GetTypeCode(obj) == TYPE_array);
4419 JS_ASSERT(CType::IsSizeDefined(obj));
4421 JSObject* baseType = ArrayType::GetBaseType(obj);
4422 ffi_type* ffiBaseType = CType::GetFFIType(cx, baseType);
4423 if (!ffiBaseType)
4424 return nullptr;
4426 size_t length = ArrayType::GetLength(obj);
4428 // Create an ffi_type to represent the array. This is necessary for the case
4429 // where the array is part of a struct. Since libffi has no intrinsic
4430 // support for array types, we approximate it by creating a struct type
4431 // with elements of type 'baseType' and with appropriate size and alignment
4432 // values. It would be nice to not do all the work of setting up 'elements',
4433 // but some libffi platforms currently require that it be meaningful. I'm
4434 // looking at you, x86_64.
4435 AutoPtr<ffi_type> ffiType(cx->new_<ffi_type>());
4436 if (!ffiType) {
4437 JS_ReportOutOfMemory(cx);
4438 return nullptr;
4439 }
4441 ffiType->type = FFI_TYPE_STRUCT;
4442 ffiType->size = CType::GetSize(obj);
4443 ffiType->alignment = CType::GetAlignment(obj);
4444 ffiType->elements = cx->pod_malloc<ffi_type*>(length + 1);
4445 if (!ffiType->elements) {
4446 JS_ReportAllocationOverflow(cx);
4447 return nullptr;
4448 }
4450 for (size_t i = 0; i < length; ++i)
4451 ffiType->elements[i] = ffiBaseType;
4452 ffiType->elements[length] = nullptr;
4454 return ffiType.forget();
4455 }
4457 bool
4458 ArrayType::IsArrayType(HandleValue v)
4459 {
4460 if (!v.isObject())
4461 return false;
4462 JSObject* obj = &v.toObject();
4463 return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_array;
4464 }
4466 bool
4467 ArrayType::IsArrayOrArrayType(HandleValue v)
4468 {
4469 if (!v.isObject())
4470 return false;
4471 JSObject* obj = &v.toObject();
4473 // Allow both CTypes and CDatas of the ArrayType persuasion by extracting the
4474 // CType if we're dealing with a CData.
4475 if (CData::IsCData(obj)) {
4476 obj = CData::GetCType(obj);
4477 }
4478 return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_array;
4479 }
4481 bool
4482 ArrayType::ElementTypeGetter(JSContext* cx, JS::CallArgs args)
4483 {
4484 RootedObject obj(cx, &args.thisv().toObject());
4485 args.rval().set(JS_GetReservedSlot(obj, SLOT_ELEMENT_T));
4486 MOZ_ASSERT(args.rval().isObject());
4487 return true;
4488 }
4490 bool
4491 ArrayType::LengthGetter(JSContext* cx, JS::CallArgs args)
4492 {
4493 JSObject *obj = &args.thisv().toObject();
4495 // This getter exists for both CTypes and CDatas of the ArrayType persuasion.
4496 // If we're dealing with a CData, get the CType from it.
4497 if (CData::IsCData(obj))
4498 obj = CData::GetCType(obj);
4500 args.rval().set(JS_GetReservedSlot(obj, SLOT_LENGTH));
4501 JS_ASSERT(args.rval().isNumber() || args.rval().isUndefined());
4502 return true;
4503 }
4505 bool
4506 ArrayType::Getter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp)
4507 {
4508 // This should never happen, but we'll check to be safe.
4509 if (!CData::IsCData(obj)) {
4510 JS_ReportError(cx, "not a CData");
4511 return false;
4512 }
4514 // Bail early if we're not an ArrayType. (This setter is present for all
4515 // CData, regardless of CType.)
4516 JSObject* typeObj = CData::GetCType(obj);
4517 if (CType::GetTypeCode(typeObj) != TYPE_array)
4518 return true;
4520 // Convert the index to a size_t and bounds-check it.
4521 size_t index;
4522 size_t length = GetLength(typeObj);
4523 bool ok = jsidToSize(cx, idval, true, &index);
4524 int32_t dummy;
4525 if (!ok && JSID_IS_STRING(idval) && !StringToInteger(cx, JSID_TO_STRING(idval), &dummy)) {
4526 // String either isn't a number, or doesn't fit in size_t.
4527 // Chances are it's a regular property lookup, so return.
4528 return true;
4529 }
4530 if (!ok || index >= length) {
4531 JS_ReportError(cx, "invalid index");
4532 return false;
4533 }
4535 RootedObject baseType(cx, GetBaseType(typeObj));
4536 size_t elementSize = CType::GetSize(baseType);
4537 char* data = static_cast<char*>(CData::GetData(obj)) + elementSize * index;
4538 return ConvertToJS(cx, baseType, obj, data, false, false, vp.address());
4539 }
4541 bool
4542 ArrayType::Setter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, MutableHandleValue vp)
4543 {
4544 // This should never happen, but we'll check to be safe.
4545 if (!CData::IsCData(obj)) {
4546 JS_ReportError(cx, "not a CData");
4547 return false;
4548 }
4550 // Bail early if we're not an ArrayType. (This setter is present for all
4551 // CData, regardless of CType.)
4552 JSObject* typeObj = CData::GetCType(obj);
4553 if (CType::GetTypeCode(typeObj) != TYPE_array)
4554 return true;
4556 // Convert the index to a size_t and bounds-check it.
4557 size_t index;
4558 size_t length = GetLength(typeObj);
4559 bool ok = jsidToSize(cx, idval, true, &index);
4560 int32_t dummy;
4561 if (!ok && JSID_IS_STRING(idval) && !StringToInteger(cx, JSID_TO_STRING(idval), &dummy)) {
4562 // String either isn't a number, or doesn't fit in size_t.
4563 // Chances are it's a regular property lookup, so return.
4564 return true;
4565 }
4566 if (!ok || index >= length) {
4567 JS_ReportError(cx, "invalid index");
4568 return false;
4569 }
4571 JSObject* baseType = GetBaseType(typeObj);
4572 size_t elementSize = CType::GetSize(baseType);
4573 char* data = static_cast<char*>(CData::GetData(obj)) + elementSize * index;
4574 return ImplicitConvert(cx, vp, baseType, data, false, nullptr);
4575 }
4577 bool
4578 ArrayType::AddressOfElement(JSContext* cx, unsigned argc, jsval* vp)
4579 {
4580 CallArgs args = CallArgsFromVp(argc, vp);
4581 RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
4582 if (!obj)
4583 return false;
4584 if (!CData::IsCData(obj)) {
4585 JS_ReportError(cx, "not a CData");
4586 return false;
4587 }
4589 RootedObject typeObj(cx, CData::GetCType(obj));
4590 if (CType::GetTypeCode(typeObj) != TYPE_array) {
4591 JS_ReportError(cx, "not an ArrayType");
4592 return false;
4593 }
4595 if (args.length() != 1) {
4596 JS_ReportError(cx, "addressOfElement takes one argument");
4597 return false;
4598 }
4600 RootedObject baseType(cx, GetBaseType(typeObj));
4601 RootedObject pointerType(cx, PointerType::CreateInternal(cx, baseType));
4602 if (!pointerType)
4603 return false;
4605 // Create a PointerType CData object containing null.
4606 RootedObject result(cx, CData::Create(cx, pointerType, NullPtr(), nullptr, true));
4607 if (!result)
4608 return false;
4610 args.rval().setObject(*result);
4612 // Convert the index to a size_t and bounds-check it.
4613 size_t index;
4614 size_t length = GetLength(typeObj);
4615 if (!jsvalToSize(cx, args[0], false, &index) ||
4616 index >= length) {
4617 JS_ReportError(cx, "invalid index");
4618 return false;
4619 }
4621 // Manually set the pointer inside the object, so we skip the conversion step.
4622 void** data = static_cast<void**>(CData::GetData(result));
4623 size_t elementSize = CType::GetSize(baseType);
4624 *data = static_cast<char*>(CData::GetData(obj)) + elementSize * index;
4625 return true;
4626 }
4628 /*******************************************************************************
4629 ** StructType implementation
4630 *******************************************************************************/
4632 // For a struct field descriptor 'val' of the form { name : type }, extract
4633 // 'name' and 'type'.
4634 static JSFlatString*
4635 ExtractStructField(JSContext* cx, jsval val, JSObject** typeObj)
4636 {
4637 if (JSVAL_IS_PRIMITIVE(val)) {
4638 JS_ReportError(cx, "struct field descriptors require a valid name and type");
4639 return nullptr;
4640 }
4642 RootedObject obj(cx, JSVAL_TO_OBJECT(val));
4643 RootedObject iter(cx, JS_NewPropertyIterator(cx, obj));
4644 if (!iter)
4645 return nullptr;
4647 RootedId nameid(cx);
4648 if (!JS_NextProperty(cx, iter, nameid.address()))
4649 return nullptr;
4650 if (JSID_IS_VOID(nameid)) {
4651 JS_ReportError(cx, "struct field descriptors require a valid name and type");
4652 return nullptr;
4653 }
4655 if (!JSID_IS_STRING(nameid)) {
4656 JS_ReportError(cx, "struct field descriptors require a valid name and type");
4657 return nullptr;
4658 }
4660 // make sure we have one, and only one, property
4661 jsid id;
4662 if (!JS_NextProperty(cx, iter, &id))
4663 return nullptr;
4664 if (!JSID_IS_VOID(id)) {
4665 JS_ReportError(cx, "struct field descriptors must contain one property");
4666 return nullptr;
4667 }
4669 RootedValue propVal(cx);
4670 if (!JS_GetPropertyById(cx, obj, nameid, &propVal))
4671 return nullptr;
4673 if (propVal.isPrimitive() || !CType::IsCType(&propVal.toObject())) {
4674 JS_ReportError(cx, "struct field descriptors require a valid name and type");
4675 return nullptr;
4676 }
4678 // Undefined size or zero size struct members are illegal.
4679 // (Zero-size arrays are legal as struct members in C++, but libffi will
4680 // choke on a zero-size struct, so we disallow them.)
4681 *typeObj = &propVal.toObject();
4682 size_t size;
4683 if (!CType::GetSafeSize(*typeObj, &size) || size == 0) {
4684 JS_ReportError(cx, "struct field types must have defined and nonzero size");
4685 return nullptr;
4686 }
4688 return JSID_TO_FLAT_STRING(nameid);
4689 }
4691 // For a struct field with 'name' and 'type', add an element of the form
4692 // { name : type }.
4693 static bool
4694 AddFieldToArray(JSContext* cx,
4695 jsval* element,
4696 JSFlatString* name_,
4697 JSObject* typeObj_)
4698 {
4699 RootedObject typeObj(cx, typeObj_);
4700 Rooted<JSFlatString*> name(cx, name_);
4701 RootedObject fieldObj(cx, JS_NewObject(cx, nullptr, NullPtr(), NullPtr()));
4702 if (!fieldObj)
4703 return false;
4705 *element = OBJECT_TO_JSVAL(fieldObj);
4707 if (!JS_DefineUCProperty(cx, fieldObj,
4708 name->chars(), name->length(),
4709 OBJECT_TO_JSVAL(typeObj), nullptr, nullptr,
4710 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
4711 return false;
4713 return JS_FreezeObject(cx, fieldObj);
4714 }
4716 bool
4717 StructType::Create(JSContext* cx, unsigned argc, jsval* vp)
4718 {
4719 CallArgs args = CallArgsFromVp(argc, vp);
4721 // Construct and return a new StructType object.
4722 if (args.length() < 1 || args.length() > 2) {
4723 JS_ReportError(cx, "StructType takes one or two arguments");
4724 return false;
4725 }
4727 jsval name = args[0];
4728 if (!name.isString()) {
4729 JS_ReportError(cx, "first argument must be a string");
4730 return false;
4731 }
4733 // Get ctypes.StructType.prototype from the ctypes.StructType constructor.
4734 RootedObject typeProto(cx, CType::GetProtoFromCtor(&args.callee(), SLOT_STRUCTPROTO));
4736 // Create a simple StructType with no defined fields. The result will be
4737 // non-instantiable as CData, will have no 'prototype' property, and will
4738 // have undefined size and alignment and no ffi_type.
4739 RootedObject result(cx, CType::Create(cx, typeProto, NullPtr(), TYPE_struct,
4740 JSVAL_TO_STRING(name), JSVAL_VOID, JSVAL_VOID, nullptr));
4741 if (!result)
4742 return false;
4744 if (args.length() == 2) {
4745 RootedObject arr(cx, JSVAL_IS_PRIMITIVE(args[1]) ? nullptr : &args[1].toObject());
4746 if (!arr || !JS_IsArrayObject(cx, arr)) {
4747 JS_ReportError(cx, "second argument must be an array");
4748 return false;
4749 }
4751 // Define the struct fields.
4752 if (!DefineInternal(cx, result, arr))
4753 return false;
4754 }
4756 args.rval().setObject(*result);
4757 return true;
4758 }
4760 static void
4761 PostBarrierCallback(JSTracer *trc, JSString *key, void *data)
4762 {
4763 typedef HashMap<JSFlatString*,
4764 UnbarrieredFieldInfo,
4765 FieldHashPolicy,
4766 SystemAllocPolicy> UnbarrieredFieldInfoHash;
4768 UnbarrieredFieldInfoHash *table = reinterpret_cast<UnbarrieredFieldInfoHash*>(data);
4769 JSString *prior = key;
4770 JS_CallStringTracer(trc, &key, "CType fieldName");
4771 table->rekeyIfMoved(JS_ASSERT_STRING_IS_FLAT(prior), JS_ASSERT_STRING_IS_FLAT(key));
4772 }
4774 bool
4775 StructType::DefineInternal(JSContext* cx, JSObject* typeObj_, JSObject* fieldsObj_)
4776 {
4777 RootedObject typeObj(cx, typeObj_);
4778 RootedObject fieldsObj(cx, fieldsObj_);
4780 uint32_t len;
4781 ASSERT_OK(JS_GetArrayLength(cx, fieldsObj, &len));
4783 // Get the common prototype for CData objects of this type from
4784 // ctypes.CType.prototype.
4785 RootedObject dataProto(cx, CType::GetProtoFromType(cx, typeObj, SLOT_STRUCTDATAPROTO));
4786 if (!dataProto)
4787 return false;
4789 // Set up the 'prototype' and 'prototype.constructor' properties.
4790 // The prototype will reflect the struct fields as properties on CData objects
4791 // created from this type.
4792 RootedObject prototype(cx, JS_NewObject(cx, &sCDataProtoClass, dataProto, NullPtr()));
4793 if (!prototype)
4794 return false;
4796 if (!JS_DefineProperty(cx, prototype, "constructor", typeObj,
4797 JSPROP_READONLY | JSPROP_PERMANENT))
4798 return false;
4800 // Create a FieldInfoHash to stash on the type object, and an array to root
4801 // its constituents. (We cannot simply stash the hash in a reserved slot now
4802 // to get GC safety for free, since if anything in this function fails we
4803 // do not want to mutate 'typeObj'.)
4804 AutoPtr<FieldInfoHash> fields(cx->new_<FieldInfoHash>());
4805 if (!fields || !fields->init(len)) {
4806 JS_ReportOutOfMemory(cx);
4807 return false;
4808 }
4809 JS::AutoValueVector fieldRoots(cx);
4810 if (!fieldRoots.resize(len)) {
4811 JS_ReportOutOfMemory(cx);
4812 return false;
4813 }
4815 // Process the field types.
4816 size_t structSize, structAlign;
4817 if (len != 0) {
4818 structSize = 0;
4819 structAlign = 0;
4821 for (uint32_t i = 0; i < len; ++i) {
4822 RootedValue item(cx);
4823 if (!JS_GetElement(cx, fieldsObj, i, &item))
4824 return false;
4826 RootedObject fieldType(cx, nullptr);
4827 Rooted<JSFlatString*> name(cx, ExtractStructField(cx, item, fieldType.address()));
4828 if (!name)
4829 return false;
4830 fieldRoots[i] = JS::ObjectValue(*fieldType);
4832 // Make sure each field name is unique
4833 FieldInfoHash::AddPtr entryPtr = fields->lookupForAdd(name);
4834 if (entryPtr) {
4835 JS_ReportError(cx, "struct fields must have unique names");
4836 return false;
4837 }
4839 // Add the field to the StructType's 'prototype' property.
4840 if (!JS_DefineUCProperty(cx, prototype,
4841 name->chars(), name->length(), JSVAL_VOID,
4842 StructType::FieldGetter, StructType::FieldSetter,
4843 JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_PERMANENT))
4844 return false;
4846 size_t fieldSize = CType::GetSize(fieldType);
4847 size_t fieldAlign = CType::GetAlignment(fieldType);
4848 size_t fieldOffset = Align(structSize, fieldAlign);
4849 // Check for overflow. Since we hold invariant that fieldSize % fieldAlign
4850 // be zero, we can safely check fieldOffset + fieldSize without first
4851 // checking fieldOffset for overflow.
4852 if (fieldOffset + fieldSize < structSize) {
4853 JS_ReportError(cx, "size overflow");
4854 return false;
4855 }
4857 // Add field name to the hash
4858 FieldInfo info;
4859 info.mType = fieldType;
4860 info.mIndex = i;
4861 info.mOffset = fieldOffset;
4862 ASSERT_OK(fields->add(entryPtr, name, info));
4863 JS_StoreStringPostBarrierCallback(cx, PostBarrierCallback, name, fields.get());
4865 structSize = fieldOffset + fieldSize;
4867 if (fieldAlign > structAlign)
4868 structAlign = fieldAlign;
4869 }
4871 // Pad the struct tail according to struct alignment.
4872 size_t structTail = Align(structSize, structAlign);
4873 if (structTail < structSize) {
4874 JS_ReportError(cx, "size overflow");
4875 return false;
4876 }
4877 structSize = structTail;
4879 } else {
4880 // Empty structs are illegal in C, but are legal and have a size of
4881 // 1 byte in C++. We're going to allow them, and trick libffi into
4882 // believing this by adding a char member. The resulting struct will have
4883 // no getters or setters, and will be initialized to zero.
4884 structSize = 1;
4885 structAlign = 1;
4886 }
4888 RootedValue sizeVal(cx);
4889 if (!SizeTojsval(cx, structSize, sizeVal.address()))
4890 return false;
4892 JS_SetReservedSlot(typeObj, SLOT_FIELDINFO, PRIVATE_TO_JSVAL(fields.forget()));
4894 JS_SetReservedSlot(typeObj, SLOT_SIZE, sizeVal);
4895 JS_SetReservedSlot(typeObj, SLOT_ALIGN, INT_TO_JSVAL(structAlign));
4896 //if (!JS_FreezeObject(cx, prototype)0 // XXX fixme - see bug 541212!
4897 // return false;
4898 JS_SetReservedSlot(typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype));
4899 return true;
4900 }
4902 ffi_type*
4903 StructType::BuildFFIType(JSContext* cx, JSObject* obj)
4904 {
4905 JS_ASSERT(CType::IsCType(obj));
4906 JS_ASSERT(CType::GetTypeCode(obj) == TYPE_struct);
4907 JS_ASSERT(CType::IsSizeDefined(obj));
4909 const FieldInfoHash* fields = GetFieldInfo(obj);
4910 size_t len = fields->count();
4912 size_t structSize = CType::GetSize(obj);
4913 size_t structAlign = CType::GetAlignment(obj);
4915 AutoPtr<ffi_type> ffiType(cx->new_<ffi_type>());
4916 if (!ffiType) {
4917 JS_ReportOutOfMemory(cx);
4918 return nullptr;
4919 }
4920 ffiType->type = FFI_TYPE_STRUCT;
4922 AutoPtr<ffi_type*> elements;
4923 if (len != 0) {
4924 elements = cx->pod_malloc<ffi_type*>(len + 1);
4925 if (!elements) {
4926 JS_ReportOutOfMemory(cx);
4927 return nullptr;
4928 }
4929 elements[len] = nullptr;
4931 for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
4932 const FieldInfoHash::Entry& entry = r.front();
4933 ffi_type* fieldType = CType::GetFFIType(cx, entry.value().mType);
4934 if (!fieldType)
4935 return nullptr;
4936 elements[entry.value().mIndex] = fieldType;
4937 }
4939 } else {
4940 // Represent an empty struct as having a size of 1 byte, just like C++.
4941 JS_ASSERT(structSize == 1);
4942 JS_ASSERT(structAlign == 1);
4943 elements = cx->pod_malloc<ffi_type*>(2);
4944 if (!elements) {
4945 JS_ReportOutOfMemory(cx);
4946 return nullptr;
4947 }
4948 elements[0] = &ffi_type_uint8;
4949 elements[1] = nullptr;
4950 }
4952 ffiType->elements = elements.get();
4954 #ifdef DEBUG
4955 // Perform a sanity check: the result of our struct size and alignment
4956 // calculations should match libffi's. We force it to do this calculation
4957 // by calling ffi_prep_cif.
4958 ffi_cif cif;
4959 ffiType->size = 0;
4960 ffiType->alignment = 0;
4961 ffi_status status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 0, ffiType.get(), nullptr);
4962 JS_ASSERT(status == FFI_OK);
4963 JS_ASSERT(structSize == ffiType->size);
4964 JS_ASSERT(structAlign == ffiType->alignment);
4965 #else
4966 // Fill in the ffi_type's size and align fields. This makes libffi treat the
4967 // type as initialized; it will not recompute the values. (We assume
4968 // everything agrees; if it doesn't, we really want to know about it, which
4969 // is the purpose of the above debug-only check.)
4970 ffiType->size = structSize;
4971 ffiType->alignment = structAlign;
4972 #endif
4974 elements.forget();
4975 return ffiType.forget();
4976 }
4978 bool
4979 StructType::Define(JSContext* cx, unsigned argc, jsval* vp)
4980 {
4981 CallArgs args = CallArgsFromVp(argc, vp);
4982 RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
4983 if (!obj)
4984 return false;
4985 if (!CType::IsCType(obj) ||
4986 CType::GetTypeCode(obj) != TYPE_struct) {
4987 JS_ReportError(cx, "not a StructType");
4988 return false;
4989 }
4991 if (CType::IsSizeDefined(obj)) {
4992 JS_ReportError(cx, "StructType has already been defined");
4993 return false;
4994 }
4996 if (args.length() != 1) {
4997 JS_ReportError(cx, "define takes one argument");
4998 return false;
4999 }
5001 jsval arg = args[0];
5002 if (JSVAL_IS_PRIMITIVE(arg)) {
5003 JS_ReportError(cx, "argument must be an array");
5004 return false;
5005 }
5006 RootedObject arr(cx, JSVAL_TO_OBJECT(arg));
5007 if (!JS_IsArrayObject(cx, arr)) {
5008 JS_ReportError(cx, "argument must be an array");
5009 return false;
5010 }
5012 return DefineInternal(cx, obj, arr);
5013 }
5015 bool
5016 StructType::ConstructData(JSContext* cx,
5017 HandleObject obj,
5018 const CallArgs& args)
5019 {
5020 if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_struct) {
5021 JS_ReportError(cx, "not a StructType");
5022 return false;
5023 }
5025 if (!CType::IsSizeDefined(obj)) {
5026 JS_ReportError(cx, "cannot construct an opaque StructType");
5027 return false;
5028 }
5030 JSObject* result = CData::Create(cx, obj, NullPtr(), nullptr, true);
5031 if (!result)
5032 return false;
5034 args.rval().setObject(*result);
5036 if (args.length() == 0)
5037 return true;
5039 char* buffer = static_cast<char*>(CData::GetData(result));
5040 const FieldInfoHash* fields = GetFieldInfo(obj);
5042 if (args.length() == 1) {
5043 // There are two possible interpretations of the argument:
5044 // 1) It may be an object '{ ... }' with properties representing the
5045 // struct fields intended to ExplicitConvert wholesale to our StructType.
5046 // 2) If the struct contains one field, the arg may be intended to
5047 // ImplicitConvert directly to that arg's CType.
5048 // Thankfully, the conditions for these two possibilities to succeed
5049 // are mutually exclusive, so we can pick the right one.
5051 // Try option 1) first.
5052 if (ExplicitConvert(cx, args[0], obj, buffer))
5053 return true;
5055 if (fields->count() != 1)
5056 return false;
5058 // If ExplicitConvert failed, and there is no pending exception, then assume
5059 // hard failure (out of memory, or some other similarly serious condition).
5060 if (!JS_IsExceptionPending(cx))
5061 return false;
5063 // Otherwise, assume soft failure, and clear the pending exception so that we
5064 // can throw a different one as required.
5065 JS_ClearPendingException(cx);
5067 // Fall through to try option 2).
5068 }
5070 // We have a type constructor of the form 'ctypes.StructType(a, b, c, ...)'.
5071 // ImplicitConvert each field.
5072 if (args.length() == fields->count()) {
5073 for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
5074 const FieldInfo& field = r.front().value();
5075 STATIC_ASSUME(field.mIndex < fields->count()); /* Quantified invariant */
5076 if (!ImplicitConvert(cx, args[field.mIndex], field.mType,
5077 buffer + field.mOffset,
5078 false, nullptr))
5079 return false;
5080 }
5082 return true;
5083 }
5085 JS_ReportError(cx, "constructor takes 0, 1, or %u arguments",
5086 fields->count());
5087 return false;
5088 }
5090 const FieldInfoHash*
5091 StructType::GetFieldInfo(JSObject* obj)
5092 {
5093 JS_ASSERT(CType::IsCType(obj));
5094 JS_ASSERT(CType::GetTypeCode(obj) == TYPE_struct);
5096 jsval slot = JS_GetReservedSlot(obj, SLOT_FIELDINFO);
5097 JS_ASSERT(!JSVAL_IS_VOID(slot) && JSVAL_TO_PRIVATE(slot));
5099 return static_cast<const FieldInfoHash*>(JSVAL_TO_PRIVATE(slot));
5100 }
5102 const FieldInfo*
5103 StructType::LookupField(JSContext* cx, JSObject* obj, JSFlatString *name)
5104 {
5105 JS_ASSERT(CType::IsCType(obj));
5106 JS_ASSERT(CType::GetTypeCode(obj) == TYPE_struct);
5108 FieldInfoHash::Ptr ptr = GetFieldInfo(obj)->lookup(name);
5109 if (ptr)
5110 return &ptr->value();
5112 JSAutoByteString bytes(cx, name);
5113 if (!bytes)
5114 return nullptr;
5116 JS_ReportError(cx, "%s does not name a field", bytes.ptr());
5117 return nullptr;
5118 }
5120 JSObject*
5121 StructType::BuildFieldsArray(JSContext* cx, JSObject* obj)
5122 {
5123 JS_ASSERT(CType::IsCType(obj));
5124 JS_ASSERT(CType::GetTypeCode(obj) == TYPE_struct);
5125 JS_ASSERT(CType::IsSizeDefined(obj));
5127 const FieldInfoHash* fields = GetFieldInfo(obj);
5128 size_t len = fields->count();
5130 // Prepare a new array for the 'fields' property of the StructType.
5131 JS::AutoValueVector fieldsVec(cx);
5132 if (!fieldsVec.resize(len))
5133 return nullptr;
5135 for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
5136 const FieldInfoHash::Entry& entry = r.front();
5137 // Add the field descriptor to the array.
5138 if (!AddFieldToArray(cx, &fieldsVec[entry.value().mIndex],
5139 entry.key(), entry.value().mType))
5140 return nullptr;
5141 }
5143 RootedObject fieldsProp(cx, JS_NewArrayObject(cx, fieldsVec));
5144 if (!fieldsProp)
5145 return nullptr;
5147 // Seal the fields array.
5148 if (!JS_FreezeObject(cx, fieldsProp))
5149 return nullptr;
5151 return fieldsProp;
5152 }
5154 /* static */ bool
5155 StructType::IsStruct(HandleValue v)
5156 {
5157 if (!v.isObject())
5158 return false;
5159 JSObject* obj = &v.toObject();
5160 return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_struct;
5161 }
5163 bool
5164 StructType::FieldsArrayGetter(JSContext* cx, JS::CallArgs args)
5165 {
5166 RootedObject obj(cx, &args.thisv().toObject());
5168 args.rval().set(JS_GetReservedSlot(obj, SLOT_FIELDS));
5170 if (!CType::IsSizeDefined(obj)) {
5171 MOZ_ASSERT(args.rval().isUndefined());
5172 return true;
5173 }
5175 if (args.rval().isUndefined()) {
5176 // Build the 'fields' array lazily.
5177 JSObject* fields = BuildFieldsArray(cx, obj);
5178 if (!fields)
5179 return false;
5180 JS_SetReservedSlot(obj, SLOT_FIELDS, OBJECT_TO_JSVAL(fields));
5182 args.rval().setObject(*fields);
5183 }
5185 MOZ_ASSERT(args.rval().isObject());
5186 MOZ_ASSERT(JS_IsArrayObject(cx, args.rval()));
5187 return true;
5188 }
5190 bool
5191 StructType::FieldGetter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp)
5192 {
5193 if (!CData::IsCData(obj)) {
5194 JS_ReportError(cx, "not a CData");
5195 return false;
5196 }
5198 JSObject* typeObj = CData::GetCType(obj);
5199 if (CType::GetTypeCode(typeObj) != TYPE_struct) {
5200 JS_ReportError(cx, "not a StructType");
5201 return false;
5202 }
5204 const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_FLAT_STRING(idval));
5205 if (!field)
5206 return false;
5208 char* data = static_cast<char*>(CData::GetData(obj)) + field->mOffset;
5209 RootedObject fieldType(cx, field->mType);
5210 return ConvertToJS(cx, fieldType, obj, data, false, false, vp.address());
5211 }
5213 bool
5214 StructType::FieldSetter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, MutableHandleValue vp)
5215 {
5216 if (!CData::IsCData(obj)) {
5217 JS_ReportError(cx, "not a CData");
5218 return false;
5219 }
5221 JSObject* typeObj = CData::GetCType(obj);
5222 if (CType::GetTypeCode(typeObj) != TYPE_struct) {
5223 JS_ReportError(cx, "not a StructType");
5224 return false;
5225 }
5227 const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_FLAT_STRING(idval));
5228 if (!field)
5229 return false;
5231 char* data = static_cast<char*>(CData::GetData(obj)) + field->mOffset;
5232 return ImplicitConvert(cx, vp, field->mType, data, false, nullptr);
5233 }
5235 bool
5236 StructType::AddressOfField(JSContext* cx, unsigned argc, jsval* vp)
5237 {
5238 CallArgs args = CallArgsFromVp(argc, vp);
5239 RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
5240 if (!obj)
5241 return false;
5242 if (!CData::IsCData(obj)) {
5243 JS_ReportError(cx, "not a CData");
5244 return false;
5245 }
5247 JSObject* typeObj = CData::GetCType(obj);
5248 if (CType::GetTypeCode(typeObj) != TYPE_struct) {
5249 JS_ReportError(cx, "not a StructType");
5250 return false;
5251 }
5253 if (args.length() != 1) {
5254 JS_ReportError(cx, "addressOfField takes one argument");
5255 return false;
5256 }
5258 JSFlatString *str = JS_FlattenString(cx, args[0].toString());
5259 if (!str)
5260 return false;
5262 const FieldInfo* field = LookupField(cx, typeObj, str);
5263 if (!field)
5264 return false;
5266 RootedObject baseType(cx, field->mType);
5267 RootedObject pointerType(cx, PointerType::CreateInternal(cx, baseType));
5268 if (!pointerType)
5269 return false;
5271 // Create a PointerType CData object containing null.
5272 JSObject* result = CData::Create(cx, pointerType, NullPtr(), nullptr, true);
5273 if (!result)
5274 return false;
5276 args.rval().setObject(*result);
5278 // Manually set the pointer inside the object, so we skip the conversion step.
5279 void** data = static_cast<void**>(CData::GetData(result));
5280 *data = static_cast<char*>(CData::GetData(obj)) + field->mOffset;
5281 return true;
5282 }
5284 /*******************************************************************************
5285 ** FunctionType implementation
5286 *******************************************************************************/
5288 // Helper class for handling allocation of function arguments.
5289 struct AutoValue
5290 {
5291 AutoValue() : mData(nullptr) { }
5293 ~AutoValue()
5294 {
5295 js_free(mData);
5296 }
5298 bool SizeToType(JSContext* cx, JSObject* type)
5299 {
5300 // Allocate a minimum of sizeof(ffi_arg) to handle small integers.
5301 size_t size = Align(CType::GetSize(type), sizeof(ffi_arg));
5302 mData = js_malloc(size);
5303 if (mData)
5304 memset(mData, 0, size);
5305 return mData != nullptr;
5306 }
5308 void* mData;
5309 };
5311 static bool
5312 GetABI(JSContext* cx, jsval abiType, ffi_abi* result)
5313 {
5314 if (JSVAL_IS_PRIMITIVE(abiType))
5315 return false;
5317 ABICode abi = GetABICode(JSVAL_TO_OBJECT(abiType));
5319 // determine the ABI from the subset of those available on the
5320 // given platform. ABI_DEFAULT specifies the default
5321 // C calling convention (cdecl) on each platform.
5322 switch (abi) {
5323 case ABI_DEFAULT:
5324 *result = FFI_DEFAULT_ABI;
5325 return true;
5326 case ABI_STDCALL:
5327 case ABI_WINAPI:
5328 #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
5329 *result = FFI_STDCALL;
5330 return true;
5331 #elif (defined(_WIN64))
5332 // We'd like the same code to work across Win32 and Win64, so stdcall_api
5333 // and winapi_abi become aliases to the lone Win64 ABI.
5334 *result = FFI_WIN64;
5335 return true;
5336 #endif
5337 case INVALID_ABI:
5338 break;
5339 }
5340 return false;
5341 }
5343 static JSObject*
5344 PrepareType(JSContext* cx, jsval type)
5345 {
5346 if (JSVAL_IS_PRIMITIVE(type) ||
5347 !CType::IsCType(JSVAL_TO_OBJECT(type))) {
5348 JS_ReportError(cx, "not a ctypes type");
5349 return nullptr;
5350 }
5352 JSObject* result = JSVAL_TO_OBJECT(type);
5353 TypeCode typeCode = CType::GetTypeCode(result);
5355 if (typeCode == TYPE_array) {
5356 // convert array argument types to pointers, just like C.
5357 // ImplicitConvert will do the same, when passing an array as data.
5358 RootedObject baseType(cx, ArrayType::GetBaseType(result));
5359 result = PointerType::CreateInternal(cx, baseType);
5360 if (!result)
5361 return nullptr;
5363 } else if (typeCode == TYPE_void_t || typeCode == TYPE_function) {
5364 // disallow void or function argument types
5365 JS_ReportError(cx, "Cannot have void or function argument type");
5366 return nullptr;
5367 }
5369 if (!CType::IsSizeDefined(result)) {
5370 JS_ReportError(cx, "Argument type must have defined size");
5371 return nullptr;
5372 }
5374 // libffi cannot pass types of zero size by value.
5375 JS_ASSERT(CType::GetSize(result) != 0);
5377 return result;
5378 }
5380 static JSObject*
5381 PrepareReturnType(JSContext* cx, jsval type)
5382 {
5383 if (JSVAL_IS_PRIMITIVE(type) ||
5384 !CType::IsCType(JSVAL_TO_OBJECT(type))) {
5385 JS_ReportError(cx, "not a ctypes type");
5386 return nullptr;
5387 }
5389 JSObject* result = JSVAL_TO_OBJECT(type);
5390 TypeCode typeCode = CType::GetTypeCode(result);
5392 // Arrays and functions can never be return types.
5393 if (typeCode == TYPE_array || typeCode == TYPE_function) {
5394 JS_ReportError(cx, "Return type cannot be an array or function");
5395 return nullptr;
5396 }
5398 if (typeCode != TYPE_void_t && !CType::IsSizeDefined(result)) {
5399 JS_ReportError(cx, "Return type must have defined size");
5400 return nullptr;
5401 }
5403 // libffi cannot pass types of zero size by value.
5404 JS_ASSERT(typeCode == TYPE_void_t || CType::GetSize(result) != 0);
5406 return result;
5407 }
5409 static MOZ_ALWAYS_INLINE bool
5410 IsEllipsis(JSContext* cx, jsval v, bool* isEllipsis)
5411 {
5412 *isEllipsis = false;
5413 if (!JSVAL_IS_STRING(v))
5414 return true;
5415 JSString* str = JSVAL_TO_STRING(v);
5416 if (str->length() != 3)
5417 return true;
5418 const jschar* chars = str->getChars(cx);
5419 if (!chars)
5420 return false;
5421 jschar dot = '.';
5422 *isEllipsis = (chars[0] == dot &&
5423 chars[1] == dot &&
5424 chars[2] == dot);
5425 return true;
5426 }
5428 static bool
5429 PrepareCIF(JSContext* cx,
5430 FunctionInfo* fninfo)
5431 {
5432 ffi_abi abi;
5433 if (!GetABI(cx, OBJECT_TO_JSVAL(fninfo->mABI), &abi)) {
5434 JS_ReportError(cx, "Invalid ABI specification");
5435 return false;
5436 }
5438 ffi_type* rtype = CType::GetFFIType(cx, fninfo->mReturnType);
5439 if (!rtype)
5440 return false;
5442 ffi_status status =
5443 ffi_prep_cif(&fninfo->mCIF,
5444 abi,
5445 fninfo->mFFITypes.length(),
5446 rtype,
5447 fninfo->mFFITypes.begin());
5449 switch (status) {
5450 case FFI_OK:
5451 return true;
5452 case FFI_BAD_ABI:
5453 JS_ReportError(cx, "Invalid ABI specification");
5454 return false;
5455 case FFI_BAD_TYPEDEF:
5456 JS_ReportError(cx, "Invalid type specification");
5457 return false;
5458 default:
5459 JS_ReportError(cx, "Unknown libffi error");
5460 return false;
5461 }
5462 }
5464 void
5465 FunctionType::BuildSymbolName(JSString* name,
5466 JSObject* typeObj,
5467 AutoCString& result)
5468 {
5469 FunctionInfo* fninfo = GetFunctionInfo(typeObj);
5471 switch (GetABICode(fninfo->mABI)) {
5472 case ABI_DEFAULT:
5473 case ABI_WINAPI:
5474 // For cdecl or WINAPI functions, no mangling is necessary.
5475 AppendString(result, name);
5476 break;
5478 case ABI_STDCALL: {
5479 #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
5480 // On WIN32, stdcall functions look like:
5481 // _foo@40
5482 // where 'foo' is the function name, and '40' is the aligned size of the
5483 // arguments.
5484 AppendString(result, "_");
5485 AppendString(result, name);
5486 AppendString(result, "@");
5488 // Compute the suffix by aligning each argument to sizeof(ffi_arg).
5489 size_t size = 0;
5490 for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
5491 JSObject* argType = fninfo->mArgTypes[i];
5492 size += Align(CType::GetSize(argType), sizeof(ffi_arg));
5493 }
5495 IntegerToString(size, 10, result);
5496 #elif defined(_WIN64)
5497 // On Win64, stdcall is an alias to the default ABI for compatibility, so no
5498 // mangling is done.
5499 AppendString(result, name);
5500 #endif
5501 break;
5502 }
5504 case INVALID_ABI:
5505 MOZ_ASSUME_UNREACHABLE("invalid abi");
5506 }
5507 }
5509 static FunctionInfo*
5510 NewFunctionInfo(JSContext* cx,
5511 jsval abiType,
5512 jsval returnType,
5513 jsval* argTypes,
5514 unsigned argLength)
5515 {
5516 AutoPtr<FunctionInfo> fninfo(cx->new_<FunctionInfo>());
5517 if (!fninfo) {
5518 JS_ReportOutOfMemory(cx);
5519 return nullptr;
5520 }
5522 ffi_abi abi;
5523 if (!GetABI(cx, abiType, &abi)) {
5524 JS_ReportError(cx, "Invalid ABI specification");
5525 return nullptr;
5526 }
5527 fninfo->mABI = JSVAL_TO_OBJECT(abiType);
5529 // prepare the result type
5530 fninfo->mReturnType = PrepareReturnType(cx, returnType);
5531 if (!fninfo->mReturnType)
5532 return nullptr;
5534 // prepare the argument types
5535 if (!fninfo->mArgTypes.reserve(argLength) ||
5536 !fninfo->mFFITypes.reserve(argLength)) {
5537 JS_ReportOutOfMemory(cx);
5538 return nullptr;
5539 }
5541 fninfo->mIsVariadic = false;
5543 for (uint32_t i = 0; i < argLength; ++i) {
5544 bool isEllipsis;
5545 if (!IsEllipsis(cx, argTypes[i], &isEllipsis))
5546 return nullptr;
5547 if (isEllipsis) {
5548 fninfo->mIsVariadic = true;
5549 if (i < 1) {
5550 JS_ReportError(cx, "\"...\" may not be the first and only parameter "
5551 "type of a variadic function declaration");
5552 return nullptr;
5553 }
5554 if (i < argLength - 1) {
5555 JS_ReportError(cx, "\"...\" must be the last parameter type of a "
5556 "variadic function declaration");
5557 return nullptr;
5558 }
5559 if (GetABICode(fninfo->mABI) != ABI_DEFAULT) {
5560 JS_ReportError(cx, "Variadic functions must use the __cdecl calling "
5561 "convention");
5562 return nullptr;
5563 }
5564 break;
5565 }
5567 JSObject* argType = PrepareType(cx, argTypes[i]);
5568 if (!argType)
5569 return nullptr;
5571 ffi_type* ffiType = CType::GetFFIType(cx, argType);
5572 if (!ffiType)
5573 return nullptr;
5575 fninfo->mArgTypes.infallibleAppend(argType);
5576 fninfo->mFFITypes.infallibleAppend(ffiType);
5577 }
5579 if (fninfo->mIsVariadic)
5580 // wait to PrepareCIF until function is called
5581 return fninfo.forget();
5583 if (!PrepareCIF(cx, fninfo.get()))
5584 return nullptr;
5586 return fninfo.forget();
5587 }
5589 bool
5590 FunctionType::Create(JSContext* cx, unsigned argc, jsval* vp)
5591 {
5592 // Construct and return a new FunctionType object.
5593 CallArgs args = CallArgsFromVp(argc, vp);
5594 if (args.length() < 2 || args.length() > 3) {
5595 JS_ReportError(cx, "FunctionType takes two or three arguments");
5596 return false;
5597 }
5599 AutoValueVector argTypes(cx);
5600 RootedObject arrayObj(cx, nullptr);
5602 if (args.length() == 3) {
5603 // Prepare an array of jsvals for the arguments.
5604 if (!JSVAL_IS_PRIMITIVE(args[2]))
5605 arrayObj = &args[2].toObject();
5606 if (!arrayObj || !JS_IsArrayObject(cx, arrayObj)) {
5607 JS_ReportError(cx, "third argument must be an array");
5608 return false;
5609 }
5611 uint32_t len;
5612 ASSERT_OK(JS_GetArrayLength(cx, arrayObj, &len));
5614 if (!argTypes.resize(len)) {
5615 JS_ReportOutOfMemory(cx);
5616 return false;
5617 }
5618 }
5620 // Pull out the argument types from the array, if any.
5621 JS_ASSERT_IF(argTypes.length(), arrayObj);
5622 for (uint32_t i = 0; i < argTypes.length(); ++i) {
5623 if (!JS_GetElement(cx, arrayObj, i, argTypes.handleAt(i)))
5624 return false;
5625 }
5627 JSObject* result = CreateInternal(cx, args[0], args[1],
5628 argTypes.begin(), argTypes.length());
5629 if (!result)
5630 return false;
5632 args.rval().setObject(*result);
5633 return true;
5634 }
5636 JSObject*
5637 FunctionType::CreateInternal(JSContext* cx,
5638 jsval abi,
5639 jsval rtype,
5640 jsval* argtypes,
5641 unsigned arglen)
5642 {
5643 // Determine and check the types, and prepare the function CIF.
5644 AutoPtr<FunctionInfo> fninfo(NewFunctionInfo(cx, abi, rtype, argtypes, arglen));
5645 if (!fninfo)
5646 return nullptr;
5648 // Get ctypes.FunctionType.prototype and the common prototype for CData objects
5649 // of this type, from ctypes.CType.prototype.
5650 RootedObject typeProto(cx, CType::GetProtoFromType(cx, fninfo->mReturnType,
5651 SLOT_FUNCTIONPROTO));
5652 if (!typeProto)
5653 return nullptr;
5654 RootedObject dataProto(cx, CType::GetProtoFromType(cx, fninfo->mReturnType,
5655 SLOT_FUNCTIONDATAPROTO));
5656 if (!dataProto)
5657 return nullptr;
5659 // Create a new CType object with the common properties and slots.
5660 JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_function,
5661 nullptr, JSVAL_VOID, JSVAL_VOID, nullptr);
5662 if (!typeObj)
5663 return nullptr;
5665 // Stash the FunctionInfo in a reserved slot.
5666 JS_SetReservedSlot(typeObj, SLOT_FNINFO, PRIVATE_TO_JSVAL(fninfo.forget()));
5668 return typeObj;
5669 }
5671 // Construct a function pointer to a JS function (see CClosure::Create()).
5672 // Regular function pointers are constructed directly in
5673 // PointerType::ConstructData().
5674 bool
5675 FunctionType::ConstructData(JSContext* cx,
5676 HandleObject typeObj,
5677 HandleObject dataObj,
5678 HandleObject fnObj,
5679 HandleObject thisObj,
5680 jsval errVal)
5681 {
5682 JS_ASSERT(CType::GetTypeCode(typeObj) == TYPE_function);
5684 PRFuncPtr* data = static_cast<PRFuncPtr*>(CData::GetData(dataObj));
5686 FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
5687 if (fninfo->mIsVariadic) {
5688 JS_ReportError(cx, "Can't declare a variadic callback function");
5689 return false;
5690 }
5691 if (GetABICode(fninfo->mABI) == ABI_WINAPI) {
5692 JS_ReportError(cx, "Can't declare a ctypes.winapi_abi callback function, "
5693 "use ctypes.stdcall_abi instead");
5694 return false;
5695 }
5697 RootedObject closureObj(cx, CClosure::Create(cx, typeObj, fnObj, thisObj, errVal, data));
5698 if (!closureObj)
5699 return false;
5701 // Set the closure object as the referent of the new CData object.
5702 JS_SetReservedSlot(dataObj, SLOT_REFERENT, OBJECT_TO_JSVAL(closureObj));
5704 // Seal the CData object, to prevent modification of the function pointer.
5705 // This permanently associates this object with the closure, and avoids
5706 // having to do things like reset SLOT_REFERENT when someone tries to
5707 // change the pointer value.
5708 // XXX This will need to change when bug 541212 is fixed -- CData::ValueSetter
5709 // could be called on a frozen object.
5710 return JS_FreezeObject(cx, dataObj);
5711 }
5713 typedef Array<AutoValue, 16> AutoValueAutoArray;
5715 static bool
5716 ConvertArgument(JSContext* cx,
5717 HandleValue arg,
5718 JSObject* type,
5719 AutoValue* value,
5720 AutoValueAutoArray* strings)
5721 {
5722 if (!value->SizeToType(cx, type)) {
5723 JS_ReportAllocationOverflow(cx);
5724 return false;
5725 }
5727 bool freePointer = false;
5728 if (!ImplicitConvert(cx, arg, type, value->mData, true, &freePointer))
5729 return false;
5731 if (freePointer) {
5732 // ImplicitConvert converted a string for us, which we have to free.
5733 // Keep track of it.
5734 if (!strings->growBy(1)) {
5735 JS_ReportOutOfMemory(cx);
5736 return false;
5737 }
5738 strings->back().mData = *static_cast<char**>(value->mData);
5739 }
5741 return true;
5742 }
5744 bool
5745 FunctionType::Call(JSContext* cx,
5746 unsigned argc,
5747 jsval* vp)
5748 {
5749 CallArgs args = CallArgsFromVp(argc, vp);
5750 // get the callee object...
5751 RootedObject obj(cx, &args.callee());
5752 if (!CData::IsCData(obj)) {
5753 JS_ReportError(cx, "not a CData");
5754 return false;
5755 }
5757 RootedObject typeObj(cx, CData::GetCType(obj));
5758 if (CType::GetTypeCode(typeObj) != TYPE_pointer) {
5759 JS_ReportError(cx, "not a FunctionType.ptr");
5760 return false;
5761 }
5763 typeObj = PointerType::GetBaseType(typeObj);
5764 if (CType::GetTypeCode(typeObj) != TYPE_function) {
5765 JS_ReportError(cx, "not a FunctionType.ptr");
5766 return false;
5767 }
5769 FunctionInfo* fninfo = GetFunctionInfo(typeObj);
5770 uint32_t argcFixed = fninfo->mArgTypes.length();
5772 if ((!fninfo->mIsVariadic && args.length() != argcFixed) ||
5773 (fninfo->mIsVariadic && args.length() < argcFixed)) {
5774 JS_ReportError(cx, "Number of arguments does not match declaration");
5775 return false;
5776 }
5778 // Check if we have a Library object. If we do, make sure it's open.
5779 jsval slot = JS_GetReservedSlot(obj, SLOT_REFERENT);
5780 if (!slot.isUndefined() && Library::IsLibrary(&slot.toObject())) {
5781 PRLibrary* library = Library::GetLibrary(&slot.toObject());
5782 if (!library) {
5783 JS_ReportError(cx, "library is not open");
5784 return false;
5785 }
5786 }
5788 // prepare the values for each argument
5789 AutoValueAutoArray values;
5790 AutoValueAutoArray strings;
5791 if (!values.resize(args.length())) {
5792 JS_ReportOutOfMemory(cx);
5793 return false;
5794 }
5796 for (unsigned i = 0; i < argcFixed; ++i)
5797 if (!ConvertArgument(cx, args[i], fninfo->mArgTypes[i], &values[i], &strings))
5798 return false;
5800 if (fninfo->mIsVariadic) {
5801 if (!fninfo->mFFITypes.resize(args.length())) {
5802 JS_ReportOutOfMemory(cx);
5803 return false;
5804 }
5806 RootedObject obj(cx); // Could reuse obj instead of declaring a second
5807 RootedObject type(cx); // RootedObject, but readability would suffer.
5809 for (uint32_t i = argcFixed; i < args.length(); ++i) {
5810 if (JSVAL_IS_PRIMITIVE(args[i]) ||
5811 !CData::IsCData(obj = &args[i].toObject())) {
5812 // Since we know nothing about the CTypes of the ... arguments,
5813 // they absolutely must be CData objects already.
5814 JS_ReportError(cx, "argument %d of type %s is not a CData object",
5815 i, JS_GetTypeName(cx, JS_TypeOfValue(cx, args[i])));
5816 return false;
5817 }
5818 if (!(type = CData::GetCType(obj)) ||
5819 !(type = PrepareType(cx, OBJECT_TO_JSVAL(type))) ||
5820 // Relying on ImplicitConvert only for the limited purpose of
5821 // converting one CType to another (e.g., T[] to T*).
5822 !ConvertArgument(cx, args[i], type, &values[i], &strings) ||
5823 !(fninfo->mFFITypes[i] = CType::GetFFIType(cx, type))) {
5824 // These functions report their own errors.
5825 return false;
5826 }
5827 }
5828 if (!PrepareCIF(cx, fninfo))
5829 return false;
5830 }
5832 // initialize a pointer to an appropriate location, for storing the result
5833 AutoValue returnValue;
5834 TypeCode typeCode = CType::GetTypeCode(fninfo->mReturnType);
5835 if (typeCode != TYPE_void_t &&
5836 !returnValue.SizeToType(cx, fninfo->mReturnType)) {
5837 JS_ReportAllocationOverflow(cx);
5838 return false;
5839 }
5841 // Let the runtime callback know that we are about to call into C.
5842 js::AutoCTypesActivityCallback autoCallback(cx, js::CTYPES_CALL_BEGIN, js::CTYPES_CALL_END);
5844 uintptr_t fn = *reinterpret_cast<uintptr_t*>(CData::GetData(obj));
5846 #if defined(XP_WIN)
5847 int32_t lastErrorStatus; // The status as defined by |GetLastError|
5848 int32_t savedLastError = GetLastError();
5849 SetLastError(0);
5850 #endif //defined(XP_WIN)
5851 int errnoStatus; // The status as defined by |errno|
5852 int savedErrno = errno;
5853 errno = 0;
5855 ffi_call(&fninfo->mCIF, FFI_FN(fn), returnValue.mData,
5856 reinterpret_cast<void**>(values.begin()));
5858 // Save error value.
5859 // We need to save it before leaving the scope of |suspend| as destructing
5860 // |suspend| has the side-effect of clearing |GetLastError|
5861 // (see bug 684017).
5863 errnoStatus = errno;
5864 #if defined(XP_WIN)
5865 lastErrorStatus = GetLastError();
5866 SetLastError(savedLastError);
5867 #endif // defined(XP_WIN)
5869 errno = savedErrno;
5871 // We're no longer calling into C.
5872 autoCallback.DoEndCallback();
5874 // Store the error value for later consultation with |ctypes.getStatus|
5875 JSObject *objCTypes = CType::GetGlobalCTypes(cx, typeObj);
5876 if (!objCTypes)
5877 return false;
5879 JS_SetReservedSlot(objCTypes, SLOT_ERRNO, INT_TO_JSVAL(errnoStatus));
5880 #if defined(XP_WIN)
5881 JS_SetReservedSlot(objCTypes, SLOT_LASTERROR, INT_TO_JSVAL(lastErrorStatus));
5882 #endif // defined(XP_WIN)
5884 // Small integer types get returned as a word-sized ffi_arg. Coerce it back
5885 // into the correct size for ConvertToJS.
5886 switch (typeCode) {
5887 #define DEFINE_INT_TYPE(name, type, ffiType) \
5888 case TYPE_##name: \
5889 if (sizeof(type) < sizeof(ffi_arg)) { \
5890 ffi_arg data = *static_cast<ffi_arg*>(returnValue.mData); \
5891 *static_cast<type*>(returnValue.mData) = static_cast<type>(data); \
5892 } \
5893 break;
5894 #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
5895 #define DEFINE_BOOL_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
5896 #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
5897 #define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
5898 #include "ctypes/typedefs.h"
5899 default:
5900 break;
5901 }
5903 // prepare a JS object from the result
5904 RootedObject returnType(cx, fninfo->mReturnType);
5905 return ConvertToJS(cx, returnType, NullPtr(), returnValue.mData, false, true, vp);
5906 }
5908 FunctionInfo*
5909 FunctionType::GetFunctionInfo(JSObject* obj)
5910 {
5911 JS_ASSERT(CType::IsCType(obj));
5912 JS_ASSERT(CType::GetTypeCode(obj) == TYPE_function);
5914 jsval slot = JS_GetReservedSlot(obj, SLOT_FNINFO);
5915 JS_ASSERT(!JSVAL_IS_VOID(slot) && JSVAL_TO_PRIVATE(slot));
5917 return static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot));
5918 }
5920 bool
5921 FunctionType::IsFunctionType(HandleValue v)
5922 {
5923 if (!v.isObject())
5924 return false;
5925 JSObject* obj = &v.toObject();
5926 return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_function;
5927 }
5929 bool
5930 FunctionType::ArgTypesGetter(JSContext* cx, JS::CallArgs args)
5931 {
5932 JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
5934 args.rval().set(JS_GetReservedSlot(obj, SLOT_ARGS_T));
5935 if (!args.rval().isUndefined())
5936 return true;
5938 FunctionInfo* fninfo = GetFunctionInfo(obj);
5939 size_t len = fninfo->mArgTypes.length();
5941 // Prepare a new array.
5942 JS::Rooted<JSObject*> argTypes(cx);
5943 {
5944 JS::AutoValueVector vec(cx);
5945 if (!vec.resize(len))
5946 return false;
5948 for (size_t i = 0; i < len; ++i)
5949 vec[i] = JS::ObjectValue(*fninfo->mArgTypes[i]);
5951 argTypes = JS_NewArrayObject(cx, vec);
5952 if (!argTypes)
5953 return false;
5954 }
5956 // Seal and cache it.
5957 if (!JS_FreezeObject(cx, argTypes))
5958 return false;
5959 JS_SetReservedSlot(obj, SLOT_ARGS_T, JS::ObjectValue(*argTypes));
5961 args.rval().setObject(*argTypes);
5962 return true;
5963 }
5965 bool
5966 FunctionType::ReturnTypeGetter(JSContext* cx, JS::CallArgs args)
5967 {
5968 // Get the returnType object from the FunctionInfo.
5969 args.rval().setObject(*GetFunctionInfo(&args.thisv().toObject())->mReturnType);
5970 return true;
5971 }
5973 bool
5974 FunctionType::ABIGetter(JSContext* cx, JS::CallArgs args)
5975 {
5976 // Get the abi object from the FunctionInfo.
5977 args.rval().setObject(*GetFunctionInfo(&args.thisv().toObject())->mABI);
5978 return true;
5979 }
5981 bool
5982 FunctionType::IsVariadicGetter(JSContext* cx, JS::CallArgs args)
5983 {
5984 args.rval().setBoolean(GetFunctionInfo(&args.thisv().toObject())->mIsVariadic);
5985 return true;
5986 }
5988 /*******************************************************************************
5989 ** CClosure implementation
5990 *******************************************************************************/
5992 JSObject*
5993 CClosure::Create(JSContext* cx,
5994 HandleObject typeObj,
5995 HandleObject fnObj,
5996 HandleObject thisObj,
5997 jsval errVal_,
5998 PRFuncPtr* fnptr)
5999 {
6000 RootedValue errVal(cx, errVal_);
6001 JS_ASSERT(fnObj);
6003 RootedObject result(cx, JS_NewObject(cx, &sCClosureClass, NullPtr(), NullPtr()));
6004 if (!result)
6005 return nullptr;
6007 // Get the FunctionInfo from the FunctionType.
6008 FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
6009 JS_ASSERT(!fninfo->mIsVariadic);
6010 JS_ASSERT(GetABICode(fninfo->mABI) != ABI_WINAPI);
6012 AutoPtr<ClosureInfo> cinfo(cx->new_<ClosureInfo>(JS_GetRuntime(cx)));
6013 if (!cinfo) {
6014 JS_ReportOutOfMemory(cx);
6015 return nullptr;
6016 }
6018 // Get the prototype of the FunctionType object, of class CTypeProto,
6019 // which stores our JSContext for use with the closure.
6020 RootedObject proto(cx);
6021 if (!JS_GetPrototype(cx, typeObj, &proto))
6022 return nullptr;
6023 JS_ASSERT(proto);
6024 JS_ASSERT(CType::IsCTypeProto(proto));
6026 // Get a JSContext for use with the closure.
6027 cinfo->cx = js::DefaultJSContext(JS_GetRuntime(cx));
6029 // Prepare the error sentinel value. It's important to do this now, because
6030 // we might be unable to convert the value to the proper type. If so, we want
6031 // the caller to know about it _now_, rather than some uncertain time in the
6032 // future when the error sentinel is actually needed.
6033 if (!JSVAL_IS_VOID(errVal)) {
6035 // Make sure the callback returns something.
6036 if (CType::GetTypeCode(fninfo->mReturnType) == TYPE_void_t) {
6037 JS_ReportError(cx, "A void callback can't pass an error sentinel");
6038 return nullptr;
6039 }
6041 // With the exception of void, the FunctionType constructor ensures that
6042 // the return type has a defined size.
6043 JS_ASSERT(CType::IsSizeDefined(fninfo->mReturnType));
6045 // Allocate a buffer for the return value.
6046 size_t rvSize = CType::GetSize(fninfo->mReturnType);
6047 cinfo->errResult = cx->malloc_(rvSize);
6048 if (!cinfo->errResult)
6049 return nullptr;
6051 // Do the value conversion. This might fail, in which case we throw.
6052 if (!ImplicitConvert(cx, errVal, fninfo->mReturnType, cinfo->errResult,
6053 false, nullptr))
6054 return nullptr;
6055 } else {
6056 cinfo->errResult = nullptr;
6057 }
6059 // Copy the important bits of context into cinfo.
6060 cinfo->closureObj = result;
6061 cinfo->typeObj = typeObj;
6062 cinfo->thisObj = thisObj;
6063 cinfo->jsfnObj = fnObj;
6065 // Create an ffi_closure object and initialize it.
6066 void* code;
6067 cinfo->closure =
6068 static_cast<ffi_closure*>(ffi_closure_alloc(sizeof(ffi_closure), &code));
6069 if (!cinfo->closure || !code) {
6070 JS_ReportError(cx, "couldn't create closure - libffi error");
6071 return nullptr;
6072 }
6074 ffi_status status = ffi_prep_closure_loc(cinfo->closure, &fninfo->mCIF,
6075 CClosure::ClosureStub, cinfo.get(), code);
6076 if (status != FFI_OK) {
6077 JS_ReportError(cx, "couldn't create closure - libffi error");
6078 return nullptr;
6079 }
6081 // Stash the ClosureInfo struct on our new object.
6082 JS_SetReservedSlot(result, SLOT_CLOSUREINFO, PRIVATE_TO_JSVAL(cinfo.forget()));
6084 // Casting between void* and a function pointer is forbidden in C and C++.
6085 // Do it via an integral type.
6086 *fnptr = reinterpret_cast<PRFuncPtr>(reinterpret_cast<uintptr_t>(code));
6087 return result;
6088 }
6090 void
6091 CClosure::Trace(JSTracer* trc, JSObject* obj)
6092 {
6093 // Make sure our ClosureInfo slot is legit. If it's not, bail.
6094 jsval slot = JS_GetReservedSlot(obj, SLOT_CLOSUREINFO);
6095 if (JSVAL_IS_VOID(slot))
6096 return;
6098 ClosureInfo* cinfo = static_cast<ClosureInfo*>(JSVAL_TO_PRIVATE(slot));
6100 // Identify our objects to the tracer. (There's no need to identify
6101 // 'closureObj', since that's us.)
6102 JS_CallHeapObjectTracer(trc, &cinfo->typeObj, "typeObj");
6103 JS_CallHeapObjectTracer(trc, &cinfo->jsfnObj, "jsfnObj");
6104 if (cinfo->thisObj)
6105 JS_CallHeapObjectTracer(trc, &cinfo->thisObj, "thisObj");
6106 }
6108 void
6109 CClosure::Finalize(JSFreeOp *fop, JSObject* obj)
6110 {
6111 // Make sure our ClosureInfo slot is legit. If it's not, bail.
6112 jsval slot = JS_GetReservedSlot(obj, SLOT_CLOSUREINFO);
6113 if (JSVAL_IS_VOID(slot))
6114 return;
6116 ClosureInfo* cinfo = static_cast<ClosureInfo*>(JSVAL_TO_PRIVATE(slot));
6117 FreeOp::get(fop)->delete_(cinfo);
6118 }
6120 void
6121 CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData)
6122 {
6123 JS_ASSERT(cif);
6124 JS_ASSERT(result);
6125 JS_ASSERT(args);
6126 JS_ASSERT(userData);
6128 // Retrieve the essentials from our closure object.
6129 ClosureInfo* cinfo = static_cast<ClosureInfo*>(userData);
6130 JSContext* cx = cinfo->cx;
6132 // Let the runtime callback know that we are about to call into JS again. The end callback will
6133 // fire automatically when we exit this function.
6134 js::AutoCTypesActivityCallback autoCallback(cx, js::CTYPES_CALLBACK_BEGIN,
6135 js::CTYPES_CALLBACK_END);
6137 RootedObject typeObj(cx, cinfo->typeObj);
6138 RootedObject thisObj(cx, cinfo->thisObj);
6139 RootedValue jsfnVal(cx, ObjectValue(*cinfo->jsfnObj));
6141 JS_AbortIfWrongThread(JS_GetRuntime(cx));
6143 JSAutoRequest ar(cx);
6144 JSAutoCompartment ac(cx, cinfo->jsfnObj);
6146 // Assert that our CIFs agree.
6147 FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
6148 JS_ASSERT(cif == &fninfo->mCIF);
6150 TypeCode typeCode = CType::GetTypeCode(fninfo->mReturnType);
6152 // Initialize the result to zero, in case something fails. Small integer types
6153 // are promoted to a word-sized ffi_arg, so we must be careful to zero the
6154 // whole word.
6155 size_t rvSize = 0;
6156 if (cif->rtype != &ffi_type_void) {
6157 rvSize = cif->rtype->size;
6158 switch (typeCode) {
6159 #define DEFINE_INT_TYPE(name, type, ffiType) \
6160 case TYPE_##name:
6161 #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
6162 #define DEFINE_BOOL_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
6163 #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
6164 #define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
6165 #include "ctypes/typedefs.h"
6166 rvSize = Align(rvSize, sizeof(ffi_arg));
6167 break;
6168 default:
6169 break;
6170 }
6171 memset(result, 0, rvSize);
6172 }
6174 // Set up an array for converted arguments.
6175 JS::AutoValueVector argv(cx);
6176 if (!argv.resize(cif->nargs)) {
6177 JS_ReportOutOfMemory(cx);
6178 return;
6179 }
6181 for (uint32_t i = 0; i < cif->nargs; ++i) {
6182 // Convert each argument, and have any CData objects created depend on
6183 // the existing buffers.
6184 RootedObject argType(cx, fninfo->mArgTypes[i]);
6185 if (!ConvertToJS(cx, argType, NullPtr(), args[i], false, false, &argv[i]))
6186 return;
6187 }
6189 // Call the JS function. 'thisObj' may be nullptr, in which case the JS
6190 // engine will find an appropriate object to use.
6191 RootedValue rval(cx);
6192 bool success = JS_CallFunctionValue(cx, thisObj, jsfnVal, argv, &rval);
6194 // Convert the result. Note that we pass 'isArgument = false', such that
6195 // ImplicitConvert will *not* autoconvert a JS string into a pointer-to-char
6196 // type, which would require an allocation that we can't track. The JS
6197 // function must perform this conversion itself and return a PointerType
6198 // CData; thusly, the burden of freeing the data is left to the user.
6199 if (success && cif->rtype != &ffi_type_void)
6200 success = ImplicitConvert(cx, rval, fninfo->mReturnType, result, false,
6201 nullptr);
6203 if (!success) {
6204 // Something failed. The callee may have thrown, or it may not have
6205 // returned a value that ImplicitConvert() was happy with. Depending on how
6206 // prudent the consumer has been, we may or may not have a recovery plan.
6208 // In any case, a JS exception cannot be passed to C code, so report the
6209 // exception if any and clear it from the cx.
6210 if (JS_IsExceptionPending(cx))
6211 JS_ReportPendingException(cx);
6213 if (cinfo->errResult) {
6214 // Good case: we have a sentinel that we can return. Copy it in place of
6215 // the actual return value, and then proceed.
6217 // The buffer we're returning might be larger than the size of the return
6218 // type, due to libffi alignment issues (see above). But it should never
6219 // be smaller.
6220 size_t copySize = CType::GetSize(fninfo->mReturnType);
6221 JS_ASSERT(copySize <= rvSize);
6222 memcpy(result, cinfo->errResult, copySize);
6223 } else {
6224 // Bad case: not much we can do here. The rv is already zeroed out, so we
6225 // just report (another) error and hope for the best. JS_ReportError will
6226 // actually throw an exception here, so then we have to report it. Again.
6227 // Ugh.
6228 JS_ReportError(cx, "JavaScript callback failed, and an error sentinel "
6229 "was not specified.");
6230 if (JS_IsExceptionPending(cx))
6231 JS_ReportPendingException(cx);
6233 return;
6234 }
6235 }
6237 // Small integer types must be returned as a word-sized ffi_arg. Coerce it
6238 // back into the size libffi expects.
6239 switch (typeCode) {
6240 #define DEFINE_INT_TYPE(name, type, ffiType) \
6241 case TYPE_##name: \
6242 if (sizeof(type) < sizeof(ffi_arg)) { \
6243 ffi_arg data = *static_cast<type*>(result); \
6244 *static_cast<ffi_arg*>(result) = data; \
6245 } \
6246 break;
6247 #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
6248 #define DEFINE_BOOL_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
6249 #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
6250 #define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
6251 #include "ctypes/typedefs.h"
6252 default:
6253 break;
6254 }
6255 }
6257 /*******************************************************************************
6258 ** CData implementation
6259 *******************************************************************************/
6261 // Create a new CData object of type 'typeObj' containing binary data supplied
6262 // in 'source', optionally with a referent object 'refObj'.
6263 //
6264 // * 'typeObj' must be a CType of defined (but possibly zero) size.
6265 //
6266 // * If an object 'refObj' is supplied, the new CData object stores the
6267 // referent object in a reserved slot for GC safety, such that 'refObj' will
6268 // be held alive by the resulting CData object. 'refObj' may or may not be
6269 // a CData object; merely an object we want to keep alive.
6270 // * If 'refObj' is a CData object, 'ownResult' must be false.
6271 // * Otherwise, 'refObj' is a Library or CClosure object, and 'ownResult'
6272 // may be true or false.
6273 // * Otherwise 'refObj' is nullptr. In this case, 'ownResult' may be true or
6274 // false.
6275 //
6276 // * If 'ownResult' is true, the CData object will allocate an appropriately
6277 // sized buffer, and free it upon finalization. If 'source' data is
6278 // supplied, the data will be copied from 'source' into the buffer;
6279 // otherwise, the entirety of the new buffer will be initialized to zero.
6280 // * If 'ownResult' is false, the new CData's buffer refers to a slice of
6281 // another buffer kept alive by 'refObj'. 'source' data must be provided,
6282 // and the new CData's buffer will refer to 'source'.
6283 JSObject*
6284 CData::Create(JSContext* cx,
6285 HandleObject typeObj,
6286 HandleObject refObj,
6287 void* source,
6288 bool ownResult)
6289 {
6290 JS_ASSERT(typeObj);
6291 JS_ASSERT(CType::IsCType(typeObj));
6292 JS_ASSERT(CType::IsSizeDefined(typeObj));
6293 JS_ASSERT(ownResult || source);
6294 JS_ASSERT_IF(refObj && CData::IsCData(refObj), !ownResult);
6296 // Get the 'prototype' property from the type.
6297 jsval slot = JS_GetReservedSlot(typeObj, SLOT_PROTO);
6298 JS_ASSERT(!JSVAL_IS_PRIMITIVE(slot));
6300 RootedObject proto(cx, JSVAL_TO_OBJECT(slot));
6301 RootedObject parent(cx, JS_GetParent(typeObj));
6302 JS_ASSERT(parent);
6304 RootedObject dataObj(cx, JS_NewObject(cx, &sCDataClass, proto, parent));
6305 if (!dataObj)
6306 return nullptr;
6308 // set the CData's associated type
6309 JS_SetReservedSlot(dataObj, SLOT_CTYPE, OBJECT_TO_JSVAL(typeObj));
6311 // Stash the referent object, if any, for GC safety.
6312 if (refObj)
6313 JS_SetReservedSlot(dataObj, SLOT_REFERENT, OBJECT_TO_JSVAL(refObj));
6315 // Set our ownership flag.
6316 JS_SetReservedSlot(dataObj, SLOT_OWNS, BOOLEAN_TO_JSVAL(ownResult));
6318 // attach the buffer. since it might not be 2-byte aligned, we need to
6319 // allocate an aligned space for it and store it there. :(
6320 char** buffer = cx->new_<char*>();
6321 if (!buffer) {
6322 JS_ReportOutOfMemory(cx);
6323 return nullptr;
6324 }
6326 char* data;
6327 if (!ownResult) {
6328 data = static_cast<char*>(source);
6329 } else {
6330 // Initialize our own buffer.
6331 size_t size = CType::GetSize(typeObj);
6332 data = (char*)cx->malloc_(size);
6333 if (!data) {
6334 // Report a catchable allocation error.
6335 JS_ReportAllocationOverflow(cx);
6336 js_free(buffer);
6337 return nullptr;
6338 }
6340 if (!source)
6341 memset(data, 0, size);
6342 else
6343 memcpy(data, source, size);
6344 }
6346 *buffer = data;
6347 JS_SetReservedSlot(dataObj, SLOT_DATA, PRIVATE_TO_JSVAL(buffer));
6349 return dataObj;
6350 }
6352 void
6353 CData::Finalize(JSFreeOp *fop, JSObject* obj)
6354 {
6355 // Delete our buffer, and the data it contains if we own it.
6356 jsval slot = JS_GetReservedSlot(obj, SLOT_OWNS);
6357 if (JSVAL_IS_VOID(slot))
6358 return;
6360 bool owns = JSVAL_TO_BOOLEAN(slot);
6362 slot = JS_GetReservedSlot(obj, SLOT_DATA);
6363 if (JSVAL_IS_VOID(slot))
6364 return;
6365 char** buffer = static_cast<char**>(JSVAL_TO_PRIVATE(slot));
6367 if (owns)
6368 FreeOp::get(fop)->free_(*buffer);
6369 FreeOp::get(fop)->delete_(buffer);
6370 }
6372 JSObject*
6373 CData::GetCType(JSObject* dataObj)
6374 {
6375 JS_ASSERT(CData::IsCData(dataObj));
6377 jsval slot = JS_GetReservedSlot(dataObj, SLOT_CTYPE);
6378 JSObject* typeObj = JSVAL_TO_OBJECT(slot);
6379 JS_ASSERT(CType::IsCType(typeObj));
6380 return typeObj;
6381 }
6383 void*
6384 CData::GetData(JSObject* dataObj)
6385 {
6386 JS_ASSERT(CData::IsCData(dataObj));
6388 jsval slot = JS_GetReservedSlot(dataObj, SLOT_DATA);
6390 void** buffer = static_cast<void**>(JSVAL_TO_PRIVATE(slot));
6391 JS_ASSERT(buffer);
6392 JS_ASSERT(*buffer);
6393 return *buffer;
6394 }
6396 bool
6397 CData::IsCData(JSObject* obj)
6398 {
6399 return JS_GetClass(obj) == &sCDataClass;
6400 }
6402 bool
6403 CData::IsCData(HandleValue v)
6404 {
6405 return v.isObject() && CData::IsCData(&v.toObject());
6406 }
6408 bool
6409 CData::IsCDataProto(JSObject* obj)
6410 {
6411 return JS_GetClass(obj) == &sCDataProtoClass;
6412 }
6414 bool
6415 CData::ValueGetter(JSContext* cx, JS::CallArgs args)
6416 {
6417 RootedObject obj(cx, &args.thisv().toObject());
6419 // Convert the value to a primitive; do not create a new CData object.
6420 RootedObject ctype(cx, GetCType(obj));
6421 return ConvertToJS(cx, ctype, NullPtr(), GetData(obj), true, false, args.rval().address());
6422 }
6424 bool
6425 CData::ValueSetter(JSContext* cx, JS::CallArgs args)
6426 {
6427 RootedObject obj(cx, &args.thisv().toObject());
6428 args.rval().setUndefined();
6429 return ImplicitConvert(cx, args.get(0), GetCType(obj), GetData(obj), false, nullptr);
6430 }
6432 bool
6433 CData::Address(JSContext* cx, unsigned argc, jsval* vp)
6434 {
6435 CallArgs args = CallArgsFromVp(argc, vp);
6436 if (args.length() != 0) {
6437 JS_ReportError(cx, "address takes zero arguments");
6438 return false;
6439 }
6441 RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
6442 if (!obj)
6443 return false;
6444 if (!IsCData(obj)) {
6445 JS_ReportError(cx, "not a CData");
6446 return false;
6447 }
6449 RootedObject typeObj(cx, CData::GetCType(obj));
6450 RootedObject pointerType(cx, PointerType::CreateInternal(cx, typeObj));
6451 if (!pointerType)
6452 return false;
6454 // Create a PointerType CData object containing null.
6455 JSObject* result = CData::Create(cx, pointerType, NullPtr(), nullptr, true);
6456 if (!result)
6457 return false;
6459 args.rval().setObject(*result);
6461 // Manually set the pointer inside the object, so we skip the conversion step.
6462 void** data = static_cast<void**>(GetData(result));
6463 *data = GetData(obj);
6464 return true;
6465 }
6467 bool
6468 CData::Cast(JSContext* cx, unsigned argc, jsval* vp)
6469 {
6470 CallArgs args = CallArgsFromVp(argc, vp);
6471 if (args.length() != 2) {
6472 JS_ReportError(cx, "cast takes two arguments");
6473 return false;
6474 }
6476 if (JSVAL_IS_PRIMITIVE(args[0]) ||
6477 !CData::IsCData(&args[0].toObject())) {
6478 JS_ReportError(cx, "first argument must be a CData");
6479 return false;
6480 }
6481 RootedObject sourceData(cx, &args[0].toObject());
6482 JSObject* sourceType = CData::GetCType(sourceData);
6484 if (JSVAL_IS_PRIMITIVE(args[1]) ||
6485 !CType::IsCType(&args[1].toObject())) {
6486 JS_ReportError(cx, "second argument must be a CType");
6487 return false;
6488 }
6490 RootedObject targetType(cx, &args[1].toObject());
6491 size_t targetSize;
6492 if (!CType::GetSafeSize(targetType, &targetSize) ||
6493 targetSize > CType::GetSize(sourceType)) {
6494 JS_ReportError(cx,
6495 "target CType has undefined or larger size than source CType");
6496 return false;
6497 }
6499 // Construct a new CData object with a type of 'targetType' and a referent
6500 // of 'sourceData'.
6501 void* data = CData::GetData(sourceData);
6502 JSObject* result = CData::Create(cx, targetType, sourceData, data, false);
6503 if (!result)
6504 return false;
6506 args.rval().setObject(*result);
6507 return true;
6508 }
6510 bool
6511 CData::GetRuntime(JSContext* cx, unsigned argc, jsval* vp)
6512 {
6513 CallArgs args = CallArgsFromVp(argc, vp);
6514 if (args.length() != 1) {
6515 JS_ReportError(cx, "getRuntime takes one argument");
6516 return false;
6517 }
6519 if (JSVAL_IS_PRIMITIVE(args[0]) ||
6520 !CType::IsCType(&args[0].toObject())) {
6521 JS_ReportError(cx, "first argument must be a CType");
6522 return false;
6523 }
6525 RootedObject targetType(cx, &args[0].toObject());
6526 size_t targetSize;
6527 if (!CType::GetSafeSize(targetType, &targetSize) ||
6528 targetSize != sizeof(void*)) {
6529 JS_ReportError(cx, "target CType has non-pointer size");
6530 return false;
6531 }
6533 void* data = static_cast<void*>(cx->runtime());
6534 JSObject* result = CData::Create(cx, targetType, NullPtr(), &data, true);
6535 if (!result)
6536 return false;
6538 args.rval().setObject(*result);
6539 return true;
6540 }
6542 typedef JS::TwoByteCharsZ (*InflateUTF8Method)(JSContext *, const JS::UTF8Chars, size_t *);
6544 static bool
6545 ReadStringCommon(JSContext* cx, InflateUTF8Method inflateUTF8, unsigned argc, jsval *vp)
6546 {
6547 CallArgs args = CallArgsFromVp(argc, vp);
6548 if (args.length() != 0) {
6549 JS_ReportError(cx, "readString takes zero arguments");
6550 return false;
6551 }
6553 JSObject* obj = CDataFinalizer::GetCData(cx, JS_THIS_OBJECT(cx, vp));
6554 if (!obj || !CData::IsCData(obj)) {
6555 JS_ReportError(cx, "not a CData");
6556 return false;
6557 }
6559 // Make sure we are a pointer to, or an array of, an 8-bit or 16-bit
6560 // character or integer type.
6561 JSObject* baseType;
6562 JSObject* typeObj = CData::GetCType(obj);
6563 TypeCode typeCode = CType::GetTypeCode(typeObj);
6564 void* data;
6565 size_t maxLength = -1;
6566 switch (typeCode) {
6567 case TYPE_pointer:
6568 baseType = PointerType::GetBaseType(typeObj);
6569 data = *static_cast<void**>(CData::GetData(obj));
6570 if (data == nullptr) {
6571 JS_ReportError(cx, "cannot read contents of null pointer");
6572 return false;
6573 }
6574 break;
6575 case TYPE_array:
6576 baseType = ArrayType::GetBaseType(typeObj);
6577 data = CData::GetData(obj);
6578 maxLength = ArrayType::GetLength(typeObj);
6579 break;
6580 default:
6581 JS_ReportError(cx, "not a PointerType or ArrayType");
6582 return false;
6583 }
6585 // Convert the string buffer, taking care to determine the correct string
6586 // length in the case of arrays (which may contain embedded nulls).
6587 JSString* result;
6588 switch (CType::GetTypeCode(baseType)) {
6589 case TYPE_int8_t:
6590 case TYPE_uint8_t:
6591 case TYPE_char:
6592 case TYPE_signed_char:
6593 case TYPE_unsigned_char: {
6594 char* bytes = static_cast<char*>(data);
6595 size_t length = strnlen(bytes, maxLength);
6597 // Determine the length.
6598 jschar *dst = inflateUTF8(cx, JS::UTF8Chars(bytes, length), &length).get();
6599 if (!dst)
6600 return false;
6602 result = JS_NewUCString(cx, dst, length);
6603 break;
6604 }
6605 case TYPE_int16_t:
6606 case TYPE_uint16_t:
6607 case TYPE_short:
6608 case TYPE_unsigned_short:
6609 case TYPE_jschar: {
6610 jschar* chars = static_cast<jschar*>(data);
6611 size_t length = strnlen(chars, maxLength);
6612 result = JS_NewUCStringCopyN(cx, chars, length);
6613 break;
6614 }
6615 default:
6616 JS_ReportError(cx,
6617 "base type is not an 8-bit or 16-bit integer or character type");
6618 return false;
6619 }
6621 if (!result)
6622 return false;
6624 args.rval().setString(result);
6625 return true;
6626 }
6628 bool
6629 CData::ReadString(JSContext* cx, unsigned argc, jsval* vp)
6630 {
6631 return ReadStringCommon(cx, JS::UTF8CharsToNewTwoByteCharsZ, argc, vp);
6632 }
6634 bool
6635 CData::ReadStringReplaceMalformed(JSContext* cx, unsigned argc, jsval* vp)
6636 {
6637 return ReadStringCommon(cx, JS::LossyUTF8CharsToNewTwoByteCharsZ, argc, vp);
6638 }
6640 JSString *
6641 CData::GetSourceString(JSContext *cx, HandleObject typeObj, void *data)
6642 {
6643 // Walk the types, building up the toSource() string.
6644 // First, we build up the type expression:
6645 // 't.ptr' for pointers;
6646 // 't.array([n])' for arrays;
6647 // 'n' for structs, where n = t.name, the struct's name. (We assume this is
6648 // bound to a variable in the current scope.)
6649 AutoString source;
6650 BuildTypeSource(cx, typeObj, true, source);
6651 AppendString(source, "(");
6652 if (!BuildDataSource(cx, typeObj, data, false, source))
6653 return nullptr;
6655 AppendString(source, ")");
6657 return NewUCString(cx, source);
6658 }
6660 bool
6661 CData::ToSource(JSContext* cx, unsigned argc, jsval* vp)
6662 {
6663 CallArgs args = CallArgsFromVp(argc, vp);
6664 if (args.length() != 0) {
6665 JS_ReportError(cx, "toSource takes zero arguments");
6666 return false;
6667 }
6669 JSObject* obj = JS_THIS_OBJECT(cx, vp);
6670 if (!obj)
6671 return false;
6672 if (!CData::IsCData(obj) && !CData::IsCDataProto(obj)) {
6673 JS_ReportError(cx, "not a CData");
6674 return false;
6675 }
6677 JSString* result;
6678 if (CData::IsCData(obj)) {
6679 RootedObject typeObj(cx, CData::GetCType(obj));
6680 void* data = CData::GetData(obj);
6682 result = CData::GetSourceString(cx, typeObj, data);
6683 } else {
6684 result = JS_NewStringCopyZ(cx, "[CData proto object]");
6685 }
6687 if (!result)
6688 return false;
6690 args.rval().setString(result);
6691 return true;
6692 }
6694 bool
6695 CData::ErrnoGetter(JSContext* cx, JS::CallArgs args)
6696 {
6697 args.rval().set(JS_GetReservedSlot(&args.thisv().toObject(), SLOT_ERRNO));
6698 return true;
6699 }
6701 #if defined(XP_WIN)
6702 bool
6703 CData::LastErrorGetter(JSContext* cx, JS::CallArgs args)
6704 {
6705 args.rval().set(JS_GetReservedSlot(&args.thisv().toObject(), SLOT_LASTERROR));
6706 return true;
6707 }
6708 #endif // defined(XP_WIN)
6710 bool
6711 CDataFinalizer::Methods::ToSource(JSContext *cx, unsigned argc, jsval *vp)
6712 {
6713 CallArgs args = CallArgsFromVp(argc, vp);
6714 RootedObject objThis(cx, JS_THIS_OBJECT(cx, vp));
6715 if (!objThis)
6716 return false;
6717 if (!CDataFinalizer::IsCDataFinalizer(objThis)) {
6718 JS_ReportError(cx, "not a CDataFinalizer");
6719 return false;
6720 }
6722 CDataFinalizer::Private *p = (CDataFinalizer::Private *)
6723 JS_GetPrivate(objThis);
6725 JSString *strMessage;
6726 if (!p) {
6727 strMessage = JS_NewStringCopyZ(cx, "ctypes.CDataFinalizer()");
6728 } else {
6729 RootedObject objType(cx, CDataFinalizer::GetCType(cx, objThis));
6730 if (!objType) {
6731 JS_ReportError(cx, "CDataFinalizer has no type");
6732 return false;
6733 }
6735 AutoString source;
6736 AppendString(source, "ctypes.CDataFinalizer(");
6737 JSString *srcValue = CData::GetSourceString(cx, objType, p->cargs);
6738 if (!srcValue) {
6739 return false;
6740 }
6741 AppendString(source, srcValue);
6742 AppendString(source, ", ");
6743 jsval valCodePtrType = JS_GetReservedSlot(objThis,
6744 SLOT_DATAFINALIZER_CODETYPE);
6745 if (JSVAL_IS_PRIMITIVE(valCodePtrType)) {
6746 return false;
6747 }
6749 RootedObject typeObj(cx, JSVAL_TO_OBJECT(valCodePtrType));
6750 JSString *srcDispose = CData::GetSourceString(cx, typeObj, &(p->code));
6751 if (!srcDispose) {
6752 return false;
6753 }
6755 AppendString(source, srcDispose);
6756 AppendString(source, ")");
6757 strMessage = NewUCString(cx, source);
6758 }
6760 if (!strMessage) {
6761 // This is a memory issue, no error message
6762 return false;
6763 }
6765 args.rval().setString(strMessage);
6766 return true;
6767 }
6769 bool
6770 CDataFinalizer::Methods::ToString(JSContext *cx, unsigned argc, jsval *vp)
6771 {
6772 CallArgs args = CallArgsFromVp(argc, vp);
6773 JSObject* objThis = JS_THIS_OBJECT(cx, vp);
6774 if (!objThis)
6775 return false;
6776 if (!CDataFinalizer::IsCDataFinalizer(objThis)) {
6777 JS_ReportError(cx, "not a CDataFinalizer");
6778 return false;
6779 }
6781 JSString *strMessage;
6782 RootedValue value(cx);
6783 if (!JS_GetPrivate(objThis)) {
6784 // Pre-check whether CDataFinalizer::GetValue can fail
6785 // to avoid reporting an error when not appropriate.
6786 strMessage = JS_NewStringCopyZ(cx, "[CDataFinalizer - empty]");
6787 if (!strMessage) {
6788 return false;
6789 }
6790 } else if (!CDataFinalizer::GetValue(cx, objThis, value.address())) {
6791 MOZ_ASSUME_UNREACHABLE("Could not convert an empty CDataFinalizer");
6792 } else {
6793 strMessage = ToString(cx, value);
6794 if (!strMessage) {
6795 return false;
6796 }
6797 }
6798 args.rval().setString(strMessage);
6799 return true;
6800 }
6802 bool
6803 CDataFinalizer::IsCDataFinalizer(JSObject *obj)
6804 {
6805 return JS_GetClass(obj) == &sCDataFinalizerClass;
6806 }
6809 JSObject *
6810 CDataFinalizer::GetCType(JSContext *cx, JSObject *obj)
6811 {
6812 MOZ_ASSERT(IsCDataFinalizer(obj));
6814 jsval valData = JS_GetReservedSlot(obj,
6815 SLOT_DATAFINALIZER_VALTYPE);
6816 if (JSVAL_IS_VOID(valData)) {
6817 return nullptr;
6818 }
6820 return JSVAL_TO_OBJECT(valData);
6821 }
6823 JSObject*
6824 CDataFinalizer::GetCData(JSContext *cx, JSObject *obj)
6825 {
6826 if (!obj) {
6827 JS_ReportError(cx, "No C data");
6828 return nullptr;
6829 }
6830 if (CData::IsCData(obj)) {
6831 return obj;
6832 }
6833 if (!CDataFinalizer::IsCDataFinalizer(obj)) {
6834 JS_ReportError(cx, "Not C data");
6835 return nullptr;
6836 }
6837 RootedValue val(cx);
6838 if (!CDataFinalizer::GetValue(cx, obj, val.address()) || JSVAL_IS_PRIMITIVE(val)) {
6839 JS_ReportError(cx, "Empty CDataFinalizer");
6840 return nullptr;
6841 }
6842 return JSVAL_TO_OBJECT(val);
6843 }
6845 bool
6846 CDataFinalizer::GetValue(JSContext *cx, JSObject *obj, jsval *aResult)
6847 {
6848 MOZ_ASSERT(IsCDataFinalizer(obj));
6850 CDataFinalizer::Private *p = (CDataFinalizer::Private *)
6851 JS_GetPrivate(obj);
6853 if (!p) {
6854 JS_ReportError(cx, "Attempting to get the value of an empty CDataFinalizer");
6855 return false; // We have called |dispose| or |forget| already.
6856 }
6858 RootedObject ctype(cx, GetCType(cx, obj));
6859 return ConvertToJS(cx, ctype, /*parent*/NullPtr(), p -> cargs, false, true, aResult);
6860 }
6862 /*
6863 * Attach a C function as a finalizer to a JS object.
6864 *
6865 * Pseudo-JS signature:
6866 * function(CData<T>, CData<T -> U>): CDataFinalizer<T>
6867 * value, finalizer
6868 *
6869 * This function attaches strong references to the following values:
6870 * - the CType of |value|
6871 *
6872 * Note: This function takes advantage of the fact that non-variadic
6873 * CData functions are initialized during creation.
6874 */
6875 bool
6876 CDataFinalizer::Construct(JSContext* cx, unsigned argc, jsval *vp)
6877 {
6878 CallArgs args = CallArgsFromVp(argc, vp);
6879 RootedObject objSelf(cx, &args.callee());
6880 RootedObject objProto(cx);
6881 if (!GetObjectProperty(cx, objSelf, "prototype", &objProto)) {
6882 JS_ReportError(cx, "CDataFinalizer.prototype does not exist");
6883 return false;
6884 }
6886 // Get arguments
6887 if (args.length() == 0) { // Special case: the empty (already finalized) object
6888 JSObject *objResult = JS_NewObject(cx, &sCDataFinalizerClass, objProto, NullPtr());
6889 args.rval().setObject(*objResult);
6890 return true;
6891 }
6893 if (args.length() != 2) {
6894 JS_ReportError(cx, "CDataFinalizer takes 2 arguments");
6895 return false;
6896 }
6898 JS::HandleValue valCodePtr = args[1];
6899 if (!valCodePtr.isObject()) {
6900 return TypeError(cx, "_a CData object_ of a function pointer type",
6901 valCodePtr);
6902 }
6903 JSObject *objCodePtr = &valCodePtr.toObject();
6905 //Note: Using a custom argument formatter here would be awkward (requires
6906 //a destructor just to uninstall the formatter).
6908 // 2. Extract argument type of |objCodePtr|
6909 if (!CData::IsCData(objCodePtr)) {
6910 return TypeError(cx, "a _CData_ object of a function pointer type",
6911 valCodePtr);
6912 }
6913 RootedObject objCodePtrType(cx, CData::GetCType(objCodePtr));
6914 RootedValue valCodePtrType(cx, ObjectValue(*objCodePtrType));
6915 MOZ_ASSERT(objCodePtrType);
6917 TypeCode typCodePtr = CType::GetTypeCode(objCodePtrType);
6918 if (typCodePtr != TYPE_pointer) {
6919 return TypeError(cx, "a CData object of a function _pointer_ type",
6920 valCodePtrType);
6921 }
6923 JSObject *objCodeType = PointerType::GetBaseType(objCodePtrType);
6924 MOZ_ASSERT(objCodeType);
6926 TypeCode typCode = CType::GetTypeCode(objCodeType);
6927 if (typCode != TYPE_function) {
6928 return TypeError(cx, "a CData object of a _function_ pointer type",
6929 valCodePtrType);
6930 }
6931 uintptr_t code = *reinterpret_cast<uintptr_t*>(CData::GetData(objCodePtr));
6932 if (!code) {
6933 return TypeError(cx, "a CData object of a _non-NULL_ function pointer type",
6934 valCodePtrType);
6935 }
6937 FunctionInfo* funInfoFinalizer =
6938 FunctionType::GetFunctionInfo(objCodeType);
6939 MOZ_ASSERT(funInfoFinalizer);
6941 if ((funInfoFinalizer->mArgTypes.length() != 1)
6942 || (funInfoFinalizer->mIsVariadic)) {
6943 RootedValue valCodeType(cx, ObjectValue(*objCodeType));
6944 return TypeError(cx, "a function accepting exactly one argument",
6945 valCodeType);
6946 }
6947 RootedObject objArgType(cx, funInfoFinalizer->mArgTypes[0]);
6948 RootedObject returnType(cx, funInfoFinalizer->mReturnType);
6950 // Invariant: At this stage, we know that funInfoFinalizer->mIsVariadic
6951 // is |false|. Therefore, funInfoFinalizer->mCIF has already been initialized.
6953 bool freePointer = false;
6955 // 3. Perform dynamic cast of |args[0]| into |objType|, store it in |cargs|
6957 size_t sizeArg;
6958 RootedValue valData(cx, args[0]);
6959 if (!CType::GetSafeSize(objArgType, &sizeArg)) {
6960 return TypeError(cx, "(an object with known size)", valData);
6961 }
6963 ScopedJSFreePtr<void> cargs(malloc(sizeArg));
6965 if (!ImplicitConvert(cx, valData, objArgType, cargs.get(),
6966 false, &freePointer)) {
6967 RootedValue valArgType(cx, ObjectValue(*objArgType));
6968 return TypeError(cx, "(an object that can be converted to the following type)",
6969 valArgType);
6970 }
6971 if (freePointer) {
6972 // Note: We could handle that case, if necessary.
6973 JS_ReportError(cx, "Internal Error during CDataFinalizer. Object cannot be represented");
6974 return false;
6975 }
6977 // 4. Prepare buffer for holding return value
6979 ScopedJSFreePtr<void> rvalue;
6980 if (CType::GetTypeCode(returnType) != TYPE_void_t) {
6981 rvalue = malloc(Align(CType::GetSize(returnType),
6982 sizeof(ffi_arg)));
6983 } //Otherwise, simply do not allocate
6985 // 5. Create |objResult|
6987 JSObject *objResult = JS_NewObject(cx, &sCDataFinalizerClass, objProto, NullPtr());
6988 if (!objResult) {
6989 return false;
6990 }
6992 // If our argument is a CData, it holds a type.
6993 // This is the type that we should capture, not that
6994 // of the function, which may be less precise.
6995 JSObject *objBestArgType = objArgType;
6996 if (!JSVAL_IS_PRIMITIVE(valData)) {
6997 JSObject *objData = &valData.toObject();
6998 if (CData::IsCData(objData)) {
6999 objBestArgType = CData::GetCType(objData);
7000 size_t sizeBestArg;
7001 if (!CType::GetSafeSize(objBestArgType, &sizeBestArg)) {
7002 MOZ_ASSUME_UNREACHABLE("object with unknown size");
7003 }
7004 if (sizeBestArg != sizeArg) {
7005 return TypeError(cx, "(an object with the same size as that expected by the C finalization function)", valData);
7006 }
7007 }
7008 }
7010 // Used by GetCType
7011 JS_SetReservedSlot(objResult,
7012 SLOT_DATAFINALIZER_VALTYPE,
7013 OBJECT_TO_JSVAL(objBestArgType));
7015 // Used by ToSource
7016 JS_SetReservedSlot(objResult,
7017 SLOT_DATAFINALIZER_CODETYPE,
7018 OBJECT_TO_JSVAL(objCodePtrType));
7020 ffi_abi abi;
7021 if (!GetABI(cx, OBJECT_TO_JSVAL(funInfoFinalizer->mABI), &abi)) {
7022 JS_ReportError(cx, "Internal Error: "
7023 "Invalid ABI specification in CDataFinalizer");
7024 return false;
7025 }
7027 ffi_type* rtype = CType::GetFFIType(cx, funInfoFinalizer->mReturnType);
7028 if (!rtype) {
7029 JS_ReportError(cx, "Internal Error: "
7030 "Could not access ffi type of CDataFinalizer");
7031 return false;
7032 }
7034 // 7. Store C information as private
7035 ScopedJSFreePtr<CDataFinalizer::Private>
7036 p((CDataFinalizer::Private*)malloc(sizeof(CDataFinalizer::Private)));
7038 memmove(&p->CIF, &funInfoFinalizer->mCIF, sizeof(ffi_cif));
7040 p->cargs = cargs.forget();
7041 p->rvalue = rvalue.forget();
7042 p->cargs_size = sizeArg;
7043 p->code = code;
7046 JS_SetPrivate(objResult, p.forget());
7047 args.rval().setObject(*objResult);
7048 return true;
7049 }
7052 /*
7053 * Actually call the finalizer. Does not perform any cleanup on the object.
7054 *
7055 * Preconditions: |this| must be a |CDataFinalizer|, |p| must be non-null.
7056 * The function fails if |this| has gone through |Forget|/|Dispose|
7057 * or |Finalize|.
7058 *
7059 * This function does not alter the value of |errno|/|GetLastError|.
7060 *
7061 * If argument |errnoStatus| is non-nullptr, it receives the value of |errno|
7062 * immediately after the call. Under Windows, if argument |lastErrorStatus|
7063 * is non-nullptr, it receives the value of |GetLastError| immediately after
7064 * the call. On other platforms, |lastErrorStatus| is ignored.
7065 */
7066 void
7067 CDataFinalizer::CallFinalizer(CDataFinalizer::Private *p,
7068 int* errnoStatus,
7069 int32_t* lastErrorStatus)
7070 {
7071 int savedErrno = errno;
7072 errno = 0;
7073 #if defined(XP_WIN)
7074 int32_t savedLastError = GetLastError();
7075 SetLastError(0);
7076 #endif // defined(XP_WIN)
7078 ffi_call(&p->CIF, FFI_FN(p->code), p->rvalue, &p->cargs);
7080 if (errnoStatus) {
7081 *errnoStatus = errno;
7082 }
7083 errno = savedErrno;
7084 #if defined(XP_WIN)
7085 if (lastErrorStatus) {
7086 *lastErrorStatus = GetLastError();
7087 }
7088 SetLastError(savedLastError);
7089 #endif // defined(XP_WIN)
7090 }
7092 /*
7093 * Forget the value.
7094 *
7095 * Preconditions: |this| must be a |CDataFinalizer|.
7096 * The function fails if |this| has gone through |Forget|/|Dispose|
7097 * or |Finalize|.
7098 *
7099 * Does not call the finalizer. Cleans up the Private memory and releases all
7100 * strong references.
7101 */
7102 bool
7103 CDataFinalizer::Methods::Forget(JSContext* cx, unsigned argc, jsval *vp)
7104 {
7105 CallArgs args = CallArgsFromVp(argc, vp);
7106 if (args.length() != 0) {
7107 JS_ReportError(cx, "CDataFinalizer.prototype.forget takes no arguments");
7108 return false;
7109 }
7111 JS::Rooted<JSObject*> obj(cx, args.thisv().toObjectOrNull());
7112 if (!obj)
7113 return false;
7114 if (!CDataFinalizer::IsCDataFinalizer(obj)) {
7115 RootedValue val(cx, ObjectValue(*obj));
7116 return TypeError(cx, "a CDataFinalizer", val);
7117 }
7119 CDataFinalizer::Private *p = (CDataFinalizer::Private *)
7120 JS_GetPrivate(obj);
7122 if (!p) {
7123 JS_ReportError(cx, "forget called on an empty CDataFinalizer");
7124 return false;
7125 }
7127 RootedValue valJSData(cx);
7128 RootedObject ctype(cx, GetCType(cx, obj));
7129 if (!ConvertToJS(cx, ctype, NullPtr(), p->cargs, false, true, valJSData.address())) {
7130 JS_ReportError(cx, "CDataFinalizer value cannot be represented");
7131 return false;
7132 }
7134 CDataFinalizer::Cleanup(p, obj);
7136 args.rval().set(valJSData);
7137 return true;
7138 }
7140 /*
7141 * Clean up the value.
7142 *
7143 * Preconditions: |this| must be a |CDataFinalizer|.
7144 * The function fails if |this| has gone through |Forget|/|Dispose|
7145 * or |Finalize|.
7146 *
7147 * Calls the finalizer, cleans up the Private memory and releases all
7148 * strong references.
7149 */
7150 bool
7151 CDataFinalizer::Methods::Dispose(JSContext* cx, unsigned argc, jsval *vp)
7152 {
7153 CallArgs args = CallArgsFromVp(argc, vp);
7154 if (args.length() != 0) {
7155 JS_ReportError(cx, "CDataFinalizer.prototype.dispose takes no arguments");
7156 return false;
7157 }
7159 RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
7160 if (!obj)
7161 return false;
7162 if (!CDataFinalizer::IsCDataFinalizer(obj)) {
7163 RootedValue val(cx, ObjectValue(*obj));
7164 return TypeError(cx, "a CDataFinalizer", val);
7165 }
7167 CDataFinalizer::Private *p = (CDataFinalizer::Private *)
7168 JS_GetPrivate(obj);
7170 if (!p) {
7171 JS_ReportError(cx, "dispose called on an empty CDataFinalizer.");
7172 return false;
7173 }
7175 jsval valType = JS_GetReservedSlot(obj, SLOT_DATAFINALIZER_VALTYPE);
7176 JS_ASSERT(!JSVAL_IS_PRIMITIVE(valType));
7178 JSObject *objCTypes = CType::GetGlobalCTypes(cx, &valType.toObject());
7179 if (!objCTypes)
7180 return false;
7182 jsval valCodePtrType = JS_GetReservedSlot(obj, SLOT_DATAFINALIZER_CODETYPE);
7183 JS_ASSERT(!JSVAL_IS_PRIMITIVE(valCodePtrType));
7184 JSObject *objCodePtrType = &valCodePtrType.toObject();
7186 JSObject *objCodeType = PointerType::GetBaseType(objCodePtrType);
7187 JS_ASSERT(objCodeType);
7188 JS_ASSERT(CType::GetTypeCode(objCodeType) == TYPE_function);
7190 RootedObject resultType(cx, FunctionType::GetFunctionInfo(objCodeType)->mReturnType);
7191 RootedValue result(cx, JSVAL_VOID);
7193 int errnoStatus;
7194 #if defined(XP_WIN)
7195 int32_t lastErrorStatus;
7196 CDataFinalizer::CallFinalizer(p, &errnoStatus, &lastErrorStatus);
7197 #else
7198 CDataFinalizer::CallFinalizer(p, &errnoStatus, nullptr);
7199 #endif // defined(XP_WIN)
7201 JS_SetReservedSlot(objCTypes, SLOT_ERRNO, INT_TO_JSVAL(errnoStatus));
7202 #if defined(XP_WIN)
7203 JS_SetReservedSlot(objCTypes, SLOT_LASTERROR, INT_TO_JSVAL(lastErrorStatus));
7204 #endif // defined(XP_WIN)
7206 if (ConvertToJS(cx, resultType, NullPtr(), p->rvalue, false, true, result.address())) {
7207 CDataFinalizer::Cleanup(p, obj);
7208 args.rval().set(result);
7209 return true;
7210 }
7211 CDataFinalizer::Cleanup(p, obj);
7212 return false;
7213 }
7215 /*
7216 * Perform finalization.
7217 *
7218 * Preconditions: |this| must be the result of |CDataFinalizer|.
7219 * It may have gone through |Forget|/|Dispose|.
7220 *
7221 * If |this| has not gone through |Forget|/|Dispose|, calls the
7222 * finalizer, cleans up the Private memory and releases all
7223 * strong references.
7224 */
7225 void
7226 CDataFinalizer::Finalize(JSFreeOp* fop, JSObject* obj)
7227 {
7228 CDataFinalizer::Private *p = (CDataFinalizer::Private *)
7229 JS_GetPrivate(obj);
7231 if (!p) {
7232 return;
7233 }
7235 CDataFinalizer::CallFinalizer(p, nullptr, nullptr);
7236 CDataFinalizer::Cleanup(p, nullptr);
7237 }
7239 /*
7240 * Perform cleanup of a CDataFinalizer
7241 *
7242 * Release strong references, cleanup |Private|.
7243 *
7244 * Argument |p| contains the private information of the CDataFinalizer. If
7245 * nullptr, this function does nothing.
7246 * Argument |obj| should contain |nullptr| during finalization (or in any
7247 * context in which the object itself should not be cleaned up), or a
7248 * CDataFinalizer object otherwise.
7249 */
7250 void
7251 CDataFinalizer::Cleanup(CDataFinalizer::Private *p, JSObject *obj)
7252 {
7253 if (!p) {
7254 return; // We have already cleaned up
7255 }
7257 free(p->cargs);
7258 free(p->rvalue);
7259 free(p);
7261 if (!obj) {
7262 return; // No slots to clean up
7263 }
7265 JS_ASSERT(CDataFinalizer::IsCDataFinalizer(obj));
7267 JS_SetPrivate(obj, nullptr);
7268 for (int i = 0; i < CDATAFINALIZER_SLOTS; ++i) {
7269 JS_SetReservedSlot(obj, i, JSVAL_NULL);
7270 }
7271 }
7274 /*******************************************************************************
7275 ** Int64 and UInt64 implementation
7276 *******************************************************************************/
7278 JSObject*
7279 Int64Base::Construct(JSContext* cx,
7280 HandleObject proto,
7281 uint64_t data,
7282 bool isUnsigned)
7283 {
7284 const JSClass* clasp = isUnsigned ? &sUInt64Class : &sInt64Class;
7285 RootedObject parent(cx, JS_GetParent(proto));
7286 RootedObject result(cx, JS_NewObject(cx, clasp, proto, parent));
7287 if (!result)
7288 return nullptr;
7290 // attach the Int64's data
7291 uint64_t* buffer = cx->new_<uint64_t>(data);
7292 if (!buffer) {
7293 JS_ReportOutOfMemory(cx);
7294 return nullptr;
7295 }
7297 JS_SetReservedSlot(result, SLOT_INT64, PRIVATE_TO_JSVAL(buffer));
7299 if (!JS_FreezeObject(cx, result))
7300 return nullptr;
7302 return result;
7303 }
7305 void
7306 Int64Base::Finalize(JSFreeOp *fop, JSObject* obj)
7307 {
7308 jsval slot = JS_GetReservedSlot(obj, SLOT_INT64);
7309 if (JSVAL_IS_VOID(slot))
7310 return;
7312 FreeOp::get(fop)->delete_(static_cast<uint64_t*>(JSVAL_TO_PRIVATE(slot)));
7313 }
7315 uint64_t
7316 Int64Base::GetInt(JSObject* obj) {
7317 JS_ASSERT(Int64::IsInt64(obj) || UInt64::IsUInt64(obj));
7319 jsval slot = JS_GetReservedSlot(obj, SLOT_INT64);
7320 return *static_cast<uint64_t*>(JSVAL_TO_PRIVATE(slot));
7321 }
7323 bool
7324 Int64Base::ToString(JSContext* cx,
7325 JSObject* obj,
7326 const CallArgs& args,
7327 bool isUnsigned)
7328 {
7329 if (args.length() > 1) {
7330 JS_ReportError(cx, "toString takes zero or one argument");
7331 return false;
7332 }
7334 int radix = 10;
7335 if (args.length() == 1) {
7336 jsval arg = args[0];
7337 if (arg.isInt32())
7338 radix = arg.toInt32();
7339 if (!arg.isInt32() || radix < 2 || radix > 36) {
7340 JS_ReportError(cx, "radix argument must be an integer between 2 and 36");
7341 return false;
7342 }
7343 }
7345 AutoString intString;
7346 if (isUnsigned) {
7347 IntegerToString(GetInt(obj), radix, intString);
7348 } else {
7349 IntegerToString(static_cast<int64_t>(GetInt(obj)), radix, intString);
7350 }
7352 JSString *result = NewUCString(cx, intString);
7353 if (!result)
7354 return false;
7356 args.rval().setString(result);
7357 return true;
7358 }
7360 bool
7361 Int64Base::ToSource(JSContext* cx,
7362 JSObject* obj,
7363 const CallArgs& args,
7364 bool isUnsigned)
7365 {
7366 if (args.length() != 0) {
7367 JS_ReportError(cx, "toSource takes zero arguments");
7368 return false;
7369 }
7371 // Return a decimal string suitable for constructing the number.
7372 AutoString source;
7373 if (isUnsigned) {
7374 AppendString(source, "ctypes.UInt64(\"");
7375 IntegerToString(GetInt(obj), 10, source);
7376 } else {
7377 AppendString(source, "ctypes.Int64(\"");
7378 IntegerToString(static_cast<int64_t>(GetInt(obj)), 10, source);
7379 }
7380 AppendString(source, "\")");
7382 JSString *result = NewUCString(cx, source);
7383 if (!result)
7384 return false;
7386 args.rval().setString(result);
7387 return true;
7388 }
7390 bool
7391 Int64::Construct(JSContext* cx,
7392 unsigned argc,
7393 jsval* vp)
7394 {
7395 CallArgs args = CallArgsFromVp(argc, vp);
7397 // Construct and return a new Int64 object.
7398 if (args.length() != 1) {
7399 JS_ReportError(cx, "Int64 takes one argument");
7400 return false;
7401 }
7403 int64_t i = 0;
7404 if (!jsvalToBigInteger(cx, args[0], true, &i))
7405 return TypeError(cx, "int64", args[0]);
7407 // Get ctypes.Int64.prototype from the 'prototype' property of the ctor.
7408 RootedValue slot(cx);
7409 RootedObject callee(cx, &args.callee());
7410 ASSERT_OK(JS_GetProperty(cx, callee, "prototype", &slot));
7411 RootedObject proto(cx, JSVAL_TO_OBJECT(slot));
7412 JS_ASSERT(JS_GetClass(proto) == &sInt64ProtoClass);
7414 JSObject* result = Int64Base::Construct(cx, proto, i, false);
7415 if (!result)
7416 return false;
7418 args.rval().setObject(*result);
7419 return true;
7420 }
7422 bool
7423 Int64::IsInt64(JSObject* obj)
7424 {
7425 return JS_GetClass(obj) == &sInt64Class;
7426 }
7428 bool
7429 Int64::ToString(JSContext* cx, unsigned argc, jsval* vp)
7430 {
7431 CallArgs args = CallArgsFromVp(argc, vp);
7432 JSObject* obj = JS_THIS_OBJECT(cx, vp);
7433 if (!obj)
7434 return false;
7435 if (!Int64::IsInt64(obj)) {
7436 JS_ReportError(cx, "not an Int64");
7437 return false;
7438 }
7440 return Int64Base::ToString(cx, obj, args, false);
7441 }
7443 bool
7444 Int64::ToSource(JSContext* cx, unsigned argc, jsval* vp)
7445 {
7446 CallArgs args = CallArgsFromVp(argc, vp);
7447 JSObject* obj = JS_THIS_OBJECT(cx, vp);
7448 if (!obj)
7449 return false;
7450 if (!Int64::IsInt64(obj)) {
7451 JS_ReportError(cx, "not an Int64");
7452 return false;
7453 }
7455 return Int64Base::ToSource(cx, obj, args, false);
7456 }
7458 bool
7459 Int64::Compare(JSContext* cx, unsigned argc, jsval* vp)
7460 {
7461 CallArgs args = CallArgsFromVp(argc, vp);
7462 if (args.length() != 2 ||
7463 JSVAL_IS_PRIMITIVE(args[0]) ||
7464 JSVAL_IS_PRIMITIVE(args[1]) ||
7465 !Int64::IsInt64(&args[0].toObject()) ||
7466 !Int64::IsInt64(&args[1].toObject())) {
7467 JS_ReportError(cx, "compare takes two Int64 arguments");
7468 return false;
7469 }
7471 JSObject* obj1 = &args[0].toObject();
7472 JSObject* obj2 = &args[1].toObject();
7474 int64_t i1 = Int64Base::GetInt(obj1);
7475 int64_t i2 = Int64Base::GetInt(obj2);
7477 if (i1 == i2)
7478 args.rval().setInt32(0);
7479 else if (i1 < i2)
7480 args.rval().setInt32(-1);
7481 else
7482 args.rval().setInt32(1);
7484 return true;
7485 }
7487 #define LO_MASK ((uint64_t(1) << 32) - 1)
7488 #define INT64_LO(i) ((i) & LO_MASK)
7489 #define INT64_HI(i) ((i) >> 32)
7491 bool
7492 Int64::Lo(JSContext* cx, unsigned argc, jsval* vp)
7493 {
7494 CallArgs args = CallArgsFromVp(argc, vp);
7495 if (args.length() != 1 || JSVAL_IS_PRIMITIVE(args[0]) ||
7496 !Int64::IsInt64(&args[0].toObject())) {
7497 JS_ReportError(cx, "lo takes one Int64 argument");
7498 return false;
7499 }
7501 JSObject* obj = &args[0].toObject();
7502 int64_t u = Int64Base::GetInt(obj);
7503 double d = uint32_t(INT64_LO(u));
7505 args.rval().setNumber(d);
7506 return true;
7507 }
7509 bool
7510 Int64::Hi(JSContext* cx, unsigned argc, jsval* vp)
7511 {
7512 CallArgs args = CallArgsFromVp(argc, vp);
7513 if (args.length() != 1 || JSVAL_IS_PRIMITIVE(args[0]) ||
7514 !Int64::IsInt64(&args[0].toObject())) {
7515 JS_ReportError(cx, "hi takes one Int64 argument");
7516 return false;
7517 }
7519 JSObject* obj = &args[0].toObject();
7520 int64_t u = Int64Base::GetInt(obj);
7521 double d = int32_t(INT64_HI(u));
7523 args.rval().setDouble(d);
7524 return true;
7525 }
7527 bool
7528 Int64::Join(JSContext* cx, unsigned argc, jsval* vp)
7529 {
7530 CallArgs args = CallArgsFromVp(argc, vp);
7531 if (args.length() != 2) {
7532 JS_ReportError(cx, "join takes two arguments");
7533 return false;
7534 }
7536 int32_t hi;
7537 uint32_t lo;
7538 if (!jsvalToInteger(cx, args[0], &hi))
7539 return TypeError(cx, "int32", args[0]);
7540 if (!jsvalToInteger(cx, args[1], &lo))
7541 return TypeError(cx, "uint32", args[1]);
7543 int64_t i = (int64_t(hi) << 32) + int64_t(lo);
7545 // Get Int64.prototype from the function's reserved slot.
7546 JSObject* callee = &args.callee();
7548 jsval slot = js::GetFunctionNativeReserved(callee, SLOT_FN_INT64PROTO);
7549 RootedObject proto(cx, &slot.toObject());
7550 JS_ASSERT(JS_GetClass(proto) == &sInt64ProtoClass);
7552 JSObject* result = Int64Base::Construct(cx, proto, i, false);
7553 if (!result)
7554 return false;
7556 args.rval().setObject(*result);
7557 return true;
7558 }
7560 bool
7561 UInt64::Construct(JSContext* cx,
7562 unsigned argc,
7563 jsval* vp)
7564 {
7565 CallArgs args = CallArgsFromVp(argc, vp);
7567 // Construct and return a new UInt64 object.
7568 if (args.length() != 1) {
7569 JS_ReportError(cx, "UInt64 takes one argument");
7570 return false;
7571 }
7573 uint64_t u = 0;
7574 if (!jsvalToBigInteger(cx, args[0], true, &u))
7575 return TypeError(cx, "uint64", args[0]);
7577 // Get ctypes.UInt64.prototype from the 'prototype' property of the ctor.
7578 RootedValue slot(cx);
7579 RootedObject callee(cx, &args.callee());
7580 ASSERT_OK(JS_GetProperty(cx, callee, "prototype", &slot));
7581 RootedObject proto(cx, &slot.toObject());
7582 JS_ASSERT(JS_GetClass(proto) == &sUInt64ProtoClass);
7584 JSObject* result = Int64Base::Construct(cx, proto, u, true);
7585 if (!result)
7586 return false;
7588 args.rval().setObject(*result);
7589 return true;
7590 }
7592 bool
7593 UInt64::IsUInt64(JSObject* obj)
7594 {
7595 return JS_GetClass(obj) == &sUInt64Class;
7596 }
7598 bool
7599 UInt64::ToString(JSContext* cx, unsigned argc, jsval* vp)
7600 {
7601 CallArgs args = CallArgsFromVp(argc, vp);
7602 JSObject* obj = JS_THIS_OBJECT(cx, vp);
7603 if (!obj)
7604 return false;
7605 if (!UInt64::IsUInt64(obj)) {
7606 JS_ReportError(cx, "not a UInt64");
7607 return false;
7608 }
7610 return Int64Base::ToString(cx, obj, args, true);
7611 }
7613 bool
7614 UInt64::ToSource(JSContext* cx, unsigned argc, jsval* vp)
7615 {
7616 CallArgs args = CallArgsFromVp(argc, vp);
7617 JSObject* obj = JS_THIS_OBJECT(cx, vp);
7618 if (!obj)
7619 return false;
7620 if (!UInt64::IsUInt64(obj)) {
7621 JS_ReportError(cx, "not a UInt64");
7622 return false;
7623 }
7625 return Int64Base::ToSource(cx, obj, args, true);
7626 }
7628 bool
7629 UInt64::Compare(JSContext* cx, unsigned argc, jsval* vp)
7630 {
7631 CallArgs args = CallArgsFromVp(argc, vp);
7632 if (args.length() != 2 ||
7633 JSVAL_IS_PRIMITIVE(args[0]) ||
7634 JSVAL_IS_PRIMITIVE(args[1]) ||
7635 !UInt64::IsUInt64(&args[0].toObject()) ||
7636 !UInt64::IsUInt64(&args[1].toObject())) {
7637 JS_ReportError(cx, "compare takes two UInt64 arguments");
7638 return false;
7639 }
7641 JSObject* obj1 = &args[0].toObject();
7642 JSObject* obj2 = &args[1].toObject();
7644 uint64_t u1 = Int64Base::GetInt(obj1);
7645 uint64_t u2 = Int64Base::GetInt(obj2);
7647 if (u1 == u2)
7648 args.rval().setInt32(0);
7649 else if (u1 < u2)
7650 args.rval().setInt32(-1);
7651 else
7652 args.rval().setInt32(1);
7654 return true;
7655 }
7657 bool
7658 UInt64::Lo(JSContext* cx, unsigned argc, jsval* vp)
7659 {
7660 CallArgs args = CallArgsFromVp(argc, vp);
7661 if (args.length() != 1 || JSVAL_IS_PRIMITIVE(args[0]) ||
7662 !UInt64::IsUInt64(&args[0].toObject())) {
7663 JS_ReportError(cx, "lo takes one UInt64 argument");
7664 return false;
7665 }
7667 JSObject* obj = &args[0].toObject();
7668 uint64_t u = Int64Base::GetInt(obj);
7669 double d = uint32_t(INT64_LO(u));
7671 args.rval().setDouble(d);
7672 return true;
7673 }
7675 bool
7676 UInt64::Hi(JSContext* cx, unsigned argc, jsval* vp)
7677 {
7678 CallArgs args = CallArgsFromVp(argc, vp);
7679 if (args.length() != 1 || JSVAL_IS_PRIMITIVE(args[0]) ||
7680 !UInt64::IsUInt64(&args[0].toObject())) {
7681 JS_ReportError(cx, "hi takes one UInt64 argument");
7682 return false;
7683 }
7685 JSObject* obj = &args[0].toObject();
7686 uint64_t u = Int64Base::GetInt(obj);
7687 double d = uint32_t(INT64_HI(u));
7689 args.rval().setDouble(d);
7690 return true;
7691 }
7693 bool
7694 UInt64::Join(JSContext* cx, unsigned argc, jsval* vp)
7695 {
7696 CallArgs args = CallArgsFromVp(argc, vp);
7697 if (args.length() != 2) {
7698 JS_ReportError(cx, "join takes two arguments");
7699 return false;
7700 }
7702 uint32_t hi;
7703 uint32_t lo;
7704 if (!jsvalToInteger(cx, args[0], &hi))
7705 return TypeError(cx, "uint32_t", args[0]);
7706 if (!jsvalToInteger(cx, args[1], &lo))
7707 return TypeError(cx, "uint32_t", args[1]);
7709 uint64_t u = (uint64_t(hi) << 32) + uint64_t(lo);
7711 // Get UInt64.prototype from the function's reserved slot.
7712 JSObject* callee = &args.callee();
7714 jsval slot = js::GetFunctionNativeReserved(callee, SLOT_FN_INT64PROTO);
7715 RootedObject proto(cx, &slot.toObject());
7716 JS_ASSERT(JS_GetClass(proto) == &sUInt64ProtoClass);
7718 JSObject* result = Int64Base::Construct(cx, proto, u, true);
7719 if (!result)
7720 return false;
7722 args.rval().setObject(*result);
7723 return true;
7724 }
7726 }
7727 }