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"
25 #include <sys/types.h>
39 #include "builtin/TypedObject.h"
40 #include "ctypes/Library.h"
44 using mozilla::NumericLimits
;
46 using JS::AutoCheckCannotGC
;
51 template <typename CharT
>
53 GetDeflatedUTF8StringLength(JSContext
* maybecx
, const CharT
* chars
,
62 for (end
= chars
+ nchars
; chars
!= end
; chars
++) {
66 if (0xD800 <= c
&& c
<= 0xDFFF) {
70 /* nbytes sets 1 length since this is surrogate pair. */
72 if (c
>= 0xDC00 || chars
== end
)
75 if (c2
< 0xDC00 || c2
> 0xDFFF)
77 c
= ((c
- 0xD800) << 10) + (c2
- 0xDC00) + 0x10000;
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
);
99 GetDeflatedUTF8StringLength(JSContext
* maybecx
, const Latin1Char
* chars
,
103 GetDeflatedUTF8StringLength(JSContext
* maybecx
, const char16_t
* chars
,
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
>
119 DeflateStringToUTF8Buffer(JSContext
* maybecx
, const CharT
* src
, size_t srclen
,
120 char* dst
, size_t* dstlenp
)
127 size_t dstlen
= *dstlenp
;
128 size_t origDstlen
= dstlen
;
133 if (c
>= 0xDC00 && c
<= 0xDFFF)
135 if (c
< 0xD800 || c
> 0xDBFF) {
141 if ((c2
< 0xDC00) || (c2
> 0xDFFF))
145 v
= ((c
- 0xD800) << 10) + (c2
- 0xDC00) + 0x10000;
148 /* no encoding necessary - performance hack */
154 utf8Len
= js_OneUcs4ToUtf8Char(utf8buf
, v
);
155 if (utf8Len
> dstlen
)
157 for (i
= 0; i
< utf8Len
; i
++)
158 *dst
++ = (char) utf8buf
[i
];
162 *dstlenp
= (origDstlen
- dstlen
);
166 *dstlenp
= (origDstlen
- dstlen
);
167 /* Delegate error reporting to the measurement function. */
169 GetDeflatedUTF8StringLength(maybecx
, src
- 1, srclen
+ 1);
173 *dstlenp
= (origDstlen
- dstlen
);
175 js::gc::AutoSuppressGC
suppress(maybecx
);
176 JS_ReportErrorNumber(maybecx
, js_GetErrorMessage
, nullptr,
177 JSMSG_BUFFER_TOO_SMALL
);
183 DeflateStringToUTF8Buffer(JSContext
* maybecx
, const Latin1Char
* src
, size_t srclen
,
184 char* dst
, size_t* dstlenp
);
187 DeflateStringToUTF8Buffer(JSContext
* maybecx
, const char16_t
* src
, size_t srclen
,
188 char* dst
, size_t* dstlenp
);
191 DeflateStringToUTF8Buffer(JSContext
* maybecx
, JSLinearString
* str
, char* dst
,
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
>
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
);
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
);
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
);
326 static void Trace(JSTracer
* trc
, JSObject
* obj
);
327 static void Finalize(JSFreeOp
* fop
, JSObject
* obj
);
330 static void ClosureStub(ffi_cif
* cif
, void* result
, void** args
,
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
,
347 bool ErrnoGetter(JSContext
* cx
, JS::CallArgs args
);
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
375 * Note: the private data may be nullptr, if |dispose|, |forget| or the
376 * finalizer has already been called.
380 * The C data to pass to the code.
381 * Finalization/|dispose|/|forget| release this memory.
386 * The total size of the buffer pointed by |cargs|
391 * Low-level signature information.
392 * Finalization/|dispose|/|forget| release this memory.
397 * The C function to invoke during finalization.
398 * Do not deallocate this.
403 * A buffer for holding the return value.
404 * Finalization/|dispose|/|forget| release this memory.
410 * Methods of instances of |CDataFinalizer|
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
);
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
,
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
,
473 uint64_t GetInt(JSObject
* obj
);
475 bool ToString(JSContext
* cx
, JSObject
* obj
, const CallArgs
& args
,
478 bool ToSource(JSContext
* cx
, JSObject
* obj
, const CallArgs
& args
,
481 static void Finalize(JSFreeOp
* fop
, JSObject
* obj
);
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
);
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
= {
516 JSCLASS_HAS_RESERVED_SLOTS(CTYPESGLOBAL_SLOTS
)
519 static const JSClass sCABIClass
= {
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
= {
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
= {
542 static const JSClass sCTypeClass
= {
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
,
551 static const JSClass sCDataClass
= {
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
= {
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
= {
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
= {
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
[] = {
609 (Property
<CType::IsCType
, CType::NameGetter
>::Fun
),
612 (Property
<CType::IsCType
, CType::SizeGetter
>::Fun
),
615 (Property
<CType::IsCType
, CType::PtrGetter
>::Fun
),
618 (Property
<CType::IsCTypeOrProto
, CType::PrototypeGetter
>::Fun
),
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
),
630 static const JSFunctionSpec sCABIFunctions
[] = {
631 JS_FN("toSource", ABI::ToSource
, 0, CABIFN_FLAGS
),
632 JS_FN("toString", ABI::ToSource
, 0, CABIFN_FLAGS
),
636 static const JSPropertySpec sCDataProps
[] = {
638 (Property
<CData::IsCData
, CData::ValueGetter
>::Fun
),
639 (Property
<CData::IsCData
, CData::ValueSetter
>::Fun
),
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
),
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
),
662 static const JSFunctionSpec sPointerFunction
=
663 JS_FN("PointerType", PointerType::Create
, 1, CTYPESCTOR_FLAGS
);
665 static const JSPropertySpec sPointerProps
[] = {
667 (Property
<PointerType::IsPointerType
, PointerType::TargetTypeGetter
>::Fun
),
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
),
679 static const JSPropertySpec sPointerInstanceProps
[] = {
681 (Property
<PointerType::IsPointer
, PointerType::ContentsGetter
>::Fun
),
682 (Property
<PointerType::IsPointer
, PointerType::ContentsSetter
>::Fun
),
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
),
695 (Property
<ArrayType::IsArrayOrArrayType
, ArrayType::LengthGetter
>::Fun
),
700 static const JSFunctionSpec sArrayInstanceFunctions
[] = {
701 JS_FN("addressOfElement", ArrayType::AddressOfElement
, 1, CDATAFN_FLAGS
),
705 static const JSPropertySpec sArrayInstanceProps
[] = {
707 (Property
<ArrayType::IsArrayOrArrayType
, ArrayType::LengthGetter
>::Fun
),
712 static const JSFunctionSpec sStructFunction
=
713 JS_FN("StructType", StructType::Create
, 2, CTYPESCTOR_FLAGS
);
715 static const JSPropertySpec sStructProps
[] = {
717 (Property
<StructType::IsStruct
, StructType::FieldsArrayGetter
>::Fun
),
722 static const JSFunctionSpec sStructFunctions
[] = {
723 JS_FN("define", StructType::Define
, 1, CDATAFN_FLAGS
),
727 static const JSFunctionSpec sStructInstanceFunctions
[] = {
728 JS_FN("addressOfField", StructType::AddressOfField
, 1, CDATAFN_FLAGS
),
732 static const JSFunctionSpec sFunctionFunction
=
733 JS_FN("FunctionType", FunctionType::Create
, 2, CTYPESCTOR_FLAGS
);
735 static const JSPropertySpec sFunctionProps
[] = {
737 (Property
<FunctionType::IsFunctionType
, FunctionType::ArgTypesGetter
>::Fun
),
740 (Property
<FunctionType::IsFunctionType
, FunctionType::ReturnTypeGetter
>::Fun
),
743 (Property
<FunctionType::IsFunctionType
, FunctionType::ABIGetter
>::Fun
),
746 (Property
<FunctionType::IsFunctionType
, FunctionType::IsVariadicGetter
>::Fun
),
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
),
757 static const JSClass sInt64ProtoClass
= {
762 static const JSClass sUInt64ProtoClass
= {
767 static const JSClass sInt64Class
= {
769 JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS
),
770 nullptr, nullptr, nullptr, nullptr,
771 nullptr, nullptr, nullptr, Int64Base::Finalize
774 static const JSClass sUInt64Class
= {
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
),
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
),
797 static const JSFunctionSpec sInt64Functions
[] = {
798 JS_FN("toString", Int64::ToString
, 0, CTYPESFN_FLAGS
),
799 JS_FN("toSource", Int64::ToSource
, 0, CTYPESFN_FLAGS
),
803 static const JSFunctionSpec sUInt64Functions
[] = {
804 JS_FN("toString", UInt64::ToString
, 0, CTYPESFN_FLAGS
),
805 JS_FN("toSource", UInt64::ToSource
, 0, CTYPESFN_FLAGS
),
809 static const JSPropertySpec sModuleProps
[] = {
811 (Property
<IsCTypesGlobal
, CData::ErrnoGetter
>::Fun
),
814 JS_PSG("winLastError",
815 (Property
<IsCTypesGlobal
, CData::LastErrorGetter
>::Fun
),
817 #endif // defined(XP_WIN)
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
),
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;
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
)
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"
868 static const JSErrorFormatString
*
869 GetErrorMessage(void* userRef
, const unsigned errorNumber
)
871 if (0 < errorNumber
&& errorNumber
< CTYPESERR_LIMIT
)
872 return &ErrorFormatString
[errorNumber
];
877 TypeError(JSContext
* cx
, const char* expected
, HandleValue actual
)
879 JSString
* str
= JS_ValueToSource(cx
, actual
);
880 JSAutoByteString bytes
;
884 src
= bytes
.encodeLatin1(cx
, str
);
888 JS_ClearPendingException(cx
);
889 src
= "<<error converting value to string>>";
891 JS_ReportErrorNumber(cx
, GetErrorMessage
, nullptr,
892 CTYPESMSG_TYPE_ERROR
, expected
, src
);
897 InitCTypeClass(JSContext
* cx
, HandleObject parent
)
899 JSFunction
* fun
= JS_DefineFunction(cx
, parent
, "CType", ConstructAbstract
, 0,
904 RootedObject
ctor(cx
, JS_GetFunctionObject(fun
));
905 RootedObject
fnproto(cx
);
906 if (!JS_GetPrototype(cx
, ctor
, &fnproto
))
911 // Set up ctypes.CType.prototype.
912 RootedObject
prototype(cx
, JS_NewObject(cx
, &sCTypeProtoClass
, fnproto
, parent
));
916 if (!JS_DefineProperty(cx
, ctor
, "prototype", prototype
,
917 JSPROP_ENUMERATE
| JSPROP_READONLY
| JSPROP_PERMANENT
))
920 if (!JS_DefineProperty(cx
, prototype
, "constructor", ctor
,
921 JSPROP_ENUMERATE
| JSPROP_READONLY
| JSPROP_PERMANENT
))
924 // Define properties and functions common to all CTypes.
925 if (!JS_DefineProperties(cx
, prototype
, sCTypeProps
) ||
926 !JS_DefineFunctions(cx
, prototype
, sCTypeFunctions
))
929 if (!JS_FreezeObject(cx
, ctor
) || !JS_FreezeObject(cx
, prototype
))
936 InitABIClass(JSContext
* cx
, JSObject
* parent
)
938 RootedObject
obj(cx
, JS_NewObject(cx
, nullptr, NullPtr(), NullPtr()));
943 if (!JS_DefineFunctions(cx
, obj
, sCABIFunctions
))
951 InitCDataClass(JSContext
* cx
, HandleObject parent
, HandleObject CTypeProto
)
953 JSFunction
* fun
= JS_DefineFunction(cx
, parent
, "CData", ConstructAbstract
, 0,
958 RootedObject
ctor(cx
, JS_GetFunctionObject(fun
));
961 // Set up ctypes.CData.__proto__ === ctypes.CType.prototype.
962 // (Note that 'ctypes.CData instanceof Function' is still true, thanks to the
964 if (!JS_SetPrototype(cx
, ctor
, CTypeProto
))
967 // Set up ctypes.CData.prototype.
968 RootedObject
prototype(cx
, JS_NewObject(cx
, &sCDataProtoClass
, NullPtr(), parent
));
972 if (!JS_DefineProperty(cx
, ctor
, "prototype", prototype
,
973 JSPROP_ENUMERATE
| JSPROP_READONLY
| JSPROP_PERMANENT
))
976 if (!JS_DefineProperty(cx
, prototype
, "constructor", ctor
,
977 JSPROP_ENUMERATE
| JSPROP_READONLY
| JSPROP_PERMANENT
))
980 // Define properties and functions common to all CDatas.
981 if (!JS_DefineProperties(cx
, prototype
, sCDataProps
) ||
982 !JS_DefineFunctions(cx
, prototype
, sCDataFunctions
))
985 if (//!JS_FreezeObject(cx, prototype) || // XXX fixme - see bug 541212!
986 !JS_FreezeObject(cx
, ctor
))
993 DefineABIConstant(JSContext
* cx
,
997 HandleObject prototype
)
999 RootedObject
obj(cx
, JS_DefineObject(cx
, parent
, name
, &sCABIClass
, prototype
,
1000 JSPROP_ENUMERATE
| JSPROP_READONLY
| JSPROP_PERMANENT
));
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.
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
);
1027 RootedObject
obj(cx
, JS_GetFunctionObject(fun
));
1031 // Set up the .prototype and .prototype.constructor properties.
1032 typeProto
.set(JS_NewObject(cx
, &sCTypeProtoClass
, CTypeProto
, parent
));
1036 // Define property before proceeding, for GC safety.
1037 if (!JS_DefineProperty(cx
, obj
, "prototype", typeProto
,
1038 JSPROP_ENUMERATE
| JSPROP_READONLY
| JSPROP_PERMANENT
))
1041 if (fns
&& !JS_DefineFunctions(cx
, typeProto
, fns
))
1044 if (!JS_DefineProperties(cx
, typeProto
, props
))
1047 if (!JS_DefineProperty(cx
, typeProto
, "constructor", obj
,
1048 JSPROP_ENUMERATE
| JSPROP_READONLY
| JSPROP_PERMANENT
))
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
));
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
))
1069 if (instanceProps
&& !JS_DefineProperties(cx
, dataProto
, instanceProps
))
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
))
1084 InitInt64Class(JSContext
* cx
,
1085 HandleObject parent
,
1086 const JSClass
* clasp
,
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
));
1097 RootedObject
ctor(cx
, JS_GetConstructor(cx
, prototype
));
1100 if (!JS_FreezeObject(cx
, ctor
))
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
,
1112 js::SetFunctionNativeReserved(fun
, SLOT_FN_INT64PROTO
,
1113 OBJECT_TO_JSVAL(prototype
));
1115 if (!JS_FreezeObject(cx
, prototype
))
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
]));
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
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
));
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.
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
));
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
]))
1203 if (!InitTypeConstructor(cx
, parent
, CTypeProto
, CDataProto
,
1204 sArrayFunction
, nullptr, sArrayProps
,
1205 sArrayInstanceFunctions
, sArrayInstanceProps
,
1206 protos
[SLOT_ARRAYPROTO
], protos
[SLOT_ARRAYDATAPROTO
]))
1209 if (!InitTypeConstructor(cx
, parent
, CTypeProto
, CDataProto
,
1210 sStructFunction
, sStructFunctions
, sStructProps
,
1211 sStructInstanceFunctions
, nullptr,
1212 protos
[SLOT_STRUCTPROTO
], protos
[SLOT_STRUCTDATAPROTO
]))
1215 if (!InitTypeConstructor(cx
, parent
, CTypeProto
, protos
[SLOT_POINTERDATAPROTO
],
1216 sFunctionFunction
, nullptr, sFunctionProps
, sFunctionInstanceFunctions
, nullptr,
1217 protos
[SLOT_FUNCTIONPROTO
], protos
[SLOT_FUNCTIONDATAPROTO
]))
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
])
1234 protos
[SLOT_UINT64PROTO
].set(InitInt64Class(cx
, parent
, &sUInt64ProtoClass
,
1235 UInt64::Construct
, sUInt64Functions
, sUInt64StaticFunctions
));
1236 if (!protos
[SLOT_UINT64PROTO
])
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
));
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
))
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) \
1279 CTYPES_FOR_EACH_TYPE(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
))
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
))
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
));
1301 typeObj
= PointerType::CreateInternal(cx
, typeObj
);
1304 if (!JS_DefineProperty(cx
, parent
, "voidptr_t", typeObj
,
1305 JSPROP_ENUMERATE
| JSPROP_READONLY
| JSPROP_PERMANENT
))
1312 IsCTypesGlobal(JSObject
* obj
)
1314 return JS_GetClass(obj
) == &sCTypesGlobalClass
;
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())
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
)) {
1347 if (val
.isPrimitive()) {
1348 JS_ReportError(cx
, "missing or non-object field");
1352 result
.set(val
.toObjectOrNull());
1356 } /* namespace ctypes */
1357 } /* namespace js */
1360 using namespace js::ctypes
;
1363 JS_InitCTypesClass(JSContext
* cx
, HandleObject global
)
1365 // attach ctypes property to global object
1366 RootedObject
ctypes(cx
, JS_NewObject(cx
, &sCTypesGlobalClass
, NullPtr(), NullPtr()));
1370 if (!JS_DefineProperty(cx
, global
, "ctypes", ctypes
,
1371 JSPROP_READONLY
| JSPROP_PERMANENT
,
1372 JS_STUBGETTER
, JS_STUBSETTER
)){
1376 if (!InitTypeClasses(cx
, ctypes
))
1379 // attach API functions and properties
1380 if (!JS_DefineFunctions(cx
, ctypes
, sModuleFunctions
) ||
1381 !JS_DefineProperties(cx
, ctypes
, sModuleProps
))
1384 // Set up ctypes.CDataFinalizer.prototype.
1385 RootedObject
ctor(cx
);
1386 if (!GetObjectProperty(cx
, ctypes
, "CDataFinalizer", &ctor
))
1389 RootedObject
prototype(cx
, JS_NewObject(cx
, &sCDataFinalizerProtoClass
, NullPtr(), ctypes
));
1393 if (!JS_DefineFunctions(cx
, prototype
, sCDataFinalizerFunctions
))
1396 if (!JS_DefineProperty(cx
, ctor
, "prototype", prototype
,
1397 JSPROP_ENUMERATE
| JSPROP_READONLY
| JSPROP_PERMANENT
))
1400 if (!JS_DefineProperty(cx
, prototype
, "constructor", ctor
,
1401 JSPROP_ENUMERATE
| JSPROP_READONLY
| JSPROP_PERMANENT
))
1405 // Seal the ctypes object, to prevent modification.
1406 return JS_FreezeObject(cx
, ctypes
);
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
)));
1422 JS_FRIEND_API(size_t)
1423 SizeOfDataIfCDataObject(mozilla::MallocSizeOf mallocSizeOf
, JSObject
* obj
)
1425 if (!CData::IsCData(obj
))
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
);
1437 n
+= mallocSizeOf(*buffer
);
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
);
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.
1479 struct ConvertImpl
<uint64_t, double> {
1480 static MOZ_ALWAYS_INLINE
uint64_t Convert(double d
) {
1481 return d
> 0x7fffffffffffffffui
64 ?
1482 uint64_t(d
- 0x8000000000000000ui
64) + 0x8000000000000000ui
64 :
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
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
);
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
);
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.
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
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
)
1532 if (NumericLimits
<FromType
>::is_signed
&&
1533 !NumericLimits
<TargetType
>::is_signed
)
1536 if (!NumericLimits
<FromType
>::is_exact
&&
1537 NumericLimits
<TargetType
>::is_exact
)
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
>())
1585 // Return 'true' if 'i' is exactly representable in 'TargetType'.
1586 return IsExactImpl
<TargetType
,
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
) {
1601 // Specialization where Type is signed.
1602 template<class Type
>
1603 struct IsNegativeImpl
<Type
, true> {
1604 static MOZ_ALWAYS_INLINE
bool Test(Type i
) {
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.
1619 jsvalToBool(JSContext
* cx
, jsval val
, bool* result
)
1621 if (val
.isBoolean()) {
1622 *result
= val
.toBoolean();
1625 if (val
.isInt32()) {
1626 int32_t i
= val
.toInt32();
1628 return i
== 0 || i
== 1;
1630 if (val
.isDouble()) {
1631 double d
= val
.toDouble();
1634 return d
== 1 || d
== 0;
1636 // Don't silently convert null to bool. It's probably a mistake.
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
>
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
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) \
1672 if (!IsAlwaysExact<IntegerType, fromType>()) \
1674 *result = IntegerType(*static_cast<fromType*>(data)); \
1676 CTYPES_FOR_EACH_INT_TYPE(INTEGER_CASE
)
1677 CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGER_CASE
)
1683 case TYPE_float32_t
:
1684 case TYPE_float64_t
:
1686 case TYPE_signed_char
:
1687 case TYPE_unsigned_char
:
1693 // Not a compatible number type.
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
);
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);
1726 // Don't silently convert null to an integer. It's probably a mistake.
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
>
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());
1747 if (val
.isDouble()) {
1748 *result
= FloatType(val
.toDouble());
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) \
1762 if (!IsAlwaysExact<FloatType, fromType>()) \
1764 *result = FloatType(*static_cast<fromType*>(data)); \
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
)
1773 case TYPE_signed_char
:
1774 case TYPE_unsigned_char
:
1780 // Not a compatible number type.
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.
1790 template <class IntegerType
, class CharT
>
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
;
1800 IntegerType sign
= 1;
1802 if (!NumericLimits
<IntegerType
>::is_signed
)
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')) {
1816 // Scan the string left to right and build the number,
1817 // checking for valid characters 0 - 9, a - f, A - F and overflow.
1821 if (c
>= '0' && c
<= '9')
1823 else if (base
== 16 && c
>= 'a' && c
<= 'f')
1825 else if (base
== 16 && c
>= 'A' && c
<= 'F')
1831 i
= ii
* base
+ sign
* c
;
1832 if (i
/ base
!= ii
) // overflow
1840 template<class IntegerType
>
1842 StringToInteger(JSContext
* cx
, JSString
* string
, IntegerType
* result
)
1844 JSLinearString
* linear
= string
->ensureLinear(cx
);
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
>
1860 jsvalToBigInteger(JSContext
* cx
,
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
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
);
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.
1917 jsvalToSize(JSContext
* cx
, jsval val
, bool allowString
, size_t* result
)
1919 if (!jsvalToBigInteger(cx
, val
, allowString
, result
))
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
>
1931 jsidToBigInteger(JSContext
* cx
,
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
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
);
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.
1957 jsidToSize(JSContext
* cx
, jsid val
, bool allowString
, size_t* result
)
1959 if (!jsidToBigInteger(cx
, val
, allowString
, result
))
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.
1969 SizeTojsval(JSContext
* cx
, size_t size
, MutableHandleValue result
)
1971 if (Convert
<size_t>(double(size
)) != size
) {
1972 JS_ReportError(cx
, "size overflow");
1976 result
.setNumber(double(size
));
1980 // Forcefully convert val to IntegerType when explicitly requested.
1981 template<class IntegerType
>
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;
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
);
2001 if (UInt64::IsUInt64(obj
)) {
2002 uint64_t i
= Int64Base::GetInt(obj
);
2003 *result
= IntegerType(i
);
2010 // Forcefully convert val to a pointer value when explicitly requested.
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
);
2021 if (val
.isDouble()) {
2022 double d
= val
.toDouble();
2024 // Cast through an intptr_t intermediate to sign-extend.
2025 intptr_t i
= Convert
<intptr_t>(d
);
2029 *result
= uintptr_t(i
);
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
)
2047 *result
= uintptr_t(p
);
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
;
2062 template<class IntegerType
, class CharType
, size_t N
, class AP
>
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
);
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;
2079 IntegerType ii
= i
/ IntegerType(radix
);
2080 size_t index
= sign
* size_t(i
- ii
* IntegerType(radix
));
2081 *--cp
= "0123456789abcdefghijklmnopqrstuvwxyz"[index
];
2088 MOZ_ASSERT(cp
>= buffer
);
2089 result
.append(cp
, end
);
2092 template<class CharType
>
2094 strnlen(const CharType
* begin
, size_t max
)
2096 for (const CharType
* s
= begin
; s
!= begin
+ max
; ++s
)
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
2112 // * If 'parentObj' is null, the new CData object may or may not own its
2113 // resulting buffer depending on the 'ownResult' argument.
2115 ConvertToJS(JSContext
* cx
,
2116 HandleObject typeObj
,
2117 HandleObject parentObj
,
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
);
2131 result
.setUndefined();
2134 result
.setBoolean(*static_cast<bool*>(data
));
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)); \
2142 result.setDouble(double(value)); \
2145 CTYPES_FOR_EACH_INT_TYPE(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. */ \
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); \
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); \
2166 JSObject* obj = Int64Base::Construct(cx, proto, value, \
2167 !NumericLimits<type>::is_signed); \
2170 result.setObject(*obj); \
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)); \
2181 CTYPES_FOR_EACH_FLOAT_TYPE(FLOAT_CASE
)
2183 #define CHAR_CASE(name, type, ffiType) \
2185 /* Convert to an integer. We have no idea what character encoding to */ \
2186 /* use, if any. */ \
2187 result.setInt32(*static_cast<type*>(data)); \
2189 CTYPES_FOR_EACH_CHAR_TYPE(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);
2197 result
.setString(str
);
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");
2210 JSObject
* obj
= CData::Create(cx
, typeObj
, parentObj
, data
, ownResult
);
2214 result
.setObject(*obj
);
2218 MOZ_CRASH("cannot return a FunctionType");
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
) {
2232 TypeCode elementTypeCode
;
2233 switch (JS_GetArrayBufferViewType(valObj
)) {
2235 elementTypeCode
= TYPE_int8_t
;
2238 case Scalar::Uint8Clamped
:
2239 elementTypeCode
= TYPE_uint8_t
;
2242 elementTypeCode
= TYPE_int16_t
;
2244 case Scalar::Uint16
:
2245 elementTypeCode
= TYPE_uint16_t
;
2248 elementTypeCode
= TYPE_int32_t
;
2250 case Scalar::Uint32
:
2251 elementTypeCode
= TYPE_uint32_t
;
2253 case Scalar::Float32
:
2254 elementTypeCode
= TYPE_float32_t
;
2256 case Scalar::Float64
:
2257 elementTypeCode
= TYPE_float64_t
;
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
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.
2278 ImplicitConvert(JSContext
* cx
,
2280 JSObject
* targetType_
,
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
);
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
);
2314 // We have called |dispose| or |forget| already.
2315 JS_ReportError(cx
, "Attempting to convert an empty CDataFinalizer");
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
);
2327 TypeCode targetCode
= CType::GetTypeCode(targetType
);
2329 switch (targetCode
) {
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`.
2334 if (!jsvalToBool(cx
, val
, &result
))
2335 return TypeError(cx
, "boolean", val
);
2336 *static_cast<bool*>(buffer
) = result
;
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'. */ \
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); \
2351 result = linear->latin1OrTwoByteChar(0); \
2352 } else if (!jsvalToInteger(cx, val, &result)) { \
2353 return TypeError(cx, #name, val); \
2355 *static_cast<type*>(buffer) = result; \
2358 CTYPES_FOR_EACH_CHAR16_TYPE(CHAR16_CASE
)
2360 #define INTEGRAL_CASE(name, type, ffiType) \
2361 case TYPE_##name: { \
2362 /* Do not implicitly lose bits. */ \
2364 if (!jsvalToInteger(cx, val, &result)) \
2365 return TypeError(cx, #name, val); \
2366 *static_cast<type*>(buffer) = result; \
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: { \
2380 if (!jsvalToFloat(cx, val, &result)) \
2381 return TypeError(cx, #name, val); \
2382 *static_cast<type*>(buffer) = result; \
2385 CTYPES_FOR_EACH_FLOAT_TYPE(FLOAT_CASE
)
2387 case TYPE_pointer
: {
2389 // Convert to a null pointer.
2390 *static_cast<void**>(buffer
) = nullptr;
2394 JS::Rooted
<JSObject
*> baseType(cx
, PointerType::GetBaseType(targetType
));
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
);
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
;
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
);
2426 switch (CType::GetTypeCode(baseType
)) {
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)
2435 char** charBuffer
= static_cast<char**>(buffer
);
2436 *charBuffer
= cx
->pod_malloc
<char>(nbytes
+ 1);
2438 JS_ReportAllocationOverflow(cx
);
2442 ASSERT_OK(DeflateStringToUTF8Buffer(cx
, sourceLinear
, *charBuffer
, &nbytes
));
2443 (*charBuffer
)[nbytes
] = 0;
2444 *freePointer
= true;
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
);
2458 *freePointer
= true;
2459 if (sourceLinear
->hasLatin1Chars()) {
2460 AutoCheckCannotGC nogc
;
2461 CopyAndInflateChars(*char16Buffer
, sourceLinear
->latin1Chars(nogc
), sourceLength
);
2463 AutoCheckCannotGC nogc
;
2464 mozilla::PodCopy(*char16Buffer
, sourceLinear
->twoByteChars(nogc
), sourceLength
);
2466 (*char16Buffer
)[sourceLength
] = 0;
2470 return TypeError(cx
, "string pointer", val
);
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.)
2479 return TypeError(cx
, "arraybuffer pointer", val
);
2483 JS::AutoCheckCannotGC nogc
;
2484 ptr
= JS_GetArrayBufferData(valObj
, nogc
);
2487 return TypeError(cx
, "arraybuffer pointer", val
);
2489 *static_cast<void**>(buffer
) = ptr
;
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
);
2498 return TypeError(cx
, "typed array pointer", val
);
2502 JS::AutoCheckCannotGC nogc
;
2503 ptr
= JS_GetArrayBufferViewData(valObj
, nogc
);
2506 return TypeError(cx
, "typed array pointer", val
);
2508 *static_cast<void**>(buffer
) = ptr
;
2511 return TypeError(cx
, "pointer", val
);
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
);
2524 switch (CType::GetTypeCode(baseType
)) {
2526 case TYPE_signed_char
:
2527 case TYPE_unsigned_char
: {
2528 // Convert from UTF-16 or Latin1 to UTF-8.
2530 GetDeflatedUTF8StringLength(cx
, sourceLinear
);
2531 if (nbytes
== (size_t) -1)
2534 if (targetLength
< nbytes
) {
2535 JS_ReportError(cx
, "ArrayType has insufficient length");
2539 char* charBuffer
= static_cast<char*>(buffer
);
2540 ASSERT_OK(DeflateStringToUTF8Buffer(cx
, sourceLinear
, charBuffer
,
2543 if (targetLength
> nbytes
)
2544 charBuffer
[nbytes
] = 0;
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");
2556 char16_t
* dest
= static_cast<char16_t
*>(buffer
);
2557 if (sourceLinear
->hasLatin1Chars()) {
2558 AutoCheckCannotGC nogc
;
2559 CopyAndInflateChars(dest
, sourceLinear
->latin1Chars(nogc
), sourceLength
);
2561 AutoCheckCannotGC nogc
;
2562 mozilla::PodCopy(dest
, sourceLinear
->twoByteChars(nogc
), sourceLength
);
2565 if (targetLength
> sourceLength
)
2566 dest
[sourceLength
] = 0;
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");
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
);
2592 for (uint32_t i
= 0; i
< sourceLength
; ++i
) {
2593 RootedValue
item(cx
);
2594 if (!JS_GetElement(cx
, valObj
, i
, &item
))
2597 char* data
= intermediate
.get() + elementSize
* i
;
2598 if (!ImplicitConvert(cx
, item
, baseType
, data
, false, nullptr))
2602 memcpy(buffer
, intermediate
.get(), arraySize
);
2604 } else if (val
.isObject() && JS_IsArrayBufferObject(valObj
)) {
2605 // Check that array is consistent with type, then
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");
2614 JS::AutoCheckCannotGC nogc
;
2615 memcpy(buffer
, JS_GetArrayBufferData(valObj
, nogc
), sourceLength
);
2617 } else if (val
.isObject() && JS_IsTypedArrayObject(valObj
)) {
2618 // Check that array is consistent with type, then
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");
2631 JS::AutoCheckCannotGC nogc
;
2632 memcpy(buffer
, JS_GetArrayBufferViewData(valObj
, nogc
), sourceLength
);
2635 // Don't implicitly convert to string. Users can implicitly convert
2636 // with `String(x)` or `""+x`.
2637 return TypeError(cx
, "array", val
);
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
));
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
);
2657 const FieldInfoHash
* fields
= StructType::GetFieldInfo(targetType
);
2658 if (props
.length() != fields
->count()) {
2659 JS_ReportError(cx
, "missing fields");
2664 for (size_t i
= 0; i
< props
.length(); ++i
) {
2667 if (!JSID_IS_STRING(id
)) {
2668 JS_ReportError(cx
, "property name is not a string");
2672 JSFlatString
* name
= JSID_TO_FLAT_STRING(id
);
2673 const FieldInfo
* field
= StructType::LookupField(cx
, targetType
, name
);
2677 RootedValue
prop(cx
);
2678 if (!JS_GetPropertyById(cx
, valObj
, id
, &prop
))
2681 // Convert the field via ImplicitConvert().
2682 char* fieldData
= intermediate
.get() + field
->mOffset
;
2683 if (!ImplicitConvert(cx
, prop
, field
->mType
, fieldData
, false, nullptr))
2687 memcpy(buffer
, intermediate
.get(), structSize
);
2691 return TypeError(cx
, "struct", val
);
2695 MOZ_CRASH("invalid type");
2701 // Convert jsval 'val' to a C binary representation of CType 'targetType',
2702 // storing the result in 'buffer'. This function is more forceful than
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))
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.
2715 if (!JS_GetPendingException(cx
, &ex
))
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
);
2726 *static_cast<bool*>(buffer
) = ToBoolean(val
);
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. */ \
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; \
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.
2749 if (!jsvalToPtrExplicit(cx
, val
, &result
))
2750 return TypeError(cx
, "pointer", val
);
2751 *static_cast<uintptr_t*>(buffer
) = result
;
2754 case TYPE_float32_t
:
2755 case TYPE_float64_t
:
2760 // ImplicitConvert is sufficient. Re-throw the exception it generated.
2761 JS_SetPendingException(cx
, ex
);
2765 MOZ_CRASH("invalid type");
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]'.
2775 BuildTypeName(JSContext
* cx
, JSObject
* typeObj_
)
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
;
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
;
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
, "[");
2808 if (ArrayType::GetSafeLength(typeObj
, &length
))
2809 IntegerToString(length
, 10, result
);
2811 AppendString(result
, "]");
2813 typeObj
= ArrayType::GetBaseType(typeObj
);
2814 prevGrouping
= currentGrouping
;
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
;
2861 // Either a basic or struct type. Use the type's name as the base type.
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') ||
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.)
2888 BuildTypeSource(JSContext
* cx
,
2893 RootedObject
typeObj(cx
, typeObj_
);
2895 // Walk the types, building up the toSource() string.
2896 switch (CType::GetTypeCode(typeObj
)) {
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
);
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");
2916 // Recursively build the source string, and append '.ptr'.
2917 BuildTypeSource(cx
, baseType
, makeShort
, result
);
2918 AppendString(result
, ".ptr");
2921 case TYPE_function
: {
2922 FunctionInfo
* fninfo
= FunctionType::GetFunctionInfo(typeObj
);
2924 AppendString(result
, "ctypes.FunctionType(");
2926 switch (GetABICode(fninfo
->mABI
)) {
2928 AppendString(result
, "ctypes.default_abi, ");
2931 AppendString(result
, "ctypes.stdcall_abi, ");
2934 AppendString(result
, "ctypes.winapi_abi, ");
2937 MOZ_CRASH("invalid abi");
2940 // Recursively build the source string describing the function return and
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
, ")");
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
2964 JSObject
* baseType
= ArrayType::GetBaseType(typeObj
);
2965 BuildTypeSource(cx
, baseType
, makeShort
, result
);
2966 AppendString(result
, ".array(");
2969 if (ArrayType::GetSafeLength(typeObj
, &length
))
2970 IntegerToString(length
, 10, result
);
2972 AppendString(result
, ")");
2976 JSString
* name
= CType::GetName(cx
, typeObj
);
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
);
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
, ")");
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
))
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
, "])");
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.)
3035 BuildDataSource(JSContext
* cx
,
3036 HandleObject typeObj
,
3041 TypeCode type
= CType::GetTypeCode(typeObj
);
3044 if (*static_cast<bool*>(data
))
3045 AppendString(result
, "true");
3047 AppendString(result
, "false");
3049 #define INTEGRAL_CASE(name, type, ffiType) \
3051 /* Serialize as a primitive decimal integer. */ \
3052 IntegerToString(*static_cast<type*>(data), 10, result); \
3054 CTYPES_FOR_EACH_INT_TYPE(INTEGRAL_CASE
)
3055 #undef INTEGRAL_CASE
3056 #define WRAPPED_INT_CASE(name, type, ffiType) \
3058 /* Serialize as a wrapped decimal integer. */ \
3059 if (!NumericLimits<type>::is_signed) \
3060 AppendString(result, "ctypes.UInt64(\""); \
3062 AppendString(result, "ctypes.Int64(\""); \
3064 IntegerToString(*static_cast<type*>(data), 10, result); \
3065 AppendString(result, "\")"); \
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); \
3076 JS_ReportOutOfMemory(cx); \
3080 result.append(str, strlen(str)); \
3083 CTYPES_FOR_EACH_FLOAT_TYPE(FLOAT_CASE
)
3085 #define CHAR_CASE(name, type, ffiType) \
3087 /* Serialize as an integer. */ \
3088 IntegerToString(*static_cast<type*>(data), 10, result); \
3090 CTYPES_FOR_EACH_CHAR_TYPE(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);
3098 // Escape characters, and quote as necessary.
3099 RootedValue
valStr(cx
, StringValue(str
));
3100 JSString
* src
= JS_ValueToSource(cx
, valStr
);
3104 AppendString(result
, src
);
3108 case TYPE_function
: {
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
, "\")");
3123 AppendString(result
, ")");
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
))
3141 AppendString(result
, ", ");
3143 AppendString(result
, "]");
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
))
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
];
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
))
3179 if (i
+ 1 != length
)
3180 AppendString(result
, ", ");
3184 AppendString(result
, "}");
3189 MOZ_CRASH("invalid type");
3195 /*******************************************************************************
3196 ** JSAPI callback function implementations
3197 *******************************************************************************/
3200 ConstructAbstract(JSContext
* cx
,
3204 // Calling an abstract base class constructor is disallowed.
3205 JS_ReportError(cx
, "cannot construct from abstract type");
3209 /*******************************************************************************
3210 ** CType implementation
3211 *******************************************************************************/
3214 CType::ConstructData(JSContext
* cx
,
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");
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
)) {
3232 JS_ReportError(cx
, "cannot construct from void_t");
3235 JS_ReportError(cx
, "cannot construct from FunctionType; use FunctionType.ptr instead");
3238 return PointerType::ConstructData(cx
, obj
, args
);
3240 return ArrayType::ConstructData(cx
, obj
, args
);
3242 return StructType::ConstructData(cx
, obj
, args
);
3244 return ConstructBasic(cx
, obj
, args
);
3249 CType::ConstructBasic(JSContext
* cx
,
3251 const CallArgs
& args
)
3253 if (args
.length() > 1) {
3254 JS_ReportError(cx
, "CType constructor takes zero or one argument");
3258 // construct a CData object
3259 RootedObject
result(cx
, CData::Create(cx
, obj
, NullPtr(), nullptr, true));
3263 if (args
.length() == 1) {
3264 if (!ExplicitConvert(cx
, args
[0], obj
, CData::GetData(result
)))
3268 args
.rval().setObject(*result
);
3273 CType::Create(JSContext
* cx
,
3274 HandleObject typeProto
,
3275 HandleObject dataProto
,
3282 RootedString
name(cx
, name_
);
3283 RootedValue
size(cx
, size_
);
3284 RootedValue
align(cx
, align_
);
3285 RootedObject
parent(cx
, JS_GetParent(typeProto
));
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
));
3308 // Set up the reserved slots.
3309 JS_SetReservedSlot(typeObj
, SLOT_TYPECODE
, INT_TO_JSVAL(type
));
3311 JS_SetReservedSlot(typeObj
, SLOT_FFITYPE
, PRIVATE_TO_JSVAL(ffiType
));
3313 JS_SetReservedSlot(typeObj
, SLOT_NAME
, STRING_TO_JSVAL(name
));
3314 JS_SetReservedSlot(typeObj
, SLOT_SIZE
, size
);
3315 JS_SetReservedSlot(typeObj
, SLOT_ALIGN
, align
);
3318 // Set up the 'prototype' and 'prototype.constructor' properties.
3319 RootedObject
prototype(cx
, JS_NewObject(cx
, &sCDataProtoClass
, dataProto
, parent
));
3323 if (!JS_DefineProperty(cx
, prototype
, "constructor", typeObj
,
3324 JSPROP_READONLY
| JSPROP_PERMANENT
))
3327 // Set the 'prototype' object.
3328 //if (!JS_FreezeObject(cx, prototype)) // XXX fixme - see bug 541212!
3330 JS_SetReservedSlot(typeObj
, SLOT_PROTO
, OBJECT_TO_JSVAL(prototype
));
3333 if (!JS_FreezeObject(cx
, typeObj
))
3336 // Assert a sanity check on size and alignment: size % alignment should always
3338 MOZ_ASSERT_IF(IsSizeDefined(typeObj
),
3339 GetSize(typeObj
) % GetAlignment(typeObj
) == 0);
3345 CType::DefineBuiltin(JSContext
* cx
,
3347 const char* propName
,
3348 JSObject
* typeProto_
,
3349 JSObject
* dataProto_
,
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
));
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
));
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
))
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())
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()));
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
));
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
);
3419 // Nothing to do here.
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())
3432 // The contents of our slots depends on what kind of type we are.
3433 switch (TypeCode(slot
.toInt32())) {
3435 slot
= obj
->as
<NativeObject
>().getReservedSlot(SLOT_FIELDINFO
);
3436 if (slot
.isUndefined())
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");
3450 case TYPE_function
: {
3451 // Check if we have a FunctionInfo.
3452 slot
= obj
->as
<NativeObject
>().getReservedSlot(SLOT_FNINFO
);
3453 if (slot
.isUndefined())
3456 FunctionInfo
* fninfo
= static_cast<FunctionInfo
*>(slot
.toPrivate());
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");
3468 // Nothing to do here.
3474 CType::IsCType(JSObject
* obj
)
3476 return JS_GetClass(obj
) == &sCTypeClass
;
3480 CType::IsCTypeProto(JSObject
* obj
)
3482 return JS_GetClass(obj
) == &sCTypeProtoClass
;
3486 CType::GetTypeCode(JSObject
* typeObj
)
3488 MOZ_ASSERT(IsCType(typeObj
));
3490 jsval result
= JS_GetReservedSlot(typeObj
, SLOT_TYPECODE
);
3491 return TypeCode(result
.toInt32());
3495 CType::TypesEqual(JSObject
* t1
, JSObject
* t2
)
3497 MOZ_ASSERT(IsCType(t1
) && IsCType(t2
));
3499 // Fast path: check for object equality.
3503 // First, perform shallow comparison.
3504 TypeCode c1
= GetTypeCode(t1
);
3505 TypeCode c2
= GetTypeCode(t2
);
3509 // Determine whether the types require shallow or deep comparison.
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
)
3525 if (!TypesEqual(f1
->mReturnType
, f2
->mReturnType
))
3528 if (f1
->mArgTypes
.length() != f2
->mArgTypes
.length())
3531 if (f1
->mIsVariadic
!= f2
->mIsVariadic
)
3534 for (size_t i
= 0; i
< f1
->mArgTypes
.length(); ++i
) {
3535 if (!TypesEqual(f1
->mArgTypes
[i
], f2
->mArgTypes
[i
]))
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
))
3550 JSObject
* b1
= ArrayType::GetBaseType(t1
);
3551 JSObject
* b2
= ArrayType::GetBaseType(t2
);
3552 return TypesEqual(b1
, b2
);
3555 // Require exact type object equality.
3558 // Shallow comparison is sufficient.
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();
3576 if (size
.isDouble()) {
3577 *result
= Convert
<size_t>(size
.toDouble());
3581 MOZ_ASSERT(size
.isUndefined());
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.
3598 return size
.toInt32();
3599 return Convert
<size_t>(size
.toDouble());
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();
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());
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
)) {
3638 result
= ArrayType::BuildFFIType(cx
, obj
);
3642 result
= StructType::BuildFFIType(cx
, obj
);
3646 MOZ_CRASH("simple types must have an ffi_type");
3651 JS_SetReservedSlot(obj
, SLOT_FFITYPE
, PRIVATE_TO_JSVAL(result
.get()));
3652 return result
.forget();
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
);
3668 JS_SetReservedSlot(obj
, SLOT_NAME
, STRING_TO_JSVAL(name
));
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();
3680 MOZ_ASSERT(CType::IsCTypeProto(proto
));
3682 // Get the desired prototype.
3683 jsval result
= JS_GetReservedSlot(proto
, slot
);
3684 return &result
.toObject();
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
))
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();
3707 CType::IsCTypeOrProto(HandleValue v
)
3711 JSObject
* obj
= &v
.toObject();
3712 return CType::IsCType(obj
) || CType::IsCTypeProto(obj
);
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());
3727 CType::IsCType(HandleValue v
)
3729 return v
.isObject() && CType::IsCType(&v
.toObject());
3733 CType::NameGetter(JSContext
* cx
, JS::CallArgs args
)
3735 RootedObject
obj(cx
, &args
.thisv().toObject());
3736 JSString
* name
= CType::GetName(cx
, obj
);
3740 args
.rval().setString(name
);
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());
3754 CType::PtrGetter(JSContext
* cx
, JS::CallArgs args
)
3756 RootedObject
obj(cx
, &args
.thisv().toObject());
3757 JSObject
* pointerType
= PointerType::CreateInternal(cx
, obj
);
3761 args
.rval().setObject(*pointerType
);
3766 CType::CreateArray(JSContext
* cx
, unsigned argc
, jsval
* vp
)
3768 CallArgs args
= CallArgsFromVp(argc
, vp
);
3769 RootedObject
baseType(cx
, JS_THIS_OBJECT(cx
, vp
));
3772 if (!CType::IsCType(baseType
)) {
3773 JS_ReportError(cx
, "not a CType");
3777 // Construct and return a new ArrayType object.
3778 if (args
.length() > 1) {
3779 JS_ReportError(cx
, "array takes zero or one argument");
3783 // Convert the length argument to a size_t.
3785 if (args
.length() == 1 && !jsvalToSize(cx
, args
[0], false, &length
)) {
3786 JS_ReportError(cx
, "argument must be a nonnegative integer");
3790 JSObject
* result
= ArrayType::CreateInternal(cx
, baseType
, length
, args
.length() == 1);
3794 args
.rval().setObject(*result
);
3799 CType::ToString(JSContext
* cx
, unsigned argc
, jsval
* vp
)
3801 CallArgs args
= CallArgsFromVp(argc
, vp
);
3802 RootedObject
obj(cx
, JS_THIS_OBJECT(cx
, vp
));
3805 if (!CType::IsCType(obj
) && !CType::IsCTypeProto(obj
)) {
3806 JS_ReportError(cx
, "not a CType");
3810 // Create the appropriate string depending on whether we're sCTypeClass or
3811 // sCTypeProtoClass.
3813 if (CType::IsCType(obj
)) {
3815 AppendString(type
, "type ");
3816 AppendString(type
, GetName(cx
, obj
));
3817 result
= NewUCString(cx
, type
);
3820 result
= JS_NewStringCopyZ(cx
, "[CType proto object]");
3825 args
.rval().setString(result
);
3830 CType::ToSource(JSContext
* cx
, unsigned argc
, jsval
* vp
)
3832 CallArgs args
= CallArgsFromVp(argc
, vp
);
3833 JSObject
* obj
= JS_THIS_OBJECT(cx
, vp
);
3836 if (!CType::IsCType(obj
) && !CType::IsCTypeProto(obj
))
3838 JS_ReportError(cx
, "not a CType");
3842 // Create the appropriate string depending on whether we're sCTypeClass or
3843 // sCTypeProtoClass.
3845 if (CType::IsCType(obj
)) {
3847 BuildTypeSource(cx
, obj
, false, source
);
3848 result
= NewUCString(cx
, source
);
3850 result
= JS_NewStringCopyZ(cx
, "[CType proto object]");
3855 args
.rval().setString(result
);
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
));
3870 if (v
.isPrimitive())
3873 RootedObject
proto(cx
, &v
.toObject());
3875 if (!JS_GetPrototype(cx
, proto
, &proto
))
3879 if (proto
== prototype
) {
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
))
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 *******************************************************************************/
3909 ABI::IsABI(JSObject
* obj
)
3911 return JS_GetClass(obj
) == &sCABIClass
;
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");
3923 JSObject
* obj
= JS_THIS_OBJECT(cx
, vp
);
3926 if (!ABI::IsABI(obj
)) {
3927 JS_ReportError(cx
, "not an ABI");
3932 switch (GetABICode(obj
)) {
3934 result
= JS_NewStringCopyZ(cx
, "ctypes.default_abi");
3937 result
= JS_NewStringCopyZ(cx
, "ctypes.stdcall_abi");
3940 result
= JS_NewStringCopyZ(cx
, "ctypes.winapi_abi");
3943 JS_ReportError(cx
, "not a valid ABICode");
3949 args
.rval().setString(result
);
3954 /*******************************************************************************
3955 ** PointerType implementation
3956 *******************************************************************************/
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");
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");
3975 JSObject
* result
= CreateInternal(cx
, obj
);
3979 args
.rval().setObject(*result
);
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
));
3998 RootedObject
typeProto(cx
, CType::GetProtoFromType(cx
, baseType
, SLOT_POINTERPROTO
));
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
),
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
));
4020 PointerType::ConstructData(JSContext
* cx
,
4022 const CallArgs
& args
)
4024 if (!CType::IsCType(obj
) || CType::GetTypeCode(obj
) != TYPE_pointer
) {
4025 JS_ReportError(cx
, "not a PointerType");
4029 if (args
.length() > 3) {
4030 JS_ReportError(cx
, "constructor takes 0, 1, 2, or 3 arguments");
4034 RootedObject
result(cx
, CData::Create(cx
, obj
, NullPtr(), nullptr, true));
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)
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");
4068 return ExplicitConvert(cx
, args
[0], obj
, CData::GetData(result
));
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()) {
4082 } else if (args
[1].isObject()) {
4083 thisObj
= &args
[1].toObject();
4084 } else if (!JS_ValueToObject(cx
, args
[1], &thisObj
)) {
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)
4096 RootedObject
fnObj(cx
, &args
[0].toObject());
4097 return FunctionType::ConstructData(cx
, baseObj
, result
, fnObj
, thisObj
, errVal
);
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();
4111 PointerType::IsPointerType(HandleValue v
)
4115 JSObject
* obj
= &v
.toObject();
4116 return CType::IsCType(obj
) && CType::GetTypeCode(obj
) == TYPE_pointer
;
4120 PointerType::IsPointer(HandleValue v
)
4124 JSObject
* obj
= &v
.toObject();
4125 return CData::IsCData(obj
) && CType::GetTypeCode(CData::GetCType(obj
)) == TYPE_pointer
;
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());
4138 PointerType::IsNull(JSContext
* cx
, unsigned argc
, jsval
* vp
)
4140 CallArgs args
= CallArgsFromVp(argc
, vp
);
4141 JSObject
* obj
= JS_THIS_OBJECT(cx
, vp
);
4144 if (!CData::IsCData(obj
)) {
4145 JS_ReportError(cx
, "not a CData");
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");
4156 void* data
= *static_cast<void**>(CData::GetData(obj
));
4157 args
.rval().setBoolean(data
== nullptr);
4162 PointerType::OffsetBy(JSContext
* cx
, const CallArgs
& args
, int offset
)
4164 JSObject
* obj
= JS_THIS_OBJECT(cx
, args
.base());
4167 if (!CData::IsCData(obj
)) {
4168 JS_ReportError(cx
, "not a CData");
4172 RootedObject
typeObj(cx
, CData::GetCType(obj
));
4173 if (CType::GetTypeCode(typeObj
) != TYPE_pointer
) {
4174 JS_ReportError(cx
, "not a PointerType");
4178 RootedObject
baseType(cx
, PointerType::GetBaseType(typeObj
));
4179 if (!CType::IsSizeDefined(baseType
)) {
4180 JS_ReportError(cx
, "cannot modify pointer of undefined size");
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);
4193 args
.rval().setObject(*result
);
4198 PointerType::Increment(JSContext
* cx
, unsigned argc
, jsval
* vp
)
4200 CallArgs args
= CallArgsFromVp(argc
, vp
);
4201 return OffsetBy(cx
, args
, 1);
4205 PointerType::Decrement(JSContext
* cx
, unsigned argc
, jsval
* vp
)
4207 CallArgs args
= CallArgsFromVp(argc
, vp
);
4208 return OffsetBy(cx
, args
, -1);
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");
4221 void* data
= *static_cast<void**>(CData::GetData(obj
));
4222 if (data
== nullptr) {
4223 JS_ReportError(cx
, "cannot read contents of null pointer");
4227 RootedValue
result(cx
);
4228 if (!ConvertToJS(cx
, baseType
, NullPtr(), data
, false, false, &result
))
4231 args
.rval().set(result
);
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");
4245 void* data
= *static_cast<void**>(CData::GetData(obj
));
4246 if (data
== nullptr) {
4247 JS_ReportError(cx
, "cannot write contents to null pointer");
4251 args
.rval().setUndefined();
4252 return ImplicitConvert(cx
, args
.get(0), baseType
, data
, false, nullptr);
4255 /*******************************************************************************
4256 ** ArrayType implementation
4257 *******************************************************************************/
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");
4269 if (args
[0].isPrimitive() ||
4270 !CType::IsCType(&args
[0].toObject())) {
4271 JS_ReportError(cx
, "first argument must be a CType");
4275 // Convert the length argument to a size_t.
4277 if (args
.length() == 2 && !jsvalToSize(cx
, args
[1], false, &length
)) {
4278 JS_ReportError(cx
, "second argument must be a nonnegative integer");
4282 RootedObject
baseType(cx
, &args
[0].toObject());
4283 JSObject
* result
= CreateInternal(cx
, baseType
, length
, args
.length() == 2);
4287 args
.rval().setObject(*result
);
4292 ArrayType::CreateInternal(JSContext
* cx
,
4293 HandleObject baseType
,
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
));
4302 RootedObject
dataProto(cx
, CType::GetProtoFromType(cx
, baseType
, SLOT_ARRAYDATAPROTO
));
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.
4310 if (!CType::GetSafeSize(baseType
, &baseSize
)) {
4311 JS_ReportError(cx
, "base size must be defined");
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");
4324 if (!SizeTojsval(cx
, size
, &sizeVal
) ||
4325 !SizeTojsval(cx
, length
, &lengthVal
))
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);
4337 // Set the element type.
4338 JS_SetReservedSlot(typeObj
, SLOT_ELEMENT_T
, OBJECT_TO_JSVAL(baseType
));
4341 JS_SetReservedSlot(typeObj
, SLOT_LENGTH
, lengthVal
);
4347 ArrayType::ConstructData(JSContext
* cx
,
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");
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");
4371 if (args
.length() != 1) {
4372 JS_ReportError(cx
, "constructor takes one argument");
4376 RootedObject
baseType(cx
, GetBaseType(obj
));
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");
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
);
4403 switch (CType::GetTypeCode(baseType
)) {
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)
4416 length
= sourceLength
+ 1;
4419 return TypeError(cx
, "array", args
[0]);
4423 JS_ReportError(cx
, "argument must be an array object or length");
4427 // Construct a new ArrayType of defined length, for the new CData object.
4428 obj
= CreateInternal(cx
, baseType
, length
, true);
4433 JSObject
* result
= CData::Create(cx
, obj
, NullPtr(), nullptr, true);
4437 args
.rval().setObject(*result
);
4439 if (convertObject
) {
4440 if (!ExplicitConvert(cx
, args
[0], obj
, CData::GetData(result
)))
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();
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();
4472 if (length
.isDouble()) {
4473 *result
= Convert
<size_t>(length
.toDouble());
4477 MOZ_ASSERT(length
.isUndefined());
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());
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
);
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
>());
4522 JS_ReportOutOfMemory(cx
);
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
);
4535 for (size_t i
= 0; i
< length
; ++i
)
4536 ffiType
->elements
[i
] = ffiBaseType
;
4537 ffiType
->elements
[length
] = nullptr;
4539 return ffiType
.forget();
4543 ArrayType::IsArrayType(HandleValue v
)
4547 JSObject
* obj
= &v
.toObject();
4548 return CType::IsCType(obj
) && CType::GetTypeCode(obj
) == TYPE_array
;
4552 ArrayType::IsArrayOrArrayType(HandleValue v
)
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
;
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());
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());
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");
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
)
4605 // Convert the index to a size_t and bounds-check it.
4607 size_t length
= GetLength(typeObj
);
4608 bool ok
= jsidToSize(cx
, idval
, true, &index
);
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.
4615 if (!ok
|| index
>= length
) {
4616 JS_ReportError(cx
, "invalid index");
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
);
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");
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
)
4641 // Convert the index to a size_t and bounds-check it.
4643 size_t length
= GetLength(typeObj
);
4644 bool ok
= jsidToSize(cx
, idval
, true, &index
);
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.
4651 if (!ok
|| index
>= length
) {
4652 JS_ReportError(cx
, "invalid index");
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);
4663 ArrayType::AddressOfElement(JSContext
* cx
, unsigned argc
, jsval
* vp
)
4665 CallArgs args
= CallArgsFromVp(argc
, vp
);
4666 RootedObject
obj(cx
, JS_THIS_OBJECT(cx
, vp
));
4669 if (!CData::IsCData(obj
)) {
4670 JS_ReportError(cx
, "not a CData");
4674 RootedObject
typeObj(cx
, CData::GetCType(obj
));
4675 if (CType::GetTypeCode(typeObj
) != TYPE_array
) {
4676 JS_ReportError(cx
, "not an ArrayType");
4680 if (args
.length() != 1) {
4681 JS_ReportError(cx
, "addressOfElement takes one argument");
4685 RootedObject
baseType(cx
, GetBaseType(typeObj
));
4686 RootedObject
pointerType(cx
, PointerType::CreateInternal(cx
, baseType
));
4690 // Create a PointerType CData object containing null.
4691 RootedObject
result(cx
, CData::Create(cx
, pointerType
, NullPtr(), nullptr, true));
4695 args
.rval().setObject(*result
);
4697 // Convert the index to a size_t and bounds-check it.
4699 size_t length
= GetLength(typeObj
);
4700 if (!jsvalToSize(cx
, args
[0], false, &index
) ||
4702 JS_ReportError(cx
, "invalid index");
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
;
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");
4727 RootedObject
obj(cx
, &val
.toObject());
4728 AutoIdArray
props(cx
, JS_Enumerate(cx
, obj
));
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");
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");
4744 RootedValue
propVal(cx
);
4745 if (!JS_GetPropertyById(cx
, obj
, nameid
, &propVal
))
4748 if (propVal
.isPrimitive() || !CType::IsCType(&propVal
.toObject())) {
4749 JS_ReportError(cx
, "struct field descriptors require a valid name and type");
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());
4758 if (!CType::GetSafeSize(typeObj
, &size
) || size
== 0) {
4759 JS_ReportError(cx
, "struct field types must have defined and nonzero size");
4763 return JSID_TO_FLAT_STRING(nameid
);
4766 // For a struct field with 'name' and 'type', add an element of the form
4769 AddFieldToArray(JSContext
* cx
,
4770 MutableHandleValue element
,
4771 JSFlatString
* name_
,
4774 RootedObject
typeObj(cx
, typeObj_
);
4775 Rooted
<JSFlatString
*> name(cx
, name_
);
4776 RootedObject
fieldObj(cx
, JS_NewObject(cx
, nullptr, NullPtr(), NullPtr()));
4780 element
.setObject(*fieldObj
);
4782 AutoStableStringChars
nameChars(cx
);
4783 if (!nameChars
.initTwoByte(cx
, name
))
4786 if (!JS_DefineUCProperty(cx
, fieldObj
,
4787 nameChars
.twoByteChars(), name
->length(),
4789 JSPROP_ENUMERATE
| JSPROP_READONLY
| JSPROP_PERMANENT
))
4792 return JS_FreezeObject(cx
, fieldObj
);
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");
4806 jsval name
= args
[0];
4807 if (!name
.isString()) {
4808 JS_ReportError(cx
, "first argument must be a string");
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));
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");
4830 // Define the struct fields.
4831 if (!DefineInternal(cx
, result
, arr
))
4835 args
.rval().setObject(*result
);
4840 PostBarrierCallback(JSTracer
* trc
, JSString
* key
, void* data
)
4842 typedef HashMap
<JSFlatString
*,
4843 UnbarrieredFieldInfo
,
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
));
4854 StructType::DefineInternal(JSContext
* cx
, JSObject
* typeObj_
, JSObject
* fieldsObj_
)
4856 RootedObject
typeObj(cx
, typeObj_
);
4857 RootedObject
fieldsObj(cx
, fieldsObj_
);
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
));
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()));
4875 if (!JS_DefineProperty(cx
, prototype
, "constructor", typeObj
,
4876 JSPROP_READONLY
| JSPROP_PERMANENT
))
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
);
4888 JS::AutoValueVector
fieldRoots(cx
);
4889 if (!fieldRoots
.resize(len
)) {
4890 JS_ReportOutOfMemory(cx
);
4894 // Process the field types.
4895 size_t structSize
, structAlign
;
4900 for (uint32_t i
= 0; i
< len
; ++i
) {
4901 RootedValue
item(cx
);
4902 if (!JS_GetElement(cx
, fieldsObj
, i
, &item
))
4905 RootedObject
fieldType(cx
, nullptr);
4906 Rooted
<JSFlatString
*> name(cx
, ExtractStructField(cx
, item
, &fieldType
));
4909 fieldRoots
[i
].setObject(*fieldType
);
4911 // Make sure each field name is unique
4912 FieldInfoHash::AddPtr entryPtr
= fields
->lookupForAdd(name
);
4914 JS_ReportError(cx
, "struct fields must have unique names");
4918 // Add the field to the StructType's 'prototype' property.
4919 AutoStableStringChars
nameChars(cx
);
4920 if (!nameChars
.initTwoByte(cx
, name
))
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
)))
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");
4941 // Add field name to the hash
4943 info
.mType
= nullptr; // Value of fields are not yet traceable here.
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");
4961 structSize
= structTail
;
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.
4972 RootedValue
sizeVal(cx
);
4973 if (!SizeTojsval(cx
, structSize
, &sizeVal
))
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!
4988 JS_SetReservedSlot(typeObj
, SLOT_PROTO
, OBJECT_TO_JSVAL(prototype
));
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
>());
5007 JS_ReportOutOfMemory(cx
);
5010 ffiType
->type
= FFI_TYPE_STRUCT
;
5012 AutoPtr
<ffi_type
*> elements
;
5014 elements
= cx
->pod_malloc
<ffi_type
*>(len
+ 1);
5016 JS_ReportOutOfMemory(cx
);
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
);
5026 elements
[entry
.value().mIndex
] = fieldType
;
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);
5035 JS_ReportOutOfMemory(cx
);
5038 elements
[0] = &ffi_type_uint8
;
5039 elements
[1] = nullptr;
5042 ffiType
->elements
= elements
.get();
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.
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
);
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
;
5065 return ffiType
.forget();
5069 StructType::Define(JSContext
* cx
, unsigned argc
, jsval
* vp
)
5071 CallArgs args
= CallArgsFromVp(argc
, vp
);
5072 RootedObject
obj(cx
, JS_THIS_OBJECT(cx
, vp
));
5075 if (!CType::IsCType(obj
) ||
5076 CType::GetTypeCode(obj
) != TYPE_struct
) {
5077 JS_ReportError(cx
, "not a StructType");
5081 if (CType::IsSizeDefined(obj
)) {
5082 JS_ReportError(cx
, "StructType has already been defined");
5086 if (args
.length() != 1) {
5087 JS_ReportError(cx
, "define takes one argument");
5091 jsval arg
= args
[0];
5092 if (arg
.isPrimitive()) {
5093 JS_ReportError(cx
, "argument must be an array");
5096 RootedObject
arr(cx
, arg
.toObjectOrNull());
5097 if (!JS_IsArrayObject(cx
, arr
)) {
5098 JS_ReportError(cx
, "argument must be an array");
5102 return DefineInternal(cx
, obj
, arr
);
5106 StructType::ConstructData(JSContext
* cx
,
5108 const CallArgs
& args
)
5110 if (!CType::IsCType(obj
) || CType::GetTypeCode(obj
) != TYPE_struct
) {
5111 JS_ReportError(cx
, "not a StructType");
5115 if (!CType::IsSizeDefined(obj
)) {
5116 JS_ReportError(cx
, "cannot construct an opaque StructType");
5120 JSObject
* result
= CData::Create(cx
, obj
, NullPtr(), nullptr, true);
5124 args
.rval().setObject(*result
);
5126 if (args
.length() == 0)
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
))
5145 if (fields
->count() != 1)
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
))
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
,
5175 JS_ReportError(cx
, "constructor takes 0, 1, or %u arguments",
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());
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
);
5200 return &ptr
->value();
5202 JSAutoByteString
bytes(cx
, name
);
5206 JS_ReportError(cx
, "%s does not name a field", bytes
.ptr());
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
))
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
))
5233 RootedObject
fieldsProp(cx
, JS_NewArrayObject(cx
, fieldsVec
));
5237 // Seal the fields array.
5238 if (!JS_FreezeObject(cx
, fieldsProp
))
5245 StructType::IsStruct(HandleValue v
)
5249 JSObject
* obj
= &v
.toObject();
5250 return CType::IsCType(obj
) && CType::GetTypeCode(obj
) == TYPE_struct
;
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());
5265 if (args
.rval().isUndefined()) {
5266 // Build the 'fields' array lazily.
5267 JSObject
* fields
= BuildFieldsArray(cx
, obj
);
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()));
5281 StructType::FieldGetter(JSContext
* cx
, HandleObject obj
, HandleId idval
, MutableHandleValue vp
)
5283 if (!CData::IsCData(obj
)) {
5284 JS_ReportError(cx
, "not a CData");
5288 JSObject
* typeObj
= CData::GetCType(obj
);
5289 if (CType::GetTypeCode(typeObj
) != TYPE_struct
) {
5290 JS_ReportError(cx
, "not a StructType");
5294 const FieldInfo
* field
= LookupField(cx
, typeObj
, JSID_TO_FLAT_STRING(idval
));
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
);
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");
5311 JSObject
* typeObj
= CData::GetCType(obj
);
5312 if (CType::GetTypeCode(typeObj
) != TYPE_struct
) {
5313 JS_ReportError(cx
, "not a StructType");
5317 const FieldInfo
* field
= LookupField(cx
, typeObj
, JSID_TO_FLAT_STRING(idval
));
5321 char* data
= static_cast<char*>(CData::GetData(obj
)) + field
->mOffset
;
5322 return ImplicitConvert(cx
, vp
, field
->mType
, data
, false, nullptr);
5326 StructType::AddressOfField(JSContext
* cx
, unsigned argc
, jsval
* vp
)
5328 CallArgs args
= CallArgsFromVp(argc
, vp
);
5329 RootedObject
obj(cx
, JS_THIS_OBJECT(cx
, vp
));
5332 if (!CData::IsCData(obj
)) {
5333 JS_ReportError(cx
, "not a CData");
5337 JSObject
* typeObj
= CData::GetCType(obj
);
5338 if (CType::GetTypeCode(typeObj
) != TYPE_struct
) {
5339 JS_ReportError(cx
, "not a StructType");
5343 if (args
.length() != 1) {
5344 JS_ReportError(cx
, "addressOfField takes one argument");
5348 JSFlatString
* str
= JS_FlattenString(cx
, args
[0].toString());
5352 const FieldInfo
* field
= LookupField(cx
, typeObj
, str
);
5356 RootedObject
baseType(cx
, field
->mType
);
5357 RootedObject
pointerType(cx
, PointerType::CreateInternal(cx
, baseType
));
5361 // Create a PointerType CData object containing null.
5362 JSObject
* result
= CData::Create(cx
, pointerType
, NullPtr(), nullptr, true);
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
;
5374 /*******************************************************************************
5375 ** FunctionType implementation
5376 *******************************************************************************/
5378 // Helper class for handling allocation of function arguments.
5381 AutoValue() : mData(nullptr) { }
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
);
5394 memset(mData
, 0, size
);
5395 return mData
!= nullptr;
5402 GetABI(JSContext
* cx
, jsval abiType
, ffi_abi
* result
)
5404 if (abiType
.isPrimitive())
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.
5414 *result
= FFI_DEFAULT_ABI
;
5418 #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
5419 *result
= FFI_STDCALL
;
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
;
5434 PrepareType(JSContext
* cx
, jsval type
)
5436 if (type
.isPrimitive() || !CType::IsCType(type
.toObjectOrNull())) {
5437 JS_ReportError(cx
, "not a ctypes type");
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
);
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");
5458 if (!CType::IsSizeDefined(result
)) {
5459 JS_ReportError(cx
, "Argument type must have defined size");
5463 // libffi cannot pass types of zero size by value.
5464 MOZ_ASSERT(CType::GetSize(result
) != 0);
5470 PrepareReturnType(JSContext
* cx
, jsval type
)
5472 if (type
.isPrimitive() || !CType::IsCType(type
.toObjectOrNull())) {
5473 JS_ReportError(cx
, "not a ctypes type");
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");
5486 if (typeCode
!= TYPE_void_t
&& !CType::IsSizeDefined(result
)) {
5487 JS_ReportError(cx
, "Return type must have defined size");
5491 // libffi cannot pass types of zero size by value.
5492 MOZ_ASSERT(typeCode
== TYPE_void_t
|| CType::GetSize(result
) != 0);
5497 static MOZ_ALWAYS_INLINE
bool
5498 IsEllipsis(JSContext
* cx
, jsval v
, bool* isEllipsis
)
5500 *isEllipsis
= false;
5503 JSString
* str
= v
.toString();
5504 if (str
->length() != 3)
5506 JSLinearString
* linear
= str
->ensureLinear(cx
);
5510 *isEllipsis
= (linear
->latin1OrTwoByteChar(0) == dot
&&
5511 linear
->latin1OrTwoByteChar(1) == dot
&&
5512 linear
->latin1OrTwoByteChar(2) == dot
);
5517 PrepareCIF(JSContext
* cx
,
5518 FunctionInfo
* fninfo
)
5521 if (!GetABI(cx
, OBJECT_TO_JSVAL(fninfo
->mABI
), &abi
)) {
5522 JS_ReportError(cx
, "Invalid ABI specification");
5526 ffi_type
* rtype
= CType::GetFFIType(cx
, fninfo
->mReturnType
);
5531 ffi_prep_cif(&fninfo
->mCIF
,
5533 fninfo
->mFFITypes
.length(),
5535 fninfo
->mFFITypes
.begin());
5541 JS_ReportError(cx
, "Invalid ABI specification");
5543 case FFI_BAD_TYPEDEF
:
5544 JS_ReportError(cx
, "Invalid type specification");
5547 JS_ReportError(cx
, "Unknown libffi error");
5553 FunctionType::BuildSymbolName(JSString
* name
,
5555 AutoCString
& result
)
5557 FunctionInfo
* fninfo
= GetFunctionInfo(typeObj
);
5559 switch (GetABICode(fninfo
->mABI
)) {
5562 // For cdecl or WINAPI functions, no mangling is necessary.
5563 AppendString(result
, name
);
5567 #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
5568 // On WIN32, stdcall functions look like:
5570 // where 'foo' is the function name, and '40' is the aligned size of the
5572 AppendString(result
, "_");
5573 AppendString(result
, name
);
5574 AppendString(result
, "@");
5576 // Compute the suffix by aligning each argument to sizeof(ffi_arg).
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
);
5593 MOZ_CRASH("invalid abi");
5597 static FunctionInfo
*
5598 NewFunctionInfo(JSContext
* cx
,
5604 AutoPtr
<FunctionInfo
> fninfo(cx
->new_
<FunctionInfo
>());
5606 JS_ReportOutOfMemory(cx
);
5611 if (!GetABI(cx
, abiType
, &abi
)) {
5612 JS_ReportError(cx
, "Invalid ABI specification");
5615 fninfo
->mABI
= abiType
.toObjectOrNull();
5617 // prepare the result type
5618 fninfo
->mReturnType
= PrepareReturnType(cx
, returnType
);
5619 if (!fninfo
->mReturnType
)
5622 // prepare the argument types
5623 if (!fninfo
->mArgTypes
.reserve(argLength
) ||
5624 !fninfo
->mFFITypes
.reserve(argLength
)) {
5625 JS_ReportOutOfMemory(cx
);
5629 fninfo
->mIsVariadic
= false;
5631 for (uint32_t i
= 0; i
< argLength
; ++i
) {
5633 if (!IsEllipsis(cx
, argTypes
[i
], &isEllipsis
))
5636 fninfo
->mIsVariadic
= true;
5638 JS_ReportError(cx
, "\"...\" may not be the first and only parameter "
5639 "type of a variadic function declaration");
5642 if (i
< argLength
- 1) {
5643 JS_ReportError(cx
, "\"...\" must be the last parameter type of a "
5644 "variadic function declaration");
5647 if (GetABICode(fninfo
->mABI
) != ABI_DEFAULT
) {
5648 JS_ReportError(cx
, "Variadic functions must use the __cdecl calling "
5655 JSObject
* argType
= PrepareType(cx
, argTypes
[i
]);
5659 ffi_type
* ffiType
= CType::GetFFIType(cx
, argType
);
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()))
5674 return fninfo
.forget();
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");
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");
5700 ASSERT_OK(JS_GetArrayLength(cx
, arrayObj
, &len
));
5702 if (!argTypes
.resize(len
)) {
5703 JS_ReportOutOfMemory(cx
);
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
]))
5715 JSObject
* result
= CreateInternal(cx
, args
[0], args
[1],
5716 argTypes
.begin(), argTypes
.length());
5720 args
.rval().setObject(*result
);
5725 FunctionType::CreateInternal(JSContext
* cx
,
5731 // Determine and check the types, and prepare the function CIF.
5732 AutoPtr
<FunctionInfo
> fninfo(NewFunctionInfo(cx
, abi
, rtype
, argtypes
, arglen
));
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
));
5742 RootedObject
dataProto(cx
, CType::GetProtoFromType(cx
, fninfo
->mReturnType
,
5743 SLOT_FUNCTIONDATAPROTO
));
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);
5753 // Stash the FunctionInfo in a reserved slot.
5754 JS_SetReservedSlot(typeObj
, SLOT_FNINFO
, PRIVATE_TO_JSVAL(fninfo
.forget()));
5759 // Construct a function pointer to a JS function (see CClosure::Create()).
5760 // Regular function pointers are constructed directly in
5761 // PointerType::ConstructData().
5763 FunctionType::ConstructData(JSContext
* cx
,
5764 HandleObject typeObj
,
5765 HandleObject dataObj
,
5767 HandleObject thisObj
,
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");
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");
5785 RootedObject
closureObj(cx
, CClosure::Create(cx
, typeObj
, fnObj
, thisObj
, errVal
, data
));
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
;
5804 ConvertArgument(JSContext
* cx
,
5808 AutoValueAutoArray
* strings
)
5810 if (!value
->SizeToType(cx
, type
)) {
5811 JS_ReportAllocationOverflow(cx
);
5815 bool freePointer
= false;
5816 if (!ImplicitConvert(cx
, arg
, type
, value
->mData
, true, &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
);
5826 strings
->back().mData
= *static_cast<char**>(value
->mData
);
5833 FunctionType::Call(JSContext
* cx
,
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");
5845 RootedObject
typeObj(cx
, CData::GetCType(obj
));
5846 if (CType::GetTypeCode(typeObj
) != TYPE_pointer
) {
5847 JS_ReportError(cx
, "not a FunctionType.ptr");
5851 typeObj
= PointerType::GetBaseType(typeObj
);
5852 if (CType::GetTypeCode(typeObj
) != TYPE_function
) {
5853 JS_ReportError(cx
, "not a FunctionType.ptr");
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");
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());
5871 JS_ReportError(cx
, "library is not open");
5876 // prepare the values for each argument
5877 AutoValueAutoArray values
;
5878 AutoValueAutoArray strings
;
5879 if (!values
.resize(args
.length())) {
5880 JS_ReportOutOfMemory(cx
);
5884 for (unsigned i
= 0; i
< argcFixed
; ++i
)
5885 if (!ConvertArgument(cx
, args
[i
], fninfo
->mArgTypes
[i
], &values
[i
], &strings
))
5888 if (fninfo
->mIsVariadic
) {
5889 if (!fninfo
->mFFITypes
.resize(args
.length())) {
5890 JS_ReportOutOfMemory(cx
);
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
]));
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.
5916 if (!PrepareCIF(cx
, fninfo
))
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
);
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
));
5935 int32_t lastErrorStatus
; // The status as defined by |GetLastError|
5936 int32_t savedLastError
= GetLastError();
5938 #endif //defined(XP_WIN)
5939 int errnoStatus
; // The status as defined by |errno|
5940 int savedErrno
= errno
;
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
;
5953 lastErrorStatus
= GetLastError();
5954 SetLastError(savedLastError
);
5955 #endif // defined(XP_WIN)
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
);
5967 JS_SetReservedSlot(objCTypes
, SLOT_ERRNO
, INT_TO_JSVAL(errnoStatus
));
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.
5975 #define INTEGRAL_CASE(name, type, ffiType) \
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); \
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
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());
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());
6010 FunctionType::IsFunctionType(HandleValue v
)
6014 JSObject
* obj
= &v
.toObject();
6015 return CType::IsCType(obj
) && CType::GetTypeCode(obj
) == TYPE_function
;
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())
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
))
6037 for (size_t i
= 0; i
< len
; ++i
)
6038 vec
[i
].setObject(*fninfo
->mArgTypes
[i
]);
6040 argTypes
= JS_NewArrayObject(cx
, vec
);
6045 // Seal and cache it.
6046 if (!JS_FreezeObject(cx
, argTypes
))
6048 JS_SetReservedSlot(obj
, SLOT_ARGS_T
, JS::ObjectValue(*argTypes
));
6050 args
.rval().setObject(*argTypes
);
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
);
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
);
6071 FunctionType::IsVariadicGetter(JSContext
* cx
, JS::CallArgs args
)
6073 args
.rval().setBoolean(GetFunctionInfo(&args
.thisv().toObject())->mIsVariadic
);
6077 /*******************************************************************************
6078 ** CClosure implementation
6079 *******************************************************************************/
6082 CClosure::Create(JSContext
* cx
,
6083 HandleObject typeObj
,
6085 HandleObject thisObj
,
6089 RootedValue
errVal(cx
, errVal_
);
6092 RootedObject
result(cx
, JS_NewObject(cx
, &sCClosureClass
, NullPtr(), 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
)));
6103 JS_ReportOutOfMemory(cx
);
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
))
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");
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
)
6140 // Do the value conversion. This might fail, in which case we throw.
6141 if (!ImplicitConvert(cx
, errVal
, fninfo
->mReturnType
, cinfo
->errResult
,
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.
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");
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");
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
));
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())
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");
6194 JS_CallObjectTracer(trc
, &cinfo
->thisObj
, "thisObj");
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())
6205 ClosureInfo
* cinfo
= static_cast<ClosureInfo
*>(slot
.toPrivate());
6206 FreeOp::get(fop
)->delete_(cinfo
);
6210 CClosure::ClosureStub(ffi_cif
* cif
, void* result
, void** args
, void* userData
)
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
6245 if (cif
->rtype
!= &ffi_type_void
) {
6246 rvSize
= cif
->rtype
->size
;
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
));
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
);
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
]))
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,
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
6309 size_t copySize
= CType::GetSize(fninfo
->mReturnType
);
6310 MOZ_ASSERT(copySize
<= rvSize
);
6311 memcpy(result
, cinfo
->errResult
, copySize
);
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.
6317 JS_ReportError(cx
, "JavaScript callback failed, and an error sentinel "
6318 "was not specified.");
6319 if (JS_IsExceptionPending(cx
))
6320 JS_ReportPendingException(cx
);
6326 // Small integer types must be returned as a word-sized ffi_arg. Coerce it
6327 // back into the size libffi expects.
6329 #define INTEGRAL_CASE(name, type, ffiType) \
6331 if (sizeof(type) < sizeof(ffi_arg)) { \
6332 ffi_arg data = *static_cast<type*>(result); \
6333 *static_cast<ffi_arg*>(result) = data; \
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
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
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'.
6374 CData::Create(JSContext
* cx
,
6375 HandleObject typeObj
,
6376 HandleObject refObj
,
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
));
6394 RootedObject
dataObj(cx
, JS_NewObject(cx
, &sCDataClass
, proto
, parent
));
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.
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*>();
6412 JS_ReportOutOfMemory(cx
);
6418 data
= static_cast<char*>(source
);
6420 // Initialize our own buffer.
6421 size_t size
= CType::GetSize(typeObj
);
6422 data
= dataObj
->zone()->pod_malloc
<char>(size
);
6424 // Report a catchable allocation error.
6425 JS_ReportAllocationOverflow(cx
);
6431 memset(data
, 0, size
);
6433 memcpy(data
, source
, size
);
6437 JS_SetReservedSlot(dataObj
, SLOT_DATA
, PRIVATE_TO_JSVAL(buffer
));
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())
6450 bool owns
= slot
.toBoolean();
6452 slot
= JS_GetReservedSlot(obj
, SLOT_DATA
);
6453 if (slot
.isUndefined())
6455 char** buffer
= static_cast<char**>(slot
.toPrivate());
6458 FreeOp::get(fop
)->free_(*buffer
);
6459 FreeOp::get(fop
)->delete_(buffer
);
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
));
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());
6482 MOZ_ASSERT(*buffer
);
6487 CData::IsCData(JSObject
* obj
)
6489 return JS_GetClass(obj
) == &sCDataClass
;
6493 CData::IsCData(HandleValue v
)
6495 return v
.isObject() && CData::IsCData(&v
.toObject());
6499 CData::IsCDataProto(JSObject
* obj
)
6501 return JS_GetClass(obj
) == &sCDataProtoClass
;
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());
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);
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");
6531 RootedObject
obj(cx
, JS_THIS_OBJECT(cx
, vp
));
6534 if (!IsCData(obj
)) {
6535 JS_ReportError(cx
, "not a CData");
6539 RootedObject
typeObj(cx
, CData::GetCType(obj
));
6540 RootedObject
pointerType(cx
, PointerType::CreateInternal(cx
, typeObj
));
6544 // Create a PointerType CData object containing null.
6545 JSObject
* result
= CData::Create(cx
, pointerType
, NullPtr(), nullptr, true);
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
);
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");
6566 if (args
[0].isPrimitive() || !CData::IsCData(&args
[0].toObject())) {
6567 JS_ReportError(cx
, "first argument must be a CData");
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");
6578 RootedObject
targetType(cx
, &args
[1].toObject());
6580 if (!CType::GetSafeSize(targetType
, &targetSize
) ||
6581 targetSize
> CType::GetSize(sourceType
)) {
6583 "target CType has undefined or larger size than source CType");
6587 // Construct a new CData object with a type of 'targetType' and a referent
6589 void* data
= CData::GetData(sourceData
);
6590 JSObject
* result
= CData::Create(cx
, targetType
, sourceData
, data
, false);
6594 args
.rval().setObject(*result
);
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");
6607 if (args
[0].isPrimitive() || !CType::IsCType(&args
[0].toObject())) {
6608 JS_ReportError(cx
, "first argument must be a CType");
6612 RootedObject
targetType(cx
, &args
[0].toObject());
6614 if (!CType::GetSafeSize(targetType
, &targetSize
) ||
6615 targetSize
!= sizeof(void*)) {
6616 JS_ReportError(cx
, "target CType has non-pointer size");
6620 void* data
= static_cast<void*>(cx
->runtime());
6621 JSObject
* result
= CData::Create(cx
, targetType
, NullPtr(), &data
, true);
6625 args
.rval().setObject(*result
);
6629 typedef JS::TwoByteCharsZ (*InflateUTF8Method
)(JSContext
*, const JS::UTF8Chars
, size_t*);
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");
6640 JSObject
* obj
= CDataFinalizer::GetCData(cx
, JS_THIS_OBJECT(cx
, vp
));
6641 if (!obj
|| !CData::IsCData(obj
)) {
6642 JS_ReportError(cx
, "not a CData");
6646 // Make sure we are a pointer to, or an array of, an 8-bit or 16-bit
6647 // character or integer type.
6649 JSObject
* typeObj
= CData::GetCType(obj
);
6650 TypeCode typeCode
= CType::GetTypeCode(typeObj
);
6652 size_t maxLength
= -1;
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");
6663 baseType
= ArrayType::GetBaseType(typeObj
);
6664 data
= CData::GetData(obj
);
6665 maxLength
= ArrayType::GetLength(typeObj
);
6668 JS_ReportError(cx
, "not a PointerType or ArrayType");
6672 // Convert the string buffer, taking care to determine the correct string
6673 // length in the case of arrays (which may contain embedded nulls).
6675 switch (CType::GetTypeCode(baseType
)) {
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();
6689 result
= JS_NewUCString(cx
, dst
, length
);
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
);
6704 "base type is not an 8-bit or 16-bit integer or character type");
6711 args
.rval().setString(result
);
6716 CData::ReadString(JSContext
* cx
, unsigned argc
, jsval
* vp
)
6718 return ReadStringCommon(cx
, JS::UTF8CharsToNewTwoByteCharsZ
, argc
, vp
);
6722 CData::ReadStringReplaceMalformed(JSContext
* cx
, unsigned argc
, jsval
* vp
)
6724 return ReadStringCommon(cx
, JS::LossyUTF8CharsToNewTwoByteCharsZ
, argc
, vp
);
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.)
6737 BuildTypeSource(cx
, typeObj
, true, source
);
6738 AppendString(source
, "(");
6739 if (!BuildDataSource(cx
, typeObj
, data
, false, source
))
6742 AppendString(source
, ")");
6744 return NewUCString(cx
, source
);
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");
6756 JSObject
* obj
= JS_THIS_OBJECT(cx
, vp
);
6759 if (!CData::IsCData(obj
) && !CData::IsCDataProto(obj
)) {
6760 JS_ReportError(cx
, "not a CData");
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
);
6771 result
= JS_NewStringCopyZ(cx
, "[CData proto object]");
6777 args
.rval().setString(result
);
6782 CData::ErrnoGetter(JSContext
* cx
, JS::CallArgs args
)
6784 args
.rval().set(JS_GetReservedSlot(&args
.thisv().toObject(), SLOT_ERRNO
));
6790 CData::LastErrorGetter(JSContext
* cx
, JS::CallArgs args
)
6792 args
.rval().set(JS_GetReservedSlot(&args
.thisv().toObject(), SLOT_LASTERROR
));
6795 #endif // defined(XP_WIN)
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
));
6804 if (!CDataFinalizer::IsCDataFinalizer(objThis
)) {
6805 JS_ReportError(cx
, "not a CDataFinalizer");
6809 CDataFinalizer::Private
* p
= (CDataFinalizer::Private
*)
6810 JS_GetPrivate(objThis
);
6812 JSString
* strMessage
;
6814 strMessage
= JS_NewStringCopyZ(cx
, "ctypes.CDataFinalizer()");
6816 RootedObject
objType(cx
, CDataFinalizer::GetCType(cx
, objThis
));
6818 JS_ReportError(cx
, "CDataFinalizer has no type");
6823 AppendString(source
, "ctypes.CDataFinalizer(");
6824 JSString
* srcValue
= CData::GetSourceString(cx
, objType
, p
->cargs
);
6828 AppendString(source
, srcValue
);
6829 AppendString(source
, ", ");
6830 jsval valCodePtrType
= JS_GetReservedSlot(objThis
,
6831 SLOT_DATAFINALIZER_CODETYPE
);
6832 if (valCodePtrType
.isPrimitive()) {
6836 RootedObject
typeObj(cx
, valCodePtrType
.toObjectOrNull());
6837 JSString
* srcDispose
= CData::GetSourceString(cx
, typeObj
, &(p
->code
));
6842 AppendString(source
, srcDispose
);
6843 AppendString(source
, ")");
6844 strMessage
= NewUCString(cx
, source
);
6848 // This is a memory issue, no error message
6852 args
.rval().setString(strMessage
);
6857 CDataFinalizer::Methods::ToString(JSContext
* cx
, unsigned argc
, jsval
* vp
)
6859 CallArgs args
= CallArgsFromVp(argc
, vp
);
6860 JSObject
* objThis
= JS_THIS_OBJECT(cx
, vp
);
6863 if (!CDataFinalizer::IsCDataFinalizer(objThis
)) {
6864 JS_ReportError(cx
, "not a CDataFinalizer");
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]");
6877 } else if (!CDataFinalizer::GetValue(cx
, objThis
, &value
)) {
6878 MOZ_CRASH("Could not convert an empty CDataFinalizer");
6880 strMessage
= ToString(cx
, value
);
6885 args
.rval().setString(strMessage
);
6890 CDataFinalizer::IsCDataFinalizer(JSObject
* obj
)
6892 return JS_GetClass(obj
) == &sCDataFinalizerClass
;
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()) {
6907 return valData
.toObjectOrNull();
6911 CDataFinalizer::GetCData(JSContext
* cx
, JSObject
* obj
)
6914 JS_ReportError(cx
, "No C data");
6917 if (CData::IsCData(obj
)) {
6920 if (!CDataFinalizer::IsCDataFinalizer(obj
)) {
6921 JS_ReportError(cx
, "Not C data");
6924 RootedValue
val(cx
);
6925 if (!CDataFinalizer::GetValue(cx
, obj
, &val
) || val
.isPrimitive()) {
6926 JS_ReportError(cx
, "Empty CDataFinalizer");
6929 return val
.toObjectOrNull();
6933 CDataFinalizer::GetValue(JSContext
* cx
, JSObject
* obj
, MutableHandleValue aResult
)
6935 MOZ_ASSERT(IsCDataFinalizer(obj
));
6937 CDataFinalizer::Private
* p
= (CDataFinalizer::Private
*)
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>
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.
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");
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
);
6980 if (args
.length() != 2) {
6981 JS_ReportError(cx
, "CDataFinalizer takes 2 arguments");
6985 JS::HandleValue valCodePtr
= args
[1];
6986 if (!valCodePtr
.isObject()) {
6987 return TypeError(cx
, "_a CData object_ of a function pointer type",
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",
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",
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",
7018 uintptr_t code
= *reinterpret_cast<uintptr_t*>(CData::GetData(objCodePtr
));
7020 return TypeError(cx
, "a CData object of a _non-NULL_ function pointer type",
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",
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|
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)",
7059 // Note: We could handle that case, if necessary.
7060 JS_ReportError(cx
, "Internal Error during CDataFinalizer. Object cannot be represented");
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
),
7070 } //Otherwise, simply do not allocate
7072 // 5. Create |objResult|
7074 JSObject
* objResult
= JS_NewObject(cx
, &sCDataFinalizerClass
, objProto
, NullPtr());
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
);
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
);
7098 JS_SetReservedSlot(objResult
,
7099 SLOT_DATAFINALIZER_VALTYPE
,
7100 OBJECT_TO_JSVAL(objBestArgType
));
7103 JS_SetReservedSlot(objResult
,
7104 SLOT_DATAFINALIZER_CODETYPE
,
7105 OBJECT_TO_JSVAL(objCodePtrType
));
7108 if (!GetABI(cx
, OBJECT_TO_JSVAL(funInfoFinalizer
->mABI
), &abi
)) {
7109 JS_ReportError(cx
, "Internal Error: "
7110 "Invalid ABI specification in CDataFinalizer");
7114 ffi_type
* rtype
= CType::GetFFIType(cx
, funInfoFinalizer
->mReturnType
);
7116 JS_ReportError(cx
, "Internal Error: "
7117 "Could not access ffi type of CDataFinalizer");
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
;
7133 JS_SetPrivate(objResult
, p
.forget());
7134 args
.rval().setObject(*objResult
);
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|
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.
7154 CDataFinalizer::CallFinalizer(CDataFinalizer::Private
* p
,
7156 int32_t* lastErrorStatus
)
7158 int savedErrno
= errno
;
7161 int32_t savedLastError
= GetLastError();
7163 #endif // defined(XP_WIN)
7165 void* args
[1] = {p
->cargs
};
7166 ffi_call(&p
->CIF
, FFI_FN(p
->code
), p
->rvalue
, args
);
7169 *errnoStatus
= errno
;
7173 if (lastErrorStatus
) {
7174 *lastErrorStatus
= GetLastError();
7176 SetLastError(savedLastError
);
7177 #endif // defined(XP_WIN)
7183 * Preconditions: |this| must be a |CDataFinalizer|.
7184 * The function fails if |this| has gone through |Forget|/|Dispose|
7187 * Does not call the finalizer. Cleans up the Private memory and releases all
7188 * strong references.
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");
7199 JS::Rooted
<JSObject
*> obj(cx
, args
.thisv().toObjectOrNull());
7202 if (!CDataFinalizer::IsCDataFinalizer(obj
)) {
7203 RootedValue
val(cx
, ObjectValue(*obj
));
7204 return TypeError(cx
, "a CDataFinalizer", val
);
7207 CDataFinalizer::Private
* p
= (CDataFinalizer::Private
*)
7211 JS_ReportError(cx
, "forget called on an empty CDataFinalizer");
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");
7222 CDataFinalizer::Cleanup(p
, obj
);
7224 args
.rval().set(valJSData
);
7229 * Clean up the value.
7231 * Preconditions: |this| must be a |CDataFinalizer|.
7232 * The function fails if |this| has gone through |Forget|/|Dispose|
7235 * Calls the finalizer, cleans up the Private memory and releases all
7236 * strong references.
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");
7247 RootedObject
obj(cx
, JS_THIS_OBJECT(cx
, vp
));
7250 if (!CDataFinalizer::IsCDataFinalizer(obj
)) {
7251 RootedValue
val(cx
, ObjectValue(*obj
));
7252 return TypeError(cx
, "a CDataFinalizer", val
);
7255 CDataFinalizer::Private
* p
= (CDataFinalizer::Private
*)
7259 JS_ReportError(cx
, "dispose called on an empty CDataFinalizer.");
7263 jsval valType
= JS_GetReservedSlot(obj
, SLOT_DATAFINALIZER_VALTYPE
);
7264 MOZ_ASSERT(valType
.isObject());
7266 JSObject
* objCTypes
= CType::GetGlobalCTypes(cx
, &valType
.toObject());
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
);
7283 int32_t lastErrorStatus
;
7284 CDataFinalizer::CallFinalizer(p
, &errnoStatus
, &lastErrorStatus
);
7286 CDataFinalizer::CallFinalizer(p
, &errnoStatus
, nullptr);
7287 #endif // defined(XP_WIN)
7289 JS_SetReservedSlot(objCTypes
, SLOT_ERRNO
, INT_TO_JSVAL(errnoStatus
));
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
);
7299 CDataFinalizer::Cleanup(p
, obj
);
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.
7314 CDataFinalizer::Finalize(JSFreeOp
* fop
, JSObject
* obj
)
7316 CDataFinalizer::Private
* p
= (CDataFinalizer::Private
*)
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.
7339 CDataFinalizer::Cleanup(CDataFinalizer::Private
* p
, JSObject
* obj
)
7342 return; // We have already cleaned up
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 *******************************************************************************/
7367 Int64Base::Construct(JSContext
* cx
,
7372 const JSClass
* clasp
= isUnsigned
? &sUInt64Class
: &sInt64Class
;
7373 RootedObject
parent(cx
, JS_GetParent(proto
));
7374 RootedObject
result(cx
, JS_NewObject(cx
, clasp
, proto
, parent
));
7378 // attach the Int64's data
7379 uint64_t* buffer
= cx
->new_
<uint64_t>(data
);
7381 JS_ReportOutOfMemory(cx
);
7385 JS_SetReservedSlot(result
, SLOT_INT64
, PRIVATE_TO_JSVAL(buffer
));
7387 if (!JS_FreezeObject(cx
, result
))
7394 Int64Base::Finalize(JSFreeOp
* fop
, JSObject
* obj
)
7396 jsval slot
= JS_GetReservedSlot(obj
, SLOT_INT64
);
7397 if (slot
.isUndefined())
7400 FreeOp::get(fop
)->delete_(static_cast<uint64_t*>(slot
.toPrivate()));
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());
7412 Int64Base::ToString(JSContext
* cx
,
7414 const CallArgs
& args
,
7417 if (args
.length() > 1) {
7418 JS_ReportError(cx
, "toString takes zero or one argument");
7423 if (args
.length() == 1) {
7424 jsval arg
= args
[0];
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");
7433 AutoString intString
;
7435 IntegerToString(GetInt(obj
), radix
, intString
);
7437 IntegerToString(static_cast<int64_t>(GetInt(obj
)), radix
, intString
);
7440 JSString
* result
= NewUCString(cx
, intString
);
7444 args
.rval().setString(result
);
7449 Int64Base::ToSource(JSContext
* cx
,
7451 const CallArgs
& args
,
7454 if (args
.length() != 0) {
7455 JS_ReportError(cx
, "toSource takes zero arguments");
7459 // Return a decimal string suitable for constructing the number.
7462 AppendString(source
, "ctypes.UInt64(\"");
7463 IntegerToString(GetInt(obj
), 10, source
);
7465 AppendString(source
, "ctypes.Int64(\"");
7466 IntegerToString(static_cast<int64_t>(GetInt(obj
)), 10, source
);
7468 AppendString(source
, "\")");
7470 JSString
* result
= NewUCString(cx
, source
);
7474 args
.rval().setString(result
);
7479 Int64::Construct(JSContext
* cx
,
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");
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);
7506 args
.rval().setObject(*result
);
7511 Int64::IsInt64(JSObject
* obj
)
7513 return JS_GetClass(obj
) == &sInt64Class
;
7517 Int64::ToString(JSContext
* cx
, unsigned argc
, jsval
* vp
)
7519 CallArgs args
= CallArgsFromVp(argc
, vp
);
7520 JSObject
* obj
= JS_THIS_OBJECT(cx
, vp
);
7523 if (!Int64::IsInt64(obj
)) {
7524 JS_ReportError(cx
, "not an Int64");
7528 return Int64Base::ToString(cx
, obj
, args
, false);
7532 Int64::ToSource(JSContext
* cx
, unsigned argc
, jsval
* vp
)
7534 CallArgs args
= CallArgsFromVp(argc
, vp
);
7535 JSObject
* obj
= JS_THIS_OBJECT(cx
, vp
);
7538 if (!Int64::IsInt64(obj
)) {
7539 JS_ReportError(cx
, "not an Int64");
7543 return Int64Base::ToSource(cx
, obj
, args
, false);
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");
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
);
7566 args
.rval().setInt32(0);
7568 args
.rval().setInt32(-1);
7570 args
.rval().setInt32(1);
7575 #define LO_MASK ((uint64_t(1) << 32) - 1)
7576 #define INT64_LO(i) ((i) & LO_MASK)
7577 #define INT64_HI(i) ((i) >> 32)
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");
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
);
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");
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
);
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");
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);
7644 args
.rval().setObject(*result
);
7649 UInt64::Construct(JSContext
* cx
,
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");
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);
7676 args
.rval().setObject(*result
);
7681 UInt64::IsUInt64(JSObject
* obj
)
7683 return JS_GetClass(obj
) == &sUInt64Class
;
7687 UInt64::ToString(JSContext
* cx
, unsigned argc
, jsval
* vp
)
7689 CallArgs args
= CallArgsFromVp(argc
, vp
);
7690 JSObject
* obj
= JS_THIS_OBJECT(cx
, vp
);
7693 if (!UInt64::IsUInt64(obj
)) {
7694 JS_ReportError(cx
, "not a UInt64");
7698 return Int64Base::ToString(cx
, obj
, args
, true);
7702 UInt64::ToSource(JSContext
* cx
, unsigned argc
, jsval
* vp
)
7704 CallArgs args
= CallArgsFromVp(argc
, vp
);
7705 JSObject
* obj
= JS_THIS_OBJECT(cx
, vp
);
7708 if (!UInt64::IsUInt64(obj
)) {
7709 JS_ReportError(cx
, "not a UInt64");
7713 return Int64Base::ToSource(cx
, obj
, args
, true);
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");
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
);
7736 args
.rval().setInt32(0);
7738 args
.rval().setInt32(-1);
7740 args
.rval().setInt32(1);
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");
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
);
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");
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
);
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");
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);
7810 args
.rval().setObject(*result
);