Bumping manifests a=b2g-bump
[gecko.git] / js / src / ctypes / CTypes.cpp
blobbc05f9ee443e7be976f19c6a24430daf29ab8230
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"
41 #include "gc/Zone.h"
43 using namespace std;
44 using mozilla::NumericLimits;
46 using JS::AutoCheckCannotGC;
48 namespace js {
49 namespace ctypes {
51 template <typename CharT>
52 size_t
53 GetDeflatedUTF8StringLength(JSContext* maybecx, const CharT* chars,
54 size_t nchars)
56 size_t nbytes;
57 const CharT* end;
58 unsigned c, c2;
59 char buffer[10];
61 nbytes = nchars;
62 for (end = chars + nchars; chars != end; chars++) {
63 c = *chars;
64 if (c < 0x80)
65 continue;
66 if (0xD800 <= c && c <= 0xDFFF) {
67 /* Surrogate pair. */
68 chars++;
70 /* nbytes sets 1 length since this is surrogate pair. */
71 nbytes--;
72 if (c >= 0xDC00 || chars == end)
73 goto bad_surrogate;
74 c2 = *chars;
75 if (c2 < 0xDC00 || c2 > 0xDFFF)
76 goto bad_surrogate;
77 c = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
79 c >>= 11;
80 nbytes++;
81 while (c) {
82 c >>= 5;
83 nbytes++;
86 return nbytes;
88 bad_surrogate:
89 if (maybecx) {
90 js::gc::AutoSuppressGC suppress(maybecx);
91 JS_snprintf(buffer, 10, "0x%x", c);
92 JS_ReportErrorFlagsAndNumber(maybecx, JSREPORT_ERROR, js_GetErrorMessage,
93 nullptr, JSMSG_BAD_SURROGATE_CHAR, buffer);
95 return (size_t) -1;
98 template size_t
99 GetDeflatedUTF8StringLength(JSContext* maybecx, const Latin1Char* chars,
100 size_t nchars);
102 template size_t
103 GetDeflatedUTF8StringLength(JSContext* maybecx, const char16_t* chars,
104 size_t nchars);
106 static size_t
107 GetDeflatedUTF8StringLength(JSContext* maybecx, JSLinearString* str)
109 size_t length = str->length();
111 JS::AutoCheckCannotGC nogc;
112 return str->hasLatin1Chars()
113 ? GetDeflatedUTF8StringLength(maybecx, str->latin1Chars(nogc), length)
114 : GetDeflatedUTF8StringLength(maybecx, str->twoByteChars(nogc), length);
117 template <typename CharT>
118 bool
119 DeflateStringToUTF8Buffer(JSContext* maybecx, const CharT* src, size_t srclen,
120 char* dst, size_t* dstlenp)
122 size_t i, utf8Len;
123 char16_t c, c2;
124 uint32_t v;
125 uint8_t utf8buf[6];
127 size_t dstlen = *dstlenp;
128 size_t origDstlen = dstlen;
130 while (srclen) {
131 c = *src++;
132 srclen--;
133 if (c >= 0xDC00 && c <= 0xDFFF)
134 goto badSurrogate;
135 if (c < 0xD800 || c > 0xDBFF) {
136 v = c;
137 } else {
138 if (srclen < 1)
139 goto badSurrogate;
140 c2 = *src;
141 if ((c2 < 0xDC00) || (c2 > 0xDFFF))
142 goto badSurrogate;
143 src++;
144 srclen--;
145 v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
147 if (v < 0x0080) {
148 /* no encoding necessary - performance hack */
149 if (dstlen == 0)
150 goto bufferTooSmall;
151 *dst++ = (char) v;
152 utf8Len = 1;
153 } else {
154 utf8Len = js_OneUcs4ToUtf8Char(utf8buf, v);
155 if (utf8Len > dstlen)
156 goto bufferTooSmall;
157 for (i = 0; i < utf8Len; i++)
158 *dst++ = (char) utf8buf[i];
160 dstlen -= utf8Len;
162 *dstlenp = (origDstlen - dstlen);
163 return true;
165 badSurrogate:
166 *dstlenp = (origDstlen - dstlen);
167 /* Delegate error reporting to the measurement function. */
168 if (maybecx)
169 GetDeflatedUTF8StringLength(maybecx, src - 1, srclen + 1);
170 return false;
172 bufferTooSmall:
173 *dstlenp = (origDstlen - dstlen);
174 if (maybecx) {
175 js::gc::AutoSuppressGC suppress(maybecx);
176 JS_ReportErrorNumber(maybecx, js_GetErrorMessage, nullptr,
177 JSMSG_BUFFER_TOO_SMALL);
179 return false;
182 template bool
183 DeflateStringToUTF8Buffer(JSContext* maybecx, const Latin1Char* src, size_t srclen,
184 char* dst, size_t* dstlenp);
186 template bool
187 DeflateStringToUTF8Buffer(JSContext* maybecx, const char16_t* src, size_t srclen,
188 char* dst, size_t* dstlenp);
190 static bool
191 DeflateStringToUTF8Buffer(JSContext* maybecx, JSLinearString* str, char* dst,
192 size_t* dstlenp)
194 size_t length = str->length();
196 JS::AutoCheckCannotGC nogc;
197 return str->hasLatin1Chars()
198 ? DeflateStringToUTF8Buffer(maybecx, str->latin1Chars(nogc), length, dst, dstlenp)
199 : DeflateStringToUTF8Buffer(maybecx, str->twoByteChars(nogc), length, dst, dstlenp);
202 /*******************************************************************************
203 ** JSAPI function prototypes
204 *******************************************************************************/
206 // We use an enclosing struct here out of paranoia about the ability of gcc 4.4
207 // (and maybe 4.5) to correctly compile this if it were a template function.
208 // See also the comments in dom/workers/Events.cpp (and other adjacent files) by
209 // the |struct Property| there.
210 template<JS::IsAcceptableThis Test, JS::NativeImpl Impl>
211 struct Property
213 static bool
214 Fun(JSContext* cx, unsigned argc, JS::Value* vp)
216 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
217 return JS::CallNonGenericMethod<Test, Impl>(cx, args);
221 static bool ConstructAbstract(JSContext* cx, unsigned argc, jsval* vp);
223 namespace CType {
224 static bool ConstructData(JSContext* cx, unsigned argc, jsval* vp);
225 static bool ConstructBasic(JSContext* cx, HandleObject obj, const CallArgs& args);
227 static void Trace(JSTracer* trc, JSObject* obj);
228 static void Finalize(JSFreeOp* fop, JSObject* obj);
230 bool IsCType(HandleValue v);
231 bool IsCTypeOrProto(HandleValue v);
233 bool PrototypeGetter(JSContext* cx, JS::CallArgs args);
234 bool NameGetter(JSContext* cx, JS::CallArgs args);
235 bool SizeGetter(JSContext* cx, JS::CallArgs args);
236 bool PtrGetter(JSContext* cx, JS::CallArgs args);
238 static bool CreateArray(JSContext* cx, unsigned argc, jsval* vp);
239 static bool ToString(JSContext* cx, unsigned argc, jsval* vp);
240 static bool ToSource(JSContext* cx, unsigned argc, jsval* vp);
241 static bool HasInstance(JSContext* cx, HandleObject obj, MutableHandleValue v, bool* bp);
245 * Get the global "ctypes" object.
247 * |obj| must be a CType object.
249 * This function never returns nullptr.
251 static JSObject* GetGlobalCTypes(JSContext* cx, JSObject* obj);
255 namespace ABI {
256 bool IsABI(JSObject* obj);
257 static bool ToSource(JSContext* cx, unsigned argc, jsval* vp);
260 namespace PointerType {
261 static bool Create(JSContext* cx, unsigned argc, jsval* vp);
262 static bool ConstructData(JSContext* cx, HandleObject obj, const CallArgs& args);
264 bool IsPointerType(HandleValue v);
265 bool IsPointer(HandleValue v);
267 bool TargetTypeGetter(JSContext* cx, JS::CallArgs args);
268 bool ContentsGetter(JSContext* cx, JS::CallArgs args);
269 bool ContentsSetter(JSContext* cx, JS::CallArgs args);
271 static bool IsNull(JSContext* cx, unsigned argc, jsval* vp);
272 static bool Increment(JSContext* cx, unsigned argc, jsval* vp);
273 static bool Decrement(JSContext* cx, unsigned argc, jsval* vp);
274 // The following is not an instance function, since we don't want to expose arbitrary
275 // pointer arithmetic at this moment.
276 static bool OffsetBy(JSContext* cx, const CallArgs& args, int offset);
279 namespace ArrayType {
280 bool IsArrayType(HandleValue v);
281 bool IsArrayOrArrayType(HandleValue v);
283 static bool Create(JSContext* cx, unsigned argc, jsval* vp);
284 static bool ConstructData(JSContext* cx, HandleObject obj, const CallArgs& args);
286 bool ElementTypeGetter(JSContext* cx, JS::CallArgs args);
287 bool LengthGetter(JSContext* cx, JS::CallArgs args);
289 static bool Getter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp);
290 static bool Setter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, MutableHandleValue vp);
291 static bool AddressOfElement(JSContext* cx, unsigned argc, jsval* vp);
294 namespace StructType {
295 bool IsStruct(HandleValue v);
297 static bool Create(JSContext* cx, unsigned argc, jsval* vp);
298 static bool ConstructData(JSContext* cx, HandleObject obj, const CallArgs& args);
300 bool FieldsArrayGetter(JSContext* cx, JS::CallArgs args);
302 static bool FieldGetter(JSContext* cx, HandleObject obj, HandleId idval,
303 MutableHandleValue vp);
304 static bool FieldSetter(JSContext* cx, HandleObject obj, HandleId idval, bool strict,
305 MutableHandleValue vp);
306 static bool AddressOfField(JSContext* cx, unsigned argc, jsval* vp);
307 static bool Define(JSContext* cx, unsigned argc, jsval* vp);
310 namespace FunctionType {
311 static bool Create(JSContext* cx, unsigned argc, jsval* vp);
312 static bool ConstructData(JSContext* cx, HandleObject typeObj,
313 HandleObject dataObj, HandleObject fnObj, HandleObject thisObj, jsval errVal);
315 static bool Call(JSContext* cx, unsigned argc, jsval* vp);
317 bool IsFunctionType(HandleValue v);
319 bool ArgTypesGetter(JSContext* cx, JS::CallArgs args);
320 bool ReturnTypeGetter(JSContext* cx, JS::CallArgs args);
321 bool ABIGetter(JSContext* cx, JS::CallArgs args);
322 bool IsVariadicGetter(JSContext* cx, JS::CallArgs args);
325 namespace CClosure {
326 static void Trace(JSTracer* trc, JSObject* obj);
327 static void Finalize(JSFreeOp* fop, JSObject* obj);
329 // libffi callback
330 static void ClosureStub(ffi_cif* cif, void* result, void** args,
331 void* userData);
334 namespace CData {
335 static void Finalize(JSFreeOp* fop, JSObject* obj);
337 bool ValueGetter(JSContext* cx, JS::CallArgs args);
338 bool ValueSetter(JSContext* cx, JS::CallArgs args);
340 static bool Address(JSContext* cx, unsigned argc, jsval* vp);
341 static bool ReadString(JSContext* cx, unsigned argc, jsval* vp);
342 static bool ReadStringReplaceMalformed(JSContext* cx, unsigned argc, jsval* vp);
343 static bool ToSource(JSContext* cx, unsigned argc, jsval* vp);
344 static JSString* GetSourceString(JSContext* cx, HandleObject typeObj,
345 void* data);
347 bool ErrnoGetter(JSContext* cx, JS::CallArgs args);
349 #if defined(XP_WIN)
350 bool LastErrorGetter(JSContext* cx, JS::CallArgs args);
351 #endif // defined(XP_WIN)
354 namespace CDataFinalizer {
356 * Attach a C function as a finalizer to a JS object.
358 * This function is available from JS as |ctypes.withFinalizer|.
360 * JavaScript signature:
361 * function(CData, CData): CDataFinalizer
362 * value finalizer finalizable
364 * Where |finalizer| is a one-argument function taking a value
365 * with the same type as |value|.
367 static bool Construct(JSContext* cx, unsigned argc, jsval* vp);
370 * Private data held by |CDataFinalizer|.
372 * See also |enum CDataFinalizerSlot| for the slots of
373 * |CDataFinalizer|.
375 * Note: the private data may be nullptr, if |dispose|, |forget| or the
376 * finalizer has already been called.
378 struct Private {
380 * The C data to pass to the code.
381 * Finalization/|dispose|/|forget| release this memory.
383 void* cargs;
386 * The total size of the buffer pointed by |cargs|
388 size_t cargs_size;
391 * Low-level signature information.
392 * Finalization/|dispose|/|forget| release this memory.
394 ffi_cif CIF;
397 * The C function to invoke during finalization.
398 * Do not deallocate this.
400 uintptr_t code;
403 * A buffer for holding the return value.
404 * Finalization/|dispose|/|forget| release this memory.
406 void* rvalue;
410 * Methods of instances of |CDataFinalizer|
412 namespace Methods {
413 static bool Dispose(JSContext* cx, unsigned argc, jsval* vp);
414 static bool Forget(JSContext* cx, unsigned argc, jsval* vp);
415 static bool ToSource(JSContext* cx, unsigned argc, jsval* vp);
416 static bool ToString(JSContext* cx, unsigned argc, jsval* vp);
420 * Utility functions
422 * @return true if |obj| is a CDataFinalizer, false otherwise.
424 static bool IsCDataFinalizer(JSObject* obj);
427 * Clean up the finalization information of a CDataFinalizer.
429 * Used by |Finalize|, |Dispose| and |Forget|.
431 * @param p The private information of the CDataFinalizer. If nullptr,
432 * this function does nothing.
433 * @param obj Either nullptr, if the object should not be cleaned up (i.e.
434 * during finalization) or a CDataFinalizer JSObject. Always use nullptr
435 * if you are calling from a finalizer.
437 static void Cleanup(Private* p, JSObject* obj);
440 * Perform the actual call to the finalizer code.
442 static void CallFinalizer(CDataFinalizer::Private* p,
443 int* errnoStatus,
444 int32_t* lastErrorStatus);
447 * Return the CType of a CDataFinalizer object, or nullptr if the object
448 * has been cleaned-up already.
450 static JSObject* GetCType(JSContext* cx, JSObject* obj);
453 * Perform finalization of a |CDataFinalizer|
455 static void Finalize(JSFreeOp* fop, JSObject* obj);
458 * Return the jsval contained by this finalizer.
460 * Note that the jsval is actually not recorded, but converted back from C.
462 static bool GetValue(JSContext* cx, JSObject* obj, MutableHandleValue result);
464 static JSObject* GetCData(JSContext* cx, JSObject* obj);
468 // Int64Base provides functions common to Int64 and UInt64.
469 namespace Int64Base {
470 JSObject* Construct(JSContext* cx, HandleObject proto, uint64_t data,
471 bool isUnsigned);
473 uint64_t GetInt(JSObject* obj);
475 bool ToString(JSContext* cx, JSObject* obj, const CallArgs& args,
476 bool isUnsigned);
478 bool ToSource(JSContext* cx, JSObject* obj, const CallArgs& args,
479 bool isUnsigned);
481 static void Finalize(JSFreeOp* fop, JSObject* obj);
484 namespace Int64 {
485 static bool Construct(JSContext* cx, unsigned argc, jsval* vp);
487 static bool ToString(JSContext* cx, unsigned argc, jsval* vp);
488 static bool ToSource(JSContext* cx, unsigned argc, jsval* vp);
490 static bool Compare(JSContext* cx, unsigned argc, jsval* vp);
491 static bool Lo(JSContext* cx, unsigned argc, jsval* vp);
492 static bool Hi(JSContext* cx, unsigned argc, jsval* vp);
493 static bool Join(JSContext* cx, unsigned argc, jsval* vp);
496 namespace UInt64 {
497 static bool Construct(JSContext* cx, unsigned argc, jsval* vp);
499 static bool ToString(JSContext* cx, unsigned argc, jsval* vp);
500 static bool ToSource(JSContext* cx, unsigned argc, jsval* vp);
502 static bool Compare(JSContext* cx, unsigned argc, jsval* vp);
503 static bool Lo(JSContext* cx, unsigned argc, jsval* vp);
504 static bool Hi(JSContext* cx, unsigned argc, jsval* vp);
505 static bool Join(JSContext* cx, unsigned argc, jsval* vp);
508 /*******************************************************************************
509 ** JSClass definitions and initialization functions
510 *******************************************************************************/
512 // Class representing the 'ctypes' object itself. This exists to contain the
513 // JSCTypesCallbacks set of function pointers.
514 static const JSClass sCTypesGlobalClass = {
515 "ctypes",
516 JSCLASS_HAS_RESERVED_SLOTS(CTYPESGLOBAL_SLOTS)
519 static const JSClass sCABIClass = {
520 "CABI",
521 JSCLASS_HAS_RESERVED_SLOTS(CABI_SLOTS)
524 // Class representing ctypes.{C,Pointer,Array,Struct,Function}Type.prototype.
525 // This exists to give said prototypes a class of "CType", and to provide
526 // reserved slots for stashing various other prototype objects.
527 static const JSClass sCTypeProtoClass = {
528 "CType",
529 JSCLASS_HAS_RESERVED_SLOTS(CTYPEPROTO_SLOTS),
530 nullptr, nullptr, nullptr, nullptr,
531 nullptr, nullptr, nullptr, nullptr,
532 ConstructAbstract, nullptr, ConstructAbstract
535 // Class representing ctypes.CData.prototype and the 'prototype' properties
536 // of CTypes. This exists to give said prototypes a class of "CData".
537 static const JSClass sCDataProtoClass = {
538 "CData",
542 static const JSClass sCTypeClass = {
543 "CType",
544 JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS),
545 nullptr, nullptr, nullptr, nullptr,
546 nullptr, nullptr, nullptr, CType::Finalize,
547 CType::ConstructData, CType::HasInstance, CType::ConstructData,
548 CType::Trace
551 static const JSClass sCDataClass = {
552 "CData",
553 JSCLASS_HAS_RESERVED_SLOTS(CDATA_SLOTS),
554 nullptr, nullptr, ArrayType::Getter, ArrayType::Setter,
555 nullptr, nullptr, nullptr, CData::Finalize,
556 FunctionType::Call, nullptr, FunctionType::Call
559 static const JSClass sCClosureClass = {
560 "CClosure",
561 JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS),
562 nullptr, nullptr, nullptr, nullptr,
563 nullptr, nullptr, nullptr, CClosure::Finalize,
564 nullptr, nullptr, nullptr, CClosure::Trace
568 * Class representing the prototype of CDataFinalizer.
570 static const JSClass sCDataFinalizerProtoClass = {
571 "CDataFinalizer",
576 * Class representing instances of CDataFinalizer.
578 * Instances of CDataFinalizer have both private data (with type
579 * |CDataFinalizer::Private|) and slots (see |CDataFinalizerSlots|).
581 static const JSClass sCDataFinalizerClass = {
582 "CDataFinalizer",
583 JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(CDATAFINALIZER_SLOTS),
584 nullptr, nullptr, nullptr, nullptr,
585 nullptr, nullptr, nullptr, CDataFinalizer::Finalize
589 #define CTYPESFN_FLAGS \
590 (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
592 #define CTYPESCTOR_FLAGS \
593 (CTYPESFN_FLAGS | JSFUN_CONSTRUCTOR)
595 #define CTYPESACC_FLAGS \
596 (JSPROP_ENUMERATE | JSPROP_PERMANENT)
598 #define CABIFN_FLAGS \
599 (JSPROP_READONLY | JSPROP_PERMANENT)
601 #define CDATAFN_FLAGS \
602 (JSPROP_READONLY | JSPROP_PERMANENT)
604 #define CDATAFINALIZERFN_FLAGS \
605 (JSPROP_READONLY | JSPROP_PERMANENT)
607 static const JSPropertySpec sCTypeProps[] = {
608 JS_PSG("name",
609 (Property<CType::IsCType, CType::NameGetter>::Fun),
610 CTYPESACC_FLAGS),
611 JS_PSG("size",
612 (Property<CType::IsCType, CType::SizeGetter>::Fun),
613 CTYPESACC_FLAGS),
614 JS_PSG("ptr",
615 (Property<CType::IsCType, CType::PtrGetter>::Fun),
616 CTYPESACC_FLAGS),
617 JS_PSG("prototype",
618 (Property<CType::IsCTypeOrProto, CType::PrototypeGetter>::Fun),
619 CTYPESACC_FLAGS),
620 JS_PS_END
623 static const JSFunctionSpec sCTypeFunctions[] = {
624 JS_FN("array", CType::CreateArray, 0, CTYPESFN_FLAGS),
625 JS_FN("toString", CType::ToString, 0, CTYPESFN_FLAGS),
626 JS_FN("toSource", CType::ToSource, 0, CTYPESFN_FLAGS),
627 JS_FS_END
630 static const JSFunctionSpec sCABIFunctions[] = {
631 JS_FN("toSource", ABI::ToSource, 0, CABIFN_FLAGS),
632 JS_FN("toString", ABI::ToSource, 0, CABIFN_FLAGS),
633 JS_FS_END
636 static const JSPropertySpec sCDataProps[] = {
637 JS_PSGS("value",
638 (Property<CData::IsCData, CData::ValueGetter>::Fun),
639 (Property<CData::IsCData, CData::ValueSetter>::Fun),
640 JSPROP_PERMANENT),
641 JS_PS_END
644 static const JSFunctionSpec sCDataFunctions[] = {
645 JS_FN("address", CData::Address, 0, CDATAFN_FLAGS),
646 JS_FN("readString", CData::ReadString, 0, CDATAFN_FLAGS),
647 JS_FN("readStringReplaceMalformed", CData::ReadStringReplaceMalformed, 0, CDATAFN_FLAGS),
648 JS_FN("toSource", CData::ToSource, 0, CDATAFN_FLAGS),
649 JS_FN("toString", CData::ToSource, 0, CDATAFN_FLAGS),
650 JS_FS_END
653 static const JSFunctionSpec sCDataFinalizerFunctions[] = {
654 JS_FN("dispose", CDataFinalizer::Methods::Dispose, 0, CDATAFINALIZERFN_FLAGS),
655 JS_FN("forget", CDataFinalizer::Methods::Forget, 0, CDATAFINALIZERFN_FLAGS),
656 JS_FN("readString",CData::ReadString, 0, CDATAFINALIZERFN_FLAGS),
657 JS_FN("toString", CDataFinalizer::Methods::ToString, 0, CDATAFINALIZERFN_FLAGS),
658 JS_FN("toSource", CDataFinalizer::Methods::ToSource, 0, CDATAFINALIZERFN_FLAGS),
659 JS_FS_END
662 static const JSFunctionSpec sPointerFunction =
663 JS_FN("PointerType", PointerType::Create, 1, CTYPESCTOR_FLAGS);
665 static const JSPropertySpec sPointerProps[] = {
666 JS_PSG("targetType",
667 (Property<PointerType::IsPointerType, PointerType::TargetTypeGetter>::Fun),
668 CTYPESACC_FLAGS),
669 JS_PS_END
672 static const JSFunctionSpec sPointerInstanceFunctions[] = {
673 JS_FN("isNull", PointerType::IsNull, 0, CTYPESFN_FLAGS),
674 JS_FN("increment", PointerType::Increment, 0, CTYPESFN_FLAGS),
675 JS_FN("decrement", PointerType::Decrement, 0, CTYPESFN_FLAGS),
676 JS_FS_END
679 static const JSPropertySpec sPointerInstanceProps[] = {
680 JS_PSGS("contents",
681 (Property<PointerType::IsPointer, PointerType::ContentsGetter>::Fun),
682 (Property<PointerType::IsPointer, PointerType::ContentsSetter>::Fun),
683 JSPROP_PERMANENT),
684 JS_PS_END
687 static const JSFunctionSpec sArrayFunction =
688 JS_FN("ArrayType", ArrayType::Create, 1, CTYPESCTOR_FLAGS);
690 static const JSPropertySpec sArrayProps[] = {
691 JS_PSG("elementType",
692 (Property<ArrayType::IsArrayType, ArrayType::ElementTypeGetter>::Fun),
693 CTYPESACC_FLAGS),
694 JS_PSG("length",
695 (Property<ArrayType::IsArrayOrArrayType, ArrayType::LengthGetter>::Fun),
696 CTYPESACC_FLAGS),
697 JS_PS_END
700 static const JSFunctionSpec sArrayInstanceFunctions[] = {
701 JS_FN("addressOfElement", ArrayType::AddressOfElement, 1, CDATAFN_FLAGS),
702 JS_FS_END
705 static const JSPropertySpec sArrayInstanceProps[] = {
706 JS_PSG("length",
707 (Property<ArrayType::IsArrayOrArrayType, ArrayType::LengthGetter>::Fun),
708 JSPROP_PERMANENT),
709 JS_PS_END
712 static const JSFunctionSpec sStructFunction =
713 JS_FN("StructType", StructType::Create, 2, CTYPESCTOR_FLAGS);
715 static const JSPropertySpec sStructProps[] = {
716 JS_PSG("fields",
717 (Property<StructType::IsStruct, StructType::FieldsArrayGetter>::Fun),
718 CTYPESACC_FLAGS),
719 JS_PS_END
722 static const JSFunctionSpec sStructFunctions[] = {
723 JS_FN("define", StructType::Define, 1, CDATAFN_FLAGS),
724 JS_FS_END
727 static const JSFunctionSpec sStructInstanceFunctions[] = {
728 JS_FN("addressOfField", StructType::AddressOfField, 1, CDATAFN_FLAGS),
729 JS_FS_END
732 static const JSFunctionSpec sFunctionFunction =
733 JS_FN("FunctionType", FunctionType::Create, 2, CTYPESCTOR_FLAGS);
735 static const JSPropertySpec sFunctionProps[] = {
736 JS_PSG("argTypes",
737 (Property<FunctionType::IsFunctionType, FunctionType::ArgTypesGetter>::Fun),
738 CTYPESACC_FLAGS),
739 JS_PSG("returnType",
740 (Property<FunctionType::IsFunctionType, FunctionType::ReturnTypeGetter>::Fun),
741 CTYPESACC_FLAGS),
742 JS_PSG("abi",
743 (Property<FunctionType::IsFunctionType, FunctionType::ABIGetter>::Fun),
744 CTYPESACC_FLAGS),
745 JS_PSG("isVariadic",
746 (Property<FunctionType::IsFunctionType, FunctionType::IsVariadicGetter>::Fun),
747 CTYPESACC_FLAGS),
748 JS_PS_END
751 static const JSFunctionSpec sFunctionInstanceFunctions[] = {
752 JS_FN("call", js_fun_call, 1, CDATAFN_FLAGS),
753 JS_FN("apply", js_fun_apply, 2, CDATAFN_FLAGS),
754 JS_FS_END
757 static const JSClass sInt64ProtoClass = {
758 "Int64",
762 static const JSClass sUInt64ProtoClass = {
763 "UInt64",
767 static const JSClass sInt64Class = {
768 "Int64",
769 JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS),
770 nullptr, nullptr, nullptr, nullptr,
771 nullptr, nullptr, nullptr, Int64Base::Finalize
774 static const JSClass sUInt64Class = {
775 "UInt64",
776 JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS),
777 nullptr, nullptr, nullptr, nullptr,
778 nullptr, nullptr, nullptr, Int64Base::Finalize
781 static const JSFunctionSpec sInt64StaticFunctions[] = {
782 JS_FN("compare", Int64::Compare, 2, CTYPESFN_FLAGS),
783 JS_FN("lo", Int64::Lo, 1, CTYPESFN_FLAGS),
784 JS_FN("hi", Int64::Hi, 1, CTYPESFN_FLAGS),
785 JS_FN("join", Int64::Join, 2, CTYPESFN_FLAGS),
786 JS_FS_END
789 static const JSFunctionSpec sUInt64StaticFunctions[] = {
790 JS_FN("compare", UInt64::Compare, 2, CTYPESFN_FLAGS),
791 JS_FN("lo", UInt64::Lo, 1, CTYPESFN_FLAGS),
792 JS_FN("hi", UInt64::Hi, 1, CTYPESFN_FLAGS),
793 JS_FN("join", UInt64::Join, 2, CTYPESFN_FLAGS),
794 JS_FS_END
797 static const JSFunctionSpec sInt64Functions[] = {
798 JS_FN("toString", Int64::ToString, 0, CTYPESFN_FLAGS),
799 JS_FN("toSource", Int64::ToSource, 0, CTYPESFN_FLAGS),
800 JS_FS_END
803 static const JSFunctionSpec sUInt64Functions[] = {
804 JS_FN("toString", UInt64::ToString, 0, CTYPESFN_FLAGS),
805 JS_FN("toSource", UInt64::ToSource, 0, CTYPESFN_FLAGS),
806 JS_FS_END
809 static const JSPropertySpec sModuleProps[] = {
810 JS_PSG("errno",
811 (Property<IsCTypesGlobal, CData::ErrnoGetter>::Fun),
812 JSPROP_PERMANENT),
813 #if defined(XP_WIN)
814 JS_PSG("winLastError",
815 (Property<IsCTypesGlobal, CData::LastErrorGetter>::Fun),
816 JSPROP_PERMANENT),
817 #endif // defined(XP_WIN)
818 JS_PS_END
821 static const JSFunctionSpec sModuleFunctions[] = {
822 JS_FN("CDataFinalizer", CDataFinalizer::Construct, 2, CTYPESFN_FLAGS),
823 JS_FN("open", Library::Open, 1, CTYPESFN_FLAGS),
824 JS_FN("cast", CData::Cast, 2, CTYPESFN_FLAGS),
825 JS_FN("getRuntime", CData::GetRuntime, 1, CTYPESFN_FLAGS),
826 JS_FN("libraryName", Library::Name, 1, CTYPESFN_FLAGS),
827 JS_FS_END
830 static MOZ_ALWAYS_INLINE JSString*
831 NewUCString(JSContext* cx, const AutoString& from)
833 return JS_NewUCStringCopyN(cx, from.begin(), from.length());
837 * Return a size rounded up to a multiple of a power of two.
839 * Note: |align| must be a power of 2.
841 static MOZ_ALWAYS_INLINE size_t
842 Align(size_t val, size_t align)
844 // Ensure that align is a power of two.
845 MOZ_ASSERT(align != 0 && (align & (align - 1)) == 0);
846 return ((val - 1) | (align - 1)) + 1;
849 static ABICode
850 GetABICode(JSObject* obj)
852 // make sure we have an object representing a CABI class,
853 // and extract the enumerated class type from the reserved slot.
854 if (JS_GetClass(obj) != &sCABIClass)
855 return INVALID_ABI;
857 jsval result = JS_GetReservedSlot(obj, SLOT_ABICODE);
858 return ABICode(result.toInt32());
861 static const JSErrorFormatString ErrorFormatString[CTYPESERR_LIMIT] = {
862 #define MSG_DEF(name, count, exception, format) \
863 { format, count, exception } ,
864 #include "ctypes/ctypes.msg"
865 #undef MSG_DEF
868 static const JSErrorFormatString*
869 GetErrorMessage(void* userRef, const unsigned errorNumber)
871 if (0 < errorNumber && errorNumber < CTYPESERR_LIMIT)
872 return &ErrorFormatString[errorNumber];
873 return nullptr;
876 static bool
877 TypeError(JSContext* cx, const char* expected, HandleValue actual)
879 JSString* str = JS_ValueToSource(cx, actual);
880 JSAutoByteString bytes;
882 const char* src;
883 if (str) {
884 src = bytes.encodeLatin1(cx, str);
885 if (!src)
886 return false;
887 } else {
888 JS_ClearPendingException(cx);
889 src = "<<error converting value to string>>";
891 JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
892 CTYPESMSG_TYPE_ERROR, expected, src);
893 return false;
896 static JSObject*
897 InitCTypeClass(JSContext* cx, HandleObject parent)
899 JSFunction* fun = JS_DefineFunction(cx, parent, "CType", ConstructAbstract, 0,
900 CTYPESCTOR_FLAGS);
901 if (!fun)
902 return nullptr;
904 RootedObject ctor(cx, JS_GetFunctionObject(fun));
905 RootedObject fnproto(cx);
906 if (!JS_GetPrototype(cx, ctor, &fnproto))
907 return nullptr;
908 MOZ_ASSERT(ctor);
909 MOZ_ASSERT(fnproto);
911 // Set up ctypes.CType.prototype.
912 RootedObject prototype(cx, JS_NewObject(cx, &sCTypeProtoClass, fnproto, parent));
913 if (!prototype)
914 return nullptr;
916 if (!JS_DefineProperty(cx, ctor, "prototype", prototype,
917 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
918 return nullptr;
920 if (!JS_DefineProperty(cx, prototype, "constructor", ctor,
921 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
922 return nullptr;
924 // Define properties and functions common to all CTypes.
925 if (!JS_DefineProperties(cx, prototype, sCTypeProps) ||
926 !JS_DefineFunctions(cx, prototype, sCTypeFunctions))
927 return nullptr;
929 if (!JS_FreezeObject(cx, ctor) || !JS_FreezeObject(cx, prototype))
930 return nullptr;
932 return prototype;
935 static JSObject*
936 InitABIClass(JSContext* cx, JSObject* parent)
938 RootedObject obj(cx, JS_NewObject(cx, nullptr, NullPtr(), NullPtr()));
940 if (!obj)
941 return nullptr;
943 if (!JS_DefineFunctions(cx, obj, sCABIFunctions))
944 return nullptr;
946 return obj;
950 static JSObject*
951 InitCDataClass(JSContext* cx, HandleObject parent, HandleObject CTypeProto)
953 JSFunction* fun = JS_DefineFunction(cx, parent, "CData", ConstructAbstract, 0,
954 CTYPESCTOR_FLAGS);
955 if (!fun)
956 return nullptr;
958 RootedObject ctor(cx, JS_GetFunctionObject(fun));
959 MOZ_ASSERT(ctor);
961 // Set up ctypes.CData.__proto__ === ctypes.CType.prototype.
962 // (Note that 'ctypes.CData instanceof Function' is still true, thanks to the
963 // prototype chain.)
964 if (!JS_SetPrototype(cx, ctor, CTypeProto))
965 return nullptr;
967 // Set up ctypes.CData.prototype.
968 RootedObject prototype(cx, JS_NewObject(cx, &sCDataProtoClass, NullPtr(), parent));
969 if (!prototype)
970 return nullptr;
972 if (!JS_DefineProperty(cx, ctor, "prototype", prototype,
973 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
974 return nullptr;
976 if (!JS_DefineProperty(cx, prototype, "constructor", ctor,
977 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
978 return nullptr;
980 // Define properties and functions common to all CDatas.
981 if (!JS_DefineProperties(cx, prototype, sCDataProps) ||
982 !JS_DefineFunctions(cx, prototype, sCDataFunctions))
983 return nullptr;
985 if (//!JS_FreezeObject(cx, prototype) || // XXX fixme - see bug 541212!
986 !JS_FreezeObject(cx, ctor))
987 return nullptr;
989 return prototype;
992 static bool
993 DefineABIConstant(JSContext* cx,
994 HandleObject parent,
995 const char* name,
996 ABICode code,
997 HandleObject prototype)
999 RootedObject obj(cx, JS_DefineObject(cx, parent, name, &sCABIClass, prototype,
1000 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT));
1001 if (!obj)
1002 return false;
1003 JS_SetReservedSlot(obj, SLOT_ABICODE, INT_TO_JSVAL(code));
1004 return JS_FreezeObject(cx, obj);
1007 // Set up a single type constructor for
1008 // ctypes.{Pointer,Array,Struct,Function}Type.
1009 static bool
1010 InitTypeConstructor(JSContext* cx,
1011 HandleObject parent,
1012 HandleObject CTypeProto,
1013 HandleObject CDataProto,
1014 const JSFunctionSpec spec,
1015 const JSFunctionSpec* fns,
1016 const JSPropertySpec* props,
1017 const JSFunctionSpec* instanceFns,
1018 const JSPropertySpec* instanceProps,
1019 MutableHandleObject typeProto,
1020 MutableHandleObject dataProto)
1022 JSFunction* fun = js::DefineFunctionWithReserved(cx, parent, spec.name, spec.call.op,
1023 spec.nargs, spec.flags);
1024 if (!fun)
1025 return false;
1027 RootedObject obj(cx, JS_GetFunctionObject(fun));
1028 if (!obj)
1029 return false;
1031 // Set up the .prototype and .prototype.constructor properties.
1032 typeProto.set(JS_NewObject(cx, &sCTypeProtoClass, CTypeProto, parent));
1033 if (!typeProto)
1034 return false;
1036 // Define property before proceeding, for GC safety.
1037 if (!JS_DefineProperty(cx, obj, "prototype", typeProto,
1038 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
1039 return false;
1041 if (fns && !JS_DefineFunctions(cx, typeProto, fns))
1042 return false;
1044 if (!JS_DefineProperties(cx, typeProto, props))
1045 return false;
1047 if (!JS_DefineProperty(cx, typeProto, "constructor", obj,
1048 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
1049 return false;
1051 // Stash ctypes.{Pointer,Array,Struct}Type.prototype on a reserved slot of
1052 // the type constructor, for faster lookup.
1053 js::SetFunctionNativeReserved(obj, SLOT_FN_CTORPROTO, OBJECT_TO_JSVAL(typeProto));
1055 // Create an object to serve as the common ancestor for all CData objects
1056 // created from the given type constructor. This has ctypes.CData.prototype
1057 // as its prototype, such that it inherits the properties and functions
1058 // common to all CDatas.
1059 dataProto.set(JS_NewObject(cx, &sCDataProtoClass, CDataProto, parent));
1060 if (!dataProto)
1061 return false;
1063 // Define functions and properties on the 'dataProto' object that are common
1064 // to all CData objects created from this type constructor. (These will
1065 // become functions and properties on CData objects created from this type.)
1066 if (instanceFns && !JS_DefineFunctions(cx, dataProto, instanceFns))
1067 return false;
1069 if (instanceProps && !JS_DefineProperties(cx, dataProto, instanceProps))
1070 return false;
1072 // Link the type prototype to the data prototype.
1073 JS_SetReservedSlot(typeProto, SLOT_OURDATAPROTO, OBJECT_TO_JSVAL(dataProto));
1075 if (!JS_FreezeObject(cx, obj) ||
1076 //!JS_FreezeObject(cx, dataProto) || // XXX fixme - see bug 541212!
1077 !JS_FreezeObject(cx, typeProto))
1078 return false;
1080 return true;
1083 static JSObject*
1084 InitInt64Class(JSContext* cx,
1085 HandleObject parent,
1086 const JSClass* clasp,
1087 JSNative construct,
1088 const JSFunctionSpec* fs,
1089 const JSFunctionSpec* static_fs)
1091 // Init type class and constructor
1092 RootedObject prototype(cx, JS_InitClass(cx, parent, js::NullPtr(), clasp, construct,
1093 0, nullptr, fs, nullptr, static_fs));
1094 if (!prototype)
1095 return nullptr;
1097 RootedObject ctor(cx, JS_GetConstructor(cx, prototype));
1098 if (!ctor)
1099 return nullptr;
1100 if (!JS_FreezeObject(cx, ctor))
1101 return nullptr;
1103 // Redefine the 'join' function as an extended native and stash
1104 // ctypes.{Int64,UInt64}.prototype in a reserved slot of the new function.
1105 MOZ_ASSERT(clasp == &sInt64ProtoClass || clasp == &sUInt64ProtoClass);
1106 JSNative native = (clasp == &sInt64ProtoClass) ? Int64::Join : UInt64::Join;
1107 JSFunction* fun = js::DefineFunctionWithReserved(cx, ctor, "join", native,
1108 2, CTYPESFN_FLAGS);
1109 if (!fun)
1110 return nullptr;
1112 js::SetFunctionNativeReserved(fun, SLOT_FN_INT64PROTO,
1113 OBJECT_TO_JSVAL(prototype));
1115 if (!JS_FreezeObject(cx, prototype))
1116 return nullptr;
1118 return prototype;
1121 static void
1122 AttachProtos(JSObject* proto, const AutoObjectVector& protos)
1124 // For a given 'proto' of [[Class]] "CTypeProto", attach each of the 'protos'
1125 // to the appropriate CTypeProtoSlot. (SLOT_CTYPES is the last slot
1126 // of [[Class]] "CTypeProto" that we fill in this automated manner.)
1127 for (uint32_t i = 0; i <= SLOT_CTYPES; ++i)
1128 JS_SetReservedSlot(proto, i, OBJECT_TO_JSVAL(protos[i]));
1131 static bool
1132 InitTypeClasses(JSContext* cx, HandleObject parent)
1134 // Initialize the ctypes.CType class. This acts as an abstract base class for
1135 // the various types, and provides the common API functions. It has:
1136 // * [[Class]] "Function"
1137 // * __proto__ === Function.prototype
1138 // * A constructor that throws a TypeError. (You can't construct an
1139 // abstract type!)
1140 // * 'prototype' property:
1141 // * [[Class]] "CTypeProto"
1142 // * __proto__ === Function.prototype
1143 // * A constructor that throws a TypeError. (You can't construct an
1144 // abstract type instance!)
1145 // * 'constructor' property === ctypes.CType
1146 // * Provides properties and functions common to all CTypes.
1147 RootedObject CTypeProto(cx, InitCTypeClass(cx, parent));
1148 if (!CTypeProto)
1149 return false;
1151 // Initialize the ctypes.CData class. This acts as an abstract base class for
1152 // instances of the various types, and provides the common API functions.
1153 // It has:
1154 // * [[Class]] "Function"
1155 // * __proto__ === Function.prototype
1156 // * A constructor that throws a TypeError. (You can't construct an
1157 // abstract type instance!)
1158 // * 'prototype' property:
1159 // * [[Class]] "CDataProto"
1160 // * 'constructor' property === ctypes.CData
1161 // * Provides properties and functions common to all CDatas.
1162 RootedObject CDataProto(cx, InitCDataClass(cx, parent, CTypeProto));
1163 if (!CDataProto)
1164 return false;
1166 // Link CTypeProto to CDataProto.
1167 JS_SetReservedSlot(CTypeProto, SLOT_OURDATAPROTO, OBJECT_TO_JSVAL(CDataProto));
1169 // Create and attach the special class constructors: ctypes.PointerType,
1170 // ctypes.ArrayType, ctypes.StructType, and ctypes.FunctionType.
1171 // Each of these constructors 'c' has, respectively:
1172 // * [[Class]] "Function"
1173 // * __proto__ === Function.prototype
1174 // * A constructor that creates a user-defined type.
1175 // * 'prototype' property:
1176 // * [[Class]] "CTypeProto"
1177 // * __proto__ === ctypes.CType.prototype
1178 // * 'constructor' property === 'c'
1179 // We also construct an object 'p' to serve, given a type object 't'
1180 // constructed from one of these type constructors, as
1181 // 't.prototype.__proto__'. This object has:
1182 // * [[Class]] "CDataProto"
1183 // * __proto__ === ctypes.CData.prototype
1184 // * Properties and functions common to all CDatas.
1185 // Therefore an instance 't' of ctypes.{Pointer,Array,Struct,Function}Type
1186 // will have, resp.:
1187 // * [[Class]] "CType"
1188 // * __proto__ === ctypes.{Pointer,Array,Struct,Function}Type.prototype
1189 // * A constructor which creates and returns a CData object, containing
1190 // binary data of the given type.
1191 // * 'prototype' property:
1192 // * [[Class]] "CDataProto"
1193 // * __proto__ === 'p', the prototype object from above
1194 // * 'constructor' property === 't'
1195 AutoObjectVector protos(cx);
1196 protos.resize(CTYPEPROTO_SLOTS);
1197 if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
1198 sPointerFunction, nullptr, sPointerProps,
1199 sPointerInstanceFunctions, sPointerInstanceProps,
1200 protos[SLOT_POINTERPROTO], protos[SLOT_POINTERDATAPROTO]))
1201 return false;
1203 if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
1204 sArrayFunction, nullptr, sArrayProps,
1205 sArrayInstanceFunctions, sArrayInstanceProps,
1206 protos[SLOT_ARRAYPROTO], protos[SLOT_ARRAYDATAPROTO]))
1207 return false;
1209 if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
1210 sStructFunction, sStructFunctions, sStructProps,
1211 sStructInstanceFunctions, nullptr,
1212 protos[SLOT_STRUCTPROTO], protos[SLOT_STRUCTDATAPROTO]))
1213 return false;
1215 if (!InitTypeConstructor(cx, parent, CTypeProto, protos[SLOT_POINTERDATAPROTO],
1216 sFunctionFunction, nullptr, sFunctionProps, sFunctionInstanceFunctions, nullptr,
1217 protos[SLOT_FUNCTIONPROTO], protos[SLOT_FUNCTIONDATAPROTO]))
1218 return false;
1220 protos[SLOT_CDATAPROTO].set(CDataProto);
1222 // Create and attach the ctypes.{Int64,UInt64} constructors.
1223 // Each of these has, respectively:
1224 // * [[Class]] "Function"
1225 // * __proto__ === Function.prototype
1226 // * A constructor that creates a ctypes.{Int64,UInt64} object, respectively.
1227 // * 'prototype' property:
1228 // * [[Class]] {"Int64Proto","UInt64Proto"}
1229 // * 'constructor' property === ctypes.{Int64,UInt64}
1230 protos[SLOT_INT64PROTO].set(InitInt64Class(cx, parent, &sInt64ProtoClass,
1231 Int64::Construct, sInt64Functions, sInt64StaticFunctions));
1232 if (!protos[SLOT_INT64PROTO])
1233 return false;
1234 protos[SLOT_UINT64PROTO].set(InitInt64Class(cx, parent, &sUInt64ProtoClass,
1235 UInt64::Construct, sUInt64Functions, sUInt64StaticFunctions));
1236 if (!protos[SLOT_UINT64PROTO])
1237 return false;
1239 // Finally, store a pointer to the global ctypes object.
1240 // Note that there is no other reliable manner of locating this object.
1241 protos[SLOT_CTYPES].set(parent);
1243 // Attach the prototypes just created to each of ctypes.CType.prototype,
1244 // and the special type constructors, so we can access them when constructing
1245 // instances of those types.
1246 AttachProtos(CTypeProto, protos);
1247 AttachProtos(protos[SLOT_POINTERPROTO], protos);
1248 AttachProtos(protos[SLOT_ARRAYPROTO], protos);
1249 AttachProtos(protos[SLOT_STRUCTPROTO], protos);
1250 AttachProtos(protos[SLOT_FUNCTIONPROTO], protos);
1252 RootedObject ABIProto(cx, InitABIClass(cx, parent));
1253 if (!ABIProto)
1254 return false;
1256 // Attach objects representing ABI constants.
1257 if (!DefineABIConstant(cx, parent, "default_abi", ABI_DEFAULT, ABIProto) ||
1258 !DefineABIConstant(cx, parent, "stdcall_abi", ABI_STDCALL, ABIProto) ||
1259 !DefineABIConstant(cx, parent, "winapi_abi", ABI_WINAPI, ABIProto))
1260 return false;
1262 // Create objects representing the builtin types, and attach them to the
1263 // ctypes object. Each type object 't' has:
1264 // * [[Class]] "CType"
1265 // * __proto__ === ctypes.CType.prototype
1266 // * A constructor which creates and returns a CData object, containing
1267 // binary data of the given type.
1268 // * 'prototype' property:
1269 // * [[Class]] "CDataProto"
1270 // * __proto__ === ctypes.CData.prototype
1271 // * 'constructor' property === 't'
1272 #define DEFINE_TYPE(name, type, ffiType) \
1273 RootedObject typeObj_##name(cx, \
1274 CType::DefineBuiltin(cx, parent, #name, CTypeProto, CDataProto, #name, \
1275 TYPE_##name, INT_TO_JSVAL(sizeof(type)), \
1276 INT_TO_JSVAL(ffiType.alignment), &ffiType)); \
1277 if (!typeObj_##name) \
1278 return false;
1279 CTYPES_FOR_EACH_TYPE(DEFINE_TYPE)
1280 #undef DEFINE_TYPE
1282 // Alias 'ctypes.unsigned' as 'ctypes.unsigned_int', since they represent
1283 // the same type in C.
1284 if (!JS_DefineProperty(cx, parent, "unsigned", typeObj_unsigned_int,
1285 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
1286 return false;
1288 // Alias 'ctypes.jschar' as 'ctypes.char16_t' to prevent breaking addons
1289 // that are still using jschar (bug 1064935).
1290 if (!JS_DefineProperty(cx, parent, "jschar", typeObj_char16_t,
1291 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
1292 return false;
1294 // Create objects representing the special types void_t and voidptr_t.
1295 RootedObject typeObj(cx,
1296 CType::DefineBuiltin(cx, parent, "void_t", CTypeProto, CDataProto, "void",
1297 TYPE_void_t, JSVAL_VOID, JSVAL_VOID, &ffi_type_void));
1298 if (!typeObj)
1299 return false;
1301 typeObj = PointerType::CreateInternal(cx, typeObj);
1302 if (!typeObj)
1303 return false;
1304 if (!JS_DefineProperty(cx, parent, "voidptr_t", typeObj,
1305 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
1306 return false;
1308 return true;
1311 bool
1312 IsCTypesGlobal(JSObject* obj)
1314 return JS_GetClass(obj) == &sCTypesGlobalClass;
1317 bool
1318 IsCTypesGlobal(HandleValue v)
1320 return v.isObject() && IsCTypesGlobal(&v.toObject());
1323 // Get the JSCTypesCallbacks struct from the 'ctypes' object 'obj'.
1324 const JSCTypesCallbacks*
1325 GetCallbacks(JSObject* obj)
1327 MOZ_ASSERT(IsCTypesGlobal(obj));
1329 jsval result = JS_GetReservedSlot(obj, SLOT_CALLBACKS);
1330 if (result.isUndefined())
1331 return nullptr;
1333 return static_cast<const JSCTypesCallbacks*>(result.toPrivate());
1336 // Utility function to access a property of an object as an object
1337 // returns false and sets the error if the property does not exist
1338 // or is not an object
1339 static bool GetObjectProperty(JSContext* cx, HandleObject obj,
1340 const char* property, MutableHandleObject result)
1342 RootedValue val(cx);
1343 if (!JS_GetProperty(cx, obj, property, &val)) {
1344 return false;
1347 if (val.isPrimitive()) {
1348 JS_ReportError(cx, "missing or non-object field");
1349 return false;
1352 result.set(val.toObjectOrNull());
1353 return true;
1356 } /* namespace ctypes */
1357 } /* namespace js */
1359 using namespace js;
1360 using namespace js::ctypes;
1362 JS_PUBLIC_API(bool)
1363 JS_InitCTypesClass(JSContext* cx, HandleObject global)
1365 // attach ctypes property to global object
1366 RootedObject ctypes(cx, JS_NewObject(cx, &sCTypesGlobalClass, NullPtr(), NullPtr()));
1367 if (!ctypes)
1368 return false;
1370 if (!JS_DefineProperty(cx, global, "ctypes", ctypes,
1371 JSPROP_READONLY | JSPROP_PERMANENT,
1372 JS_STUBGETTER, JS_STUBSETTER)){
1373 return false;
1376 if (!InitTypeClasses(cx, ctypes))
1377 return false;
1379 // attach API functions and properties
1380 if (!JS_DefineFunctions(cx, ctypes, sModuleFunctions) ||
1381 !JS_DefineProperties(cx, ctypes, sModuleProps))
1382 return false;
1384 // Set up ctypes.CDataFinalizer.prototype.
1385 RootedObject ctor(cx);
1386 if (!GetObjectProperty(cx, ctypes, "CDataFinalizer", &ctor))
1387 return false;
1389 RootedObject prototype(cx, JS_NewObject(cx, &sCDataFinalizerProtoClass, NullPtr(), ctypes));
1390 if (!prototype)
1391 return false;
1393 if (!JS_DefineFunctions(cx, prototype, sCDataFinalizerFunctions))
1394 return false;
1396 if (!JS_DefineProperty(cx, ctor, "prototype", prototype,
1397 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
1398 return false;
1400 if (!JS_DefineProperty(cx, prototype, "constructor", ctor,
1401 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
1402 return false;
1405 // Seal the ctypes object, to prevent modification.
1406 return JS_FreezeObject(cx, ctypes);
1409 JS_PUBLIC_API(void)
1410 JS_SetCTypesCallbacks(JSObject* ctypesObj, const JSCTypesCallbacks* callbacks)
1412 MOZ_ASSERT(callbacks);
1413 MOZ_ASSERT(IsCTypesGlobal(ctypesObj));
1415 // Set the callbacks on a reserved slot.
1416 JS_SetReservedSlot(ctypesObj, SLOT_CALLBACKS,
1417 PRIVATE_TO_JSVAL(const_cast<JSCTypesCallbacks*>(callbacks)));
1420 namespace js {
1422 JS_FRIEND_API(size_t)
1423 SizeOfDataIfCDataObject(mozilla::MallocSizeOf mallocSizeOf, JSObject* obj)
1425 if (!CData::IsCData(obj))
1426 return 0;
1428 size_t n = 0;
1429 jsval slot = JS_GetReservedSlot(obj, ctypes::SLOT_OWNS);
1430 if (!slot.isUndefined()) {
1431 bool owns = slot.toBoolean();
1432 slot = JS_GetReservedSlot(obj, ctypes::SLOT_DATA);
1433 if (!slot.isUndefined()) {
1434 char** buffer = static_cast<char**>(slot.toPrivate());
1435 n += mallocSizeOf(buffer);
1436 if (owns)
1437 n += mallocSizeOf(*buffer);
1440 return n;
1443 namespace ctypes {
1445 /*******************************************************************************
1446 ** Type conversion functions
1447 *******************************************************************************/
1449 // Enforce some sanity checks on type widths and properties.
1450 // Where the architecture is 64-bit, make sure it's LP64 or LLP64. (ctypes.int
1451 // autoconverts to a primitive JS number; to support ILP64 architectures, it
1452 // would need to autoconvert to an Int64 object instead. Therefore we enforce
1453 // this invariant here.)
1454 JS_STATIC_ASSERT(sizeof(bool) == 1 || sizeof(bool) == 4);
1455 JS_STATIC_ASSERT(sizeof(char) == 1);
1456 JS_STATIC_ASSERT(sizeof(short) == 2);
1457 JS_STATIC_ASSERT(sizeof(int) == 4);
1458 JS_STATIC_ASSERT(sizeof(unsigned) == 4);
1459 JS_STATIC_ASSERT(sizeof(long) == 4 || sizeof(long) == 8);
1460 JS_STATIC_ASSERT(sizeof(long long) == 8);
1461 JS_STATIC_ASSERT(sizeof(size_t) == sizeof(uintptr_t));
1462 JS_STATIC_ASSERT(sizeof(float) == 4);
1463 JS_STATIC_ASSERT(sizeof(PRFuncPtr) == sizeof(void*));
1464 JS_STATIC_ASSERT(NumericLimits<double>::is_signed);
1466 // Templated helper to convert FromType to TargetType, for the default case
1467 // where the trivial POD constructor will do.
1468 template<class TargetType, class FromType>
1469 struct ConvertImpl {
1470 static MOZ_ALWAYS_INLINE TargetType Convert(FromType d) {
1471 return TargetType(d);
1475 #ifdef _MSC_VER
1476 // MSVC can't perform double to unsigned __int64 conversion when the
1477 // double is greater than 2^63 - 1. Help it along a little.
1478 template<>
1479 struct ConvertImpl<uint64_t, double> {
1480 static MOZ_ALWAYS_INLINE uint64_t Convert(double d) {
1481 return d > 0x7fffffffffffffffui64 ?
1482 uint64_t(d - 0x8000000000000000ui64) + 0x8000000000000000ui64 :
1483 uint64_t(d);
1486 #endif
1488 // C++ doesn't guarantee that exact values are the only ones that will
1489 // round-trip. In fact, on some platforms, including SPARC, there are pairs of
1490 // values, a uint64_t and a double, such that neither value is exactly
1491 // representable in the other type, but they cast to each other.
1492 #if defined(SPARC) || defined(__powerpc__)
1493 // Simulate x86 overflow behavior
1494 template<>
1495 struct ConvertImpl<uint64_t, double> {
1496 static MOZ_ALWAYS_INLINE uint64_t Convert(double d) {
1497 return d >= 0xffffffffffffffff ?
1498 0x8000000000000000 : uint64_t(d);
1502 template<>
1503 struct ConvertImpl<int64_t, double> {
1504 static MOZ_ALWAYS_INLINE int64_t Convert(double d) {
1505 return d >= 0x7fffffffffffffff ?
1506 0x8000000000000000 : int64_t(d);
1509 #endif
1511 template<class TargetType, class FromType>
1512 static MOZ_ALWAYS_INLINE TargetType Convert(FromType d)
1514 return ConvertImpl<TargetType, FromType>::Convert(d);
1517 template<class TargetType, class FromType>
1518 static MOZ_ALWAYS_INLINE bool IsAlwaysExact()
1520 // Return 'true' if TargetType can always exactly represent FromType.
1521 // This means that:
1522 // 1) TargetType must be the same or more bits wide as FromType. For integers
1523 // represented in 'n' bits, unsigned variants will have 'n' digits while
1524 // signed will have 'n - 1'. For floating point types, 'digits' is the
1525 // mantissa width.
1526 // 2) If FromType is signed, TargetType must also be signed. (Floating point
1527 // types are always signed.)
1528 // 3) If TargetType is an exact integral type, FromType must be also.
1529 if (NumericLimits<TargetType>::digits < NumericLimits<FromType>::digits)
1530 return false;
1532 if (NumericLimits<FromType>::is_signed &&
1533 !NumericLimits<TargetType>::is_signed)
1534 return false;
1536 if (!NumericLimits<FromType>::is_exact &&
1537 NumericLimits<TargetType>::is_exact)
1538 return false;
1540 return true;
1543 // Templated helper to determine if FromType 'i' converts losslessly to
1544 // TargetType 'j'. Default case where both types are the same signedness.
1545 template<class TargetType, class FromType, bool TargetSigned, bool FromSigned>
1546 struct IsExactImpl {
1547 static MOZ_ALWAYS_INLINE bool Test(FromType i, TargetType j) {
1548 JS_STATIC_ASSERT(NumericLimits<TargetType>::is_exact);
1549 return FromType(j) == i;
1553 // Specialization where TargetType is unsigned, FromType is signed.
1554 template<class TargetType, class FromType>
1555 struct IsExactImpl<TargetType, FromType, false, true> {
1556 static MOZ_ALWAYS_INLINE bool Test(FromType i, TargetType j) {
1557 JS_STATIC_ASSERT(NumericLimits<TargetType>::is_exact);
1558 return i >= 0 && FromType(j) == i;
1562 // Specialization where TargetType is signed, FromType is unsigned.
1563 template<class TargetType, class FromType>
1564 struct IsExactImpl<TargetType, FromType, true, false> {
1565 static MOZ_ALWAYS_INLINE bool Test(FromType i, TargetType j) {
1566 JS_STATIC_ASSERT(NumericLimits<TargetType>::is_exact);
1567 return TargetType(i) >= 0 && FromType(j) == i;
1571 // Convert FromType 'i' to TargetType 'result', returning true iff 'result'
1572 // is an exact representation of 'i'.
1573 template<class TargetType, class FromType>
1574 static MOZ_ALWAYS_INLINE bool ConvertExact(FromType i, TargetType* result)
1576 // Require that TargetType is integral, to simplify conversion.
1577 JS_STATIC_ASSERT(NumericLimits<TargetType>::is_exact);
1579 *result = Convert<TargetType>(i);
1581 // See if we can avoid a dynamic check.
1582 if (IsAlwaysExact<TargetType, FromType>())
1583 return true;
1585 // Return 'true' if 'i' is exactly representable in 'TargetType'.
1586 return IsExactImpl<TargetType,
1587 FromType,
1588 NumericLimits<TargetType>::is_signed,
1589 NumericLimits<FromType>::is_signed>::Test(i, *result);
1592 // Templated helper to determine if Type 'i' is negative. Default case
1593 // where IntegerType is unsigned.
1594 template<class Type, bool IsSigned>
1595 struct IsNegativeImpl {
1596 static MOZ_ALWAYS_INLINE bool Test(Type i) {
1597 return false;
1601 // Specialization where Type is signed.
1602 template<class Type>
1603 struct IsNegativeImpl<Type, true> {
1604 static MOZ_ALWAYS_INLINE bool Test(Type i) {
1605 return i < 0;
1609 // Determine whether Type 'i' is negative.
1610 template<class Type>
1611 static MOZ_ALWAYS_INLINE bool IsNegative(Type i)
1613 return IsNegativeImpl<Type, NumericLimits<Type>::is_signed>::Test(i);
1616 // Implicitly convert val to bool, allowing bool, int, and double
1617 // arguments numerically equal to 0 or 1.
1618 static bool
1619 jsvalToBool(JSContext* cx, jsval val, bool* result)
1621 if (val.isBoolean()) {
1622 *result = val.toBoolean();
1623 return true;
1625 if (val.isInt32()) {
1626 int32_t i = val.toInt32();
1627 *result = i != 0;
1628 return i == 0 || i == 1;
1630 if (val.isDouble()) {
1631 double d = val.toDouble();
1632 *result = d != 0;
1633 // Allow -0.
1634 return d == 1 || d == 0;
1636 // Don't silently convert null to bool. It's probably a mistake.
1637 return false;
1640 // Implicitly convert val to IntegerType, allowing bool, int, double,
1641 // Int64, UInt64, and CData integer types 't' where all values of 't' are
1642 // representable by IntegerType.
1643 template<class IntegerType>
1644 static bool
1645 jsvalToInteger(JSContext* cx, jsval val, IntegerType* result)
1647 JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact);
1649 if (val.isInt32()) {
1650 // Make sure the integer fits in the alotted precision, and has the right
1651 // sign.
1652 int32_t i = val.toInt32();
1653 return ConvertExact(i, result);
1655 if (val.isDouble()) {
1656 // Don't silently lose bits here -- check that val really is an
1657 // integer value, and has the right sign.
1658 double d = val.toDouble();
1659 return ConvertExact(d, result);
1661 if (val.isObject()) {
1662 JSObject* obj = &val.toObject();
1663 if (CData::IsCData(obj)) {
1664 JSObject* typeObj = CData::GetCType(obj);
1665 void* data = CData::GetData(obj);
1667 // Check whether the source type is always representable, with exact
1668 // precision, by the target type. If it is, convert the value.
1669 switch (CType::GetTypeCode(typeObj)) {
1670 #define INTEGER_CASE(name, fromType, ffiType) \
1671 case TYPE_##name: \
1672 if (!IsAlwaysExact<IntegerType, fromType>()) \
1673 return false; \
1674 *result = IntegerType(*static_cast<fromType*>(data)); \
1675 return true;
1676 CTYPES_FOR_EACH_INT_TYPE(INTEGER_CASE)
1677 CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGER_CASE)
1678 #undef INTEGER_CASE
1679 case TYPE_void_t:
1680 case TYPE_bool:
1681 case TYPE_float:
1682 case TYPE_double:
1683 case TYPE_float32_t:
1684 case TYPE_float64_t:
1685 case TYPE_char:
1686 case TYPE_signed_char:
1687 case TYPE_unsigned_char:
1688 case TYPE_char16_t:
1689 case TYPE_pointer:
1690 case TYPE_function:
1691 case TYPE_array:
1692 case TYPE_struct:
1693 // Not a compatible number type.
1694 return false;
1698 if (Int64::IsInt64(obj)) {
1699 // Make sure the integer fits in IntegerType.
1700 int64_t i = Int64Base::GetInt(obj);
1701 return ConvertExact(i, result);
1704 if (UInt64::IsUInt64(obj)) {
1705 // Make sure the integer fits in IntegerType.
1706 uint64_t i = Int64Base::GetInt(obj);
1707 return ConvertExact(i, result);
1710 if (CDataFinalizer::IsCDataFinalizer(obj)) {
1711 RootedValue innerData(cx);
1712 if (!CDataFinalizer::GetValue(cx, obj, &innerData)) {
1713 return false; // Nothing to convert
1715 return jsvalToInteger(cx, innerData, result);
1718 return false;
1720 if (val.isBoolean()) {
1721 // Implicitly promote boolean values to 0 or 1, like C.
1722 *result = val.toBoolean();
1723 MOZ_ASSERT(*result == 0 || *result == 1);
1724 return true;
1726 // Don't silently convert null to an integer. It's probably a mistake.
1727 return false;
1730 // Implicitly convert val to FloatType, allowing int, double,
1731 // Int64, UInt64, and CData numeric types 't' where all values of 't' are
1732 // representable by FloatType.
1733 template<class FloatType>
1734 static bool
1735 jsvalToFloat(JSContext* cx, jsval val, FloatType* result)
1737 JS_STATIC_ASSERT(!NumericLimits<FloatType>::is_exact);
1739 // The following casts may silently throw away some bits, but there's
1740 // no good way around it. Sternly requiring that the 64-bit double
1741 // argument be exactly representable as a 32-bit float is
1742 // unrealistic: it would allow 1/2 to pass but not 1/3.
1743 if (val.isInt32()) {
1744 *result = FloatType(val.toInt32());
1745 return true;
1747 if (val.isDouble()) {
1748 *result = FloatType(val.toDouble());
1749 return true;
1751 if (val.isObject()) {
1752 JSObject* obj = &val.toObject();
1753 if (CData::IsCData(obj)) {
1754 JSObject* typeObj = CData::GetCType(obj);
1755 void* data = CData::GetData(obj);
1757 // Check whether the source type is always representable, with exact
1758 // precision, by the target type. If it is, convert the value.
1759 switch (CType::GetTypeCode(typeObj)) {
1760 #define NUMERIC_CASE(name, fromType, ffiType) \
1761 case TYPE_##name: \
1762 if (!IsAlwaysExact<FloatType, fromType>()) \
1763 return false; \
1764 *result = FloatType(*static_cast<fromType*>(data)); \
1765 return true;
1766 CTYPES_FOR_EACH_FLOAT_TYPE(NUMERIC_CASE)
1767 CTYPES_FOR_EACH_INT_TYPE(NUMERIC_CASE)
1768 CTYPES_FOR_EACH_WRAPPED_INT_TYPE(NUMERIC_CASE)
1769 #undef NUMERIC_CASE
1770 case TYPE_void_t:
1771 case TYPE_bool:
1772 case TYPE_char:
1773 case TYPE_signed_char:
1774 case TYPE_unsigned_char:
1775 case TYPE_char16_t:
1776 case TYPE_pointer:
1777 case TYPE_function:
1778 case TYPE_array:
1779 case TYPE_struct:
1780 // Not a compatible number type.
1781 return false;
1785 // Don't silently convert true to 1.0 or false to 0.0, even though C/C++
1786 // does it. It's likely to be a mistake.
1787 return false;
1790 template <class IntegerType, class CharT>
1791 static bool
1792 StringToInteger(JSContext* cx, CharT* cp, size_t length, IntegerType* result)
1794 JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact);
1796 const CharT* end = cp + length;
1797 if (cp == end)
1798 return false;
1800 IntegerType sign = 1;
1801 if (cp[0] == '-') {
1802 if (!NumericLimits<IntegerType>::is_signed)
1803 return false;
1805 sign = -1;
1806 ++cp;
1809 // Assume base-10, unless the string begins with '0x' or '0X'.
1810 IntegerType base = 10;
1811 if (end - cp > 2 && cp[0] == '0' && (cp[1] == 'x' || cp[1] == 'X')) {
1812 cp += 2;
1813 base = 16;
1816 // Scan the string left to right and build the number,
1817 // checking for valid characters 0 - 9, a - f, A - F and overflow.
1818 IntegerType i = 0;
1819 while (cp != end) {
1820 char16_t c = *cp++;
1821 if (c >= '0' && c <= '9')
1822 c -= '0';
1823 else if (base == 16 && c >= 'a' && c <= 'f')
1824 c = c - 'a' + 10;
1825 else if (base == 16 && c >= 'A' && c <= 'F')
1826 c = c - 'A' + 10;
1827 else
1828 return false;
1830 IntegerType ii = i;
1831 i = ii * base + sign * c;
1832 if (i / base != ii) // overflow
1833 return false;
1836 *result = i;
1837 return true;
1840 template<class IntegerType>
1841 static bool
1842 StringToInteger(JSContext* cx, JSString* string, IntegerType* result)
1844 JSLinearString* linear = string->ensureLinear(cx);
1845 if (!linear)
1846 return false;
1848 AutoCheckCannotGC nogc;
1849 size_t length = linear->length();
1850 return string->hasLatin1Chars()
1851 ? StringToInteger<IntegerType>(cx, linear->latin1Chars(nogc), length, result)
1852 : StringToInteger<IntegerType>(cx, linear->twoByteChars(nogc), length, result);
1855 // Implicitly convert val to IntegerType, allowing int, double,
1856 // Int64, UInt64, and optionally a decimal or hexadecimal string argument.
1857 // (This is common code shared by jsvalToSize and the Int64/UInt64 constructors.)
1858 template<class IntegerType>
1859 static bool
1860 jsvalToBigInteger(JSContext* cx,
1861 jsval val,
1862 bool allowString,
1863 IntegerType* result)
1865 JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact);
1867 if (val.isInt32()) {
1868 // Make sure the integer fits in the alotted precision, and has the right
1869 // sign.
1870 int32_t i = val.toInt32();
1871 return ConvertExact(i, result);
1873 if (val.isDouble()) {
1874 // Don't silently lose bits here -- check that val really is an
1875 // integer value, and has the right sign.
1876 double d = val.toDouble();
1877 return ConvertExact(d, result);
1879 if (allowString && val.isString()) {
1880 // Allow conversion from base-10 or base-16 strings, provided the result
1881 // fits in IntegerType. (This allows an Int64 or UInt64 object to be passed
1882 // to the JS array element operator, which will automatically call
1883 // toString() on the object for us.)
1884 return StringToInteger(cx, val.toString(), result);
1886 if (val.isObject()) {
1887 // Allow conversion from an Int64 or UInt64 object directly.
1888 JSObject* obj = &val.toObject();
1890 if (UInt64::IsUInt64(obj)) {
1891 // Make sure the integer fits in IntegerType.
1892 uint64_t i = Int64Base::GetInt(obj);
1893 return ConvertExact(i, result);
1896 if (Int64::IsInt64(obj)) {
1897 // Make sure the integer fits in IntegerType.
1898 int64_t i = Int64Base::GetInt(obj);
1899 return ConvertExact(i, result);
1902 if (CDataFinalizer::IsCDataFinalizer(obj)) {
1903 RootedValue innerData(cx);
1904 if (!CDataFinalizer::GetValue(cx, obj, &innerData)) {
1905 return false; // Nothing to convert
1907 return jsvalToBigInteger(cx, innerData, allowString, result);
1911 return false;
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 jsvalToSize(JSContext* cx, jsval val, bool allowString, size_t* result)
1919 if (!jsvalToBigInteger(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;
1926 // Implicitly convert val to IntegerType, allowing int, double,
1927 // Int64, UInt64, and optionally a decimal or hexadecimal string argument.
1928 // (This is common code shared by jsvalToSize and the Int64/UInt64 constructors.)
1929 template<class IntegerType>
1930 static bool
1931 jsidToBigInteger(JSContext* cx,
1932 jsid val,
1933 bool allowString,
1934 IntegerType* result)
1936 JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact);
1938 if (JSID_IS_INT(val)) {
1939 // Make sure the integer fits in the alotted precision, and has the right
1940 // sign.
1941 int32_t i = JSID_TO_INT(val);
1942 return ConvertExact(i, result);
1944 if (allowString && JSID_IS_STRING(val)) {
1945 // Allow conversion from base-10 or base-16 strings, provided the result
1946 // fits in IntegerType. (This allows an Int64 or UInt64 object to be passed
1947 // to the JS array element operator, which will automatically call
1948 // toString() on the object for us.)
1949 return StringToInteger(cx, JSID_TO_STRING(val), result);
1951 return false;
1954 // Implicitly convert val to a size value, where the size value is represented
1955 // by size_t but must also fit in a double.
1956 static bool
1957 jsidToSize(JSContext* cx, jsid val, bool allowString, size_t* result)
1959 if (!jsidToBigInteger(cx, val, allowString, result))
1960 return false;
1962 // Also check that the result fits in a double.
1963 return Convert<size_t>(double(*result)) == *result;
1966 // Implicitly convert a size value to a jsval, ensuring that the size_t value
1967 // fits in a double.
1968 static bool
1969 SizeTojsval(JSContext* cx, size_t size, MutableHandleValue result)
1971 if (Convert<size_t>(double(size)) != size) {
1972 JS_ReportError(cx, "size overflow");
1973 return false;
1976 result.setNumber(double(size));
1977 return true;
1980 // Forcefully convert val to IntegerType when explicitly requested.
1981 template<class IntegerType>
1982 static bool
1983 jsvalToIntegerExplicit(jsval val, IntegerType* result)
1985 JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact);
1987 if (val.isDouble()) {
1988 // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
1989 double d = val.toDouble();
1990 *result = mozilla::IsFinite(d) ? IntegerType(d) : 0;
1991 return true;
1993 if (val.isObject()) {
1994 // Convert Int64 and UInt64 values by C-style cast.
1995 JSObject* obj = &val.toObject();
1996 if (Int64::IsInt64(obj)) {
1997 int64_t i = Int64Base::GetInt(obj);
1998 *result = IntegerType(i);
1999 return true;
2001 if (UInt64::IsUInt64(obj)) {
2002 uint64_t i = Int64Base::GetInt(obj);
2003 *result = IntegerType(i);
2004 return true;
2007 return false;
2010 // Forcefully convert val to a pointer value when explicitly requested.
2011 static bool
2012 jsvalToPtrExplicit(JSContext* cx, jsval val, uintptr_t* result)
2014 if (val.isInt32()) {
2015 // int32_t always fits in intptr_t. If the integer is negative, cast through
2016 // an intptr_t intermediate to sign-extend.
2017 int32_t i = val.toInt32();
2018 *result = i < 0 ? uintptr_t(intptr_t(i)) : uintptr_t(i);
2019 return true;
2021 if (val.isDouble()) {
2022 double d = val.toDouble();
2023 if (d < 0) {
2024 // Cast through an intptr_t intermediate to sign-extend.
2025 intptr_t i = Convert<intptr_t>(d);
2026 if (double(i) != d)
2027 return false;
2029 *result = uintptr_t(i);
2030 return true;
2033 // Don't silently lose bits here -- check that val really is an
2034 // integer value, and has the right sign.
2035 *result = Convert<uintptr_t>(d);
2036 return double(*result) == d;
2038 if (val.isObject()) {
2039 JSObject* obj = &val.toObject();
2040 if (Int64::IsInt64(obj)) {
2041 int64_t i = Int64Base::GetInt(obj);
2042 intptr_t p = intptr_t(i);
2044 // Make sure the integer fits in the alotted precision.
2045 if (int64_t(p) != i)
2046 return false;
2047 *result = uintptr_t(p);
2048 return true;
2051 if (UInt64::IsUInt64(obj)) {
2052 uint64_t i = Int64Base::GetInt(obj);
2054 // Make sure the integer fits in the alotted precision.
2055 *result = uintptr_t(i);
2056 return uint64_t(*result) == i;
2059 return false;
2062 template<class IntegerType, class CharType, size_t N, class AP>
2063 void
2064 IntegerToString(IntegerType i, int radix, Vector<CharType, N, AP>& result)
2066 JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact);
2068 // The buffer must be big enough for all the bits of IntegerType to fit,
2069 // in base-2, including '-'.
2070 CharType buffer[sizeof(IntegerType) * 8 + 1];
2071 CharType* end = buffer + sizeof(buffer) / sizeof(CharType);
2072 CharType* cp = end;
2074 // Build the string in reverse. We use multiplication and subtraction
2075 // instead of modulus because that's much faster.
2076 const bool isNegative = IsNegative(i);
2077 size_t sign = isNegative ? -1 : 1;
2078 do {
2079 IntegerType ii = i / IntegerType(radix);
2080 size_t index = sign * size_t(i - ii * IntegerType(radix));
2081 *--cp = "0123456789abcdefghijklmnopqrstuvwxyz"[index];
2082 i = ii;
2083 } while (i != 0);
2085 if (isNegative)
2086 *--cp = '-';
2088 MOZ_ASSERT(cp >= buffer);
2089 result.append(cp, end);
2092 template<class CharType>
2093 static size_t
2094 strnlen(const CharType* begin, size_t max)
2096 for (const CharType* s = begin; s != begin + max; ++s)
2097 if (*s == 0)
2098 return s - begin;
2100 return max;
2103 // Convert C binary value 'data' of CType 'typeObj' to a JS primitive, where
2104 // possible; otherwise, construct and return a CData object. The following
2105 // semantics apply when constructing a CData object for return:
2106 // * If 'wantPrimitive' is true, the caller indicates that 'result' must be
2107 // a JS primitive, and ConvertToJS will fail if 'result' would be a CData
2108 // object. Otherwise:
2109 // * If a CData object 'parentObj' is supplied, the new CData object is
2110 // dependent on the given parent and its buffer refers to a slice of the
2111 // parent's buffer.
2112 // * If 'parentObj' is null, the new CData object may or may not own its
2113 // resulting buffer depending on the 'ownResult' argument.
2114 static bool
2115 ConvertToJS(JSContext* cx,
2116 HandleObject typeObj,
2117 HandleObject parentObj,
2118 void* data,
2119 bool wantPrimitive,
2120 bool ownResult,
2121 MutableHandleValue result)
2123 MOZ_ASSERT(!parentObj || CData::IsCData(parentObj));
2124 MOZ_ASSERT(!parentObj || !ownResult);
2125 MOZ_ASSERT(!wantPrimitive || !ownResult);
2127 TypeCode typeCode = CType::GetTypeCode(typeObj);
2129 switch (typeCode) {
2130 case TYPE_void_t:
2131 result.setUndefined();
2132 break;
2133 case TYPE_bool:
2134 result.setBoolean(*static_cast<bool*>(data));
2135 break;
2136 #define INT_CASE(name, type, ffiType) \
2137 case TYPE_##name: { \
2138 type value = *static_cast<type*>(data); \
2139 if (sizeof(type) < 4) \
2140 result.setInt32(int32_t(value)); \
2141 else \
2142 result.setDouble(double(value)); \
2143 break; \
2145 CTYPES_FOR_EACH_INT_TYPE(INT_CASE)
2146 #undef INT_CASE
2147 #define WRAPPED_INT_CASE(name, type, ffiType) \
2148 case TYPE_##name: { \
2149 /* Return an Int64 or UInt64 object - do not convert to a JS number. */ \
2150 uint64_t value; \
2151 RootedObject proto(cx); \
2152 if (!NumericLimits<type>::is_signed) { \
2153 value = *static_cast<type*>(data); \
2154 /* Get ctypes.UInt64.prototype from ctypes.CType.prototype. */ \
2155 proto = CType::GetProtoFromType(cx, typeObj, SLOT_UINT64PROTO); \
2156 if (!proto) \
2157 return false; \
2158 } else { \
2159 value = int64_t(*static_cast<type*>(data)); \
2160 /* Get ctypes.Int64.prototype from ctypes.CType.prototype. */ \
2161 proto = CType::GetProtoFromType(cx, typeObj, SLOT_INT64PROTO); \
2162 if (!proto) \
2163 return false; \
2166 JSObject* obj = Int64Base::Construct(cx, proto, value, \
2167 !NumericLimits<type>::is_signed); \
2168 if (!obj) \
2169 return false; \
2170 result.setObject(*obj); \
2171 break; \
2173 CTYPES_FOR_EACH_WRAPPED_INT_TYPE(WRAPPED_INT_CASE)
2174 #undef WRAPPED_INT_CASE
2175 #define FLOAT_CASE(name, type, ffiType) \
2176 case TYPE_##name: { \
2177 type value = *static_cast<type*>(data); \
2178 result.setDouble(double(value)); \
2179 break; \
2181 CTYPES_FOR_EACH_FLOAT_TYPE(FLOAT_CASE)
2182 #undef FLOAT_CASE
2183 #define CHAR_CASE(name, type, ffiType) \
2184 case TYPE_##name: \
2185 /* Convert to an integer. We have no idea what character encoding to */ \
2186 /* use, if any. */ \
2187 result.setInt32(*static_cast<type*>(data)); \
2188 break;
2189 CTYPES_FOR_EACH_CHAR_TYPE(CHAR_CASE)
2190 #undef CHAR_CASE
2191 case TYPE_char16_t: {
2192 // Convert the char16_t to a 1-character string.
2193 JSString* str = JS_NewUCStringCopyN(cx, static_cast<char16_t*>(data), 1);
2194 if (!str)
2195 return false;
2197 result.setString(str);
2198 break;
2200 case TYPE_pointer:
2201 case TYPE_array:
2202 case TYPE_struct: {
2203 // We're about to create a new CData object to return. If the caller doesn't
2204 // want this, return early.
2205 if (wantPrimitive) {
2206 JS_ReportError(cx, "cannot convert to primitive value");
2207 return false;
2210 JSObject* obj = CData::Create(cx, typeObj, parentObj, data, ownResult);
2211 if (!obj)
2212 return false;
2214 result.setObject(*obj);
2215 break;
2217 case TYPE_function:
2218 MOZ_CRASH("cannot return a FunctionType");
2221 return true;
2224 // Determine if the contents of a typed array can be converted without
2225 // ambiguity to a C type. Elements of a Int8Array are converted to
2226 // ctypes.int8_t, UInt8Array to ctypes.uint8_t, etc.
2227 bool CanConvertTypedArrayItemTo(JSObject* baseType, JSObject* valObj, JSContext* cx) {
2228 TypeCode baseTypeCode = CType::GetTypeCode(baseType);
2229 if (baseTypeCode == TYPE_void_t || baseTypeCode == TYPE_char) {
2230 return true;
2232 TypeCode elementTypeCode;
2233 switch (JS_GetArrayBufferViewType(valObj)) {
2234 case Scalar::Int8:
2235 elementTypeCode = TYPE_int8_t;
2236 break;
2237 case Scalar::Uint8:
2238 case Scalar::Uint8Clamped:
2239 elementTypeCode = TYPE_uint8_t;
2240 break;
2241 case Scalar::Int16:
2242 elementTypeCode = TYPE_int16_t;
2243 break;
2244 case Scalar::Uint16:
2245 elementTypeCode = TYPE_uint16_t;
2246 break;
2247 case Scalar::Int32:
2248 elementTypeCode = TYPE_int32_t;
2249 break;
2250 case Scalar::Uint32:
2251 elementTypeCode = TYPE_uint32_t;
2252 break;
2253 case Scalar::Float32:
2254 elementTypeCode = TYPE_float32_t;
2255 break;
2256 case Scalar::Float64:
2257 elementTypeCode = TYPE_float64_t;
2258 break;
2259 default:
2260 return false;
2263 return elementTypeCode == baseTypeCode;
2266 // Implicitly convert jsval 'val' to a C binary representation of CType
2267 // 'targetType', storing the result in 'buffer'. Adequate space must be
2268 // provided in 'buffer' by the caller. This function generally does minimal
2269 // coercion between types. There are two cases in which this function is used:
2270 // 1) The target buffer is internal to a CData object; we simply write data
2271 // into it.
2272 // 2) We are converting an argument for an ffi call, in which case 'isArgument'
2273 // will be true. This allows us to handle a special case: if necessary,
2274 // we can autoconvert a JS string primitive to a pointer-to-character type.
2275 // In this case, ownership of the allocated string is handed off to the
2276 // caller; 'freePointer' will be set to indicate this.
2277 static bool
2278 ImplicitConvert(JSContext* cx,
2279 HandleValue val,
2280 JSObject* targetType_,
2281 void* buffer,
2282 bool isArgument,
2283 bool* freePointer)
2285 RootedObject targetType(cx, targetType_);
2286 MOZ_ASSERT(CType::IsSizeDefined(targetType));
2288 // First, check if val is either a CData object or a CDataFinalizer
2289 // of type targetType.
2290 JSObject* sourceData = nullptr;
2291 JSObject* sourceType = nullptr;
2292 RootedObject valObj(cx, nullptr);
2293 if (val.isObject()) {
2294 valObj = &val.toObject();
2295 if (CData::IsCData(valObj)) {
2296 sourceData = valObj;
2297 sourceType = CData::GetCType(sourceData);
2299 // If the types are equal, copy the buffer contained within the CData.
2300 // (Note that the buffers may overlap partially or completely.)
2301 if (CType::TypesEqual(sourceType, targetType)) {
2302 size_t size = CType::GetSize(sourceType);
2303 memmove(buffer, CData::GetData(sourceData), size);
2304 return true;
2306 } else if (CDataFinalizer::IsCDataFinalizer(valObj)) {
2307 sourceData = valObj;
2308 sourceType = CDataFinalizer::GetCType(cx, sourceData);
2310 CDataFinalizer::Private* p = (CDataFinalizer::Private*)
2311 JS_GetPrivate(sourceData);
2313 if (!p) {
2314 // We have called |dispose| or |forget| already.
2315 JS_ReportError(cx, "Attempting to convert an empty CDataFinalizer");
2316 return false;
2319 // If the types are equal, copy the buffer contained within the CData.
2320 if (CType::TypesEqual(sourceType, targetType)) {
2321 memmove(buffer, p->cargs, p->cargs_size);
2322 return true;
2327 TypeCode targetCode = CType::GetTypeCode(targetType);
2329 switch (targetCode) {
2330 case TYPE_bool: {
2331 // Do not implicitly lose bits, but allow the values 0, 1, and -0.
2332 // Programs can convert explicitly, if needed, using `Boolean(v)` or `!!v`.
2333 bool result;
2334 if (!jsvalToBool(cx, val, &result))
2335 return TypeError(cx, "boolean", val);
2336 *static_cast<bool*>(buffer) = result;
2337 break;
2339 #define CHAR16_CASE(name, type, ffiType) \
2340 case TYPE_##name: { \
2341 /* Convert from a 1-character string, regardless of encoding, */ \
2342 /* or from an integer, provided the result fits in 'type'. */ \
2343 type result; \
2344 if (val.isString()) { \
2345 JSString* str = val.toString(); \
2346 if (str->length() != 1) \
2347 return TypeError(cx, #name, val); \
2348 JSLinearString* linear = str->ensureLinear(cx); \
2349 if (!linear) \
2350 return false; \
2351 result = linear->latin1OrTwoByteChar(0); \
2352 } else if (!jsvalToInteger(cx, val, &result)) { \
2353 return TypeError(cx, #name, val); \
2355 *static_cast<type*>(buffer) = result; \
2356 break; \
2358 CTYPES_FOR_EACH_CHAR16_TYPE(CHAR16_CASE)
2359 #undef CHAR16_CASE
2360 #define INTEGRAL_CASE(name, type, ffiType) \
2361 case TYPE_##name: { \
2362 /* Do not implicitly lose bits. */ \
2363 type result; \
2364 if (!jsvalToInteger(cx, val, &result)) \
2365 return TypeError(cx, #name, val); \
2366 *static_cast<type*>(buffer) = result; \
2367 break; \
2369 CTYPES_FOR_EACH_INT_TYPE(INTEGRAL_CASE)
2370 CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGRAL_CASE)
2371 // It's hard to believe ctypes.char16_t("f") should work yet ctypes.char("f")
2372 // should not. Ditto for ctypes.{un,}signed_char. But this is how ctypes
2373 // has always worked, so preserve these semantics, and don't switch to an
2374 // algorithm similar to that in DEFINE_CHAR16_TYPE above, just yet.
2375 CTYPES_FOR_EACH_CHAR_TYPE(INTEGRAL_CASE)
2376 #undef INTEGRAL_CASE
2377 #define FLOAT_CASE(name, type, ffiType) \
2378 case TYPE_##name: { \
2379 type result; \
2380 if (!jsvalToFloat(cx, val, &result)) \
2381 return TypeError(cx, #name, val); \
2382 *static_cast<type*>(buffer) = result; \
2383 break; \
2385 CTYPES_FOR_EACH_FLOAT_TYPE(FLOAT_CASE)
2386 #undef FLOAT_CASE
2387 case TYPE_pointer: {
2388 if (val.isNull()) {
2389 // Convert to a null pointer.
2390 *static_cast<void**>(buffer) = nullptr;
2391 break;
2394 JS::Rooted<JSObject*> baseType(cx, PointerType::GetBaseType(targetType));
2395 if (sourceData) {
2396 // First, determine if the targetType is ctypes.void_t.ptr.
2397 TypeCode sourceCode = CType::GetTypeCode(sourceType);
2398 void* sourceBuffer = CData::GetData(sourceData);
2399 bool voidptrTarget = CType::GetTypeCode(baseType) == TYPE_void_t;
2401 if (sourceCode == TYPE_pointer && voidptrTarget) {
2402 // Autoconvert if targetType is ctypes.voidptr_t.
2403 *static_cast<void**>(buffer) = *static_cast<void**>(sourceBuffer);
2404 break;
2406 if (sourceCode == TYPE_array) {
2407 // Autoconvert an array to a ctypes.void_t.ptr or to
2408 // sourceType.elementType.ptr, just like C.
2409 JSObject* elementType = ArrayType::GetBaseType(sourceType);
2410 if (voidptrTarget || CType::TypesEqual(baseType, elementType)) {
2411 *static_cast<void**>(buffer) = sourceBuffer;
2412 break;
2416 } else if (isArgument && val.isString()) {
2417 // Convert the string for the ffi call. This requires allocating space
2418 // which the caller assumes ownership of.
2419 // TODO: Extend this so we can safely convert strings at other times also.
2420 JSString* sourceString = val.toString();
2421 size_t sourceLength = sourceString->length();
2422 JSLinearString* sourceLinear = sourceString->ensureLinear(cx);
2423 if (!sourceLinear)
2424 return false;
2426 switch (CType::GetTypeCode(baseType)) {
2427 case TYPE_char:
2428 case TYPE_signed_char:
2429 case TYPE_unsigned_char: {
2430 // Convert from UTF-16 to UTF-8.
2431 size_t nbytes = GetDeflatedUTF8StringLength(cx, sourceLinear);
2432 if (nbytes == (size_t) -1)
2433 return false;
2435 char** charBuffer = static_cast<char**>(buffer);
2436 *charBuffer = cx->pod_malloc<char>(nbytes + 1);
2437 if (!*charBuffer) {
2438 JS_ReportAllocationOverflow(cx);
2439 return false;
2442 ASSERT_OK(DeflateStringToUTF8Buffer(cx, sourceLinear, *charBuffer, &nbytes));
2443 (*charBuffer)[nbytes] = 0;
2444 *freePointer = true;
2445 break;
2447 case TYPE_char16_t: {
2448 // Copy the char16_t string data. (We could provide direct access to the
2449 // JSString's buffer, but this approach is safer if the caller happens
2450 // to modify the string.)
2451 char16_t** char16Buffer = static_cast<char16_t**>(buffer);
2452 *char16Buffer = cx->pod_malloc<char16_t>(sourceLength + 1);
2453 if (!*char16Buffer) {
2454 JS_ReportAllocationOverflow(cx);
2455 return false;
2458 *freePointer = true;
2459 if (sourceLinear->hasLatin1Chars()) {
2460 AutoCheckCannotGC nogc;
2461 CopyAndInflateChars(*char16Buffer, sourceLinear->latin1Chars(nogc), sourceLength);
2462 } else {
2463 AutoCheckCannotGC nogc;
2464 mozilla::PodCopy(*char16Buffer, sourceLinear->twoByteChars(nogc), sourceLength);
2466 (*char16Buffer)[sourceLength] = 0;
2467 break;
2469 default:
2470 return TypeError(cx, "string pointer", val);
2472 break;
2473 } else if (val.isObject() && JS_IsArrayBufferObject(valObj)) {
2474 // Convert ArrayBuffer to pointer without any copy. This is only valid
2475 // when converting an argument to a function call, as it is possible for
2476 // the pointer to be invalidated by anything that runs JS code. (It is
2477 // invalid to invoke JS code from a ctypes function call.)
2478 if (!isArgument) {
2479 return TypeError(cx, "arraybuffer pointer", val);
2481 void* ptr;
2483 JS::AutoCheckCannotGC nogc;
2484 ptr = JS_GetArrayBufferData(valObj, nogc);
2486 if (!ptr) {
2487 return TypeError(cx, "arraybuffer pointer", val);
2489 *static_cast<void**>(buffer) = ptr;
2490 break;
2491 } else if (val.isObject() && JS_IsArrayBufferViewObject(valObj)) {
2492 // Same as ArrayBuffer, above, though note that this will take the
2493 // offset of the view into account.
2494 if(!CanConvertTypedArrayItemTo(baseType, valObj, cx)) {
2495 return TypeError(cx, "typed array with the appropriate type", val);
2497 if (!isArgument) {
2498 return TypeError(cx, "typed array pointer", val);
2500 void* ptr;
2502 JS::AutoCheckCannotGC nogc;
2503 ptr = JS_GetArrayBufferViewData(valObj, nogc);
2505 if (!ptr) {
2506 return TypeError(cx, "typed array pointer", val);
2508 *static_cast<void**>(buffer) = ptr;
2509 break;
2511 return TypeError(cx, "pointer", val);
2513 case TYPE_array: {
2514 RootedObject baseType(cx, ArrayType::GetBaseType(targetType));
2515 size_t targetLength = ArrayType::GetLength(targetType);
2517 if (val.isString()) {
2518 JSString* sourceString = val.toString();
2519 size_t sourceLength = sourceString->length();
2520 JSLinearString* sourceLinear = sourceString->ensureLinear(cx);
2521 if (!sourceLinear)
2522 return false;
2524 switch (CType::GetTypeCode(baseType)) {
2525 case TYPE_char:
2526 case TYPE_signed_char:
2527 case TYPE_unsigned_char: {
2528 // Convert from UTF-16 or Latin1 to UTF-8.
2529 size_t nbytes =
2530 GetDeflatedUTF8StringLength(cx, sourceLinear);
2531 if (nbytes == (size_t) -1)
2532 return false;
2534 if (targetLength < nbytes) {
2535 JS_ReportError(cx, "ArrayType has insufficient length");
2536 return false;
2539 char* charBuffer = static_cast<char*>(buffer);
2540 ASSERT_OK(DeflateStringToUTF8Buffer(cx, sourceLinear, charBuffer,
2541 &nbytes));
2543 if (targetLength > nbytes)
2544 charBuffer[nbytes] = 0;
2546 break;
2548 case TYPE_char16_t: {
2549 // Copy the string data, char16_t for char16_t, including the terminator
2550 // if there's space.
2551 if (targetLength < sourceLength) {
2552 JS_ReportError(cx, "ArrayType has insufficient length");
2553 return false;
2556 char16_t* dest = static_cast<char16_t*>(buffer);
2557 if (sourceLinear->hasLatin1Chars()) {
2558 AutoCheckCannotGC nogc;
2559 CopyAndInflateChars(dest, sourceLinear->latin1Chars(nogc), sourceLength);
2560 } else {
2561 AutoCheckCannotGC nogc;
2562 mozilla::PodCopy(dest, sourceLinear->twoByteChars(nogc), sourceLength);
2565 if (targetLength > sourceLength)
2566 dest[sourceLength] = 0;
2568 break;
2570 default:
2571 return TypeError(cx, "array", val);
2574 } else if (val.isObject() && JS_IsArrayObject(cx, valObj)) {
2575 // Convert each element of the array by calling ImplicitConvert.
2576 uint32_t sourceLength;
2577 if (!JS_GetArrayLength(cx, valObj, &sourceLength) ||
2578 targetLength != size_t(sourceLength)) {
2579 JS_ReportError(cx, "ArrayType length does not match source array length");
2580 return false;
2583 // Convert into an intermediate, in case of failure.
2584 size_t elementSize = CType::GetSize(baseType);
2585 size_t arraySize = elementSize * targetLength;
2586 AutoPtr<char> intermediate(cx->pod_malloc<char>(arraySize));
2587 if (!intermediate) {
2588 JS_ReportAllocationOverflow(cx);
2589 return false;
2592 for (uint32_t i = 0; i < sourceLength; ++i) {
2593 RootedValue item(cx);
2594 if (!JS_GetElement(cx, valObj, i, &item))
2595 return false;
2597 char* data = intermediate.get() + elementSize * i;
2598 if (!ImplicitConvert(cx, item, baseType, data, false, nullptr))
2599 return false;
2602 memcpy(buffer, intermediate.get(), arraySize);
2604 } else if (val.isObject() && JS_IsArrayBufferObject(valObj)) {
2605 // Check that array is consistent with type, then
2606 // copy the array.
2607 uint32_t sourceLength = JS_GetArrayBufferByteLength(valObj);
2608 size_t elementSize = CType::GetSize(baseType);
2609 size_t arraySize = elementSize * targetLength;
2610 if (arraySize != size_t(sourceLength)) {
2611 JS_ReportError(cx, "ArrayType length does not match source ArrayBuffer length");
2612 return false;
2614 JS::AutoCheckCannotGC nogc;
2615 memcpy(buffer, JS_GetArrayBufferData(valObj, nogc), sourceLength);
2616 break;
2617 } else if (val.isObject() && JS_IsTypedArrayObject(valObj)) {
2618 // Check that array is consistent with type, then
2619 // copy the array.
2620 if(!CanConvertTypedArrayItemTo(baseType, valObj, cx)) {
2621 return TypeError(cx, "typed array with the appropriate type", val);
2624 uint32_t sourceLength = JS_GetTypedArrayByteLength(valObj);
2625 size_t elementSize = CType::GetSize(baseType);
2626 size_t arraySize = elementSize * targetLength;
2627 if (arraySize != size_t(sourceLength)) {
2628 JS_ReportError(cx, "typed array length does not match source TypedArray length");
2629 return false;
2631 JS::AutoCheckCannotGC nogc;
2632 memcpy(buffer, JS_GetArrayBufferViewData(valObj, nogc), sourceLength);
2633 break;
2634 } else {
2635 // Don't implicitly convert to string. Users can implicitly convert
2636 // with `String(x)` or `""+x`.
2637 return TypeError(cx, "array", val);
2639 break;
2641 case TYPE_struct: {
2642 if (val.isObject() && !sourceData) {
2643 // Enumerate the properties of the object; if they match the struct
2644 // specification, convert the fields.
2645 AutoIdArray props(cx, JS_Enumerate(cx, valObj));
2646 if (!props)
2647 return false;
2649 // Convert into an intermediate, in case of failure.
2650 size_t structSize = CType::GetSize(targetType);
2651 AutoPtr<char> intermediate(cx->pod_malloc<char>(structSize));
2652 if (!intermediate) {
2653 JS_ReportAllocationOverflow(cx);
2654 return false;
2657 const FieldInfoHash* fields = StructType::GetFieldInfo(targetType);
2658 if (props.length() != fields->count()) {
2659 JS_ReportError(cx, "missing fields");
2660 return false;
2663 RootedId id(cx);
2664 for (size_t i = 0; i < props.length(); ++i) {
2665 id = props[i];
2667 if (!JSID_IS_STRING(id)) {
2668 JS_ReportError(cx, "property name is not a string");
2669 return false;
2672 JSFlatString* name = JSID_TO_FLAT_STRING(id);
2673 const FieldInfo* field = StructType::LookupField(cx, targetType, name);
2674 if (!field)
2675 return false;
2677 RootedValue prop(cx);
2678 if (!JS_GetPropertyById(cx, valObj, id, &prop))
2679 return false;
2681 // Convert the field via ImplicitConvert().
2682 char* fieldData = intermediate.get() + field->mOffset;
2683 if (!ImplicitConvert(cx, prop, field->mType, fieldData, false, nullptr))
2684 return false;
2687 memcpy(buffer, intermediate.get(), structSize);
2688 break;
2691 return TypeError(cx, "struct", val);
2693 case TYPE_void_t:
2694 case TYPE_function:
2695 MOZ_CRASH("invalid type");
2698 return true;
2701 // Convert jsval 'val' to a C binary representation of CType 'targetType',
2702 // storing the result in 'buffer'. This function is more forceful than
2703 // ImplicitConvert.
2704 static bool
2705 ExplicitConvert(JSContext* cx, HandleValue val, HandleObject targetType, void* buffer)
2707 // If ImplicitConvert succeeds, use that result.
2708 if (ImplicitConvert(cx, val, targetType, buffer, false, nullptr))
2709 return true;
2711 // If ImplicitConvert failed, and there is no pending exception, then assume
2712 // hard failure (out of memory, or some other similarly serious condition).
2713 // We store any pending exception in case we need to re-throw it.
2714 RootedValue ex(cx);
2715 if (!JS_GetPendingException(cx, &ex))
2716 return false;
2718 // Otherwise, assume soft failure. Clear the pending exception so that we
2719 // can throw a different one as required.
2720 JS_ClearPendingException(cx);
2722 TypeCode type = CType::GetTypeCode(targetType);
2724 switch (type) {
2725 case TYPE_bool: {
2726 *static_cast<bool*>(buffer) = ToBoolean(val);
2727 break;
2729 #define INTEGRAL_CASE(name, type, ffiType) \
2730 case TYPE_##name: { \
2731 /* Convert numeric values with a C-style cast, and */ \
2732 /* allow conversion from a base-10 or base-16 string. */ \
2733 type result; \
2734 if (!jsvalToIntegerExplicit(val, &result) && \
2735 (!val.isString() || \
2736 !StringToInteger(cx, val.toString(), &result))) \
2737 return TypeError(cx, #name, val); \
2738 *static_cast<type*>(buffer) = result; \
2739 break; \
2741 CTYPES_FOR_EACH_INT_TYPE(INTEGRAL_CASE)
2742 CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGRAL_CASE)
2743 CTYPES_FOR_EACH_CHAR_TYPE(INTEGRAL_CASE)
2744 CTYPES_FOR_EACH_CHAR16_TYPE(INTEGRAL_CASE)
2745 #undef INTEGRAL_CASE
2746 case TYPE_pointer: {
2747 // Convert a number, Int64 object, or UInt64 object to a pointer.
2748 uintptr_t result;
2749 if (!jsvalToPtrExplicit(cx, val, &result))
2750 return TypeError(cx, "pointer", val);
2751 *static_cast<uintptr_t*>(buffer) = result;
2752 break;
2754 case TYPE_float32_t:
2755 case TYPE_float64_t:
2756 case TYPE_float:
2757 case TYPE_double:
2758 case TYPE_array:
2759 case TYPE_struct:
2760 // ImplicitConvert is sufficient. Re-throw the exception it generated.
2761 JS_SetPendingException(cx, ex);
2762 return false;
2763 case TYPE_void_t:
2764 case TYPE_function:
2765 MOZ_CRASH("invalid type");
2767 return true;
2770 // Given a CType 'typeObj', generate a string describing the C type declaration
2771 // corresponding to 'typeObj'. For instance, the CType constructed from
2772 // 'ctypes.int32_t.ptr.array(4).ptr.ptr' will result in the type string
2773 // 'int32_t*(**)[4]'.
2774 static JSString*
2775 BuildTypeName(JSContext* cx, JSObject* typeObj_)
2777 AutoString result;
2778 RootedObject typeObj(cx, typeObj_);
2780 // Walk the hierarchy of types, outermost to innermost, building up the type
2781 // string. This consists of the base type, which goes on the left.
2782 // Derived type modifiers (* and []) build from the inside outward, with
2783 // pointers on the left and arrays on the right. An excellent description
2784 // of the rules for building C type declarations can be found at:
2785 // http://unixwiz.net/techtips/reading-cdecl.html
2786 TypeCode prevGrouping = CType::GetTypeCode(typeObj), currentGrouping;
2787 while (1) {
2788 currentGrouping = CType::GetTypeCode(typeObj);
2789 switch (currentGrouping) {
2790 case TYPE_pointer: {
2791 // Pointer types go on the left.
2792 PrependString(result, "*");
2794 typeObj = PointerType::GetBaseType(typeObj);
2795 prevGrouping = currentGrouping;
2796 continue;
2798 case TYPE_array: {
2799 if (prevGrouping == TYPE_pointer) {
2800 // Outer type is pointer, inner type is array. Grouping is required.
2801 PrependString(result, "(");
2802 AppendString(result, ")");
2805 // Array types go on the right.
2806 AppendString(result, "[");
2807 size_t length;
2808 if (ArrayType::GetSafeLength(typeObj, &length))
2809 IntegerToString(length, 10, result);
2811 AppendString(result, "]");
2813 typeObj = ArrayType::GetBaseType(typeObj);
2814 prevGrouping = currentGrouping;
2815 continue;
2817 case TYPE_function: {
2818 FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
2820 // Add in the calling convention, if it's not cdecl.
2821 // There's no trailing or leading space needed here, as none of the
2822 // modifiers can produce a string beginning with an identifier ---
2823 // except for TYPE_function itself, which is fine because functions
2824 // can't return functions.
2825 ABICode abi = GetABICode(fninfo->mABI);
2826 if (abi == ABI_STDCALL)
2827 PrependString(result, "__stdcall");
2828 else if (abi == ABI_WINAPI)
2829 PrependString(result, "WINAPI");
2831 // Function application binds more tightly than dereferencing, so
2832 // wrap pointer types in parens. Functions can't return functions
2833 // (only pointers to them), and arrays can't hold functions
2834 // (similarly), so we don't need to address those cases.
2835 if (prevGrouping == TYPE_pointer) {
2836 PrependString(result, "(");
2837 AppendString(result, ")");
2840 // Argument list goes on the right.
2841 AppendString(result, "(");
2842 for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
2843 RootedObject argType(cx, fninfo->mArgTypes[i]);
2844 JSString* argName = CType::GetName(cx, argType);
2845 AppendString(result, argName);
2846 if (i != fninfo->mArgTypes.length() - 1 ||
2847 fninfo->mIsVariadic)
2848 AppendString(result, ", ");
2850 if (fninfo->mIsVariadic)
2851 AppendString(result, "...");
2852 AppendString(result, ")");
2854 // Set 'typeObj' to the return type, and let the loop process it.
2855 // 'prevGrouping' doesn't matter here, because functions cannot return
2856 // arrays -- thus the parenthetical rules don't get tickled.
2857 typeObj = fninfo->mReturnType;
2858 continue;
2860 default:
2861 // Either a basic or struct type. Use the type's name as the base type.
2862 break;
2864 break;
2867 // If prepending the base type name directly would splice two
2868 // identifiers, insert a space.
2869 if (('a' <= result[0] && result[0] <= 'z') ||
2870 ('A' <= result[0] && result[0] <= 'Z') ||
2871 (result[0] == '_'))
2872 PrependString(result, " ");
2874 // Stick the base type and derived type parts together.
2875 JSString* baseName = CType::GetName(cx, typeObj);
2876 PrependString(result, baseName);
2877 return NewUCString(cx, result);
2880 // Given a CType 'typeObj', generate a string 'result' such that 'eval(result)'
2881 // would construct the same CType. If 'makeShort' is true, assume that any
2882 // StructType 't' is bound to an in-scope variable of name 't.name', and use
2883 // that variable in place of generating a string to construct the type 't'.
2884 // (This means the type comparison function CType::TypesEqual will return true
2885 // when comparing the input and output of BuildTypeSource, since struct
2886 // equality is determined by strict JSObject pointer equality.)
2887 static void
2888 BuildTypeSource(JSContext* cx,
2889 JSObject* typeObj_,
2890 bool makeShort,
2891 AutoString& result)
2893 RootedObject typeObj(cx, typeObj_);
2895 // Walk the types, building up the toSource() string.
2896 switch (CType::GetTypeCode(typeObj)) {
2897 case TYPE_void_t:
2898 #define CASE_FOR_TYPE(name, type, ffiType) case TYPE_##name:
2899 CTYPES_FOR_EACH_TYPE(CASE_FOR_TYPE)
2900 #undef CASE_FOR_TYPE
2902 AppendString(result, "ctypes.");
2903 JSString* nameStr = CType::GetName(cx, typeObj);
2904 AppendString(result, nameStr);
2905 break;
2907 case TYPE_pointer: {
2908 RootedObject baseType(cx, PointerType::GetBaseType(typeObj));
2910 // Specialcase ctypes.voidptr_t.
2911 if (CType::GetTypeCode(baseType) == TYPE_void_t) {
2912 AppendString(result, "ctypes.voidptr_t");
2913 break;
2916 // Recursively build the source string, and append '.ptr'.
2917 BuildTypeSource(cx, baseType, makeShort, result);
2918 AppendString(result, ".ptr");
2919 break;
2921 case TYPE_function: {
2922 FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
2924 AppendString(result, "ctypes.FunctionType(");
2926 switch (GetABICode(fninfo->mABI)) {
2927 case ABI_DEFAULT:
2928 AppendString(result, "ctypes.default_abi, ");
2929 break;
2930 case ABI_STDCALL:
2931 AppendString(result, "ctypes.stdcall_abi, ");
2932 break;
2933 case ABI_WINAPI:
2934 AppendString(result, "ctypes.winapi_abi, ");
2935 break;
2936 case INVALID_ABI:
2937 MOZ_CRASH("invalid abi");
2940 // Recursively build the source string describing the function return and
2941 // argument types.
2942 BuildTypeSource(cx, fninfo->mReturnType, true, result);
2944 if (fninfo->mArgTypes.length() > 0) {
2945 AppendString(result, ", [");
2946 for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
2947 BuildTypeSource(cx, fninfo->mArgTypes[i], true, result);
2948 if (i != fninfo->mArgTypes.length() - 1 ||
2949 fninfo->mIsVariadic)
2950 AppendString(result, ", ");
2952 if (fninfo->mIsVariadic)
2953 AppendString(result, "\"...\"");
2954 AppendString(result, "]");
2957 AppendString(result, ")");
2958 break;
2960 case TYPE_array: {
2961 // Recursively build the source string, and append '.array(n)',
2962 // where n is the array length, or the empty string if the array length
2963 // is undefined.
2964 JSObject* baseType = ArrayType::GetBaseType(typeObj);
2965 BuildTypeSource(cx, baseType, makeShort, result);
2966 AppendString(result, ".array(");
2968 size_t length;
2969 if (ArrayType::GetSafeLength(typeObj, &length))
2970 IntegerToString(length, 10, result);
2972 AppendString(result, ")");
2973 break;
2975 case TYPE_struct: {
2976 JSString* name = CType::GetName(cx, typeObj);
2978 if (makeShort) {
2979 // Shorten the type declaration by assuming that StructType 't' is bound
2980 // to an in-scope variable of name 't.name'.
2981 AppendString(result, name);
2982 break;
2985 // Write the full struct declaration.
2986 AppendString(result, "ctypes.StructType(\"");
2987 AppendString(result, name);
2988 AppendString(result, "\"");
2990 // If it's an opaque struct, we're done.
2991 if (!CType::IsSizeDefined(typeObj)) {
2992 AppendString(result, ")");
2993 break;
2996 AppendString(result, ", [");
2998 const FieldInfoHash* fields = StructType::GetFieldInfo(typeObj);
2999 size_t length = fields->count();
3000 Array<const FieldInfoHash::Entry*, 64> fieldsArray;
3001 if (!fieldsArray.resize(length))
3002 break;
3004 for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront())
3005 fieldsArray[r.front().value().mIndex] = &r.front();
3007 for (size_t i = 0; i < length; ++i) {
3008 const FieldInfoHash::Entry* entry = fieldsArray[i];
3009 AppendString(result, "{ \"");
3010 AppendString(result, entry->key());
3011 AppendString(result, "\": ");
3012 BuildTypeSource(cx, entry->value().mType, true, result);
3013 AppendString(result, " }");
3014 if (i != length - 1)
3015 AppendString(result, ", ");
3018 AppendString(result, "])");
3019 break;
3024 // Given a CData object of CType 'typeObj' with binary value 'data', generate a
3025 // string 'result' such that 'eval(result)' would construct a CData object with
3026 // the same CType and containing the same binary value. This assumes that any
3027 // StructType 't' is bound to an in-scope variable of name 't.name'. (This means
3028 // the type comparison function CType::TypesEqual will return true when
3029 // comparing the types, since struct equality is determined by strict JSObject
3030 // pointer equality.) Further, if 'isImplicit' is true, ensure that the
3031 // resulting string can ImplicitConvert successfully if passed to another data
3032 // constructor. (This is important when called recursively, since fields of
3033 // structs and arrays are converted with ImplicitConvert.)
3034 static bool
3035 BuildDataSource(JSContext* cx,
3036 HandleObject typeObj,
3037 void* data,
3038 bool isImplicit,
3039 AutoString& result)
3041 TypeCode type = CType::GetTypeCode(typeObj);
3042 switch (type) {
3043 case TYPE_bool:
3044 if (*static_cast<bool*>(data))
3045 AppendString(result, "true");
3046 else
3047 AppendString(result, "false");
3048 break;
3049 #define INTEGRAL_CASE(name, type, ffiType) \
3050 case TYPE_##name: \
3051 /* Serialize as a primitive decimal integer. */ \
3052 IntegerToString(*static_cast<type*>(data), 10, result); \
3053 break;
3054 CTYPES_FOR_EACH_INT_TYPE(INTEGRAL_CASE)
3055 #undef INTEGRAL_CASE
3056 #define WRAPPED_INT_CASE(name, type, ffiType) \
3057 case TYPE_##name: \
3058 /* Serialize as a wrapped decimal integer. */ \
3059 if (!NumericLimits<type>::is_signed) \
3060 AppendString(result, "ctypes.UInt64(\""); \
3061 else \
3062 AppendString(result, "ctypes.Int64(\""); \
3064 IntegerToString(*static_cast<type*>(data), 10, result); \
3065 AppendString(result, "\")"); \
3066 break;
3067 CTYPES_FOR_EACH_WRAPPED_INT_TYPE(WRAPPED_INT_CASE)
3068 #undef WRAPPED_INT_CASE
3069 #define FLOAT_CASE(name, type, ffiType) \
3070 case TYPE_##name: { \
3071 /* Serialize as a primitive double. */ \
3072 double fp = *static_cast<type*>(data); \
3073 ToCStringBuf cbuf; \
3074 char* str = NumberToCString(cx, &cbuf, fp); \
3075 if (!str) { \
3076 JS_ReportOutOfMemory(cx); \
3077 return false; \
3080 result.append(str, strlen(str)); \
3081 break; \
3083 CTYPES_FOR_EACH_FLOAT_TYPE(FLOAT_CASE)
3084 #undef FLOAT_CASE
3085 #define CHAR_CASE(name, type, ffiType) \
3086 case TYPE_##name: \
3087 /* Serialize as an integer. */ \
3088 IntegerToString(*static_cast<type*>(data), 10, result); \
3089 break;
3090 CTYPES_FOR_EACH_CHAR_TYPE(CHAR_CASE)
3091 #undef CHAR_CASE
3092 case TYPE_char16_t: {
3093 // Serialize as a 1-character JS string.
3094 JSString* str = JS_NewUCStringCopyN(cx, static_cast<char16_t*>(data), 1);
3095 if (!str)
3096 return false;
3098 // Escape characters, and quote as necessary.
3099 RootedValue valStr(cx, StringValue(str));
3100 JSString* src = JS_ValueToSource(cx, valStr);
3101 if (!src)
3102 return false;
3104 AppendString(result, src);
3105 break;
3107 case TYPE_pointer:
3108 case TYPE_function: {
3109 if (isImplicit) {
3110 // The result must be able to ImplicitConvert successfully.
3111 // Wrap in a type constructor, then serialize for ExplicitConvert.
3112 BuildTypeSource(cx, typeObj, true, result);
3113 AppendString(result, "(");
3116 // Serialize the pointer value as a wrapped hexadecimal integer.
3117 uintptr_t ptr = *static_cast<uintptr_t*>(data);
3118 AppendString(result, "ctypes.UInt64(\"0x");
3119 IntegerToString(ptr, 16, result);
3120 AppendString(result, "\")");
3122 if (isImplicit)
3123 AppendString(result, ")");
3125 break;
3127 case TYPE_array: {
3128 // Serialize each element of the array recursively. Each element must
3129 // be able to ImplicitConvert successfully.
3130 RootedObject baseType(cx, ArrayType::GetBaseType(typeObj));
3131 AppendString(result, "[");
3133 size_t length = ArrayType::GetLength(typeObj);
3134 size_t elementSize = CType::GetSize(baseType);
3135 for (size_t i = 0; i < length; ++i) {
3136 char* element = static_cast<char*>(data) + elementSize * i;
3137 if (!BuildDataSource(cx, baseType, element, true, result))
3138 return false;
3140 if (i + 1 < length)
3141 AppendString(result, ", ");
3143 AppendString(result, "]");
3144 break;
3146 case TYPE_struct: {
3147 if (isImplicit) {
3148 // The result must be able to ImplicitConvert successfully.
3149 // Serialize the data as an object with properties, rather than
3150 // a sequence of arguments to the StructType constructor.
3151 AppendString(result, "{");
3154 // Serialize each field of the struct recursively. Each field must
3155 // be able to ImplicitConvert successfully.
3156 const FieldInfoHash* fields = StructType::GetFieldInfo(typeObj);
3157 size_t length = fields->count();
3158 Array<const FieldInfoHash::Entry*, 64> fieldsArray;
3159 if (!fieldsArray.resize(length))
3160 return false;
3162 for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront())
3163 fieldsArray[r.front().value().mIndex] = &r.front();
3165 for (size_t i = 0; i < length; ++i) {
3166 const FieldInfoHash::Entry* entry = fieldsArray[i];
3168 if (isImplicit) {
3169 AppendString(result, "\"");
3170 AppendString(result, entry->key());
3171 AppendString(result, "\": ");
3174 char* fieldData = static_cast<char*>(data) + entry->value().mOffset;
3175 RootedObject entryType(cx, entry->value().mType);
3176 if (!BuildDataSource(cx, entryType, fieldData, true, result))
3177 return false;
3179 if (i + 1 != length)
3180 AppendString(result, ", ");
3183 if (isImplicit)
3184 AppendString(result, "}");
3186 break;
3188 case TYPE_void_t:
3189 MOZ_CRASH("invalid type");
3192 return true;
3195 /*******************************************************************************
3196 ** JSAPI callback function implementations
3197 *******************************************************************************/
3199 bool
3200 ConstructAbstract(JSContext* cx,
3201 unsigned argc,
3202 jsval* vp)
3204 // Calling an abstract base class constructor is disallowed.
3205 JS_ReportError(cx, "cannot construct from abstract type");
3206 return false;
3209 /*******************************************************************************
3210 ** CType implementation
3211 *******************************************************************************/
3213 bool
3214 CType::ConstructData(JSContext* cx,
3215 unsigned argc,
3216 jsval* vp)
3218 CallArgs args = CallArgsFromVp(argc, vp);
3219 // get the callee object...
3220 RootedObject obj(cx, &args.callee());
3221 if (!CType::IsCType(obj)) {
3222 JS_ReportError(cx, "not a CType");
3223 return false;
3226 // How we construct the CData object depends on what type we represent.
3227 // An instance 'd' of a CData object of type 't' has:
3228 // * [[Class]] "CData"
3229 // * __proto__ === t.prototype
3230 switch (GetTypeCode(obj)) {
3231 case TYPE_void_t:
3232 JS_ReportError(cx, "cannot construct from void_t");
3233 return false;
3234 case TYPE_function:
3235 JS_ReportError(cx, "cannot construct from FunctionType; use FunctionType.ptr instead");
3236 return false;
3237 case TYPE_pointer:
3238 return PointerType::ConstructData(cx, obj, args);
3239 case TYPE_array:
3240 return ArrayType::ConstructData(cx, obj, args);
3241 case TYPE_struct:
3242 return StructType::ConstructData(cx, obj, args);
3243 default:
3244 return ConstructBasic(cx, obj, args);
3248 bool
3249 CType::ConstructBasic(JSContext* cx,
3250 HandleObject obj,
3251 const CallArgs& args)
3253 if (args.length() > 1) {
3254 JS_ReportError(cx, "CType constructor takes zero or one argument");
3255 return false;
3258 // construct a CData object
3259 RootedObject result(cx, CData::Create(cx, obj, NullPtr(), nullptr, true));
3260 if (!result)
3261 return false;
3263 if (args.length() == 1) {
3264 if (!ExplicitConvert(cx, args[0], obj, CData::GetData(result)))
3265 return false;
3268 args.rval().setObject(*result);
3269 return true;
3272 JSObject*
3273 CType::Create(JSContext* cx,
3274 HandleObject typeProto,
3275 HandleObject dataProto,
3276 TypeCode type,
3277 JSString* name_,
3278 jsval size_,
3279 jsval align_,
3280 ffi_type* ffiType)
3282 RootedString name(cx, name_);
3283 RootedValue size(cx, size_);
3284 RootedValue align(cx, align_);
3285 RootedObject parent(cx, JS_GetParent(typeProto));
3286 MOZ_ASSERT(parent);
3288 // Create a CType object with the properties and slots common to all CTypes.
3289 // Each type object 't' has:
3290 // * [[Class]] "CType"
3291 // * __proto__ === 'typeProto'; one of ctypes.{CType,PointerType,ArrayType,
3292 // StructType}.prototype
3293 // * A constructor which creates and returns a CData object, containing
3294 // binary data of the given type.
3295 // * 'prototype' property:
3296 // * [[Class]] "CDataProto"
3297 // * __proto__ === 'dataProto'; an object containing properties and
3298 // functions common to all CData objects of types derived from
3299 // 'typeProto'. (For instance, this could be ctypes.CData.prototype
3300 // for simple types, or something representing structs for StructTypes.)
3301 // * 'constructor' property === 't'
3302 // * Additional properties specified by 'ps', as appropriate for the
3303 // specific type instance 't'.
3304 RootedObject typeObj(cx, JS_NewObject(cx, &sCTypeClass, typeProto, parent));
3305 if (!typeObj)
3306 return nullptr;
3308 // Set up the reserved slots.
3309 JS_SetReservedSlot(typeObj, SLOT_TYPECODE, INT_TO_JSVAL(type));
3310 if (ffiType)
3311 JS_SetReservedSlot(typeObj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(ffiType));
3312 if (name)
3313 JS_SetReservedSlot(typeObj, SLOT_NAME, STRING_TO_JSVAL(name));
3314 JS_SetReservedSlot(typeObj, SLOT_SIZE, size);
3315 JS_SetReservedSlot(typeObj, SLOT_ALIGN, align);
3317 if (dataProto) {
3318 // Set up the 'prototype' and 'prototype.constructor' properties.
3319 RootedObject prototype(cx, JS_NewObject(cx, &sCDataProtoClass, dataProto, parent));
3320 if (!prototype)
3321 return nullptr;
3323 if (!JS_DefineProperty(cx, prototype, "constructor", typeObj,
3324 JSPROP_READONLY | JSPROP_PERMANENT))
3325 return nullptr;
3327 // Set the 'prototype' object.
3328 //if (!JS_FreezeObject(cx, prototype)) // XXX fixme - see bug 541212!
3329 // return nullptr;
3330 JS_SetReservedSlot(typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype));
3333 if (!JS_FreezeObject(cx, typeObj))
3334 return nullptr;
3336 // Assert a sanity check on size and alignment: size % alignment should always
3337 // be zero.
3338 MOZ_ASSERT_IF(IsSizeDefined(typeObj),
3339 GetSize(typeObj) % GetAlignment(typeObj) == 0);
3341 return typeObj;
3344 JSObject*
3345 CType::DefineBuiltin(JSContext* cx,
3346 JSObject* parent_,
3347 const char* propName,
3348 JSObject* typeProto_,
3349 JSObject* dataProto_,
3350 const char* name,
3351 TypeCode type,
3352 jsval size_,
3353 jsval align_,
3354 ffi_type* ffiType)
3356 RootedObject parent(cx, parent_);
3357 RootedObject typeProto(cx, typeProto_);
3358 RootedObject dataProto(cx, dataProto_);
3359 RootedValue size(cx, size_);
3360 RootedValue align(cx, align_);
3362 RootedString nameStr(cx, JS_NewStringCopyZ(cx, name));
3363 if (!nameStr)
3364 return nullptr;
3366 // Create a new CType object with the common properties and slots.
3367 RootedObject typeObj(cx, Create(cx, typeProto, dataProto, type, nameStr, size, align, ffiType));
3368 if (!typeObj)
3369 return nullptr;
3371 // Define the CType as a 'propName' property on 'parent'.
3372 if (!JS_DefineProperty(cx, parent, propName, typeObj,
3373 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
3374 return nullptr;
3376 return typeObj;
3379 void
3380 CType::Finalize(JSFreeOp* fop, JSObject* obj)
3382 // Make sure our TypeCode slot is legit. If it's not, bail.
3383 jsval slot = JS_GetReservedSlot(obj, SLOT_TYPECODE);
3384 if (slot.isUndefined())
3385 return;
3387 // The contents of our slots depends on what kind of type we are.
3388 switch (TypeCode(slot.toInt32())) {
3389 case TYPE_function: {
3390 // Free the FunctionInfo.
3391 slot = JS_GetReservedSlot(obj, SLOT_FNINFO);
3392 if (!slot.isUndefined())
3393 FreeOp::get(fop)->delete_(static_cast<FunctionInfo*>(slot.toPrivate()));
3394 break;
3397 case TYPE_struct: {
3398 // Free the FieldInfoHash table.
3399 slot = JS_GetReservedSlot(obj, SLOT_FIELDINFO);
3400 if (!slot.isUndefined()) {
3401 void* info = slot.toPrivate();
3402 FreeOp::get(fop)->delete_(static_cast<FieldInfoHash*>(info));
3406 // Fall through.
3407 case TYPE_array: {
3408 // Free the ffi_type info.
3409 slot = JS_GetReservedSlot(obj, SLOT_FFITYPE);
3410 if (!slot.isUndefined()) {
3411 ffi_type* ffiType = static_cast<ffi_type*>(slot.toPrivate());
3412 FreeOp::get(fop)->free_(ffiType->elements);
3413 FreeOp::get(fop)->delete_(ffiType);
3416 break;
3418 default:
3419 // Nothing to do here.
3420 break;
3424 void
3425 CType::Trace(JSTracer* trc, JSObject* obj)
3427 // Make sure our TypeCode slot is legit. If it's not, bail.
3428 jsval slot = obj->as<NativeObject>().getSlot(SLOT_TYPECODE);
3429 if (slot.isUndefined())
3430 return;
3432 // The contents of our slots depends on what kind of type we are.
3433 switch (TypeCode(slot.toInt32())) {
3434 case TYPE_struct: {
3435 slot = obj->as<NativeObject>().getReservedSlot(SLOT_FIELDINFO);
3436 if (slot.isUndefined())
3437 return;
3439 FieldInfoHash* fields = static_cast<FieldInfoHash*>(slot.toPrivate());
3440 for (FieldInfoHash::Enum e(*fields); !e.empty(); e.popFront()) {
3441 JSString* key = e.front().key();
3442 JS_CallUnbarrieredStringTracer(trc, &key, "fieldName");
3443 if (key != e.front().key())
3444 e.rekeyFront(JS_ASSERT_STRING_IS_FLAT(key));
3445 JS_CallObjectTracer(trc, &e.front().value().mType, "fieldType");
3448 break;
3450 case TYPE_function: {
3451 // Check if we have a FunctionInfo.
3452 slot = obj->as<NativeObject>().getReservedSlot(SLOT_FNINFO);
3453 if (slot.isUndefined())
3454 return;
3456 FunctionInfo* fninfo = static_cast<FunctionInfo*>(slot.toPrivate());
3457 MOZ_ASSERT(fninfo);
3459 // Identify our objects to the tracer.
3460 JS_CallObjectTracer(trc, &fninfo->mABI, "abi");
3461 JS_CallObjectTracer(trc, &fninfo->mReturnType, "returnType");
3462 for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i)
3463 JS_CallObjectTracer(trc, &fninfo->mArgTypes[i], "argType");
3465 break;
3467 default:
3468 // Nothing to do here.
3469 break;
3473 bool
3474 CType::IsCType(JSObject* obj)
3476 return JS_GetClass(obj) == &sCTypeClass;
3479 bool
3480 CType::IsCTypeProto(JSObject* obj)
3482 return JS_GetClass(obj) == &sCTypeProtoClass;
3485 TypeCode
3486 CType::GetTypeCode(JSObject* typeObj)
3488 MOZ_ASSERT(IsCType(typeObj));
3490 jsval result = JS_GetReservedSlot(typeObj, SLOT_TYPECODE);
3491 return TypeCode(result.toInt32());
3494 bool
3495 CType::TypesEqual(JSObject* t1, JSObject* t2)
3497 MOZ_ASSERT(IsCType(t1) && IsCType(t2));
3499 // Fast path: check for object equality.
3500 if (t1 == t2)
3501 return true;
3503 // First, perform shallow comparison.
3504 TypeCode c1 = GetTypeCode(t1);
3505 TypeCode c2 = GetTypeCode(t2);
3506 if (c1 != c2)
3507 return false;
3509 // Determine whether the types require shallow or deep comparison.
3510 switch (c1) {
3511 case TYPE_pointer: {
3512 // Compare base types.
3513 JSObject* b1 = PointerType::GetBaseType(t1);
3514 JSObject* b2 = PointerType::GetBaseType(t2);
3515 return TypesEqual(b1, b2);
3517 case TYPE_function: {
3518 FunctionInfo* f1 = FunctionType::GetFunctionInfo(t1);
3519 FunctionInfo* f2 = FunctionType::GetFunctionInfo(t2);
3521 // Compare abi, return type, and argument types.
3522 if (f1->mABI != f2->mABI)
3523 return false;
3525 if (!TypesEqual(f1->mReturnType, f2->mReturnType))
3526 return false;
3528 if (f1->mArgTypes.length() != f2->mArgTypes.length())
3529 return false;
3531 if (f1->mIsVariadic != f2->mIsVariadic)
3532 return false;
3534 for (size_t i = 0; i < f1->mArgTypes.length(); ++i) {
3535 if (!TypesEqual(f1->mArgTypes[i], f2->mArgTypes[i]))
3536 return false;
3539 return true;
3541 case TYPE_array: {
3542 // Compare length, then base types.
3543 // An undefined length array matches other undefined length arrays.
3544 size_t s1 = 0, s2 = 0;
3545 bool d1 = ArrayType::GetSafeLength(t1, &s1);
3546 bool d2 = ArrayType::GetSafeLength(t2, &s2);
3547 if (d1 != d2 || (d1 && s1 != s2))
3548 return false;
3550 JSObject* b1 = ArrayType::GetBaseType(t1);
3551 JSObject* b2 = ArrayType::GetBaseType(t2);
3552 return TypesEqual(b1, b2);
3554 case TYPE_struct:
3555 // Require exact type object equality.
3556 return false;
3557 default:
3558 // Shallow comparison is sufficient.
3559 return true;
3563 bool
3564 CType::GetSafeSize(JSObject* obj, size_t* result)
3566 MOZ_ASSERT(CType::IsCType(obj));
3568 jsval size = JS_GetReservedSlot(obj, SLOT_SIZE);
3570 // The "size" property can be an int, a double, or JSVAL_VOID
3571 // (for arrays of undefined length), and must always fit in a size_t.
3572 if (size.isInt32()) {
3573 *result = size.toInt32();
3574 return true;
3576 if (size.isDouble()) {
3577 *result = Convert<size_t>(size.toDouble());
3578 return true;
3581 MOZ_ASSERT(size.isUndefined());
3582 return false;
3585 size_t
3586 CType::GetSize(JSObject* obj)
3588 MOZ_ASSERT(CType::IsCType(obj));
3590 jsval size = JS_GetReservedSlot(obj, SLOT_SIZE);
3592 MOZ_ASSERT(!size.isUndefined());
3594 // The "size" property can be an int, a double, or JSVAL_VOID
3595 // (for arrays of undefined length), and must always fit in a size_t.
3596 // For callers who know it can never be JSVAL_VOID, return a size_t directly.
3597 if (size.isInt32())
3598 return size.toInt32();
3599 return Convert<size_t>(size.toDouble());
3602 bool
3603 CType::IsSizeDefined(JSObject* obj)
3605 MOZ_ASSERT(CType::IsCType(obj));
3607 jsval size = JS_GetReservedSlot(obj, SLOT_SIZE);
3609 // The "size" property can be an int, a double, or JSVAL_VOID
3610 // (for arrays of undefined length), and must always fit in a size_t.
3611 MOZ_ASSERT(size.isInt32() || size.isDouble() || size.isUndefined());
3612 return !size.isUndefined();
3615 size_t
3616 CType::GetAlignment(JSObject* obj)
3618 MOZ_ASSERT(CType::IsCType(obj));
3620 jsval slot = JS_GetReservedSlot(obj, SLOT_ALIGN);
3621 return static_cast<size_t>(slot.toInt32());
3624 ffi_type*
3625 CType::GetFFIType(JSContext* cx, JSObject* obj)
3627 MOZ_ASSERT(CType::IsCType(obj));
3629 jsval slot = JS_GetReservedSlot(obj, SLOT_FFITYPE);
3631 if (!slot.isUndefined()) {
3632 return static_cast<ffi_type*>(slot.toPrivate());
3635 AutoPtr<ffi_type> result;
3636 switch (CType::GetTypeCode(obj)) {
3637 case TYPE_array:
3638 result = ArrayType::BuildFFIType(cx, obj);
3639 break;
3641 case TYPE_struct:
3642 result = StructType::BuildFFIType(cx, obj);
3643 break;
3645 default:
3646 MOZ_CRASH("simple types must have an ffi_type");
3649 if (!result)
3650 return nullptr;
3651 JS_SetReservedSlot(obj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(result.get()));
3652 return result.forget();
3655 JSString*
3656 CType::GetName(JSContext* cx, HandleObject obj)
3658 MOZ_ASSERT(CType::IsCType(obj));
3660 jsval string = JS_GetReservedSlot(obj, SLOT_NAME);
3661 if (!string.isUndefined())
3662 return string.toString();
3664 // Build the type name lazily.
3665 JSString* name = BuildTypeName(cx, obj);
3666 if (!name)
3667 return nullptr;
3668 JS_SetReservedSlot(obj, SLOT_NAME, STRING_TO_JSVAL(name));
3669 return name;
3672 JSObject*
3673 CType::GetProtoFromCtor(JSObject* obj, CTypeProtoSlot slot)
3675 // Get ctypes.{Pointer,Array,Struct}Type.prototype from a reserved slot
3676 // on the type constructor.
3677 jsval protoslot = js::GetFunctionNativeReserved(obj, SLOT_FN_CTORPROTO);
3678 JSObject* proto = &protoslot.toObject();
3679 MOZ_ASSERT(proto);
3680 MOZ_ASSERT(CType::IsCTypeProto(proto));
3682 // Get the desired prototype.
3683 jsval result = JS_GetReservedSlot(proto, slot);
3684 return &result.toObject();
3687 JSObject*
3688 CType::GetProtoFromType(JSContext* cx, JSObject* objArg, CTypeProtoSlot slot)
3690 MOZ_ASSERT(IsCType(objArg));
3691 RootedObject obj(cx, objArg);
3693 // Get the prototype of the type object.
3694 RootedObject proto(cx);
3695 if (!JS_GetPrototype(cx, obj, &proto))
3696 return nullptr;
3697 MOZ_ASSERT(proto);
3698 MOZ_ASSERT(CType::IsCTypeProto(proto));
3700 // Get the requested ctypes.{Pointer,Array,Struct,Function}Type.prototype.
3701 jsval result = JS_GetReservedSlot(proto, slot);
3702 MOZ_ASSERT(result.isObject());
3703 return &result.toObject();
3706 bool
3707 CType::IsCTypeOrProto(HandleValue v)
3709 if (!v.isObject())
3710 return false;
3711 JSObject* obj = &v.toObject();
3712 return CType::IsCType(obj) || CType::IsCTypeProto(obj);
3715 bool
3716 CType::PrototypeGetter(JSContext* cx, JS::CallArgs args)
3718 RootedObject obj(cx, &args.thisv().toObject());
3719 unsigned slot = CType::IsCTypeProto(obj) ? (unsigned) SLOT_OURDATAPROTO
3720 : (unsigned) SLOT_PROTO;
3721 args.rval().set(JS_GetReservedSlot(obj, slot));
3722 MOZ_ASSERT(args.rval().isObject() || args.rval().isUndefined());
3723 return true;
3726 bool
3727 CType::IsCType(HandleValue v)
3729 return v.isObject() && CType::IsCType(&v.toObject());
3732 bool
3733 CType::NameGetter(JSContext* cx, JS::CallArgs args)
3735 RootedObject obj(cx, &args.thisv().toObject());
3736 JSString* name = CType::GetName(cx, obj);
3737 if (!name)
3738 return false;
3740 args.rval().setString(name);
3741 return true;
3744 bool
3745 CType::SizeGetter(JSContext* cx, JS::CallArgs args)
3747 RootedObject obj(cx, &args.thisv().toObject());
3748 args.rval().set(JS_GetReservedSlot(obj, SLOT_SIZE));
3749 MOZ_ASSERT(args.rval().isNumber() || args.rval().isUndefined());
3750 return true;
3753 bool
3754 CType::PtrGetter(JSContext* cx, JS::CallArgs args)
3756 RootedObject obj(cx, &args.thisv().toObject());
3757 JSObject* pointerType = PointerType::CreateInternal(cx, obj);
3758 if (!pointerType)
3759 return false;
3761 args.rval().setObject(*pointerType);
3762 return true;
3765 bool
3766 CType::CreateArray(JSContext* cx, unsigned argc, jsval* vp)
3768 CallArgs args = CallArgsFromVp(argc, vp);
3769 RootedObject baseType(cx, JS_THIS_OBJECT(cx, vp));
3770 if (!baseType)
3771 return false;
3772 if (!CType::IsCType(baseType)) {
3773 JS_ReportError(cx, "not a CType");
3774 return false;
3777 // Construct and return a new ArrayType object.
3778 if (args.length() > 1) {
3779 JS_ReportError(cx, "array takes zero or one argument");
3780 return false;
3783 // Convert the length argument to a size_t.
3784 size_t length = 0;
3785 if (args.length() == 1 && !jsvalToSize(cx, args[0], false, &length)) {
3786 JS_ReportError(cx, "argument must be a nonnegative integer");
3787 return false;
3790 JSObject* result = ArrayType::CreateInternal(cx, baseType, length, args.length() == 1);
3791 if (!result)
3792 return false;
3794 args.rval().setObject(*result);
3795 return true;
3798 bool
3799 CType::ToString(JSContext* cx, unsigned argc, jsval* vp)
3801 CallArgs args = CallArgsFromVp(argc, vp);
3802 RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
3803 if (!obj)
3804 return false;
3805 if (!CType::IsCType(obj) && !CType::IsCTypeProto(obj)) {
3806 JS_ReportError(cx, "not a CType");
3807 return false;
3810 // Create the appropriate string depending on whether we're sCTypeClass or
3811 // sCTypeProtoClass.
3812 JSString* result;
3813 if (CType::IsCType(obj)) {
3814 AutoString type;
3815 AppendString(type, "type ");
3816 AppendString(type, GetName(cx, obj));
3817 result = NewUCString(cx, type);
3819 else {
3820 result = JS_NewStringCopyZ(cx, "[CType proto object]");
3822 if (!result)
3823 return false;
3825 args.rval().setString(result);
3826 return true;
3829 bool
3830 CType::ToSource(JSContext* cx, unsigned argc, jsval* vp)
3832 CallArgs args = CallArgsFromVp(argc, vp);
3833 JSObject* obj = JS_THIS_OBJECT(cx, vp);
3834 if (!obj)
3835 return false;
3836 if (!CType::IsCType(obj) && !CType::IsCTypeProto(obj))
3838 JS_ReportError(cx, "not a CType");
3839 return false;
3842 // Create the appropriate string depending on whether we're sCTypeClass or
3843 // sCTypeProtoClass.
3844 JSString* result;
3845 if (CType::IsCType(obj)) {
3846 AutoString source;
3847 BuildTypeSource(cx, obj, false, source);
3848 result = NewUCString(cx, source);
3849 } else {
3850 result = JS_NewStringCopyZ(cx, "[CType proto object]");
3852 if (!result)
3853 return false;
3855 args.rval().setString(result);
3856 return true;
3859 bool
3860 CType::HasInstance(JSContext* cx, HandleObject obj, MutableHandleValue v, bool* bp)
3862 MOZ_ASSERT(CType::IsCType(obj));
3864 jsval slot = JS_GetReservedSlot(obj, SLOT_PROTO);
3865 JS::Rooted<JSObject*> prototype(cx, &slot.toObject());
3866 MOZ_ASSERT(prototype);
3867 MOZ_ASSERT(CData::IsCDataProto(prototype));
3869 *bp = false;
3870 if (v.isPrimitive())
3871 return true;
3873 RootedObject proto(cx, &v.toObject());
3874 for (;;) {
3875 if (!JS_GetPrototype(cx, proto, &proto))
3876 return false;
3877 if (!proto)
3878 break;
3879 if (proto == prototype) {
3880 *bp = true;
3881 break;
3884 return true;
3887 static JSObject*
3888 CType::GetGlobalCTypes(JSContext* cx, JSObject* objArg)
3890 MOZ_ASSERT(CType::IsCType(objArg));
3892 RootedObject obj(cx, objArg);
3893 RootedObject objTypeProto(cx);
3894 if (!JS_GetPrototype(cx, obj, &objTypeProto))
3895 return nullptr;
3896 MOZ_ASSERT(objTypeProto);
3897 MOZ_ASSERT(CType::IsCTypeProto(objTypeProto));
3899 jsval valCTypes = JS_GetReservedSlot(objTypeProto, SLOT_CTYPES);
3900 MOZ_ASSERT(valCTypes.isObject());
3901 return &valCTypes.toObject();
3904 /*******************************************************************************
3905 ** ABI implementation
3906 *******************************************************************************/
3908 bool
3909 ABI::IsABI(JSObject* obj)
3911 return JS_GetClass(obj) == &sCABIClass;
3914 bool
3915 ABI::ToSource(JSContext* cx, unsigned argc, jsval* vp)
3917 CallArgs args = CallArgsFromVp(argc, vp);
3918 if (args.length() != 0) {
3919 JS_ReportError(cx, "toSource takes zero arguments");
3920 return false;
3923 JSObject* obj = JS_THIS_OBJECT(cx, vp);
3924 if (!obj)
3925 return false;
3926 if (!ABI::IsABI(obj)) {
3927 JS_ReportError(cx, "not an ABI");
3928 return false;
3931 JSString* result;
3932 switch (GetABICode(obj)) {
3933 case ABI_DEFAULT:
3934 result = JS_NewStringCopyZ(cx, "ctypes.default_abi");
3935 break;
3936 case ABI_STDCALL:
3937 result = JS_NewStringCopyZ(cx, "ctypes.stdcall_abi");
3938 break;
3939 case ABI_WINAPI:
3940 result = JS_NewStringCopyZ(cx, "ctypes.winapi_abi");
3941 break;
3942 default:
3943 JS_ReportError(cx, "not a valid ABICode");
3944 return false;
3946 if (!result)
3947 return false;
3949 args.rval().setString(result);
3950 return true;
3954 /*******************************************************************************
3955 ** PointerType implementation
3956 *******************************************************************************/
3958 bool
3959 PointerType::Create(JSContext* cx, unsigned argc, jsval* vp)
3961 CallArgs args = CallArgsFromVp(argc, vp);
3962 // Construct and return a new PointerType object.
3963 if (args.length() != 1) {
3964 JS_ReportError(cx, "PointerType takes one argument");
3965 return false;
3968 jsval arg = args[0];
3969 RootedObject obj(cx);
3970 if (arg.isPrimitive() || !CType::IsCType(obj = &arg.toObject())) {
3971 JS_ReportError(cx, "first argument must be a CType");
3972 return false;
3975 JSObject* result = CreateInternal(cx, obj);
3976 if (!result)
3977 return false;
3979 args.rval().setObject(*result);
3980 return true;
3983 JSObject*
3984 PointerType::CreateInternal(JSContext* cx, HandleObject baseType)
3986 // check if we have a cached PointerType on our base CType.
3987 jsval slot = JS_GetReservedSlot(baseType, SLOT_PTR);
3988 if (!slot.isUndefined())
3989 return &slot.toObject();
3991 // Get ctypes.PointerType.prototype and the common prototype for CData objects
3992 // of this type, or ctypes.FunctionType.prototype for function pointers.
3993 CTypeProtoSlot slotId = CType::GetTypeCode(baseType) == TYPE_function ?
3994 SLOT_FUNCTIONDATAPROTO : SLOT_POINTERDATAPROTO;
3995 RootedObject dataProto(cx, CType::GetProtoFromType(cx, baseType, slotId));
3996 if (!dataProto)
3997 return nullptr;
3998 RootedObject typeProto(cx, CType::GetProtoFromType(cx, baseType, SLOT_POINTERPROTO));
3999 if (!typeProto)
4000 return nullptr;
4002 // Create a new CType object with the common properties and slots.
4003 JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_pointer,
4004 nullptr, INT_TO_JSVAL(sizeof(void*)),
4005 INT_TO_JSVAL(ffi_type_pointer.alignment),
4006 &ffi_type_pointer);
4007 if (!typeObj)
4008 return nullptr;
4010 // Set the target type. (This will be 'null' for an opaque pointer type.)
4011 JS_SetReservedSlot(typeObj, SLOT_TARGET_T, OBJECT_TO_JSVAL(baseType));
4013 // Finally, cache our newly-created PointerType on our pointed-to CType.
4014 JS_SetReservedSlot(baseType, SLOT_PTR, OBJECT_TO_JSVAL(typeObj));
4016 return typeObj;
4019 bool
4020 PointerType::ConstructData(JSContext* cx,
4021 HandleObject obj,
4022 const CallArgs& args)
4024 if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_pointer) {
4025 JS_ReportError(cx, "not a PointerType");
4026 return false;
4029 if (args.length() > 3) {
4030 JS_ReportError(cx, "constructor takes 0, 1, 2, or 3 arguments");
4031 return false;
4034 RootedObject result(cx, CData::Create(cx, obj, NullPtr(), nullptr, true));
4035 if (!result)
4036 return false;
4038 // Set return value early, must not observe *vp after
4039 args.rval().setObject(*result);
4041 // There are 3 things that we might be creating here:
4042 // 1 - A null pointer (no arguments)
4043 // 2 - An initialized pointer (1 argument)
4044 // 3 - A closure (1-3 arguments)
4046 // The API doesn't give us a perfect way to distinguish 2 and 3, but the
4047 // heuristics we use should be fine.
4050 // Case 1 - Null pointer
4052 if (args.length() == 0)
4053 return true;
4055 // Analyze the arguments a bit to decide what to do next.
4056 RootedObject baseObj(cx, PointerType::GetBaseType(obj));
4057 bool looksLikeClosure = CType::GetTypeCode(baseObj) == TYPE_function &&
4058 args[0].isObject() && JS::IsCallable(&args[0].toObject());
4061 // Case 2 - Initialized pointer
4063 if (!looksLikeClosure) {
4064 if (args.length() != 1) {
4065 JS_ReportError(cx, "first argument must be a function");
4066 return false;
4068 return ExplicitConvert(cx, args[0], obj, CData::GetData(result));
4072 // Case 3 - Closure
4075 // The second argument is an optional 'this' parameter with which to invoke
4076 // the given js function. Callers may leave this blank, or pass null if they
4077 // wish to pass the third argument.
4078 RootedObject thisObj(cx, nullptr);
4079 if (args.length() >= 2) {
4080 if (args[1].isNull()) {
4081 thisObj = nullptr;
4082 } else if (args[1].isObject()) {
4083 thisObj = &args[1].toObject();
4084 } else if (!JS_ValueToObject(cx, args[1], &thisObj)) {
4085 return false;
4089 // The third argument is an optional error sentinel that js-ctypes will return
4090 // if an exception is raised while executing the closure. The type must match
4091 // the return type of the callback.
4092 jsval errVal = JSVAL_VOID;
4093 if (args.length() == 3)
4094 errVal = args[2];
4096 RootedObject fnObj(cx, &args[0].toObject());
4097 return FunctionType::ConstructData(cx, baseObj, result, fnObj, thisObj, errVal);
4100 JSObject*
4101 PointerType::GetBaseType(JSObject* obj)
4103 MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_pointer);
4105 jsval type = JS_GetReservedSlot(obj, SLOT_TARGET_T);
4106 MOZ_ASSERT(!type.isNull());
4107 return &type.toObject();
4110 bool
4111 PointerType::IsPointerType(HandleValue v)
4113 if (!v.isObject())
4114 return false;
4115 JSObject* obj = &v.toObject();
4116 return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_pointer;
4119 bool
4120 PointerType::IsPointer(HandleValue v)
4122 if (!v.isObject())
4123 return false;
4124 JSObject* obj = &v.toObject();
4125 return CData::IsCData(obj) && CType::GetTypeCode(CData::GetCType(obj)) == TYPE_pointer;
4128 bool
4129 PointerType::TargetTypeGetter(JSContext* cx, JS::CallArgs args)
4131 RootedObject obj(cx, &args.thisv().toObject());
4132 args.rval().set(JS_GetReservedSlot(obj, SLOT_TARGET_T));
4133 MOZ_ASSERT(args.rval().isObject());
4134 return true;
4137 bool
4138 PointerType::IsNull(JSContext* cx, unsigned argc, jsval* vp)
4140 CallArgs args = CallArgsFromVp(argc, vp);
4141 JSObject* obj = JS_THIS_OBJECT(cx, vp);
4142 if (!obj)
4143 return false;
4144 if (!CData::IsCData(obj)) {
4145 JS_ReportError(cx, "not a CData");
4146 return false;
4149 // Get pointer type and base type.
4150 JSObject* typeObj = CData::GetCType(obj);
4151 if (CType::GetTypeCode(typeObj) != TYPE_pointer) {
4152 JS_ReportError(cx, "not a PointerType");
4153 return false;
4156 void* data = *static_cast<void**>(CData::GetData(obj));
4157 args.rval().setBoolean(data == nullptr);
4158 return true;
4161 bool
4162 PointerType::OffsetBy(JSContext* cx, const CallArgs& args, int offset)
4164 JSObject* obj = JS_THIS_OBJECT(cx, args.base());
4165 if (!obj)
4166 return false;
4167 if (!CData::IsCData(obj)) {
4168 JS_ReportError(cx, "not a CData");
4169 return false;
4172 RootedObject typeObj(cx, CData::GetCType(obj));
4173 if (CType::GetTypeCode(typeObj) != TYPE_pointer) {
4174 JS_ReportError(cx, "not a PointerType");
4175 return false;
4178 RootedObject baseType(cx, PointerType::GetBaseType(typeObj));
4179 if (!CType::IsSizeDefined(baseType)) {
4180 JS_ReportError(cx, "cannot modify pointer of undefined size");
4181 return false;
4184 size_t elementSize = CType::GetSize(baseType);
4185 char* data = static_cast<char*>(*static_cast<void**>(CData::GetData(obj)));
4186 void* address = data + offset * elementSize;
4188 // Create a PointerType CData object containing the new address.
4189 JSObject* result = CData::Create(cx, typeObj, NullPtr(), &address, true);
4190 if (!result)
4191 return false;
4193 args.rval().setObject(*result);
4194 return true;
4197 bool
4198 PointerType::Increment(JSContext* cx, unsigned argc, jsval* vp)
4200 CallArgs args = CallArgsFromVp(argc, vp);
4201 return OffsetBy(cx, args, 1);
4204 bool
4205 PointerType::Decrement(JSContext* cx, unsigned argc, jsval* vp)
4207 CallArgs args = CallArgsFromVp(argc, vp);
4208 return OffsetBy(cx, args, -1);
4211 bool
4212 PointerType::ContentsGetter(JSContext* cx, JS::CallArgs args)
4214 RootedObject obj(cx, &args.thisv().toObject());
4215 RootedObject baseType(cx, GetBaseType(CData::GetCType(obj)));
4216 if (!CType::IsSizeDefined(baseType)) {
4217 JS_ReportError(cx, "cannot get contents of undefined size");
4218 return false;
4221 void* data = *static_cast<void**>(CData::GetData(obj));
4222 if (data == nullptr) {
4223 JS_ReportError(cx, "cannot read contents of null pointer");
4224 return false;
4227 RootedValue result(cx);
4228 if (!ConvertToJS(cx, baseType, NullPtr(), data, false, false, &result))
4229 return false;
4231 args.rval().set(result);
4232 return true;
4235 bool
4236 PointerType::ContentsSetter(JSContext* cx, JS::CallArgs args)
4238 RootedObject obj(cx, &args.thisv().toObject());
4239 RootedObject baseType(cx, GetBaseType(CData::GetCType(obj)));
4240 if (!CType::IsSizeDefined(baseType)) {
4241 JS_ReportError(cx, "cannot set contents of undefined size");
4242 return false;
4245 void* data = *static_cast<void**>(CData::GetData(obj));
4246 if (data == nullptr) {
4247 JS_ReportError(cx, "cannot write contents to null pointer");
4248 return false;
4251 args.rval().setUndefined();
4252 return ImplicitConvert(cx, args.get(0), baseType, data, false, nullptr);
4255 /*******************************************************************************
4256 ** ArrayType implementation
4257 *******************************************************************************/
4259 bool
4260 ArrayType::Create(JSContext* cx, unsigned argc, jsval* vp)
4262 CallArgs args = CallArgsFromVp(argc, vp);
4263 // Construct and return a new ArrayType object.
4264 if (args.length() < 1 || args.length() > 2) {
4265 JS_ReportError(cx, "ArrayType takes one or two arguments");
4266 return false;
4269 if (args[0].isPrimitive() ||
4270 !CType::IsCType(&args[0].toObject())) {
4271 JS_ReportError(cx, "first argument must be a CType");
4272 return false;
4275 // Convert the length argument to a size_t.
4276 size_t length = 0;
4277 if (args.length() == 2 && !jsvalToSize(cx, args[1], false, &length)) {
4278 JS_ReportError(cx, "second argument must be a nonnegative integer");
4279 return false;
4282 RootedObject baseType(cx, &args[0].toObject());
4283 JSObject* result = CreateInternal(cx, baseType, length, args.length() == 2);
4284 if (!result)
4285 return false;
4287 args.rval().setObject(*result);
4288 return true;
4291 JSObject*
4292 ArrayType::CreateInternal(JSContext* cx,
4293 HandleObject baseType,
4294 size_t length,
4295 bool lengthDefined)
4297 // Get ctypes.ArrayType.prototype and the common prototype for CData objects
4298 // of this type, from ctypes.CType.prototype.
4299 RootedObject typeProto(cx, CType::GetProtoFromType(cx, baseType, SLOT_ARRAYPROTO));
4300 if (!typeProto)
4301 return nullptr;
4302 RootedObject dataProto(cx, CType::GetProtoFromType(cx, baseType, SLOT_ARRAYDATAPROTO));
4303 if (!dataProto)
4304 return nullptr;
4306 // Determine the size of the array from the base type, if possible.
4307 // The size of the base type must be defined.
4308 // If our length is undefined, both our size and length will be undefined.
4309 size_t baseSize;
4310 if (!CType::GetSafeSize(baseType, &baseSize)) {
4311 JS_ReportError(cx, "base size must be defined");
4312 return nullptr;
4315 RootedValue sizeVal(cx, JSVAL_VOID);
4316 RootedValue lengthVal(cx, JSVAL_VOID);
4317 if (lengthDefined) {
4318 // Check for overflow, and convert to an int or double as required.
4319 size_t size = length * baseSize;
4320 if (length > 0 && size / length != baseSize) {
4321 JS_ReportError(cx, "size overflow");
4322 return nullptr;
4324 if (!SizeTojsval(cx, size, &sizeVal) ||
4325 !SizeTojsval(cx, length, &lengthVal))
4326 return nullptr;
4329 size_t align = CType::GetAlignment(baseType);
4331 // Create a new CType object with the common properties and slots.
4332 JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_array, nullptr,
4333 sizeVal, INT_TO_JSVAL(align), nullptr);
4334 if (!typeObj)
4335 return nullptr;
4337 // Set the element type.
4338 JS_SetReservedSlot(typeObj, SLOT_ELEMENT_T, OBJECT_TO_JSVAL(baseType));
4340 // Set the length.
4341 JS_SetReservedSlot(typeObj, SLOT_LENGTH, lengthVal);
4343 return typeObj;
4346 bool
4347 ArrayType::ConstructData(JSContext* cx,
4348 HandleObject obj_,
4349 const CallArgs& args)
4351 RootedObject obj(cx, obj_); // Make a mutable version
4353 if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_array) {
4354 JS_ReportError(cx, "not an ArrayType");
4355 return false;
4358 // Decide whether we have an object to initialize from. We'll override this
4359 // if we get a length argument instead.
4360 bool convertObject = args.length() == 1;
4362 // Check if we're an array of undefined length. If we are, allow construction
4363 // with a length argument, or with an actual JS array.
4364 if (CType::IsSizeDefined(obj)) {
4365 if (args.length() > 1) {
4366 JS_ReportError(cx, "constructor takes zero or one argument");
4367 return false;
4370 } else {
4371 if (args.length() != 1) {
4372 JS_ReportError(cx, "constructor takes one argument");
4373 return false;
4376 RootedObject baseType(cx, GetBaseType(obj));
4378 size_t length;
4379 if (jsvalToSize(cx, args[0], false, &length)) {
4380 // Have a length, rather than an object to initialize from.
4381 convertObject = false;
4383 } else if (args[0].isObject()) {
4384 // We were given an object with a .length property.
4385 // This could be a JS array, or a CData array.
4386 RootedObject arg(cx, &args[0].toObject());
4387 RootedValue lengthVal(cx);
4388 if (!JS_GetProperty(cx, arg, "length", &lengthVal) ||
4389 !jsvalToSize(cx, lengthVal, false, &length)) {
4390 JS_ReportError(cx, "argument must be an array object or length");
4391 return false;
4394 } else if (args[0].isString()) {
4395 // We were given a string. Size the array to the appropriate length,
4396 // including space for the terminator.
4397 JSString* sourceString = args[0].toString();
4398 size_t sourceLength = sourceString->length();
4399 JSLinearString* sourceLinear = sourceString->ensureLinear(cx);
4400 if (!sourceLinear)
4401 return false;
4403 switch (CType::GetTypeCode(baseType)) {
4404 case TYPE_char:
4405 case TYPE_signed_char:
4406 case TYPE_unsigned_char: {
4407 // Determine the UTF-8 length.
4408 length = GetDeflatedUTF8StringLength(cx, sourceLinear);
4409 if (length == (size_t) -1)
4410 return false;
4412 ++length;
4413 break;
4415 case TYPE_char16_t:
4416 length = sourceLength + 1;
4417 break;
4418 default:
4419 return TypeError(cx, "array", args[0]);
4422 } else {
4423 JS_ReportError(cx, "argument must be an array object or length");
4424 return false;
4427 // Construct a new ArrayType of defined length, for the new CData object.
4428 obj = CreateInternal(cx, baseType, length, true);
4429 if (!obj)
4430 return false;
4433 JSObject* result = CData::Create(cx, obj, NullPtr(), nullptr, true);
4434 if (!result)
4435 return false;
4437 args.rval().setObject(*result);
4439 if (convertObject) {
4440 if (!ExplicitConvert(cx, args[0], obj, CData::GetData(result)))
4441 return false;
4444 return true;
4447 JSObject*
4448 ArrayType::GetBaseType(JSObject* obj)
4450 MOZ_ASSERT(CType::IsCType(obj));
4451 MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_array);
4453 jsval type = JS_GetReservedSlot(obj, SLOT_ELEMENT_T);
4454 MOZ_ASSERT(!type.isNull());
4455 return &type.toObject();
4458 bool
4459 ArrayType::GetSafeLength(JSObject* obj, size_t* result)
4461 MOZ_ASSERT(CType::IsCType(obj));
4462 MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_array);
4464 jsval length = JS_GetReservedSlot(obj, SLOT_LENGTH);
4466 // The "length" property can be an int, a double, or JSVAL_VOID
4467 // (for arrays of undefined length), and must always fit in a size_t.
4468 if (length.isInt32()) {
4469 *result = length.toInt32();
4470 return true;
4472 if (length.isDouble()) {
4473 *result = Convert<size_t>(length.toDouble());
4474 return true;
4477 MOZ_ASSERT(length.isUndefined());
4478 return false;
4481 size_t
4482 ArrayType::GetLength(JSObject* obj)
4484 MOZ_ASSERT(CType::IsCType(obj));
4485 MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_array);
4487 jsval length = JS_GetReservedSlot(obj, SLOT_LENGTH);
4489 MOZ_ASSERT(!length.isUndefined());
4491 // The "length" property can be an int, a double, or JSVAL_VOID
4492 // (for arrays of undefined length), and must always fit in a size_t.
4493 // For callers who know it can never be JSVAL_VOID, return a size_t directly.
4494 if (length.isInt32())
4495 return length.toInt32();
4496 return Convert<size_t>(length.toDouble());
4499 ffi_type*
4500 ArrayType::BuildFFIType(JSContext* cx, JSObject* obj)
4502 MOZ_ASSERT(CType::IsCType(obj));
4503 MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_array);
4504 MOZ_ASSERT(CType::IsSizeDefined(obj));
4506 JSObject* baseType = ArrayType::GetBaseType(obj);
4507 ffi_type* ffiBaseType = CType::GetFFIType(cx, baseType);
4508 if (!ffiBaseType)
4509 return nullptr;
4511 size_t length = ArrayType::GetLength(obj);
4513 // Create an ffi_type to represent the array. This is necessary for the case
4514 // where the array is part of a struct. Since libffi has no intrinsic
4515 // support for array types, we approximate it by creating a struct type
4516 // with elements of type 'baseType' and with appropriate size and alignment
4517 // values. It would be nice to not do all the work of setting up 'elements',
4518 // but some libffi platforms currently require that it be meaningful. I'm
4519 // looking at you, x86_64.
4520 AutoPtr<ffi_type> ffiType(cx->new_<ffi_type>());
4521 if (!ffiType) {
4522 JS_ReportOutOfMemory(cx);
4523 return nullptr;
4526 ffiType->type = FFI_TYPE_STRUCT;
4527 ffiType->size = CType::GetSize(obj);
4528 ffiType->alignment = CType::GetAlignment(obj);
4529 ffiType->elements = cx->pod_malloc<ffi_type*>(length + 1);
4530 if (!ffiType->elements) {
4531 JS_ReportAllocationOverflow(cx);
4532 return nullptr;
4535 for (size_t i = 0; i < length; ++i)
4536 ffiType->elements[i] = ffiBaseType;
4537 ffiType->elements[length] = nullptr;
4539 return ffiType.forget();
4542 bool
4543 ArrayType::IsArrayType(HandleValue v)
4545 if (!v.isObject())
4546 return false;
4547 JSObject* obj = &v.toObject();
4548 return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_array;
4551 bool
4552 ArrayType::IsArrayOrArrayType(HandleValue v)
4554 if (!v.isObject())
4555 return false;
4556 JSObject* obj = &v.toObject();
4558 // Allow both CTypes and CDatas of the ArrayType persuasion by extracting the
4559 // CType if we're dealing with a CData.
4560 if (CData::IsCData(obj)) {
4561 obj = CData::GetCType(obj);
4563 return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_array;
4566 bool
4567 ArrayType::ElementTypeGetter(JSContext* cx, JS::CallArgs args)
4569 RootedObject obj(cx, &args.thisv().toObject());
4570 args.rval().set(JS_GetReservedSlot(obj, SLOT_ELEMENT_T));
4571 MOZ_ASSERT(args.rval().isObject());
4572 return true;
4575 bool
4576 ArrayType::LengthGetter(JSContext* cx, JS::CallArgs args)
4578 JSObject* obj = &args.thisv().toObject();
4580 // This getter exists for both CTypes and CDatas of the ArrayType persuasion.
4581 // If we're dealing with a CData, get the CType from it.
4582 if (CData::IsCData(obj))
4583 obj = CData::GetCType(obj);
4585 args.rval().set(JS_GetReservedSlot(obj, SLOT_LENGTH));
4586 MOZ_ASSERT(args.rval().isNumber() || args.rval().isUndefined());
4587 return true;
4590 bool
4591 ArrayType::Getter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp)
4593 // This should never happen, but we'll check to be safe.
4594 if (!CData::IsCData(obj)) {
4595 JS_ReportError(cx, "not a CData");
4596 return false;
4599 // Bail early if we're not an ArrayType. (This setter is present for all
4600 // CData, regardless of CType.)
4601 JSObject* typeObj = CData::GetCType(obj);
4602 if (CType::GetTypeCode(typeObj) != TYPE_array)
4603 return true;
4605 // Convert the index to a size_t and bounds-check it.
4606 size_t index;
4607 size_t length = GetLength(typeObj);
4608 bool ok = jsidToSize(cx, idval, true, &index);
4609 int32_t dummy;
4610 if (!ok && JSID_IS_STRING(idval) && !StringToInteger(cx, JSID_TO_STRING(idval), &dummy)) {
4611 // String either isn't a number, or doesn't fit in size_t.
4612 // Chances are it's a regular property lookup, so return.
4613 return true;
4615 if (!ok || index >= length) {
4616 JS_ReportError(cx, "invalid index");
4617 return false;
4620 RootedObject baseType(cx, GetBaseType(typeObj));
4621 size_t elementSize = CType::GetSize(baseType);
4622 char* data = static_cast<char*>(CData::GetData(obj)) + elementSize * index;
4623 return ConvertToJS(cx, baseType, obj, data, false, false, vp);
4626 bool
4627 ArrayType::Setter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, MutableHandleValue vp)
4629 // This should never happen, but we'll check to be safe.
4630 if (!CData::IsCData(obj)) {
4631 JS_ReportError(cx, "not a CData");
4632 return false;
4635 // Bail early if we're not an ArrayType. (This setter is present for all
4636 // CData, regardless of CType.)
4637 JSObject* typeObj = CData::GetCType(obj);
4638 if (CType::GetTypeCode(typeObj) != TYPE_array)
4639 return true;
4641 // Convert the index to a size_t and bounds-check it.
4642 size_t index;
4643 size_t length = GetLength(typeObj);
4644 bool ok = jsidToSize(cx, idval, true, &index);
4645 int32_t dummy;
4646 if (!ok && JSID_IS_STRING(idval) && !StringToInteger(cx, JSID_TO_STRING(idval), &dummy)) {
4647 // String either isn't a number, or doesn't fit in size_t.
4648 // Chances are it's a regular property lookup, so return.
4649 return true;
4651 if (!ok || index >= length) {
4652 JS_ReportError(cx, "invalid index");
4653 return false;
4656 JSObject* baseType = GetBaseType(typeObj);
4657 size_t elementSize = CType::GetSize(baseType);
4658 char* data = static_cast<char*>(CData::GetData(obj)) + elementSize * index;
4659 return ImplicitConvert(cx, vp, baseType, data, false, nullptr);
4662 bool
4663 ArrayType::AddressOfElement(JSContext* cx, unsigned argc, jsval* vp)
4665 CallArgs args = CallArgsFromVp(argc, vp);
4666 RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
4667 if (!obj)
4668 return false;
4669 if (!CData::IsCData(obj)) {
4670 JS_ReportError(cx, "not a CData");
4671 return false;
4674 RootedObject typeObj(cx, CData::GetCType(obj));
4675 if (CType::GetTypeCode(typeObj) != TYPE_array) {
4676 JS_ReportError(cx, "not an ArrayType");
4677 return false;
4680 if (args.length() != 1) {
4681 JS_ReportError(cx, "addressOfElement takes one argument");
4682 return false;
4685 RootedObject baseType(cx, GetBaseType(typeObj));
4686 RootedObject pointerType(cx, PointerType::CreateInternal(cx, baseType));
4687 if (!pointerType)
4688 return false;
4690 // Create a PointerType CData object containing null.
4691 RootedObject result(cx, CData::Create(cx, pointerType, NullPtr(), nullptr, true));
4692 if (!result)
4693 return false;
4695 args.rval().setObject(*result);
4697 // Convert the index to a size_t and bounds-check it.
4698 size_t index;
4699 size_t length = GetLength(typeObj);
4700 if (!jsvalToSize(cx, args[0], false, &index) ||
4701 index >= length) {
4702 JS_ReportError(cx, "invalid index");
4703 return false;
4706 // Manually set the pointer inside the object, so we skip the conversion step.
4707 void** data = static_cast<void**>(CData::GetData(result));
4708 size_t elementSize = CType::GetSize(baseType);
4709 *data = static_cast<char*>(CData::GetData(obj)) + elementSize * index;
4710 return true;
4713 /*******************************************************************************
4714 ** StructType implementation
4715 *******************************************************************************/
4717 // For a struct field descriptor 'val' of the form { name : type }, extract
4718 // 'name' and 'type'.
4719 static JSFlatString*
4720 ExtractStructField(JSContext* cx, jsval val, MutableHandleObject typeObj)
4722 if (val.isPrimitive()) {
4723 JS_ReportError(cx, "struct field descriptors require a valid name and type");
4724 return nullptr;
4727 RootedObject obj(cx, &val.toObject());
4728 AutoIdArray props(cx, JS_Enumerate(cx, obj));
4729 if (!props)
4730 return nullptr;
4732 // make sure we have one, and only one, property
4733 if (props.length() != 1) {
4734 JS_ReportError(cx, "struct field descriptors must contain one property");
4735 return nullptr;
4738 RootedId nameid(cx, props[0]);
4739 if (!JSID_IS_STRING(nameid)) {
4740 JS_ReportError(cx, "struct field descriptors require a valid name and type");
4741 return nullptr;
4744 RootedValue propVal(cx);
4745 if (!JS_GetPropertyById(cx, obj, nameid, &propVal))
4746 return nullptr;
4748 if (propVal.isPrimitive() || !CType::IsCType(&propVal.toObject())) {
4749 JS_ReportError(cx, "struct field descriptors require a valid name and type");
4750 return nullptr;
4753 // Undefined size or zero size struct members are illegal.
4754 // (Zero-size arrays are legal as struct members in C++, but libffi will
4755 // choke on a zero-size struct, so we disallow them.)
4756 typeObj.set(&propVal.toObject());
4757 size_t size;
4758 if (!CType::GetSafeSize(typeObj, &size) || size == 0) {
4759 JS_ReportError(cx, "struct field types must have defined and nonzero size");
4760 return nullptr;
4763 return JSID_TO_FLAT_STRING(nameid);
4766 // For a struct field with 'name' and 'type', add an element of the form
4767 // { name : type }.
4768 static bool
4769 AddFieldToArray(JSContext* cx,
4770 MutableHandleValue element,
4771 JSFlatString* name_,
4772 JSObject* typeObj_)
4774 RootedObject typeObj(cx, typeObj_);
4775 Rooted<JSFlatString*> name(cx, name_);
4776 RootedObject fieldObj(cx, JS_NewObject(cx, nullptr, NullPtr(), NullPtr()));
4777 if (!fieldObj)
4778 return false;
4780 element.setObject(*fieldObj);
4782 AutoStableStringChars nameChars(cx);
4783 if (!nameChars.initTwoByte(cx, name))
4784 return false;
4786 if (!JS_DefineUCProperty(cx, fieldObj,
4787 nameChars.twoByteChars(), name->length(),
4788 typeObj,
4789 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
4790 return false;
4792 return JS_FreezeObject(cx, fieldObj);
4795 bool
4796 StructType::Create(JSContext* cx, unsigned argc, jsval* vp)
4798 CallArgs args = CallArgsFromVp(argc, vp);
4800 // Construct and return a new StructType object.
4801 if (args.length() < 1 || args.length() > 2) {
4802 JS_ReportError(cx, "StructType takes one or two arguments");
4803 return false;
4806 jsval name = args[0];
4807 if (!name.isString()) {
4808 JS_ReportError(cx, "first argument must be a string");
4809 return false;
4812 // Get ctypes.StructType.prototype from the ctypes.StructType constructor.
4813 RootedObject typeProto(cx, CType::GetProtoFromCtor(&args.callee(), SLOT_STRUCTPROTO));
4815 // Create a simple StructType with no defined fields. The result will be
4816 // non-instantiable as CData, will have no 'prototype' property, and will
4817 // have undefined size and alignment and no ffi_type.
4818 RootedObject result(cx, CType::Create(cx, typeProto, NullPtr(), TYPE_struct,
4819 name.toString(), JSVAL_VOID, JSVAL_VOID, nullptr));
4820 if (!result)
4821 return false;
4823 if (args.length() == 2) {
4824 RootedObject arr(cx, args[1].isPrimitive() ? nullptr : &args[1].toObject());
4825 if (!arr || !JS_IsArrayObject(cx, arr)) {
4826 JS_ReportError(cx, "second argument must be an array");
4827 return false;
4830 // Define the struct fields.
4831 if (!DefineInternal(cx, result, arr))
4832 return false;
4835 args.rval().setObject(*result);
4836 return true;
4839 static void
4840 PostBarrierCallback(JSTracer* trc, JSString* key, void* data)
4842 typedef HashMap<JSFlatString*,
4843 UnbarrieredFieldInfo,
4844 FieldHashPolicy,
4845 SystemAllocPolicy> UnbarrieredFieldInfoHash;
4847 UnbarrieredFieldInfoHash* table = reinterpret_cast<UnbarrieredFieldInfoHash*>(data);
4848 JSString* prior = key;
4849 JS_CallUnbarrieredStringTracer(trc, &key, "CType fieldName");
4850 table->rekeyIfMoved(JS_ASSERT_STRING_IS_FLAT(prior), JS_ASSERT_STRING_IS_FLAT(key));
4853 bool
4854 StructType::DefineInternal(JSContext* cx, JSObject* typeObj_, JSObject* fieldsObj_)
4856 RootedObject typeObj(cx, typeObj_);
4857 RootedObject fieldsObj(cx, fieldsObj_);
4859 uint32_t len;
4860 ASSERT_OK(JS_GetArrayLength(cx, fieldsObj, &len));
4862 // Get the common prototype for CData objects of this type from
4863 // ctypes.CType.prototype.
4864 RootedObject dataProto(cx, CType::GetProtoFromType(cx, typeObj, SLOT_STRUCTDATAPROTO));
4865 if (!dataProto)
4866 return false;
4868 // Set up the 'prototype' and 'prototype.constructor' properties.
4869 // The prototype will reflect the struct fields as properties on CData objects
4870 // created from this type.
4871 RootedObject prototype(cx, JS_NewObject(cx, &sCDataProtoClass, dataProto, NullPtr()));
4872 if (!prototype)
4873 return false;
4875 if (!JS_DefineProperty(cx, prototype, "constructor", typeObj,
4876 JSPROP_READONLY | JSPROP_PERMANENT))
4877 return false;
4879 // Create a FieldInfoHash to stash on the type object, and an array to root
4880 // its constituents. (We cannot simply stash the hash in a reserved slot now
4881 // to get GC safety for free, since if anything in this function fails we
4882 // do not want to mutate 'typeObj'.)
4883 AutoPtr<FieldInfoHash> fields(cx->new_<FieldInfoHash>());
4884 if (!fields || !fields->init(len)) {
4885 JS_ReportOutOfMemory(cx);
4886 return false;
4888 JS::AutoValueVector fieldRoots(cx);
4889 if (!fieldRoots.resize(len)) {
4890 JS_ReportOutOfMemory(cx);
4891 return false;
4894 // Process the field types.
4895 size_t structSize, structAlign;
4896 if (len != 0) {
4897 structSize = 0;
4898 structAlign = 0;
4900 for (uint32_t i = 0; i < len; ++i) {
4901 RootedValue item(cx);
4902 if (!JS_GetElement(cx, fieldsObj, i, &item))
4903 return false;
4905 RootedObject fieldType(cx, nullptr);
4906 Rooted<JSFlatString*> name(cx, ExtractStructField(cx, item, &fieldType));
4907 if (!name)
4908 return false;
4909 fieldRoots[i].setObject(*fieldType);
4911 // Make sure each field name is unique
4912 FieldInfoHash::AddPtr entryPtr = fields->lookupForAdd(name);
4913 if (entryPtr) {
4914 JS_ReportError(cx, "struct fields must have unique names");
4915 return false;
4918 // Add the field to the StructType's 'prototype' property.
4919 AutoStableStringChars nameChars(cx);
4920 if (!nameChars.initTwoByte(cx, name))
4921 return false;
4923 if (!JS_DefineUCProperty(cx, prototype,
4924 nameChars.twoByteChars(), name->length(), UndefinedHandleValue,
4925 JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_PROPOP_ACCESSORS,
4926 JS_PROPERTYOP_GETTER(StructType::FieldGetter),
4927 JS_PROPERTYOP_SETTER(StructType::FieldSetter)))
4928 return false;
4930 size_t fieldSize = CType::GetSize(fieldType);
4931 size_t fieldAlign = CType::GetAlignment(fieldType);
4932 size_t fieldOffset = Align(structSize, fieldAlign);
4933 // Check for overflow. Since we hold invariant that fieldSize % fieldAlign
4934 // be zero, we can safely check fieldOffset + fieldSize without first
4935 // checking fieldOffset for overflow.
4936 if (fieldOffset + fieldSize < structSize) {
4937 JS_ReportError(cx, "size overflow");
4938 return false;
4941 // Add field name to the hash
4942 FieldInfo info;
4943 info.mType = nullptr; // Value of fields are not yet traceable here.
4944 info.mIndex = i;
4945 info.mOffset = fieldOffset;
4946 ASSERT_OK(fields->add(entryPtr, name, info));
4947 JS_StoreStringPostBarrierCallback(cx, PostBarrierCallback, name, fields.get());
4949 structSize = fieldOffset + fieldSize;
4951 if (fieldAlign > structAlign)
4952 structAlign = fieldAlign;
4955 // Pad the struct tail according to struct alignment.
4956 size_t structTail = Align(structSize, structAlign);
4957 if (structTail < structSize) {
4958 JS_ReportError(cx, "size overflow");
4959 return false;
4961 structSize = structTail;
4963 } else {
4964 // Empty structs are illegal in C, but are legal and have a size of
4965 // 1 byte in C++. We're going to allow them, and trick libffi into
4966 // believing this by adding a char member. The resulting struct will have
4967 // no getters or setters, and will be initialized to zero.
4968 structSize = 1;
4969 structAlign = 1;
4972 RootedValue sizeVal(cx);
4973 if (!SizeTojsval(cx, structSize, &sizeVal))
4974 return false;
4976 for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
4977 FieldInfo& field = r.front().value();
4978 MOZ_ASSERT(field.mIndex < fieldRoots.length());
4979 field.mType = &fieldRoots[field.mIndex].toObject();
4982 JS_SetReservedSlot(typeObj, SLOT_FIELDINFO, PRIVATE_TO_JSVAL(fields.forget()));
4984 JS_SetReservedSlot(typeObj, SLOT_SIZE, sizeVal);
4985 JS_SetReservedSlot(typeObj, SLOT_ALIGN, INT_TO_JSVAL(structAlign));
4986 //if (!JS_FreezeObject(cx, prototype)0 // XXX fixme - see bug 541212!
4987 // return false;
4988 JS_SetReservedSlot(typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype));
4989 return true;
4992 ffi_type*
4993 StructType::BuildFFIType(JSContext* cx, JSObject* obj)
4995 MOZ_ASSERT(CType::IsCType(obj));
4996 MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_struct);
4997 MOZ_ASSERT(CType::IsSizeDefined(obj));
4999 const FieldInfoHash* fields = GetFieldInfo(obj);
5000 size_t len = fields->count();
5002 size_t structSize = CType::GetSize(obj);
5003 size_t structAlign = CType::GetAlignment(obj);
5005 AutoPtr<ffi_type> ffiType(cx->new_<ffi_type>());
5006 if (!ffiType) {
5007 JS_ReportOutOfMemory(cx);
5008 return nullptr;
5010 ffiType->type = FFI_TYPE_STRUCT;
5012 AutoPtr<ffi_type*> elements;
5013 if (len != 0) {
5014 elements = cx->pod_malloc<ffi_type*>(len + 1);
5015 if (!elements) {
5016 JS_ReportOutOfMemory(cx);
5017 return nullptr;
5019 elements[len] = nullptr;
5021 for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
5022 const FieldInfoHash::Entry& entry = r.front();
5023 ffi_type* fieldType = CType::GetFFIType(cx, entry.value().mType);
5024 if (!fieldType)
5025 return nullptr;
5026 elements[entry.value().mIndex] = fieldType;
5029 } else {
5030 // Represent an empty struct as having a size of 1 byte, just like C++.
5031 MOZ_ASSERT(structSize == 1);
5032 MOZ_ASSERT(structAlign == 1);
5033 elements = cx->pod_malloc<ffi_type*>(2);
5034 if (!elements) {
5035 JS_ReportOutOfMemory(cx);
5036 return nullptr;
5038 elements[0] = &ffi_type_uint8;
5039 elements[1] = nullptr;
5042 ffiType->elements = elements.get();
5044 #ifdef DEBUG
5045 // Perform a sanity check: the result of our struct size and alignment
5046 // calculations should match libffi's. We force it to do this calculation
5047 // by calling ffi_prep_cif.
5048 ffi_cif cif;
5049 ffiType->size = 0;
5050 ffiType->alignment = 0;
5051 ffi_status status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 0, ffiType.get(), nullptr);
5052 MOZ_ASSERT(status == FFI_OK);
5053 MOZ_ASSERT(structSize == ffiType->size);
5054 MOZ_ASSERT(structAlign == ffiType->alignment);
5055 #else
5056 // Fill in the ffi_type's size and align fields. This makes libffi treat the
5057 // type as initialized; it will not recompute the values. (We assume
5058 // everything agrees; if it doesn't, we really want to know about it, which
5059 // is the purpose of the above debug-only check.)
5060 ffiType->size = structSize;
5061 ffiType->alignment = structAlign;
5062 #endif
5064 elements.forget();
5065 return ffiType.forget();
5068 bool
5069 StructType::Define(JSContext* cx, unsigned argc, jsval* vp)
5071 CallArgs args = CallArgsFromVp(argc, vp);
5072 RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
5073 if (!obj)
5074 return false;
5075 if (!CType::IsCType(obj) ||
5076 CType::GetTypeCode(obj) != TYPE_struct) {
5077 JS_ReportError(cx, "not a StructType");
5078 return false;
5081 if (CType::IsSizeDefined(obj)) {
5082 JS_ReportError(cx, "StructType has already been defined");
5083 return false;
5086 if (args.length() != 1) {
5087 JS_ReportError(cx, "define takes one argument");
5088 return false;
5091 jsval arg = args[0];
5092 if (arg.isPrimitive()) {
5093 JS_ReportError(cx, "argument must be an array");
5094 return false;
5096 RootedObject arr(cx, arg.toObjectOrNull());
5097 if (!JS_IsArrayObject(cx, arr)) {
5098 JS_ReportError(cx, "argument must be an array");
5099 return false;
5102 return DefineInternal(cx, obj, arr);
5105 bool
5106 StructType::ConstructData(JSContext* cx,
5107 HandleObject obj,
5108 const CallArgs& args)
5110 if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_struct) {
5111 JS_ReportError(cx, "not a StructType");
5112 return false;
5115 if (!CType::IsSizeDefined(obj)) {
5116 JS_ReportError(cx, "cannot construct an opaque StructType");
5117 return false;
5120 JSObject* result = CData::Create(cx, obj, NullPtr(), nullptr, true);
5121 if (!result)
5122 return false;
5124 args.rval().setObject(*result);
5126 if (args.length() == 0)
5127 return true;
5129 char* buffer = static_cast<char*>(CData::GetData(result));
5130 const FieldInfoHash* fields = GetFieldInfo(obj);
5132 if (args.length() == 1) {
5133 // There are two possible interpretations of the argument:
5134 // 1) It may be an object '{ ... }' with properties representing the
5135 // struct fields intended to ExplicitConvert wholesale to our StructType.
5136 // 2) If the struct contains one field, the arg may be intended to
5137 // ImplicitConvert directly to that arg's CType.
5138 // Thankfully, the conditions for these two possibilities to succeed
5139 // are mutually exclusive, so we can pick the right one.
5141 // Try option 1) first.
5142 if (ExplicitConvert(cx, args[0], obj, buffer))
5143 return true;
5145 if (fields->count() != 1)
5146 return false;
5148 // If ExplicitConvert failed, and there is no pending exception, then assume
5149 // hard failure (out of memory, or some other similarly serious condition).
5150 if (!JS_IsExceptionPending(cx))
5151 return false;
5153 // Otherwise, assume soft failure, and clear the pending exception so that we
5154 // can throw a different one as required.
5155 JS_ClearPendingException(cx);
5157 // Fall through to try option 2).
5160 // We have a type constructor of the form 'ctypes.StructType(a, b, c, ...)'.
5161 // ImplicitConvert each field.
5162 if (args.length() == fields->count()) {
5163 for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
5164 const FieldInfo& field = r.front().value();
5165 STATIC_ASSUME(field.mIndex < fields->count()); /* Quantified invariant */
5166 if (!ImplicitConvert(cx, args[field.mIndex], field.mType,
5167 buffer + field.mOffset,
5168 false, nullptr))
5169 return false;
5172 return true;
5175 JS_ReportError(cx, "constructor takes 0, 1, or %u arguments",
5176 fields->count());
5177 return false;
5180 const FieldInfoHash*
5181 StructType::GetFieldInfo(JSObject* obj)
5183 MOZ_ASSERT(CType::IsCType(obj));
5184 MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_struct);
5186 jsval slot = JS_GetReservedSlot(obj, SLOT_FIELDINFO);
5187 MOZ_ASSERT(!slot.isUndefined() && slot.toPrivate());
5189 return static_cast<const FieldInfoHash*>(slot.toPrivate());
5192 const FieldInfo*
5193 StructType::LookupField(JSContext* cx, JSObject* obj, JSFlatString* name)
5195 MOZ_ASSERT(CType::IsCType(obj));
5196 MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_struct);
5198 FieldInfoHash::Ptr ptr = GetFieldInfo(obj)->lookup(name);
5199 if (ptr)
5200 return &ptr->value();
5202 JSAutoByteString bytes(cx, name);
5203 if (!bytes)
5204 return nullptr;
5206 JS_ReportError(cx, "%s does not name a field", bytes.ptr());
5207 return nullptr;
5210 JSObject*
5211 StructType::BuildFieldsArray(JSContext* cx, JSObject* obj)
5213 MOZ_ASSERT(CType::IsCType(obj));
5214 MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_struct);
5215 MOZ_ASSERT(CType::IsSizeDefined(obj));
5217 const FieldInfoHash* fields = GetFieldInfo(obj);
5218 size_t len = fields->count();
5220 // Prepare a new array for the 'fields' property of the StructType.
5221 JS::AutoValueVector fieldsVec(cx);
5222 if (!fieldsVec.resize(len))
5223 return nullptr;
5225 for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
5226 const FieldInfoHash::Entry& entry = r.front();
5227 // Add the field descriptor to the array.
5228 if (!AddFieldToArray(cx, fieldsVec[entry.value().mIndex],
5229 entry.key(), entry.value().mType))
5230 return nullptr;
5233 RootedObject fieldsProp(cx, JS_NewArrayObject(cx, fieldsVec));
5234 if (!fieldsProp)
5235 return nullptr;
5237 // Seal the fields array.
5238 if (!JS_FreezeObject(cx, fieldsProp))
5239 return nullptr;
5241 return fieldsProp;
5244 /* static */ bool
5245 StructType::IsStruct(HandleValue v)
5247 if (!v.isObject())
5248 return false;
5249 JSObject* obj = &v.toObject();
5250 return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_struct;
5253 bool
5254 StructType::FieldsArrayGetter(JSContext* cx, JS::CallArgs args)
5256 RootedObject obj(cx, &args.thisv().toObject());
5258 args.rval().set(JS_GetReservedSlot(obj, SLOT_FIELDS));
5260 if (!CType::IsSizeDefined(obj)) {
5261 MOZ_ASSERT(args.rval().isUndefined());
5262 return true;
5265 if (args.rval().isUndefined()) {
5266 // Build the 'fields' array lazily.
5267 JSObject* fields = BuildFieldsArray(cx, obj);
5268 if (!fields)
5269 return false;
5270 JS_SetReservedSlot(obj, SLOT_FIELDS, OBJECT_TO_JSVAL(fields));
5272 args.rval().setObject(*fields);
5275 MOZ_ASSERT(args.rval().isObject());
5276 MOZ_ASSERT(JS_IsArrayObject(cx, args.rval()));
5277 return true;
5280 bool
5281 StructType::FieldGetter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp)
5283 if (!CData::IsCData(obj)) {
5284 JS_ReportError(cx, "not a CData");
5285 return false;
5288 JSObject* typeObj = CData::GetCType(obj);
5289 if (CType::GetTypeCode(typeObj) != TYPE_struct) {
5290 JS_ReportError(cx, "not a StructType");
5291 return false;
5294 const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_FLAT_STRING(idval));
5295 if (!field)
5296 return false;
5298 char* data = static_cast<char*>(CData::GetData(obj)) + field->mOffset;
5299 RootedObject fieldType(cx, field->mType);
5300 return ConvertToJS(cx, fieldType, obj, data, false, false, vp);
5303 bool
5304 StructType::FieldSetter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, MutableHandleValue vp)
5306 if (!CData::IsCData(obj)) {
5307 JS_ReportError(cx, "not a CData");
5308 return false;
5311 JSObject* typeObj = CData::GetCType(obj);
5312 if (CType::GetTypeCode(typeObj) != TYPE_struct) {
5313 JS_ReportError(cx, "not a StructType");
5314 return false;
5317 const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_FLAT_STRING(idval));
5318 if (!field)
5319 return false;
5321 char* data = static_cast<char*>(CData::GetData(obj)) + field->mOffset;
5322 return ImplicitConvert(cx, vp, field->mType, data, false, nullptr);
5325 bool
5326 StructType::AddressOfField(JSContext* cx, unsigned argc, jsval* vp)
5328 CallArgs args = CallArgsFromVp(argc, vp);
5329 RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
5330 if (!obj)
5331 return false;
5332 if (!CData::IsCData(obj)) {
5333 JS_ReportError(cx, "not a CData");
5334 return false;
5337 JSObject* typeObj = CData::GetCType(obj);
5338 if (CType::GetTypeCode(typeObj) != TYPE_struct) {
5339 JS_ReportError(cx, "not a StructType");
5340 return false;
5343 if (args.length() != 1) {
5344 JS_ReportError(cx, "addressOfField takes one argument");
5345 return false;
5348 JSFlatString* str = JS_FlattenString(cx, args[0].toString());
5349 if (!str)
5350 return false;
5352 const FieldInfo* field = LookupField(cx, typeObj, str);
5353 if (!field)
5354 return false;
5356 RootedObject baseType(cx, field->mType);
5357 RootedObject pointerType(cx, PointerType::CreateInternal(cx, baseType));
5358 if (!pointerType)
5359 return false;
5361 // Create a PointerType CData object containing null.
5362 JSObject* result = CData::Create(cx, pointerType, NullPtr(), nullptr, true);
5363 if (!result)
5364 return false;
5366 args.rval().setObject(*result);
5368 // Manually set the pointer inside the object, so we skip the conversion step.
5369 void** data = static_cast<void**>(CData::GetData(result));
5370 *data = static_cast<char*>(CData::GetData(obj)) + field->mOffset;
5371 return true;
5374 /*******************************************************************************
5375 ** FunctionType implementation
5376 *******************************************************************************/
5378 // Helper class for handling allocation of function arguments.
5379 struct AutoValue
5381 AutoValue() : mData(nullptr) { }
5383 ~AutoValue()
5385 js_free(mData);
5388 bool SizeToType(JSContext* cx, JSObject* type)
5390 // Allocate a minimum of sizeof(ffi_arg) to handle small integers.
5391 size_t size = Align(CType::GetSize(type), sizeof(ffi_arg));
5392 mData = js_malloc(size);
5393 if (mData)
5394 memset(mData, 0, size);
5395 return mData != nullptr;
5398 void* mData;
5401 static bool
5402 GetABI(JSContext* cx, jsval abiType, ffi_abi* result)
5404 if (abiType.isPrimitive())
5405 return false;
5407 ABICode abi = GetABICode(abiType.toObjectOrNull());
5409 // determine the ABI from the subset of those available on the
5410 // given platform. ABI_DEFAULT specifies the default
5411 // C calling convention (cdecl) on each platform.
5412 switch (abi) {
5413 case ABI_DEFAULT:
5414 *result = FFI_DEFAULT_ABI;
5415 return true;
5416 case ABI_STDCALL:
5417 case ABI_WINAPI:
5418 #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
5419 *result = FFI_STDCALL;
5420 return true;
5421 #elif (defined(_WIN64))
5422 // We'd like the same code to work across Win32 and Win64, so stdcall_api
5423 // and winapi_abi become aliases to the lone Win64 ABI.
5424 *result = FFI_WIN64;
5425 return true;
5426 #endif
5427 case INVALID_ABI:
5428 break;
5430 return false;
5433 static JSObject*
5434 PrepareType(JSContext* cx, jsval type)
5436 if (type.isPrimitive() || !CType::IsCType(type.toObjectOrNull())) {
5437 JS_ReportError(cx, "not a ctypes type");
5438 return nullptr;
5441 JSObject* result = type.toObjectOrNull();
5442 TypeCode typeCode = CType::GetTypeCode(result);
5444 if (typeCode == TYPE_array) {
5445 // convert array argument types to pointers, just like C.
5446 // ImplicitConvert will do the same, when passing an array as data.
5447 RootedObject baseType(cx, ArrayType::GetBaseType(result));
5448 result = PointerType::CreateInternal(cx, baseType);
5449 if (!result)
5450 return nullptr;
5452 } else if (typeCode == TYPE_void_t || typeCode == TYPE_function) {
5453 // disallow void or function argument types
5454 JS_ReportError(cx, "Cannot have void or function argument type");
5455 return nullptr;
5458 if (!CType::IsSizeDefined(result)) {
5459 JS_ReportError(cx, "Argument type must have defined size");
5460 return nullptr;
5463 // libffi cannot pass types of zero size by value.
5464 MOZ_ASSERT(CType::GetSize(result) != 0);
5466 return result;
5469 static JSObject*
5470 PrepareReturnType(JSContext* cx, jsval type)
5472 if (type.isPrimitive() || !CType::IsCType(type.toObjectOrNull())) {
5473 JS_ReportError(cx, "not a ctypes type");
5474 return nullptr;
5477 JSObject* result = type.toObjectOrNull();
5478 TypeCode typeCode = CType::GetTypeCode(result);
5480 // Arrays and functions can never be return types.
5481 if (typeCode == TYPE_array || typeCode == TYPE_function) {
5482 JS_ReportError(cx, "Return type cannot be an array or function");
5483 return nullptr;
5486 if (typeCode != TYPE_void_t && !CType::IsSizeDefined(result)) {
5487 JS_ReportError(cx, "Return type must have defined size");
5488 return nullptr;
5491 // libffi cannot pass types of zero size by value.
5492 MOZ_ASSERT(typeCode == TYPE_void_t || CType::GetSize(result) != 0);
5494 return result;
5497 static MOZ_ALWAYS_INLINE bool
5498 IsEllipsis(JSContext* cx, jsval v, bool* isEllipsis)
5500 *isEllipsis = false;
5501 if (!v.isString())
5502 return true;
5503 JSString* str = v.toString();
5504 if (str->length() != 3)
5505 return true;
5506 JSLinearString* linear = str->ensureLinear(cx);
5507 if (!linear)
5508 return false;
5509 char16_t dot = '.';
5510 *isEllipsis = (linear->latin1OrTwoByteChar(0) == dot &&
5511 linear->latin1OrTwoByteChar(1) == dot &&
5512 linear->latin1OrTwoByteChar(2) == dot);
5513 return true;
5516 static bool
5517 PrepareCIF(JSContext* cx,
5518 FunctionInfo* fninfo)
5520 ffi_abi abi;
5521 if (!GetABI(cx, OBJECT_TO_JSVAL(fninfo->mABI), &abi)) {
5522 JS_ReportError(cx, "Invalid ABI specification");
5523 return false;
5526 ffi_type* rtype = CType::GetFFIType(cx, fninfo->mReturnType);
5527 if (!rtype)
5528 return false;
5530 ffi_status status =
5531 ffi_prep_cif(&fninfo->mCIF,
5532 abi,
5533 fninfo->mFFITypes.length(),
5534 rtype,
5535 fninfo->mFFITypes.begin());
5537 switch (status) {
5538 case FFI_OK:
5539 return true;
5540 case FFI_BAD_ABI:
5541 JS_ReportError(cx, "Invalid ABI specification");
5542 return false;
5543 case FFI_BAD_TYPEDEF:
5544 JS_ReportError(cx, "Invalid type specification");
5545 return false;
5546 default:
5547 JS_ReportError(cx, "Unknown libffi error");
5548 return false;
5552 void
5553 FunctionType::BuildSymbolName(JSString* name,
5554 JSObject* typeObj,
5555 AutoCString& result)
5557 FunctionInfo* fninfo = GetFunctionInfo(typeObj);
5559 switch (GetABICode(fninfo->mABI)) {
5560 case ABI_DEFAULT:
5561 case ABI_WINAPI:
5562 // For cdecl or WINAPI functions, no mangling is necessary.
5563 AppendString(result, name);
5564 break;
5566 case ABI_STDCALL: {
5567 #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
5568 // On WIN32, stdcall functions look like:
5569 // _foo@40
5570 // where 'foo' is the function name, and '40' is the aligned size of the
5571 // arguments.
5572 AppendString(result, "_");
5573 AppendString(result, name);
5574 AppendString(result, "@");
5576 // Compute the suffix by aligning each argument to sizeof(ffi_arg).
5577 size_t size = 0;
5578 for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
5579 JSObject* argType = fninfo->mArgTypes[i];
5580 size += Align(CType::GetSize(argType), sizeof(ffi_arg));
5583 IntegerToString(size, 10, result);
5584 #elif defined(_WIN64)
5585 // On Win64, stdcall is an alias to the default ABI for compatibility, so no
5586 // mangling is done.
5587 AppendString(result, name);
5588 #endif
5589 break;
5592 case INVALID_ABI:
5593 MOZ_CRASH("invalid abi");
5597 static FunctionInfo*
5598 NewFunctionInfo(JSContext* cx,
5599 jsval abiType,
5600 jsval returnType,
5601 jsval* argTypes,
5602 unsigned argLength)
5604 AutoPtr<FunctionInfo> fninfo(cx->new_<FunctionInfo>());
5605 if (!fninfo) {
5606 JS_ReportOutOfMemory(cx);
5607 return nullptr;
5610 ffi_abi abi;
5611 if (!GetABI(cx, abiType, &abi)) {
5612 JS_ReportError(cx, "Invalid ABI specification");
5613 return nullptr;
5615 fninfo->mABI = abiType.toObjectOrNull();
5617 // prepare the result type
5618 fninfo->mReturnType = PrepareReturnType(cx, returnType);
5619 if (!fninfo->mReturnType)
5620 return nullptr;
5622 // prepare the argument types
5623 if (!fninfo->mArgTypes.reserve(argLength) ||
5624 !fninfo->mFFITypes.reserve(argLength)) {
5625 JS_ReportOutOfMemory(cx);
5626 return nullptr;
5629 fninfo->mIsVariadic = false;
5631 for (uint32_t i = 0; i < argLength; ++i) {
5632 bool isEllipsis;
5633 if (!IsEllipsis(cx, argTypes[i], &isEllipsis))
5634 return nullptr;
5635 if (isEllipsis) {
5636 fninfo->mIsVariadic = true;
5637 if (i < 1) {
5638 JS_ReportError(cx, "\"...\" may not be the first and only parameter "
5639 "type of a variadic function declaration");
5640 return nullptr;
5642 if (i < argLength - 1) {
5643 JS_ReportError(cx, "\"...\" must be the last parameter type of a "
5644 "variadic function declaration");
5645 return nullptr;
5647 if (GetABICode(fninfo->mABI) != ABI_DEFAULT) {
5648 JS_ReportError(cx, "Variadic functions must use the __cdecl calling "
5649 "convention");
5650 return nullptr;
5652 break;
5655 JSObject* argType = PrepareType(cx, argTypes[i]);
5656 if (!argType)
5657 return nullptr;
5659 ffi_type* ffiType = CType::GetFFIType(cx, argType);
5660 if (!ffiType)
5661 return nullptr;
5663 fninfo->mArgTypes.infallibleAppend(argType);
5664 fninfo->mFFITypes.infallibleAppend(ffiType);
5667 if (fninfo->mIsVariadic)
5668 // wait to PrepareCIF until function is called
5669 return fninfo.forget();
5671 if (!PrepareCIF(cx, fninfo.get()))
5672 return nullptr;
5674 return fninfo.forget();
5677 bool
5678 FunctionType::Create(JSContext* cx, unsigned argc, jsval* vp)
5680 // Construct and return a new FunctionType object.
5681 CallArgs args = CallArgsFromVp(argc, vp);
5682 if (args.length() < 2 || args.length() > 3) {
5683 JS_ReportError(cx, "FunctionType takes two or three arguments");
5684 return false;
5687 AutoValueVector argTypes(cx);
5688 RootedObject arrayObj(cx, nullptr);
5690 if (args.length() == 3) {
5691 // Prepare an array of jsvals for the arguments.
5692 if (args[2].isObject())
5693 arrayObj = &args[2].toObject();
5694 if (!arrayObj || !JS_IsArrayObject(cx, arrayObj)) {
5695 JS_ReportError(cx, "third argument must be an array");
5696 return false;
5699 uint32_t len;
5700 ASSERT_OK(JS_GetArrayLength(cx, arrayObj, &len));
5702 if (!argTypes.resize(len)) {
5703 JS_ReportOutOfMemory(cx);
5704 return false;
5708 // Pull out the argument types from the array, if any.
5709 MOZ_ASSERT_IF(argTypes.length(), arrayObj);
5710 for (uint32_t i = 0; i < argTypes.length(); ++i) {
5711 if (!JS_GetElement(cx, arrayObj, i, argTypes[i]))
5712 return false;
5715 JSObject* result = CreateInternal(cx, args[0], args[1],
5716 argTypes.begin(), argTypes.length());
5717 if (!result)
5718 return false;
5720 args.rval().setObject(*result);
5721 return true;
5724 JSObject*
5725 FunctionType::CreateInternal(JSContext* cx,
5726 jsval abi,
5727 jsval rtype,
5728 jsval* argtypes,
5729 unsigned arglen)
5731 // Determine and check the types, and prepare the function CIF.
5732 AutoPtr<FunctionInfo> fninfo(NewFunctionInfo(cx, abi, rtype, argtypes, arglen));
5733 if (!fninfo)
5734 return nullptr;
5736 // Get ctypes.FunctionType.prototype and the common prototype for CData objects
5737 // of this type, from ctypes.CType.prototype.
5738 RootedObject typeProto(cx, CType::GetProtoFromType(cx, fninfo->mReturnType,
5739 SLOT_FUNCTIONPROTO));
5740 if (!typeProto)
5741 return nullptr;
5742 RootedObject dataProto(cx, CType::GetProtoFromType(cx, fninfo->mReturnType,
5743 SLOT_FUNCTIONDATAPROTO));
5744 if (!dataProto)
5745 return nullptr;
5747 // Create a new CType object with the common properties and slots.
5748 JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_function,
5749 nullptr, JSVAL_VOID, JSVAL_VOID, nullptr);
5750 if (!typeObj)
5751 return nullptr;
5753 // Stash the FunctionInfo in a reserved slot.
5754 JS_SetReservedSlot(typeObj, SLOT_FNINFO, PRIVATE_TO_JSVAL(fninfo.forget()));
5756 return typeObj;
5759 // Construct a function pointer to a JS function (see CClosure::Create()).
5760 // Regular function pointers are constructed directly in
5761 // PointerType::ConstructData().
5762 bool
5763 FunctionType::ConstructData(JSContext* cx,
5764 HandleObject typeObj,
5765 HandleObject dataObj,
5766 HandleObject fnObj,
5767 HandleObject thisObj,
5768 jsval errVal)
5770 MOZ_ASSERT(CType::GetTypeCode(typeObj) == TYPE_function);
5772 PRFuncPtr* data = static_cast<PRFuncPtr*>(CData::GetData(dataObj));
5774 FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
5775 if (fninfo->mIsVariadic) {
5776 JS_ReportError(cx, "Can't declare a variadic callback function");
5777 return false;
5779 if (GetABICode(fninfo->mABI) == ABI_WINAPI) {
5780 JS_ReportError(cx, "Can't declare a ctypes.winapi_abi callback function, "
5781 "use ctypes.stdcall_abi instead");
5782 return false;
5785 RootedObject closureObj(cx, CClosure::Create(cx, typeObj, fnObj, thisObj, errVal, data));
5786 if (!closureObj)
5787 return false;
5789 // Set the closure object as the referent of the new CData object.
5790 JS_SetReservedSlot(dataObj, SLOT_REFERENT, OBJECT_TO_JSVAL(closureObj));
5792 // Seal the CData object, to prevent modification of the function pointer.
5793 // This permanently associates this object with the closure, and avoids
5794 // having to do things like reset SLOT_REFERENT when someone tries to
5795 // change the pointer value.
5796 // XXX This will need to change when bug 541212 is fixed -- CData::ValueSetter
5797 // could be called on a frozen object.
5798 return JS_FreezeObject(cx, dataObj);
5801 typedef Array<AutoValue, 16> AutoValueAutoArray;
5803 static bool
5804 ConvertArgument(JSContext* cx,
5805 HandleValue arg,
5806 JSObject* type,
5807 AutoValue* value,
5808 AutoValueAutoArray* strings)
5810 if (!value->SizeToType(cx, type)) {
5811 JS_ReportAllocationOverflow(cx);
5812 return false;
5815 bool freePointer = false;
5816 if (!ImplicitConvert(cx, arg, type, value->mData, true, &freePointer))
5817 return false;
5819 if (freePointer) {
5820 // ImplicitConvert converted a string for us, which we have to free.
5821 // Keep track of it.
5822 if (!strings->growBy(1)) {
5823 JS_ReportOutOfMemory(cx);
5824 return false;
5826 strings->back().mData = *static_cast<char**>(value->mData);
5829 return true;
5832 bool
5833 FunctionType::Call(JSContext* cx,
5834 unsigned argc,
5835 jsval* vp)
5837 CallArgs args = CallArgsFromVp(argc, vp);
5838 // get the callee object...
5839 RootedObject obj(cx, &args.callee());
5840 if (!CData::IsCData(obj)) {
5841 JS_ReportError(cx, "not a CData");
5842 return false;
5845 RootedObject typeObj(cx, CData::GetCType(obj));
5846 if (CType::GetTypeCode(typeObj) != TYPE_pointer) {
5847 JS_ReportError(cx, "not a FunctionType.ptr");
5848 return false;
5851 typeObj = PointerType::GetBaseType(typeObj);
5852 if (CType::GetTypeCode(typeObj) != TYPE_function) {
5853 JS_ReportError(cx, "not a FunctionType.ptr");
5854 return false;
5857 FunctionInfo* fninfo = GetFunctionInfo(typeObj);
5858 uint32_t argcFixed = fninfo->mArgTypes.length();
5860 if ((!fninfo->mIsVariadic && args.length() != argcFixed) ||
5861 (fninfo->mIsVariadic && args.length() < argcFixed)) {
5862 JS_ReportError(cx, "Number of arguments does not match declaration");
5863 return false;
5866 // Check if we have a Library object. If we do, make sure it's open.
5867 jsval slot = JS_GetReservedSlot(obj, SLOT_REFERENT);
5868 if (!slot.isUndefined() && Library::IsLibrary(&slot.toObject())) {
5869 PRLibrary* library = Library::GetLibrary(&slot.toObject());
5870 if (!library) {
5871 JS_ReportError(cx, "library is not open");
5872 return false;
5876 // prepare the values for each argument
5877 AutoValueAutoArray values;
5878 AutoValueAutoArray strings;
5879 if (!values.resize(args.length())) {
5880 JS_ReportOutOfMemory(cx);
5881 return false;
5884 for (unsigned i = 0; i < argcFixed; ++i)
5885 if (!ConvertArgument(cx, args[i], fninfo->mArgTypes[i], &values[i], &strings))
5886 return false;
5888 if (fninfo->mIsVariadic) {
5889 if (!fninfo->mFFITypes.resize(args.length())) {
5890 JS_ReportOutOfMemory(cx);
5891 return false;
5894 RootedObject obj(cx); // Could reuse obj instead of declaring a second
5895 RootedObject type(cx); // RootedObject, but readability would suffer.
5897 for (uint32_t i = argcFixed; i < args.length(); ++i) {
5898 if (args[i].isPrimitive() ||
5899 !CData::IsCData(obj = &args[i].toObject())) {
5900 // Since we know nothing about the CTypes of the ... arguments,
5901 // they absolutely must be CData objects already.
5902 JS_ReportError(cx, "argument %d of type %s is not a CData object",
5903 i, InformalValueTypeName(args[i]));
5904 return false;
5906 if (!(type = CData::GetCType(obj)) ||
5907 !(type = PrepareType(cx, OBJECT_TO_JSVAL(type))) ||
5908 // Relying on ImplicitConvert only for the limited purpose of
5909 // converting one CType to another (e.g., T[] to T*).
5910 !ConvertArgument(cx, args[i], type, &values[i], &strings) ||
5911 !(fninfo->mFFITypes[i] = CType::GetFFIType(cx, type))) {
5912 // These functions report their own errors.
5913 return false;
5916 if (!PrepareCIF(cx, fninfo))
5917 return false;
5920 // initialize a pointer to an appropriate location, for storing the result
5921 AutoValue returnValue;
5922 TypeCode typeCode = CType::GetTypeCode(fninfo->mReturnType);
5923 if (typeCode != TYPE_void_t &&
5924 !returnValue.SizeToType(cx, fninfo->mReturnType)) {
5925 JS_ReportAllocationOverflow(cx);
5926 return false;
5929 // Let the runtime callback know that we are about to call into C.
5930 js::AutoCTypesActivityCallback autoCallback(cx, js::CTYPES_CALL_BEGIN, js::CTYPES_CALL_END);
5932 uintptr_t fn = *reinterpret_cast<uintptr_t*>(CData::GetData(obj));
5934 #if defined(XP_WIN)
5935 int32_t lastErrorStatus; // The status as defined by |GetLastError|
5936 int32_t savedLastError = GetLastError();
5937 SetLastError(0);
5938 #endif //defined(XP_WIN)
5939 int errnoStatus; // The status as defined by |errno|
5940 int savedErrno = errno;
5941 errno = 0;
5943 ffi_call(&fninfo->mCIF, FFI_FN(fn), returnValue.mData,
5944 reinterpret_cast<void**>(values.begin()));
5946 // Save error value.
5947 // We need to save it before leaving the scope of |suspend| as destructing
5948 // |suspend| has the side-effect of clearing |GetLastError|
5949 // (see bug 684017).
5951 errnoStatus = errno;
5952 #if defined(XP_WIN)
5953 lastErrorStatus = GetLastError();
5954 SetLastError(savedLastError);
5955 #endif // defined(XP_WIN)
5957 errno = savedErrno;
5959 // We're no longer calling into C.
5960 autoCallback.DoEndCallback();
5962 // Store the error value for later consultation with |ctypes.getStatus|
5963 JSObject* objCTypes = CType::GetGlobalCTypes(cx, typeObj);
5964 if (!objCTypes)
5965 return false;
5967 JS_SetReservedSlot(objCTypes, SLOT_ERRNO, INT_TO_JSVAL(errnoStatus));
5968 #if defined(XP_WIN)
5969 JS_SetReservedSlot(objCTypes, SLOT_LASTERROR, INT_TO_JSVAL(lastErrorStatus));
5970 #endif // defined(XP_WIN)
5972 // Small integer types get returned as a word-sized ffi_arg. Coerce it back
5973 // into the correct size for ConvertToJS.
5974 switch (typeCode) {
5975 #define INTEGRAL_CASE(name, type, ffiType) \
5976 case TYPE_##name: \
5977 if (sizeof(type) < sizeof(ffi_arg)) { \
5978 ffi_arg data = *static_cast<ffi_arg*>(returnValue.mData); \
5979 *static_cast<type*>(returnValue.mData) = static_cast<type>(data); \
5981 break;
5982 CTYPES_FOR_EACH_INT_TYPE(INTEGRAL_CASE)
5983 CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGRAL_CASE)
5984 CTYPES_FOR_EACH_BOOL_TYPE(INTEGRAL_CASE)
5985 CTYPES_FOR_EACH_CHAR_TYPE(INTEGRAL_CASE)
5986 CTYPES_FOR_EACH_CHAR16_TYPE(INTEGRAL_CASE)
5987 #undef INTEGRAL_CASE
5988 default:
5989 break;
5992 // prepare a JS object from the result
5993 RootedObject returnType(cx, fninfo->mReturnType);
5994 return ConvertToJS(cx, returnType, NullPtr(), returnValue.mData, false, true, args.rval());
5997 FunctionInfo*
5998 FunctionType::GetFunctionInfo(JSObject* obj)
6000 MOZ_ASSERT(CType::IsCType(obj));
6001 MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_function);
6003 jsval slot = JS_GetReservedSlot(obj, SLOT_FNINFO);
6004 MOZ_ASSERT(!slot.isUndefined() && slot.toPrivate());
6006 return static_cast<FunctionInfo*>(slot.toPrivate());
6009 bool
6010 FunctionType::IsFunctionType(HandleValue v)
6012 if (!v.isObject())
6013 return false;
6014 JSObject* obj = &v.toObject();
6015 return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_function;
6018 bool
6019 FunctionType::ArgTypesGetter(JSContext* cx, JS::CallArgs args)
6021 JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
6023 args.rval().set(JS_GetReservedSlot(obj, SLOT_ARGS_T));
6024 if (!args.rval().isUndefined())
6025 return true;
6027 FunctionInfo* fninfo = GetFunctionInfo(obj);
6028 size_t len = fninfo->mArgTypes.length();
6030 // Prepare a new array.
6031 JS::Rooted<JSObject*> argTypes(cx);
6033 JS::AutoValueVector vec(cx);
6034 if (!vec.resize(len))
6035 return false;
6037 for (size_t i = 0; i < len; ++i)
6038 vec[i].setObject(*fninfo->mArgTypes[i]);
6040 argTypes = JS_NewArrayObject(cx, vec);
6041 if (!argTypes)
6042 return false;
6045 // Seal and cache it.
6046 if (!JS_FreezeObject(cx, argTypes))
6047 return false;
6048 JS_SetReservedSlot(obj, SLOT_ARGS_T, JS::ObjectValue(*argTypes));
6050 args.rval().setObject(*argTypes);
6051 return true;
6054 bool
6055 FunctionType::ReturnTypeGetter(JSContext* cx, JS::CallArgs args)
6057 // Get the returnType object from the FunctionInfo.
6058 args.rval().setObject(*GetFunctionInfo(&args.thisv().toObject())->mReturnType);
6059 return true;
6062 bool
6063 FunctionType::ABIGetter(JSContext* cx, JS::CallArgs args)
6065 // Get the abi object from the FunctionInfo.
6066 args.rval().setObject(*GetFunctionInfo(&args.thisv().toObject())->mABI);
6067 return true;
6070 bool
6071 FunctionType::IsVariadicGetter(JSContext* cx, JS::CallArgs args)
6073 args.rval().setBoolean(GetFunctionInfo(&args.thisv().toObject())->mIsVariadic);
6074 return true;
6077 /*******************************************************************************
6078 ** CClosure implementation
6079 *******************************************************************************/
6081 JSObject*
6082 CClosure::Create(JSContext* cx,
6083 HandleObject typeObj,
6084 HandleObject fnObj,
6085 HandleObject thisObj,
6086 jsval errVal_,
6087 PRFuncPtr* fnptr)
6089 RootedValue errVal(cx, errVal_);
6090 MOZ_ASSERT(fnObj);
6092 RootedObject result(cx, JS_NewObject(cx, &sCClosureClass, NullPtr(), NullPtr()));
6093 if (!result)
6094 return nullptr;
6096 // Get the FunctionInfo from the FunctionType.
6097 FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
6098 MOZ_ASSERT(!fninfo->mIsVariadic);
6099 MOZ_ASSERT(GetABICode(fninfo->mABI) != ABI_WINAPI);
6101 AutoPtr<ClosureInfo> cinfo(cx->new_<ClosureInfo>(JS_GetRuntime(cx)));
6102 if (!cinfo) {
6103 JS_ReportOutOfMemory(cx);
6104 return nullptr;
6107 // Get the prototype of the FunctionType object, of class CTypeProto,
6108 // which stores our JSContext for use with the closure.
6109 RootedObject proto(cx);
6110 if (!JS_GetPrototype(cx, typeObj, &proto))
6111 return nullptr;
6112 MOZ_ASSERT(proto);
6113 MOZ_ASSERT(CType::IsCTypeProto(proto));
6115 // Get a JSContext for use with the closure.
6116 cinfo->cx = js::DefaultJSContext(JS_GetRuntime(cx));
6118 // Prepare the error sentinel value. It's important to do this now, because
6119 // we might be unable to convert the value to the proper type. If so, we want
6120 // the caller to know about it _now_, rather than some uncertain time in the
6121 // future when the error sentinel is actually needed.
6122 if (!errVal.isUndefined()) {
6124 // Make sure the callback returns something.
6125 if (CType::GetTypeCode(fninfo->mReturnType) == TYPE_void_t) {
6126 JS_ReportError(cx, "A void callback can't pass an error sentinel");
6127 return nullptr;
6130 // With the exception of void, the FunctionType constructor ensures that
6131 // the return type has a defined size.
6132 MOZ_ASSERT(CType::IsSizeDefined(fninfo->mReturnType));
6134 // Allocate a buffer for the return value.
6135 size_t rvSize = CType::GetSize(fninfo->mReturnType);
6136 cinfo->errResult = result->zone()->pod_malloc<uint8_t>(rvSize);
6137 if (!cinfo->errResult)
6138 return nullptr;
6140 // Do the value conversion. This might fail, in which case we throw.
6141 if (!ImplicitConvert(cx, errVal, fninfo->mReturnType, cinfo->errResult,
6142 false, nullptr))
6143 return nullptr;
6144 } else {
6145 cinfo->errResult = nullptr;
6148 // Copy the important bits of context into cinfo.
6149 cinfo->closureObj = result;
6150 cinfo->typeObj = typeObj;
6151 cinfo->thisObj = thisObj;
6152 cinfo->jsfnObj = fnObj;
6154 // Create an ffi_closure object and initialize it.
6155 void* code;
6156 cinfo->closure =
6157 static_cast<ffi_closure*>(ffi_closure_alloc(sizeof(ffi_closure), &code));
6158 if (!cinfo->closure || !code) {
6159 JS_ReportError(cx, "couldn't create closure - libffi error");
6160 return nullptr;
6163 ffi_status status = ffi_prep_closure_loc(cinfo->closure, &fninfo->mCIF,
6164 CClosure::ClosureStub, cinfo.get(), code);
6165 if (status != FFI_OK) {
6166 JS_ReportError(cx, "couldn't create closure - libffi error");
6167 return nullptr;
6170 // Stash the ClosureInfo struct on our new object.
6171 JS_SetReservedSlot(result, SLOT_CLOSUREINFO, PRIVATE_TO_JSVAL(cinfo.forget()));
6173 // Casting between void* and a function pointer is forbidden in C and C++.
6174 // Do it via an integral type.
6175 *fnptr = reinterpret_cast<PRFuncPtr>(reinterpret_cast<uintptr_t>(code));
6176 return result;
6179 void
6180 CClosure::Trace(JSTracer* trc, JSObject* obj)
6182 // Make sure our ClosureInfo slot is legit. If it's not, bail.
6183 jsval slot = JS_GetReservedSlot(obj, SLOT_CLOSUREINFO);
6184 if (slot.isUndefined())
6185 return;
6187 ClosureInfo* cinfo = static_cast<ClosureInfo*>(slot.toPrivate());
6189 // Identify our objects to the tracer. (There's no need to identify
6190 // 'closureObj', since that's us.)
6191 JS_CallObjectTracer(trc, &cinfo->typeObj, "typeObj");
6192 JS_CallObjectTracer(trc, &cinfo->jsfnObj, "jsfnObj");
6193 if (cinfo->thisObj)
6194 JS_CallObjectTracer(trc, &cinfo->thisObj, "thisObj");
6197 void
6198 CClosure::Finalize(JSFreeOp* fop, JSObject* obj)
6200 // Make sure our ClosureInfo slot is legit. If it's not, bail.
6201 jsval slot = JS_GetReservedSlot(obj, SLOT_CLOSUREINFO);
6202 if (slot.isUndefined())
6203 return;
6205 ClosureInfo* cinfo = static_cast<ClosureInfo*>(slot.toPrivate());
6206 FreeOp::get(fop)->delete_(cinfo);
6209 void
6210 CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData)
6212 MOZ_ASSERT(cif);
6213 MOZ_ASSERT(result);
6214 MOZ_ASSERT(args);
6215 MOZ_ASSERT(userData);
6217 // Retrieve the essentials from our closure object.
6218 ClosureInfo* cinfo = static_cast<ClosureInfo*>(userData);
6219 JSContext* cx = cinfo->cx;
6221 // Let the runtime callback know that we are about to call into JS again. The end callback will
6222 // fire automatically when we exit this function.
6223 js::AutoCTypesActivityCallback autoCallback(cx, js::CTYPES_CALLBACK_BEGIN,
6224 js::CTYPES_CALLBACK_END);
6226 RootedObject typeObj(cx, cinfo->typeObj);
6227 RootedObject thisObj(cx, cinfo->thisObj);
6228 RootedValue jsfnVal(cx, ObjectValue(*cinfo->jsfnObj));
6230 JS_AbortIfWrongThread(JS_GetRuntime(cx));
6232 JSAutoRequest ar(cx);
6233 JSAutoCompartment ac(cx, cinfo->jsfnObj);
6235 // Assert that our CIFs agree.
6236 FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
6237 MOZ_ASSERT(cif == &fninfo->mCIF);
6239 TypeCode typeCode = CType::GetTypeCode(fninfo->mReturnType);
6241 // Initialize the result to zero, in case something fails. Small integer types
6242 // are promoted to a word-sized ffi_arg, so we must be careful to zero the
6243 // whole word.
6244 size_t rvSize = 0;
6245 if (cif->rtype != &ffi_type_void) {
6246 rvSize = cif->rtype->size;
6247 switch (typeCode) {
6248 #define INTEGRAL_CASE(name, type, ffiType) case TYPE_##name:
6249 CTYPES_FOR_EACH_INT_TYPE(INTEGRAL_CASE)
6250 CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGRAL_CASE)
6251 CTYPES_FOR_EACH_BOOL_TYPE(INTEGRAL_CASE)
6252 CTYPES_FOR_EACH_CHAR_TYPE(INTEGRAL_CASE)
6253 CTYPES_FOR_EACH_CHAR16_TYPE(INTEGRAL_CASE)
6254 #undef INTEGRAL_CASE
6255 rvSize = Align(rvSize, sizeof(ffi_arg));
6256 break;
6257 default:
6258 break;
6260 memset(result, 0, rvSize);
6263 // Set up an array for converted arguments.
6264 JS::AutoValueVector argv(cx);
6265 if (!argv.resize(cif->nargs)) {
6266 JS_ReportOutOfMemory(cx);
6267 return;
6270 for (uint32_t i = 0; i < cif->nargs; ++i) {
6271 // Convert each argument, and have any CData objects created depend on
6272 // the existing buffers.
6273 RootedObject argType(cx, fninfo->mArgTypes[i]);
6274 if (!ConvertToJS(cx, argType, NullPtr(), args[i], false, false, argv[i]))
6275 return;
6278 // Call the JS function. 'thisObj' may be nullptr, in which case the JS
6279 // engine will find an appropriate object to use.
6280 RootedValue rval(cx);
6281 bool success = JS_CallFunctionValue(cx, thisObj, jsfnVal, argv, &rval);
6283 // Convert the result. Note that we pass 'isArgument = false', such that
6284 // ImplicitConvert will *not* autoconvert a JS string into a pointer-to-char
6285 // type, which would require an allocation that we can't track. The JS
6286 // function must perform this conversion itself and return a PointerType
6287 // CData; thusly, the burden of freeing the data is left to the user.
6288 if (success && cif->rtype != &ffi_type_void)
6289 success = ImplicitConvert(cx, rval, fninfo->mReturnType, result, false,
6290 nullptr);
6292 if (!success) {
6293 // Something failed. The callee may have thrown, or it may not have
6294 // returned a value that ImplicitConvert() was happy with. Depending on how
6295 // prudent the consumer has been, we may or may not have a recovery plan.
6297 // In any case, a JS exception cannot be passed to C code, so report the
6298 // exception if any and clear it from the cx.
6299 if (JS_IsExceptionPending(cx))
6300 JS_ReportPendingException(cx);
6302 if (cinfo->errResult) {
6303 // Good case: we have a sentinel that we can return. Copy it in place of
6304 // the actual return value, and then proceed.
6306 // The buffer we're returning might be larger than the size of the return
6307 // type, due to libffi alignment issues (see above). But it should never
6308 // be smaller.
6309 size_t copySize = CType::GetSize(fninfo->mReturnType);
6310 MOZ_ASSERT(copySize <= rvSize);
6311 memcpy(result, cinfo->errResult, copySize);
6312 } else {
6313 // Bad case: not much we can do here. The rv is already zeroed out, so we
6314 // just report (another) error and hope for the best. JS_ReportError will
6315 // actually throw an exception here, so then we have to report it. Again.
6316 // Ugh.
6317 JS_ReportError(cx, "JavaScript callback failed, and an error sentinel "
6318 "was not specified.");
6319 if (JS_IsExceptionPending(cx))
6320 JS_ReportPendingException(cx);
6322 return;
6326 // Small integer types must be returned as a word-sized ffi_arg. Coerce it
6327 // back into the size libffi expects.
6328 switch (typeCode) {
6329 #define INTEGRAL_CASE(name, type, ffiType) \
6330 case TYPE_##name: \
6331 if (sizeof(type) < sizeof(ffi_arg)) { \
6332 ffi_arg data = *static_cast<type*>(result); \
6333 *static_cast<ffi_arg*>(result) = data; \
6335 break;
6336 CTYPES_FOR_EACH_INT_TYPE(INTEGRAL_CASE)
6337 CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGRAL_CASE)
6338 CTYPES_FOR_EACH_BOOL_TYPE(INTEGRAL_CASE)
6339 CTYPES_FOR_EACH_CHAR_TYPE(INTEGRAL_CASE)
6340 CTYPES_FOR_EACH_CHAR16_TYPE(INTEGRAL_CASE)
6341 #undef INTEGRAL_CASE
6342 default:
6343 break;
6347 /*******************************************************************************
6348 ** CData implementation
6349 *******************************************************************************/
6351 // Create a new CData object of type 'typeObj' containing binary data supplied
6352 // in 'source', optionally with a referent object 'refObj'.
6354 // * 'typeObj' must be a CType of defined (but possibly zero) size.
6356 // * If an object 'refObj' is supplied, the new CData object stores the
6357 // referent object in a reserved slot for GC safety, such that 'refObj' will
6358 // be held alive by the resulting CData object. 'refObj' may or may not be
6359 // a CData object; merely an object we want to keep alive.
6360 // * If 'refObj' is a CData object, 'ownResult' must be false.
6361 // * Otherwise, 'refObj' is a Library or CClosure object, and 'ownResult'
6362 // may be true or false.
6363 // * Otherwise 'refObj' is nullptr. In this case, 'ownResult' may be true or
6364 // false.
6366 // * If 'ownResult' is true, the CData object will allocate an appropriately
6367 // sized buffer, and free it upon finalization. If 'source' data is
6368 // supplied, the data will be copied from 'source' into the buffer;
6369 // otherwise, the entirety of the new buffer will be initialized to zero.
6370 // * If 'ownResult' is false, the new CData's buffer refers to a slice of
6371 // another buffer kept alive by 'refObj'. 'source' data must be provided,
6372 // and the new CData's buffer will refer to 'source'.
6373 JSObject*
6374 CData::Create(JSContext* cx,
6375 HandleObject typeObj,
6376 HandleObject refObj,
6377 void* source,
6378 bool ownResult)
6380 MOZ_ASSERT(typeObj);
6381 MOZ_ASSERT(CType::IsCType(typeObj));
6382 MOZ_ASSERT(CType::IsSizeDefined(typeObj));
6383 MOZ_ASSERT(ownResult || source);
6384 MOZ_ASSERT_IF(refObj && CData::IsCData(refObj), !ownResult);
6386 // Get the 'prototype' property from the type.
6387 jsval slot = JS_GetReservedSlot(typeObj, SLOT_PROTO);
6388 MOZ_ASSERT(slot.isObject());
6390 RootedObject proto(cx, &slot.toObject());
6391 RootedObject parent(cx, JS_GetParent(typeObj));
6392 MOZ_ASSERT(parent);
6394 RootedObject dataObj(cx, JS_NewObject(cx, &sCDataClass, proto, parent));
6395 if (!dataObj)
6396 return nullptr;
6398 // set the CData's associated type
6399 JS_SetReservedSlot(dataObj, SLOT_CTYPE, OBJECT_TO_JSVAL(typeObj));
6401 // Stash the referent object, if any, for GC safety.
6402 if (refObj)
6403 JS_SetReservedSlot(dataObj, SLOT_REFERENT, OBJECT_TO_JSVAL(refObj));
6405 // Set our ownership flag.
6406 JS_SetReservedSlot(dataObj, SLOT_OWNS, BOOLEAN_TO_JSVAL(ownResult));
6408 // attach the buffer. since it might not be 2-byte aligned, we need to
6409 // allocate an aligned space for it and store it there. :(
6410 char** buffer = cx->new_<char*>();
6411 if (!buffer) {
6412 JS_ReportOutOfMemory(cx);
6413 return nullptr;
6416 char* data;
6417 if (!ownResult) {
6418 data = static_cast<char*>(source);
6419 } else {
6420 // Initialize our own buffer.
6421 size_t size = CType::GetSize(typeObj);
6422 data = dataObj->zone()->pod_malloc<char>(size);
6423 if (!data) {
6424 // Report a catchable allocation error.
6425 JS_ReportAllocationOverflow(cx);
6426 js_free(buffer);
6427 return nullptr;
6430 if (!source)
6431 memset(data, 0, size);
6432 else
6433 memcpy(data, source, size);
6436 *buffer = data;
6437 JS_SetReservedSlot(dataObj, SLOT_DATA, PRIVATE_TO_JSVAL(buffer));
6439 return dataObj;
6442 void
6443 CData::Finalize(JSFreeOp* fop, JSObject* obj)
6445 // Delete our buffer, and the data it contains if we own it.
6446 jsval slot = JS_GetReservedSlot(obj, SLOT_OWNS);
6447 if (slot.isUndefined())
6448 return;
6450 bool owns = slot.toBoolean();
6452 slot = JS_GetReservedSlot(obj, SLOT_DATA);
6453 if (slot.isUndefined())
6454 return;
6455 char** buffer = static_cast<char**>(slot.toPrivate());
6457 if (owns)
6458 FreeOp::get(fop)->free_(*buffer);
6459 FreeOp::get(fop)->delete_(buffer);
6462 JSObject*
6463 CData::GetCType(JSObject* dataObj)
6465 MOZ_ASSERT(CData::IsCData(dataObj));
6467 jsval slot = JS_GetReservedSlot(dataObj, SLOT_CTYPE);
6468 JSObject* typeObj = slot.toObjectOrNull();
6469 MOZ_ASSERT(CType::IsCType(typeObj));
6470 return typeObj;
6473 void*
6474 CData::GetData(JSObject* dataObj)
6476 MOZ_ASSERT(CData::IsCData(dataObj));
6478 jsval slot = JS_GetReservedSlot(dataObj, SLOT_DATA);
6480 void** buffer = static_cast<void**>(slot.toPrivate());
6481 MOZ_ASSERT(buffer);
6482 MOZ_ASSERT(*buffer);
6483 return *buffer;
6486 bool
6487 CData::IsCData(JSObject* obj)
6489 return JS_GetClass(obj) == &sCDataClass;
6492 bool
6493 CData::IsCData(HandleValue v)
6495 return v.isObject() && CData::IsCData(&v.toObject());
6498 bool
6499 CData::IsCDataProto(JSObject* obj)
6501 return JS_GetClass(obj) == &sCDataProtoClass;
6504 bool
6505 CData::ValueGetter(JSContext* cx, JS::CallArgs args)
6507 RootedObject obj(cx, &args.thisv().toObject());
6509 // Convert the value to a primitive; do not create a new CData object.
6510 RootedObject ctype(cx, GetCType(obj));
6511 return ConvertToJS(cx, ctype, NullPtr(), GetData(obj), true, false, args.rval());
6514 bool
6515 CData::ValueSetter(JSContext* cx, JS::CallArgs args)
6517 RootedObject obj(cx, &args.thisv().toObject());
6518 args.rval().setUndefined();
6519 return ImplicitConvert(cx, args.get(0), GetCType(obj), GetData(obj), false, nullptr);
6522 bool
6523 CData::Address(JSContext* cx, unsigned argc, jsval* vp)
6525 CallArgs args = CallArgsFromVp(argc, vp);
6526 if (args.length() != 0) {
6527 JS_ReportError(cx, "address takes zero arguments");
6528 return false;
6531 RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
6532 if (!obj)
6533 return false;
6534 if (!IsCData(obj)) {
6535 JS_ReportError(cx, "not a CData");
6536 return false;
6539 RootedObject typeObj(cx, CData::GetCType(obj));
6540 RootedObject pointerType(cx, PointerType::CreateInternal(cx, typeObj));
6541 if (!pointerType)
6542 return false;
6544 // Create a PointerType CData object containing null.
6545 JSObject* result = CData::Create(cx, pointerType, NullPtr(), nullptr, true);
6546 if (!result)
6547 return false;
6549 args.rval().setObject(*result);
6551 // Manually set the pointer inside the object, so we skip the conversion step.
6552 void** data = static_cast<void**>(GetData(result));
6553 *data = GetData(obj);
6554 return true;
6557 bool
6558 CData::Cast(JSContext* cx, unsigned argc, jsval* vp)
6560 CallArgs args = CallArgsFromVp(argc, vp);
6561 if (args.length() != 2) {
6562 JS_ReportError(cx, "cast takes two arguments");
6563 return false;
6566 if (args[0].isPrimitive() || !CData::IsCData(&args[0].toObject())) {
6567 JS_ReportError(cx, "first argument must be a CData");
6568 return false;
6570 RootedObject sourceData(cx, &args[0].toObject());
6571 JSObject* sourceType = CData::GetCType(sourceData);
6573 if (args[1].isPrimitive() || !CType::IsCType(&args[1].toObject())) {
6574 JS_ReportError(cx, "second argument must be a CType");
6575 return false;
6578 RootedObject targetType(cx, &args[1].toObject());
6579 size_t targetSize;
6580 if (!CType::GetSafeSize(targetType, &targetSize) ||
6581 targetSize > CType::GetSize(sourceType)) {
6582 JS_ReportError(cx,
6583 "target CType has undefined or larger size than source CType");
6584 return false;
6587 // Construct a new CData object with a type of 'targetType' and a referent
6588 // of 'sourceData'.
6589 void* data = CData::GetData(sourceData);
6590 JSObject* result = CData::Create(cx, targetType, sourceData, data, false);
6591 if (!result)
6592 return false;
6594 args.rval().setObject(*result);
6595 return true;
6598 bool
6599 CData::GetRuntime(JSContext* cx, unsigned argc, jsval* vp)
6601 CallArgs args = CallArgsFromVp(argc, vp);
6602 if (args.length() != 1) {
6603 JS_ReportError(cx, "getRuntime takes one argument");
6604 return false;
6607 if (args[0].isPrimitive() || !CType::IsCType(&args[0].toObject())) {
6608 JS_ReportError(cx, "first argument must be a CType");
6609 return false;
6612 RootedObject targetType(cx, &args[0].toObject());
6613 size_t targetSize;
6614 if (!CType::GetSafeSize(targetType, &targetSize) ||
6615 targetSize != sizeof(void*)) {
6616 JS_ReportError(cx, "target CType has non-pointer size");
6617 return false;
6620 void* data = static_cast<void*>(cx->runtime());
6621 JSObject* result = CData::Create(cx, targetType, NullPtr(), &data, true);
6622 if (!result)
6623 return false;
6625 args.rval().setObject(*result);
6626 return true;
6629 typedef JS::TwoByteCharsZ (*InflateUTF8Method)(JSContext*, const JS::UTF8Chars, size_t*);
6631 static bool
6632 ReadStringCommon(JSContext* cx, InflateUTF8Method inflateUTF8, unsigned argc, jsval* vp)
6634 CallArgs args = CallArgsFromVp(argc, vp);
6635 if (args.length() != 0) {
6636 JS_ReportError(cx, "readString takes zero arguments");
6637 return false;
6640 JSObject* obj = CDataFinalizer::GetCData(cx, JS_THIS_OBJECT(cx, vp));
6641 if (!obj || !CData::IsCData(obj)) {
6642 JS_ReportError(cx, "not a CData");
6643 return false;
6646 // Make sure we are a pointer to, or an array of, an 8-bit or 16-bit
6647 // character or integer type.
6648 JSObject* baseType;
6649 JSObject* typeObj = CData::GetCType(obj);
6650 TypeCode typeCode = CType::GetTypeCode(typeObj);
6651 void* data;
6652 size_t maxLength = -1;
6653 switch (typeCode) {
6654 case TYPE_pointer:
6655 baseType = PointerType::GetBaseType(typeObj);
6656 data = *static_cast<void**>(CData::GetData(obj));
6657 if (data == nullptr) {
6658 JS_ReportError(cx, "cannot read contents of null pointer");
6659 return false;
6661 break;
6662 case TYPE_array:
6663 baseType = ArrayType::GetBaseType(typeObj);
6664 data = CData::GetData(obj);
6665 maxLength = ArrayType::GetLength(typeObj);
6666 break;
6667 default:
6668 JS_ReportError(cx, "not a PointerType or ArrayType");
6669 return false;
6672 // Convert the string buffer, taking care to determine the correct string
6673 // length in the case of arrays (which may contain embedded nulls).
6674 JSString* result;
6675 switch (CType::GetTypeCode(baseType)) {
6676 case TYPE_int8_t:
6677 case TYPE_uint8_t:
6678 case TYPE_char:
6679 case TYPE_signed_char:
6680 case TYPE_unsigned_char: {
6681 char* bytes = static_cast<char*>(data);
6682 size_t length = strnlen(bytes, maxLength);
6684 // Determine the length.
6685 char16_t* dst = inflateUTF8(cx, JS::UTF8Chars(bytes, length), &length).get();
6686 if (!dst)
6687 return false;
6689 result = JS_NewUCString(cx, dst, length);
6690 break;
6692 case TYPE_int16_t:
6693 case TYPE_uint16_t:
6694 case TYPE_short:
6695 case TYPE_unsigned_short:
6696 case TYPE_char16_t: {
6697 char16_t* chars = static_cast<char16_t*>(data);
6698 size_t length = strnlen(chars, maxLength);
6699 result = JS_NewUCStringCopyN(cx, chars, length);
6700 break;
6702 default:
6703 JS_ReportError(cx,
6704 "base type is not an 8-bit or 16-bit integer or character type");
6705 return false;
6708 if (!result)
6709 return false;
6711 args.rval().setString(result);
6712 return true;
6715 bool
6716 CData::ReadString(JSContext* cx, unsigned argc, jsval* vp)
6718 return ReadStringCommon(cx, JS::UTF8CharsToNewTwoByteCharsZ, argc, vp);
6721 bool
6722 CData::ReadStringReplaceMalformed(JSContext* cx, unsigned argc, jsval* vp)
6724 return ReadStringCommon(cx, JS::LossyUTF8CharsToNewTwoByteCharsZ, argc, vp);
6727 JSString*
6728 CData::GetSourceString(JSContext* cx, HandleObject typeObj, void* data)
6730 // Walk the types, building up the toSource() string.
6731 // First, we build up the type expression:
6732 // 't.ptr' for pointers;
6733 // 't.array([n])' for arrays;
6734 // 'n' for structs, where n = t.name, the struct's name. (We assume this is
6735 // bound to a variable in the current scope.)
6736 AutoString source;
6737 BuildTypeSource(cx, typeObj, true, source);
6738 AppendString(source, "(");
6739 if (!BuildDataSource(cx, typeObj, data, false, source))
6740 return nullptr;
6742 AppendString(source, ")");
6744 return NewUCString(cx, source);
6747 bool
6748 CData::ToSource(JSContext* cx, unsigned argc, jsval* vp)
6750 CallArgs args = CallArgsFromVp(argc, vp);
6751 if (args.length() != 0) {
6752 JS_ReportError(cx, "toSource takes zero arguments");
6753 return false;
6756 JSObject* obj = JS_THIS_OBJECT(cx, vp);
6757 if (!obj)
6758 return false;
6759 if (!CData::IsCData(obj) && !CData::IsCDataProto(obj)) {
6760 JS_ReportError(cx, "not a CData");
6761 return false;
6764 JSString* result;
6765 if (CData::IsCData(obj)) {
6766 RootedObject typeObj(cx, CData::GetCType(obj));
6767 void* data = CData::GetData(obj);
6769 result = CData::GetSourceString(cx, typeObj, data);
6770 } else {
6771 result = JS_NewStringCopyZ(cx, "[CData proto object]");
6774 if (!result)
6775 return false;
6777 args.rval().setString(result);
6778 return true;
6781 bool
6782 CData::ErrnoGetter(JSContext* cx, JS::CallArgs args)
6784 args.rval().set(JS_GetReservedSlot(&args.thisv().toObject(), SLOT_ERRNO));
6785 return true;
6788 #if defined(XP_WIN)
6789 bool
6790 CData::LastErrorGetter(JSContext* cx, JS::CallArgs args)
6792 args.rval().set(JS_GetReservedSlot(&args.thisv().toObject(), SLOT_LASTERROR));
6793 return true;
6795 #endif // defined(XP_WIN)
6797 bool
6798 CDataFinalizer::Methods::ToSource(JSContext* cx, unsigned argc, jsval* vp)
6800 CallArgs args = CallArgsFromVp(argc, vp);
6801 RootedObject objThis(cx, JS_THIS_OBJECT(cx, vp));
6802 if (!objThis)
6803 return false;
6804 if (!CDataFinalizer::IsCDataFinalizer(objThis)) {
6805 JS_ReportError(cx, "not a CDataFinalizer");
6806 return false;
6809 CDataFinalizer::Private* p = (CDataFinalizer::Private*)
6810 JS_GetPrivate(objThis);
6812 JSString* strMessage;
6813 if (!p) {
6814 strMessage = JS_NewStringCopyZ(cx, "ctypes.CDataFinalizer()");
6815 } else {
6816 RootedObject objType(cx, CDataFinalizer::GetCType(cx, objThis));
6817 if (!objType) {
6818 JS_ReportError(cx, "CDataFinalizer has no type");
6819 return false;
6822 AutoString source;
6823 AppendString(source, "ctypes.CDataFinalizer(");
6824 JSString* srcValue = CData::GetSourceString(cx, objType, p->cargs);
6825 if (!srcValue) {
6826 return false;
6828 AppendString(source, srcValue);
6829 AppendString(source, ", ");
6830 jsval valCodePtrType = JS_GetReservedSlot(objThis,
6831 SLOT_DATAFINALIZER_CODETYPE);
6832 if (valCodePtrType.isPrimitive()) {
6833 return false;
6836 RootedObject typeObj(cx, valCodePtrType.toObjectOrNull());
6837 JSString* srcDispose = CData::GetSourceString(cx, typeObj, &(p->code));
6838 if (!srcDispose) {
6839 return false;
6842 AppendString(source, srcDispose);
6843 AppendString(source, ")");
6844 strMessage = NewUCString(cx, source);
6847 if (!strMessage) {
6848 // This is a memory issue, no error message
6849 return false;
6852 args.rval().setString(strMessage);
6853 return true;
6856 bool
6857 CDataFinalizer::Methods::ToString(JSContext* cx, unsigned argc, jsval* vp)
6859 CallArgs args = CallArgsFromVp(argc, vp);
6860 JSObject* objThis = JS_THIS_OBJECT(cx, vp);
6861 if (!objThis)
6862 return false;
6863 if (!CDataFinalizer::IsCDataFinalizer(objThis)) {
6864 JS_ReportError(cx, "not a CDataFinalizer");
6865 return false;
6868 JSString* strMessage;
6869 RootedValue value(cx);
6870 if (!JS_GetPrivate(objThis)) {
6871 // Pre-check whether CDataFinalizer::GetValue can fail
6872 // to avoid reporting an error when not appropriate.
6873 strMessage = JS_NewStringCopyZ(cx, "[CDataFinalizer - empty]");
6874 if (!strMessage) {
6875 return false;
6877 } else if (!CDataFinalizer::GetValue(cx, objThis, &value)) {
6878 MOZ_CRASH("Could not convert an empty CDataFinalizer");
6879 } else {
6880 strMessage = ToString(cx, value);
6881 if (!strMessage) {
6882 return false;
6885 args.rval().setString(strMessage);
6886 return true;
6889 bool
6890 CDataFinalizer::IsCDataFinalizer(JSObject* obj)
6892 return JS_GetClass(obj) == &sCDataFinalizerClass;
6896 JSObject*
6897 CDataFinalizer::GetCType(JSContext* cx, JSObject* obj)
6899 MOZ_ASSERT(IsCDataFinalizer(obj));
6901 jsval valData = JS_GetReservedSlot(obj,
6902 SLOT_DATAFINALIZER_VALTYPE);
6903 if (valData.isUndefined()) {
6904 return nullptr;
6907 return valData.toObjectOrNull();
6910 JSObject*
6911 CDataFinalizer::GetCData(JSContext* cx, JSObject* obj)
6913 if (!obj) {
6914 JS_ReportError(cx, "No C data");
6915 return nullptr;
6917 if (CData::IsCData(obj)) {
6918 return obj;
6920 if (!CDataFinalizer::IsCDataFinalizer(obj)) {
6921 JS_ReportError(cx, "Not C data");
6922 return nullptr;
6924 RootedValue val(cx);
6925 if (!CDataFinalizer::GetValue(cx, obj, &val) || val.isPrimitive()) {
6926 JS_ReportError(cx, "Empty CDataFinalizer");
6927 return nullptr;
6929 return val.toObjectOrNull();
6932 bool
6933 CDataFinalizer::GetValue(JSContext* cx, JSObject* obj, MutableHandleValue aResult)
6935 MOZ_ASSERT(IsCDataFinalizer(obj));
6937 CDataFinalizer::Private* p = (CDataFinalizer::Private*)
6938 JS_GetPrivate(obj);
6940 if (!p) {
6941 JS_ReportError(cx, "Attempting to get the value of an empty CDataFinalizer");
6942 return false; // We have called |dispose| or |forget| already.
6945 RootedObject ctype(cx, GetCType(cx, obj));
6946 return ConvertToJS(cx, ctype, /*parent*/NullPtr(), p->cargs, false, true, aResult);
6950 * Attach a C function as a finalizer to a JS object.
6952 * Pseudo-JS signature:
6953 * function(CData<T>, CData<T -> U>): CDataFinalizer<T>
6954 * value, finalizer
6956 * This function attaches strong references to the following values:
6957 * - the CType of |value|
6959 * Note: This function takes advantage of the fact that non-variadic
6960 * CData functions are initialized during creation.
6962 bool
6963 CDataFinalizer::Construct(JSContext* cx, unsigned argc, jsval* vp)
6965 CallArgs args = CallArgsFromVp(argc, vp);
6966 RootedObject objSelf(cx, &args.callee());
6967 RootedObject objProto(cx);
6968 if (!GetObjectProperty(cx, objSelf, "prototype", &objProto)) {
6969 JS_ReportError(cx, "CDataFinalizer.prototype does not exist");
6970 return false;
6973 // Get arguments
6974 if (args.length() == 0) { // Special case: the empty (already finalized) object
6975 JSObject* objResult = JS_NewObject(cx, &sCDataFinalizerClass, objProto, NullPtr());
6976 args.rval().setObject(*objResult);
6977 return true;
6980 if (args.length() != 2) {
6981 JS_ReportError(cx, "CDataFinalizer takes 2 arguments");
6982 return false;
6985 JS::HandleValue valCodePtr = args[1];
6986 if (!valCodePtr.isObject()) {
6987 return TypeError(cx, "_a CData object_ of a function pointer type",
6988 valCodePtr);
6990 JSObject* objCodePtr = &valCodePtr.toObject();
6992 //Note: Using a custom argument formatter here would be awkward (requires
6993 //a destructor just to uninstall the formatter).
6995 // 2. Extract argument type of |objCodePtr|
6996 if (!CData::IsCData(objCodePtr)) {
6997 return TypeError(cx, "a _CData_ object of a function pointer type",
6998 valCodePtr);
7000 RootedObject objCodePtrType(cx, CData::GetCType(objCodePtr));
7001 RootedValue valCodePtrType(cx, ObjectValue(*objCodePtrType));
7002 MOZ_ASSERT(objCodePtrType);
7004 TypeCode typCodePtr = CType::GetTypeCode(objCodePtrType);
7005 if (typCodePtr != TYPE_pointer) {
7006 return TypeError(cx, "a CData object of a function _pointer_ type",
7007 valCodePtrType);
7010 JSObject* objCodeType = PointerType::GetBaseType(objCodePtrType);
7011 MOZ_ASSERT(objCodeType);
7013 TypeCode typCode = CType::GetTypeCode(objCodeType);
7014 if (typCode != TYPE_function) {
7015 return TypeError(cx, "a CData object of a _function_ pointer type",
7016 valCodePtrType);
7018 uintptr_t code = *reinterpret_cast<uintptr_t*>(CData::GetData(objCodePtr));
7019 if (!code) {
7020 return TypeError(cx, "a CData object of a _non-NULL_ function pointer type",
7021 valCodePtrType);
7024 FunctionInfo* funInfoFinalizer =
7025 FunctionType::GetFunctionInfo(objCodeType);
7026 MOZ_ASSERT(funInfoFinalizer);
7028 if ((funInfoFinalizer->mArgTypes.length() != 1)
7029 || (funInfoFinalizer->mIsVariadic)) {
7030 RootedValue valCodeType(cx, ObjectValue(*objCodeType));
7031 return TypeError(cx, "a function accepting exactly one argument",
7032 valCodeType);
7034 RootedObject objArgType(cx, funInfoFinalizer->mArgTypes[0]);
7035 RootedObject returnType(cx, funInfoFinalizer->mReturnType);
7037 // Invariant: At this stage, we know that funInfoFinalizer->mIsVariadic
7038 // is |false|. Therefore, funInfoFinalizer->mCIF has already been initialized.
7040 bool freePointer = false;
7042 // 3. Perform dynamic cast of |args[0]| into |objType|, store it in |cargs|
7044 size_t sizeArg;
7045 RootedValue valData(cx, args[0]);
7046 if (!CType::GetSafeSize(objArgType, &sizeArg)) {
7047 return TypeError(cx, "(an object with known size)", valData);
7050 ScopedJSFreePtr<void> cargs(malloc(sizeArg));
7052 if (!ImplicitConvert(cx, valData, objArgType, cargs.get(),
7053 false, &freePointer)) {
7054 RootedValue valArgType(cx, ObjectValue(*objArgType));
7055 return TypeError(cx, "(an object that can be converted to the following type)",
7056 valArgType);
7058 if (freePointer) {
7059 // Note: We could handle that case, if necessary.
7060 JS_ReportError(cx, "Internal Error during CDataFinalizer. Object cannot be represented");
7061 return false;
7064 // 4. Prepare buffer for holding return value
7066 ScopedJSFreePtr<void> rvalue;
7067 if (CType::GetTypeCode(returnType) != TYPE_void_t) {
7068 rvalue = malloc(Align(CType::GetSize(returnType),
7069 sizeof(ffi_arg)));
7070 } //Otherwise, simply do not allocate
7072 // 5. Create |objResult|
7074 JSObject* objResult = JS_NewObject(cx, &sCDataFinalizerClass, objProto, NullPtr());
7075 if (!objResult) {
7076 return false;
7079 // If our argument is a CData, it holds a type.
7080 // This is the type that we should capture, not that
7081 // of the function, which may be less precise.
7082 JSObject* objBestArgType = objArgType;
7083 if (valData.isObject()) {
7084 JSObject* objData = &valData.toObject();
7085 if (CData::IsCData(objData)) {
7086 objBestArgType = CData::GetCType(objData);
7087 size_t sizeBestArg;
7088 if (!CType::GetSafeSize(objBestArgType, &sizeBestArg)) {
7089 MOZ_CRASH("object with unknown size");
7091 if (sizeBestArg != sizeArg) {
7092 return TypeError(cx, "(an object with the same size as that expected by the C finalization function)", valData);
7097 // Used by GetCType
7098 JS_SetReservedSlot(objResult,
7099 SLOT_DATAFINALIZER_VALTYPE,
7100 OBJECT_TO_JSVAL(objBestArgType));
7102 // Used by ToSource
7103 JS_SetReservedSlot(objResult,
7104 SLOT_DATAFINALIZER_CODETYPE,
7105 OBJECT_TO_JSVAL(objCodePtrType));
7107 ffi_abi abi;
7108 if (!GetABI(cx, OBJECT_TO_JSVAL(funInfoFinalizer->mABI), &abi)) {
7109 JS_ReportError(cx, "Internal Error: "
7110 "Invalid ABI specification in CDataFinalizer");
7111 return false;
7114 ffi_type* rtype = CType::GetFFIType(cx, funInfoFinalizer->mReturnType);
7115 if (!rtype) {
7116 JS_ReportError(cx, "Internal Error: "
7117 "Could not access ffi type of CDataFinalizer");
7118 return false;
7121 // 7. Store C information as private
7122 ScopedJSFreePtr<CDataFinalizer::Private>
7123 p((CDataFinalizer::Private*)malloc(sizeof(CDataFinalizer::Private)));
7125 memmove(&p->CIF, &funInfoFinalizer->mCIF, sizeof(ffi_cif));
7127 p->cargs = cargs.forget();
7128 p->rvalue = rvalue.forget();
7129 p->cargs_size = sizeArg;
7130 p->code = code;
7133 JS_SetPrivate(objResult, p.forget());
7134 args.rval().setObject(*objResult);
7135 return true;
7140 * Actually call the finalizer. Does not perform any cleanup on the object.
7142 * Preconditions: |this| must be a |CDataFinalizer|, |p| must be non-null.
7143 * The function fails if |this| has gone through |Forget|/|Dispose|
7144 * or |Finalize|.
7146 * This function does not alter the value of |errno|/|GetLastError|.
7148 * If argument |errnoStatus| is non-nullptr, it receives the value of |errno|
7149 * immediately after the call. Under Windows, if argument |lastErrorStatus|
7150 * is non-nullptr, it receives the value of |GetLastError| immediately after
7151 * the call. On other platforms, |lastErrorStatus| is ignored.
7153 void
7154 CDataFinalizer::CallFinalizer(CDataFinalizer::Private* p,
7155 int* errnoStatus,
7156 int32_t* lastErrorStatus)
7158 int savedErrno = errno;
7159 errno = 0;
7160 #if defined(XP_WIN)
7161 int32_t savedLastError = GetLastError();
7162 SetLastError(0);
7163 #endif // defined(XP_WIN)
7165 void* args[1] = {p->cargs};
7166 ffi_call(&p->CIF, FFI_FN(p->code), p->rvalue, args);
7168 if (errnoStatus) {
7169 *errnoStatus = errno;
7171 errno = savedErrno;
7172 #if defined(XP_WIN)
7173 if (lastErrorStatus) {
7174 *lastErrorStatus = GetLastError();
7176 SetLastError(savedLastError);
7177 #endif // defined(XP_WIN)
7181 * Forget the value.
7183 * Preconditions: |this| must be a |CDataFinalizer|.
7184 * The function fails if |this| has gone through |Forget|/|Dispose|
7185 * or |Finalize|.
7187 * Does not call the finalizer. Cleans up the Private memory and releases all
7188 * strong references.
7190 bool
7191 CDataFinalizer::Methods::Forget(JSContext* cx, unsigned argc, jsval* vp)
7193 CallArgs args = CallArgsFromVp(argc, vp);
7194 if (args.length() != 0) {
7195 JS_ReportError(cx, "CDataFinalizer.prototype.forget takes no arguments");
7196 return false;
7199 JS::Rooted<JSObject*> obj(cx, args.thisv().toObjectOrNull());
7200 if (!obj)
7201 return false;
7202 if (!CDataFinalizer::IsCDataFinalizer(obj)) {
7203 RootedValue val(cx, ObjectValue(*obj));
7204 return TypeError(cx, "a CDataFinalizer", val);
7207 CDataFinalizer::Private* p = (CDataFinalizer::Private*)
7208 JS_GetPrivate(obj);
7210 if (!p) {
7211 JS_ReportError(cx, "forget called on an empty CDataFinalizer");
7212 return false;
7215 RootedValue valJSData(cx);
7216 RootedObject ctype(cx, GetCType(cx, obj));
7217 if (!ConvertToJS(cx, ctype, NullPtr(), p->cargs, false, true, &valJSData)) {
7218 JS_ReportError(cx, "CDataFinalizer value cannot be represented");
7219 return false;
7222 CDataFinalizer::Cleanup(p, obj);
7224 args.rval().set(valJSData);
7225 return true;
7229 * Clean up the value.
7231 * Preconditions: |this| must be a |CDataFinalizer|.
7232 * The function fails if |this| has gone through |Forget|/|Dispose|
7233 * or |Finalize|.
7235 * Calls the finalizer, cleans up the Private memory and releases all
7236 * strong references.
7238 bool
7239 CDataFinalizer::Methods::Dispose(JSContext* cx, unsigned argc, jsval* vp)
7241 CallArgs args = CallArgsFromVp(argc, vp);
7242 if (args.length() != 0) {
7243 JS_ReportError(cx, "CDataFinalizer.prototype.dispose takes no arguments");
7244 return false;
7247 RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
7248 if (!obj)
7249 return false;
7250 if (!CDataFinalizer::IsCDataFinalizer(obj)) {
7251 RootedValue val(cx, ObjectValue(*obj));
7252 return TypeError(cx, "a CDataFinalizer", val);
7255 CDataFinalizer::Private* p = (CDataFinalizer::Private*)
7256 JS_GetPrivate(obj);
7258 if (!p) {
7259 JS_ReportError(cx, "dispose called on an empty CDataFinalizer.");
7260 return false;
7263 jsval valType = JS_GetReservedSlot(obj, SLOT_DATAFINALIZER_VALTYPE);
7264 MOZ_ASSERT(valType.isObject());
7266 JSObject* objCTypes = CType::GetGlobalCTypes(cx, &valType.toObject());
7267 if (!objCTypes)
7268 return false;
7270 jsval valCodePtrType = JS_GetReservedSlot(obj, SLOT_DATAFINALIZER_CODETYPE);
7271 MOZ_ASSERT(valCodePtrType.isObject());
7272 JSObject* objCodePtrType = &valCodePtrType.toObject();
7274 JSObject* objCodeType = PointerType::GetBaseType(objCodePtrType);
7275 MOZ_ASSERT(objCodeType);
7276 MOZ_ASSERT(CType::GetTypeCode(objCodeType) == TYPE_function);
7278 RootedObject resultType(cx, FunctionType::GetFunctionInfo(objCodeType)->mReturnType);
7279 RootedValue result(cx, JSVAL_VOID);
7281 int errnoStatus;
7282 #if defined(XP_WIN)
7283 int32_t lastErrorStatus;
7284 CDataFinalizer::CallFinalizer(p, &errnoStatus, &lastErrorStatus);
7285 #else
7286 CDataFinalizer::CallFinalizer(p, &errnoStatus, nullptr);
7287 #endif // defined(XP_WIN)
7289 JS_SetReservedSlot(objCTypes, SLOT_ERRNO, INT_TO_JSVAL(errnoStatus));
7290 #if defined(XP_WIN)
7291 JS_SetReservedSlot(objCTypes, SLOT_LASTERROR, INT_TO_JSVAL(lastErrorStatus));
7292 #endif // defined(XP_WIN)
7294 if (ConvertToJS(cx, resultType, NullPtr(), p->rvalue, false, true, &result)) {
7295 CDataFinalizer::Cleanup(p, obj);
7296 args.rval().set(result);
7297 return true;
7299 CDataFinalizer::Cleanup(p, obj);
7300 return false;
7304 * Perform finalization.
7306 * Preconditions: |this| must be the result of |CDataFinalizer|.
7307 * It may have gone through |Forget|/|Dispose|.
7309 * If |this| has not gone through |Forget|/|Dispose|, calls the
7310 * finalizer, cleans up the Private memory and releases all
7311 * strong references.
7313 void
7314 CDataFinalizer::Finalize(JSFreeOp* fop, JSObject* obj)
7316 CDataFinalizer::Private* p = (CDataFinalizer::Private*)
7317 JS_GetPrivate(obj);
7319 if (!p) {
7320 return;
7323 CDataFinalizer::CallFinalizer(p, nullptr, nullptr);
7324 CDataFinalizer::Cleanup(p, nullptr);
7328 * Perform cleanup of a CDataFinalizer
7330 * Release strong references, cleanup |Private|.
7332 * Argument |p| contains the private information of the CDataFinalizer. If
7333 * nullptr, this function does nothing.
7334 * Argument |obj| should contain |nullptr| during finalization (or in any
7335 * context in which the object itself should not be cleaned up), or a
7336 * CDataFinalizer object otherwise.
7338 void
7339 CDataFinalizer::Cleanup(CDataFinalizer::Private* p, JSObject* obj)
7341 if (!p) {
7342 return; // We have already cleaned up
7345 free(p->cargs);
7346 free(p->rvalue);
7347 free(p);
7349 if (!obj) {
7350 return; // No slots to clean up
7353 MOZ_ASSERT(CDataFinalizer::IsCDataFinalizer(obj));
7355 JS_SetPrivate(obj, nullptr);
7356 for (int i = 0; i < CDATAFINALIZER_SLOTS; ++i) {
7357 JS_SetReservedSlot(obj, i, JSVAL_NULL);
7362 /*******************************************************************************
7363 ** Int64 and UInt64 implementation
7364 *******************************************************************************/
7366 JSObject*
7367 Int64Base::Construct(JSContext* cx,
7368 HandleObject proto,
7369 uint64_t data,
7370 bool isUnsigned)
7372 const JSClass* clasp = isUnsigned ? &sUInt64Class : &sInt64Class;
7373 RootedObject parent(cx, JS_GetParent(proto));
7374 RootedObject result(cx, JS_NewObject(cx, clasp, proto, parent));
7375 if (!result)
7376 return nullptr;
7378 // attach the Int64's data
7379 uint64_t* buffer = cx->new_<uint64_t>(data);
7380 if (!buffer) {
7381 JS_ReportOutOfMemory(cx);
7382 return nullptr;
7385 JS_SetReservedSlot(result, SLOT_INT64, PRIVATE_TO_JSVAL(buffer));
7387 if (!JS_FreezeObject(cx, result))
7388 return nullptr;
7390 return result;
7393 void
7394 Int64Base::Finalize(JSFreeOp* fop, JSObject* obj)
7396 jsval slot = JS_GetReservedSlot(obj, SLOT_INT64);
7397 if (slot.isUndefined())
7398 return;
7400 FreeOp::get(fop)->delete_(static_cast<uint64_t*>(slot.toPrivate()));
7403 uint64_t
7404 Int64Base::GetInt(JSObject* obj) {
7405 MOZ_ASSERT(Int64::IsInt64(obj) || UInt64::IsUInt64(obj));
7407 jsval slot = JS_GetReservedSlot(obj, SLOT_INT64);
7408 return *static_cast<uint64_t*>(slot.toPrivate());
7411 bool
7412 Int64Base::ToString(JSContext* cx,
7413 JSObject* obj,
7414 const CallArgs& args,
7415 bool isUnsigned)
7417 if (args.length() > 1) {
7418 JS_ReportError(cx, "toString takes zero or one argument");
7419 return false;
7422 int radix = 10;
7423 if (args.length() == 1) {
7424 jsval arg = args[0];
7425 if (arg.isInt32())
7426 radix = arg.toInt32();
7427 if (!arg.isInt32() || radix < 2 || radix > 36) {
7428 JS_ReportError(cx, "radix argument must be an integer between 2 and 36");
7429 return false;
7433 AutoString intString;
7434 if (isUnsigned) {
7435 IntegerToString(GetInt(obj), radix, intString);
7436 } else {
7437 IntegerToString(static_cast<int64_t>(GetInt(obj)), radix, intString);
7440 JSString* result = NewUCString(cx, intString);
7441 if (!result)
7442 return false;
7444 args.rval().setString(result);
7445 return true;
7448 bool
7449 Int64Base::ToSource(JSContext* cx,
7450 JSObject* obj,
7451 const CallArgs& args,
7452 bool isUnsigned)
7454 if (args.length() != 0) {
7455 JS_ReportError(cx, "toSource takes zero arguments");
7456 return false;
7459 // Return a decimal string suitable for constructing the number.
7460 AutoString source;
7461 if (isUnsigned) {
7462 AppendString(source, "ctypes.UInt64(\"");
7463 IntegerToString(GetInt(obj), 10, source);
7464 } else {
7465 AppendString(source, "ctypes.Int64(\"");
7466 IntegerToString(static_cast<int64_t>(GetInt(obj)), 10, source);
7468 AppendString(source, "\")");
7470 JSString* result = NewUCString(cx, source);
7471 if (!result)
7472 return false;
7474 args.rval().setString(result);
7475 return true;
7478 bool
7479 Int64::Construct(JSContext* cx,
7480 unsigned argc,
7481 jsval* vp)
7483 CallArgs args = CallArgsFromVp(argc, vp);
7485 // Construct and return a new Int64 object.
7486 if (args.length() != 1) {
7487 JS_ReportError(cx, "Int64 takes one argument");
7488 return false;
7491 int64_t i = 0;
7492 if (!jsvalToBigInteger(cx, args[0], true, &i))
7493 return TypeError(cx, "int64", args[0]);
7495 // Get ctypes.Int64.prototype from the 'prototype' property of the ctor.
7496 RootedValue slot(cx);
7497 RootedObject callee(cx, &args.callee());
7498 ASSERT_OK(JS_GetProperty(cx, callee, "prototype", &slot));
7499 RootedObject proto(cx, slot.toObjectOrNull());
7500 MOZ_ASSERT(JS_GetClass(proto) == &sInt64ProtoClass);
7502 JSObject* result = Int64Base::Construct(cx, proto, i, false);
7503 if (!result)
7504 return false;
7506 args.rval().setObject(*result);
7507 return true;
7510 bool
7511 Int64::IsInt64(JSObject* obj)
7513 return JS_GetClass(obj) == &sInt64Class;
7516 bool
7517 Int64::ToString(JSContext* cx, unsigned argc, jsval* vp)
7519 CallArgs args = CallArgsFromVp(argc, vp);
7520 JSObject* obj = JS_THIS_OBJECT(cx, vp);
7521 if (!obj)
7522 return false;
7523 if (!Int64::IsInt64(obj)) {
7524 JS_ReportError(cx, "not an Int64");
7525 return false;
7528 return Int64Base::ToString(cx, obj, args, false);
7531 bool
7532 Int64::ToSource(JSContext* cx, unsigned argc, jsval* vp)
7534 CallArgs args = CallArgsFromVp(argc, vp);
7535 JSObject* obj = JS_THIS_OBJECT(cx, vp);
7536 if (!obj)
7537 return false;
7538 if (!Int64::IsInt64(obj)) {
7539 JS_ReportError(cx, "not an Int64");
7540 return false;
7543 return Int64Base::ToSource(cx, obj, args, false);
7546 bool
7547 Int64::Compare(JSContext* cx, unsigned argc, jsval* vp)
7549 CallArgs args = CallArgsFromVp(argc, vp);
7550 if (args.length() != 2 ||
7551 args[0].isPrimitive() ||
7552 args[1].isPrimitive() ||
7553 !Int64::IsInt64(&args[0].toObject()) ||
7554 !Int64::IsInt64(&args[1].toObject())) {
7555 JS_ReportError(cx, "compare takes two Int64 arguments");
7556 return false;
7559 JSObject* obj1 = &args[0].toObject();
7560 JSObject* obj2 = &args[1].toObject();
7562 int64_t i1 = Int64Base::GetInt(obj1);
7563 int64_t i2 = Int64Base::GetInt(obj2);
7565 if (i1 == i2)
7566 args.rval().setInt32(0);
7567 else if (i1 < i2)
7568 args.rval().setInt32(-1);
7569 else
7570 args.rval().setInt32(1);
7572 return true;
7575 #define LO_MASK ((uint64_t(1) << 32) - 1)
7576 #define INT64_LO(i) ((i) & LO_MASK)
7577 #define INT64_HI(i) ((i) >> 32)
7579 bool
7580 Int64::Lo(JSContext* cx, unsigned argc, jsval* vp)
7582 CallArgs args = CallArgsFromVp(argc, vp);
7583 if (args.length() != 1 || args[0].isPrimitive() ||
7584 !Int64::IsInt64(&args[0].toObject())) {
7585 JS_ReportError(cx, "lo takes one Int64 argument");
7586 return false;
7589 JSObject* obj = &args[0].toObject();
7590 int64_t u = Int64Base::GetInt(obj);
7591 double d = uint32_t(INT64_LO(u));
7593 args.rval().setNumber(d);
7594 return true;
7597 bool
7598 Int64::Hi(JSContext* cx, unsigned argc, jsval* vp)
7600 CallArgs args = CallArgsFromVp(argc, vp);
7601 if (args.length() != 1 || args[0].isPrimitive() ||
7602 !Int64::IsInt64(&args[0].toObject())) {
7603 JS_ReportError(cx, "hi takes one Int64 argument");
7604 return false;
7607 JSObject* obj = &args[0].toObject();
7608 int64_t u = Int64Base::GetInt(obj);
7609 double d = int32_t(INT64_HI(u));
7611 args.rval().setDouble(d);
7612 return true;
7615 bool
7616 Int64::Join(JSContext* cx, unsigned argc, jsval* vp)
7618 CallArgs args = CallArgsFromVp(argc, vp);
7619 if (args.length() != 2) {
7620 JS_ReportError(cx, "join takes two arguments");
7621 return false;
7624 int32_t hi;
7625 uint32_t lo;
7626 if (!jsvalToInteger(cx, args[0], &hi))
7627 return TypeError(cx, "int32", args[0]);
7628 if (!jsvalToInteger(cx, args[1], &lo))
7629 return TypeError(cx, "uint32", args[1]);
7631 int64_t i = (int64_t(hi) << 32) + int64_t(lo);
7633 // Get Int64.prototype from the function's reserved slot.
7634 JSObject* callee = &args.callee();
7636 jsval slot = js::GetFunctionNativeReserved(callee, SLOT_FN_INT64PROTO);
7637 RootedObject proto(cx, &slot.toObject());
7638 MOZ_ASSERT(JS_GetClass(proto) == &sInt64ProtoClass);
7640 JSObject* result = Int64Base::Construct(cx, proto, i, false);
7641 if (!result)
7642 return false;
7644 args.rval().setObject(*result);
7645 return true;
7648 bool
7649 UInt64::Construct(JSContext* cx,
7650 unsigned argc,
7651 jsval* vp)
7653 CallArgs args = CallArgsFromVp(argc, vp);
7655 // Construct and return a new UInt64 object.
7656 if (args.length() != 1) {
7657 JS_ReportError(cx, "UInt64 takes one argument");
7658 return false;
7661 uint64_t u = 0;
7662 if (!jsvalToBigInteger(cx, args[0], true, &u))
7663 return TypeError(cx, "uint64", args[0]);
7665 // Get ctypes.UInt64.prototype from the 'prototype' property of the ctor.
7666 RootedValue slot(cx);
7667 RootedObject callee(cx, &args.callee());
7668 ASSERT_OK(JS_GetProperty(cx, callee, "prototype", &slot));
7669 RootedObject proto(cx, &slot.toObject());
7670 MOZ_ASSERT(JS_GetClass(proto) == &sUInt64ProtoClass);
7672 JSObject* result = Int64Base::Construct(cx, proto, u, true);
7673 if (!result)
7674 return false;
7676 args.rval().setObject(*result);
7677 return true;
7680 bool
7681 UInt64::IsUInt64(JSObject* obj)
7683 return JS_GetClass(obj) == &sUInt64Class;
7686 bool
7687 UInt64::ToString(JSContext* cx, unsigned argc, jsval* vp)
7689 CallArgs args = CallArgsFromVp(argc, vp);
7690 JSObject* obj = JS_THIS_OBJECT(cx, vp);
7691 if (!obj)
7692 return false;
7693 if (!UInt64::IsUInt64(obj)) {
7694 JS_ReportError(cx, "not a UInt64");
7695 return false;
7698 return Int64Base::ToString(cx, obj, args, true);
7701 bool
7702 UInt64::ToSource(JSContext* cx, unsigned argc, jsval* vp)
7704 CallArgs args = CallArgsFromVp(argc, vp);
7705 JSObject* obj = JS_THIS_OBJECT(cx, vp);
7706 if (!obj)
7707 return false;
7708 if (!UInt64::IsUInt64(obj)) {
7709 JS_ReportError(cx, "not a UInt64");
7710 return false;
7713 return Int64Base::ToSource(cx, obj, args, true);
7716 bool
7717 UInt64::Compare(JSContext* cx, unsigned argc, jsval* vp)
7719 CallArgs args = CallArgsFromVp(argc, vp);
7720 if (args.length() != 2 ||
7721 args[0].isPrimitive() ||
7722 args[1].isPrimitive() ||
7723 !UInt64::IsUInt64(&args[0].toObject()) ||
7724 !UInt64::IsUInt64(&args[1].toObject())) {
7725 JS_ReportError(cx, "compare takes two UInt64 arguments");
7726 return false;
7729 JSObject* obj1 = &args[0].toObject();
7730 JSObject* obj2 = &args[1].toObject();
7732 uint64_t u1 = Int64Base::GetInt(obj1);
7733 uint64_t u2 = Int64Base::GetInt(obj2);
7735 if (u1 == u2)
7736 args.rval().setInt32(0);
7737 else if (u1 < u2)
7738 args.rval().setInt32(-1);
7739 else
7740 args.rval().setInt32(1);
7742 return true;
7745 bool
7746 UInt64::Lo(JSContext* cx, unsigned argc, jsval* vp)
7748 CallArgs args = CallArgsFromVp(argc, vp);
7749 if (args.length() != 1 || args[0].isPrimitive() ||
7750 !UInt64::IsUInt64(&args[0].toObject())) {
7751 JS_ReportError(cx, "lo takes one UInt64 argument");
7752 return false;
7755 JSObject* obj = &args[0].toObject();
7756 uint64_t u = Int64Base::GetInt(obj);
7757 double d = uint32_t(INT64_LO(u));
7759 args.rval().setDouble(d);
7760 return true;
7763 bool
7764 UInt64::Hi(JSContext* cx, unsigned argc, jsval* vp)
7766 CallArgs args = CallArgsFromVp(argc, vp);
7767 if (args.length() != 1 || args[0].isPrimitive() ||
7768 !UInt64::IsUInt64(&args[0].toObject())) {
7769 JS_ReportError(cx, "hi takes one UInt64 argument");
7770 return false;
7773 JSObject* obj = &args[0].toObject();
7774 uint64_t u = Int64Base::GetInt(obj);
7775 double d = uint32_t(INT64_HI(u));
7777 args.rval().setDouble(d);
7778 return true;
7781 bool
7782 UInt64::Join(JSContext* cx, unsigned argc, jsval* vp)
7784 CallArgs args = CallArgsFromVp(argc, vp);
7785 if (args.length() != 2) {
7786 JS_ReportError(cx, "join takes two arguments");
7787 return false;
7790 uint32_t hi;
7791 uint32_t lo;
7792 if (!jsvalToInteger(cx, args[0], &hi))
7793 return TypeError(cx, "uint32_t", args[0]);
7794 if (!jsvalToInteger(cx, args[1], &lo))
7795 return TypeError(cx, "uint32_t", args[1]);
7797 uint64_t u = (uint64_t(hi) << 32) + uint64_t(lo);
7799 // Get UInt64.prototype from the function's reserved slot.
7800 JSObject* callee = &args.callee();
7802 jsval slot = js::GetFunctionNativeReserved(callee, SLOT_FN_INT64PROTO);
7803 RootedObject proto(cx, &slot.toObject());
7804 MOZ_ASSERT(JS_GetClass(proto) == &sUInt64ProtoClass);
7806 JSObject* result = Int64Base::Construct(cx, proto, u, true);
7807 if (!result)
7808 return false;
7810 args.rval().setObject(*result);
7811 return true;