1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
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"
8 #include "js/experimental/CTypes.h" // JS::CTypesActivity{Callback,Type}, JS::InitCTypesClass, JS::SetCTypesActivityCallback, JS::SetCTypesCallbacks
10 #include "mozilla/CheckedInt.h"
11 #include "mozilla/MemoryReporting.h"
12 #include "mozilla/Sprintf.h"
13 #include "mozilla/TextUtils.h"
14 #include "mozilla/Vector.h"
15 #include "mozilla/WrappingOperations.h"
29 #include <sys/types.h>
30 #include <type_traits>
36 #include "ctypes/Library.h"
37 #include "gc/GCContext.h"
38 #include "jit/AtomicOperations.h"
39 #include "js/Array.h" // JS::GetArrayLength, JS::IsArrayObject, JS::NewArrayObject
40 #include "js/ArrayBuffer.h" // JS::{IsArrayBufferObject,GetArrayBufferData,GetArrayBuffer{ByteLength,Data}}
41 #include "js/CallAndConstruct.h" // JS::IsCallable, JS_CallFunctionValue
42 #include "js/CharacterEncoding.h"
43 #include "js/experimental/TypedData.h" // JS_GetArrayBufferView{Type,Data}, JS_GetTypedArrayByteLength, JS_IsArrayBufferViewObject, JS_IsTypedArrayObject
44 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
45 #include "js/GlobalObject.h" // JS::CurrentGlobalOrNull
46 #include "js/Object.h" // JS::GetMaybePtrFromReservedSlot, JS::GetReservedSlot, JS::SetReservedSlot
47 #include "js/PropertyAndElement.h" // JS_DefineFunction, JS_DefineFunctions, JS_DefineProperties, JS_DefineProperty, JS_DefinePropertyById, JS_DefineUCProperty, JS_Enumerate, JS_GetElement, JS_GetProperty, JS_GetPropertyById
48 #include "js/PropertySpec.h"
49 #include "js/SharedArrayBuffer.h" // JS::{GetSharedArrayBuffer{ByteLength,Data},IsSharedArrayBufferObject}
50 #include "js/StableStringChars.h"
51 #include "js/UniquePtr.h"
52 #include "js/Utility.h"
53 #include "js/Vector.h"
54 #include "util/Text.h"
55 #include "util/Unicode.h"
56 #include "util/WindowsWrapper.h"
57 #include "vm/JSContext.h"
58 #include "vm/JSFunction.h"
59 #include "vm/JSObject.h"
61 #include "gc/GCContext-inl.h"
62 #include "vm/JSObject-inl.h"
64 using std::numeric_limits
;
66 using mozilla::CheckedInt
;
67 using mozilla::IsAsciiAlpha
;
68 using mozilla::IsAsciiDigit
;
70 using JS::AutoCheckCannotGC
;
71 using JS::AutoCTypesActivityCallback
;
72 using JS::AutoStableStringChars
;
73 using JS::CTypesActivityType
;
75 namespace js::ctypes
{
77 static bool HasUnpairedSurrogate(const char16_t
* chars
, size_t nchars
,
79 for (const char16_t
* end
= chars
+ nchars
; chars
!= end
; chars
++) {
81 if (unicode::IsSurrogate(c
)) {
83 if (unicode::IsTrailSurrogate(c
) || chars
== end
) {
88 if (!unicode::IsTrailSurrogate(c2
)) {
97 bool ReportErrorIfUnpairedSurrogatePresent(JSContext
* cx
, JSLinearString
* str
) {
98 if (str
->hasLatin1Chars()) {
104 JS::AutoCheckCannotGC nogc
;
105 if (!HasUnpairedSurrogate(str
->twoByteChars(nogc
), str
->length(),
112 SprintfLiteral(buffer
, "0x%x", unpaired
);
113 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
114 JSMSG_BAD_SURROGATE_CHAR
, buffer
);
118 /*******************************************************************************
119 ** JSAPI function prototypes
120 *******************************************************************************/
122 // We use an enclosing struct here out of paranoia about the ability of gcc 4.4
123 // (and maybe 4.5) to correctly compile this if it were a template function.
124 // See also the comments in dom/workers/Events.cpp (and other adjacent files) by
125 // the |struct Property| there.
126 template <JS::IsAcceptableThis Test
, JS::NativeImpl Impl
>
128 static bool Fun(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
129 JS::CallArgs args
= JS::CallArgsFromVp(argc
, vp
);
130 return JS::CallNonGenericMethod
<Test
, Impl
>(cx
, args
);
134 static bool ConstructAbstract(JSContext
* cx
, unsigned argc
, Value
* vp
);
137 static bool ConstructData(JSContext
* cx
, unsigned argc
, Value
* vp
);
138 static bool ConstructBasic(JSContext
* cx
, HandleObject obj
,
139 const CallArgs
& args
);
141 static void Trace(JSTracer
* trc
, JSObject
* obj
);
142 static void Finalize(JS::GCContext
* gcx
, JSObject
* obj
);
144 bool IsCType(HandleValue v
);
145 bool IsCTypeOrProto(HandleValue v
);
147 bool PrototypeGetter(JSContext
* cx
, const JS::CallArgs
& args
);
148 bool NameGetter(JSContext
* cx
, const JS::CallArgs
& args
);
149 bool SizeGetter(JSContext
* cx
, const JS::CallArgs
& args
);
150 bool PtrGetter(JSContext
* cx
, const JS::CallArgs
& args
);
152 static bool CreateArray(JSContext
* cx
, unsigned argc
, Value
* vp
);
153 static bool ToString(JSContext
* cx
, unsigned argc
, Value
* vp
);
154 static bool ToSource(JSContext
* cx
, unsigned argc
, Value
* vp
);
157 * Get the global "ctypes" object.
159 * |obj| must be a CType object.
161 * This function never returns nullptr.
163 static JSObject
* GetGlobalCTypes(JSContext
* cx
, JSObject
* obj
);
168 bool IsABI(JSObject
* obj
);
169 static bool ToSource(JSContext
* cx
, unsigned argc
, Value
* vp
);
172 namespace PointerType
{
173 static bool Create(JSContext
* cx
, unsigned argc
, Value
* vp
);
174 static bool ConstructData(JSContext
* cx
, HandleObject obj
,
175 const CallArgs
& args
);
177 bool IsPointerType(HandleValue v
);
178 bool IsPointer(HandleValue v
);
180 bool TargetTypeGetter(JSContext
* cx
, const JS::CallArgs
& args
);
181 bool ContentsGetter(JSContext
* cx
, const JS::CallArgs
& args
);
182 bool ContentsSetter(JSContext
* cx
, const JS::CallArgs
& args
);
184 static bool IsNull(JSContext
* cx
, unsigned argc
, Value
* vp
);
185 static bool Increment(JSContext
* cx
, unsigned argc
, Value
* vp
);
186 static bool Decrement(JSContext
* cx
, unsigned argc
, Value
* vp
);
187 // The following is not an instance function, since we don't want to expose
188 // arbitrary pointer arithmetic at this moment.
189 static bool OffsetBy(JSContext
* cx
, const CallArgs
& args
, int offset
,
191 } // namespace PointerType
193 namespace ArrayType
{
194 bool IsArrayType(HandleValue v
);
195 bool IsArrayOrArrayType(HandleValue v
);
197 static bool Create(JSContext
* cx
, unsigned argc
, Value
* vp
);
198 static bool ConstructData(JSContext
* cx
, HandleObject obj
,
199 const CallArgs
& args
);
201 bool ElementTypeGetter(JSContext
* cx
, const JS::CallArgs
& args
);
202 bool LengthGetter(JSContext
* cx
, const JS::CallArgs
& args
);
204 static bool Getter(JSContext
* cx
, HandleObject obj
, HandleId idval
,
205 MutableHandleValue vp
, bool* handled
);
206 static bool Setter(JSContext
* cx
, HandleObject obj
, HandleId idval
,
207 HandleValue v
, ObjectOpResult
& result
, bool* handled
);
208 static bool AddressOfElement(JSContext
* cx
, unsigned argc
, Value
* vp
);
209 } // namespace ArrayType
211 namespace StructType
{
212 bool IsStruct(HandleValue v
);
214 static bool Create(JSContext
* cx
, unsigned argc
, Value
* vp
);
215 static bool ConstructData(JSContext
* cx
, HandleObject obj
,
216 const CallArgs
& args
);
218 bool FieldsArrayGetter(JSContext
* cx
, const JS::CallArgs
& args
);
220 enum { SLOT_FIELDNAME
};
222 static bool FieldGetter(JSContext
* cx
, unsigned argc
, Value
* vp
);
223 static bool FieldSetter(JSContext
* cx
, unsigned argc
, Value
* vp
);
224 static bool AddressOfField(JSContext
* cx
, unsigned argc
, Value
* vp
);
225 static bool Define(JSContext
* cx
, unsigned argc
, Value
* vp
);
226 } // namespace StructType
228 namespace FunctionType
{
229 static bool Create(JSContext
* cx
, unsigned argc
, Value
* vp
);
230 static bool ConstructData(JSContext
* cx
, HandleObject typeObj
,
231 HandleObject dataObj
, HandleObject fnObj
,
232 HandleObject thisObj
, HandleValue errVal
);
234 static bool Call(JSContext
* cx
, unsigned argc
, Value
* vp
);
236 bool IsFunctionType(HandleValue v
);
238 bool ArgTypesGetter(JSContext
* cx
, const JS::CallArgs
& args
);
239 bool ReturnTypeGetter(JSContext
* cx
, const JS::CallArgs
& args
);
240 bool ABIGetter(JSContext
* cx
, const JS::CallArgs
& args
);
241 bool IsVariadicGetter(JSContext
* cx
, const JS::CallArgs
& args
);
242 } // namespace FunctionType
245 static void Trace(JSTracer
* trc
, JSObject
* obj
);
246 static void Finalize(JS::GCContext
* gcx
, JSObject
* obj
);
249 static void ClosureStub(ffi_cif
* cif
, void* result
, void** args
,
252 struct ArgClosure
: public ScriptEnvironmentPreparer::Closure
{
253 ArgClosure(ffi_cif
* cifArg
, void* resultArg
, void** argsArg
,
254 ClosureInfo
* cinfoArg
)
255 : cif(cifArg
), result(resultArg
), args(argsArg
), cinfo(cinfoArg
) {}
257 bool operator()(JSContext
* cx
) override
;
264 } // namespace CClosure
267 static void Finalize(JS::GCContext
* gcx
, JSObject
* obj
);
269 bool ValueGetter(JSContext
* cx
, const JS::CallArgs
& args
);
270 bool ValueSetter(JSContext
* cx
, const JS::CallArgs
& args
);
272 static bool Address(JSContext
* cx
, unsigned argc
, Value
* vp
);
273 static bool ReadString(JSContext
* cx
, unsigned argc
, Value
* vp
);
274 static bool ReadStringReplaceMalformed(JSContext
* cx
, unsigned argc
, Value
* vp
);
275 static bool ReadTypedArray(JSContext
* cx
, unsigned argc
, Value
* vp
);
276 static bool ToSource(JSContext
* cx
, unsigned argc
, Value
* vp
);
277 static JSString
* GetSourceString(JSContext
* cx
, HandleObject typeObj
,
280 bool ErrnoGetter(JSContext
* cx
, const JS::CallArgs
& args
);
283 bool LastErrorGetter(JSContext
* cx
, const JS::CallArgs
& args
);
284 #endif // defined(XP_WIN)
287 namespace CDataFinalizer
{
289 * Attach a C function as a finalizer to a JS object.
291 * This function is available from JS as |ctypes.withFinalizer|.
293 * JavaScript signature:
294 * function(CData, CData): CDataFinalizer
295 * value finalizer finalizable
297 * Where |finalizer| is a one-argument function taking a value
298 * with the same type as |value|.
300 static bool Construct(JSContext
* cx
, unsigned argc
, Value
* vp
);
303 * Private data held by |CDataFinalizer|.
305 * See also |enum CDataFinalizerSlot| for the slots of
308 * Note: the private data may be nullptr, if |dispose|, |forget| or the
309 * finalizer has already been called.
313 * The C data to pass to the code.
314 * Finalization/|dispose|/|forget| release this memory.
319 * The total size of the buffer pointed by |cargs|
324 * Low-level signature information.
325 * Finalization/|dispose|/|forget| release this memory.
330 * The C function to invoke during finalization.
331 * Do not deallocate this.
336 * A buffer for holding the return value.
337 * Finalization/|dispose|/|forget| release this memory.
343 * Methods of instances of |CDataFinalizer|
346 static bool Dispose(JSContext
* cx
, unsigned argc
, Value
* vp
);
347 static bool Forget(JSContext
* cx
, unsigned argc
, Value
* vp
);
348 static bool ReadString(JSContext
* cx
, unsigned argc
, Value
* vp
);
349 static bool ReadTypedArray(JSContext
* cx
, unsigned argc
, Value
* vp
);
350 static bool ToSource(JSContext
* cx
, unsigned argc
, Value
* vp
);
351 static bool ToString(JSContext
* cx
, unsigned argc
, Value
* vp
);
352 } // namespace Methods
357 * @return true if |obj| is a CDataFinalizer, false otherwise.
359 static bool IsCDataFinalizer(JSObject
* obj
);
362 * Clean up the finalization information of a CDataFinalizer.
364 * Used by |Finalize|, |Dispose| and |Forget|.
366 * @param p The private information of the CDataFinalizer. If nullptr,
367 * this function does nothing.
368 * @param obj Either nullptr, if the object should not be cleaned up (i.e.
369 * during finalization) or a CDataFinalizer JSObject. Always use nullptr
370 * if you are calling from a finalizer.
372 static void Cleanup(Private
* p
, JSObject
* obj
);
375 * Perform the actual call to the finalizer code.
377 static void CallFinalizer(CDataFinalizer::Private
* p
, int* errnoStatus
,
378 int32_t* lastErrorStatus
);
381 * Return the CType of a CDataFinalizer object, or nullptr if the object
382 * has been cleaned-up already.
384 static JSObject
* GetCType(JSContext
* cx
, JSObject
* obj
);
387 * Perform finalization of a |CDataFinalizer|
389 static void Finalize(JS::GCContext
* gcx
, JSObject
* obj
);
392 * Return the Value contained by this finalizer.
394 * Note that the Value is actually not recorded, but converted back from C.
396 static bool GetValue(JSContext
* cx
, JSObject
* obj
, MutableHandleValue result
);
398 } // namespace CDataFinalizer
400 // Int64Base provides functions common to Int64 and UInt64.
401 namespace Int64Base
{
402 JSObject
* Construct(JSContext
* cx
, HandleObject proto
, uint64_t data
,
405 uint64_t GetInt(JSObject
* obj
);
407 bool ToString(JSContext
* cx
, JSObject
* obj
, const CallArgs
& args
,
410 bool ToSource(JSContext
* cx
, JSObject
* obj
, const CallArgs
& args
,
413 static void Finalize(JS::GCContext
* gcx
, JSObject
* obj
);
414 } // namespace Int64Base
417 static bool Construct(JSContext
* cx
, unsigned argc
, Value
* vp
);
419 static bool ToString(JSContext
* cx
, unsigned argc
, Value
* vp
);
420 static bool ToSource(JSContext
* cx
, unsigned argc
, Value
* vp
);
422 static bool Compare(JSContext
* cx
, unsigned argc
, Value
* vp
);
423 static bool Lo(JSContext
* cx
, unsigned argc
, Value
* vp
);
424 static bool Hi(JSContext
* cx
, unsigned argc
, Value
* vp
);
425 static bool Join(JSContext
* cx
, unsigned argc
, Value
* vp
);
429 static bool Construct(JSContext
* cx
, unsigned argc
, Value
* vp
);
431 static bool ToString(JSContext
* cx
, unsigned argc
, Value
* vp
);
432 static bool ToSource(JSContext
* cx
, unsigned argc
, Value
* vp
);
434 static bool Compare(JSContext
* cx
, unsigned argc
, Value
* vp
);
435 static bool Lo(JSContext
* cx
, unsigned argc
, Value
* vp
);
436 static bool Hi(JSContext
* cx
, unsigned argc
, Value
* vp
);
437 static bool Join(JSContext
* cx
, unsigned argc
, Value
* vp
);
438 } // namespace UInt64
440 /*******************************************************************************
441 ** JSClass definitions and initialization functions
442 *******************************************************************************/
444 // Class representing the 'ctypes' object itself. This exists to contain the
445 // JS::CTypesCallbacks set of function pointers.
446 static const JSClass sCTypesGlobalClass
= {
448 JSCLASS_HAS_RESERVED_SLOTS(CTYPESGLOBAL_SLOTS
),
451 static const JSClass sCABIClass
= {
453 JSCLASS_HAS_RESERVED_SLOTS(CABI_SLOTS
),
456 // Class representing ctypes.{C,Pointer,Array,Struct,Function}Type.prototype.
457 // This exists to give said prototypes a class of "CType", and to provide
458 // reserved slots for stashing various other prototype objects.
459 static const JSClassOps sCTypeProtoClassOps
= {
460 nullptr, // addProperty
461 nullptr, // delProperty
462 nullptr, // enumerate
463 nullptr, // newEnumerate
465 nullptr, // mayResolve
467 ConstructAbstract
, // call
468 ConstructAbstract
, // construct
471 static const JSClass sCTypeProtoClass
= {
473 JSCLASS_HAS_RESERVED_SLOTS(CTYPEPROTO_SLOTS
),
474 &sCTypeProtoClassOps
,
477 // Class representing ctypes.CData.prototype and the 'prototype' properties
478 // of CTypes. This exists to give said prototypes a class of "CData".
479 static const JSClass sCDataProtoClass
= {
484 static const JSClassOps sCTypeClassOps
= {
485 nullptr, // addProperty
486 nullptr, // delProperty
487 nullptr, // enumerate
488 nullptr, // newEnumerate
490 nullptr, // mayResolve
491 CType::Finalize
, // finalize
492 CType::ConstructData
, // call
493 CType::ConstructData
, // construct
494 CType::Trace
, // trace
496 static const JSClass sCTypeClass
= {
498 JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS
) | JSCLASS_FOREGROUND_FINALIZE
,
502 static const JSClassOps sCDataClassOps
= {
503 nullptr, // addProperty
504 nullptr, // delProperty
505 nullptr, // enumerate
506 nullptr, // newEnumerate
508 nullptr, // mayResolve
509 CData::Finalize
, // finalize
510 FunctionType::Call
, // call
511 FunctionType::Call
, // construct
514 static const JSClass sCDataClass
= {
516 JSCLASS_HAS_RESERVED_SLOTS(CDATA_SLOTS
) | JSCLASS_FOREGROUND_FINALIZE
,
520 static const JSClassOps sCClosureClassOps
= {
521 nullptr, // addProperty
522 nullptr, // delProperty
523 nullptr, // enumerate
524 nullptr, // newEnumerate
526 nullptr, // mayResolve
527 CClosure::Finalize
, // finalize
529 nullptr, // construct
530 CClosure::Trace
, // trace
532 static const JSClass sCClosureClass
= {
534 JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS
) | JSCLASS_FOREGROUND_FINALIZE
,
539 * Class representing the prototype of CDataFinalizer.
541 static const JSClass sCDataFinalizerProtoClass
= {
547 * Class representing instances of CDataFinalizer.
549 * Instances of CDataFinalizer have both private data (with type
550 * |CDataFinalizer::Private|) and slots (see |CDataFinalizerSlots|).
552 static const JSClassOps sCDataFinalizerClassOps
= {
553 nullptr, // addProperty
554 nullptr, // delProperty
555 nullptr, // enumerate
556 nullptr, // newEnumerate
558 nullptr, // mayResolve
559 CDataFinalizer::Finalize
, // finalize
561 nullptr, // construct
564 static const JSClass sCDataFinalizerClass
= {
566 JSCLASS_HAS_RESERVED_SLOTS(CDATAFINALIZER_SLOTS
) |
567 JSCLASS_FOREGROUND_FINALIZE
,
568 &sCDataFinalizerClassOps
,
571 #define CTYPESFN_FLAGS (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
573 #define CTYPESCTOR_FLAGS (CTYPESFN_FLAGS | JSFUN_CONSTRUCTOR)
575 #define CTYPESACC_FLAGS (JSPROP_ENUMERATE | JSPROP_PERMANENT)
577 #define CABIFN_FLAGS (JSPROP_READONLY | JSPROP_PERMANENT)
579 #define CDATAFN_FLAGS (JSPROP_READONLY | JSPROP_PERMANENT)
581 #define CDATAFINALIZERFN_FLAGS (JSPROP_READONLY | JSPROP_PERMANENT)
583 static const JSPropertySpec sCTypeProps
[] = {
584 JS_PSG("name", (Property
<CType::IsCType
, CType::NameGetter
>::Fun
),
586 JS_PSG("size", (Property
<CType::IsCType
, CType::SizeGetter
>::Fun
),
588 JS_PSG("ptr", (Property
<CType::IsCType
, CType::PtrGetter
>::Fun
),
591 (Property
<CType::IsCTypeOrProto
, CType::PrototypeGetter
>::Fun
),
596 static const JSFunctionSpec sCTypeFunctions
[] = {
597 JS_FN("array", CType::CreateArray
, 0, CTYPESFN_FLAGS
),
598 JS_FN("toString", CType::ToString
, 0, CTYPESFN_FLAGS
),
599 JS_FN("toSource", CType::ToSource
, 0, CTYPESFN_FLAGS
),
603 static const JSFunctionSpec sCABIFunctions
[] = {
604 JS_FN("toSource", ABI::ToSource
, 0, CABIFN_FLAGS
),
605 JS_FN("toString", ABI::ToSource
, 0, CABIFN_FLAGS
),
609 static const JSPropertySpec sCDataProps
[] = {
610 JS_PSGS("value", (Property
<CData::IsCData
, CData::ValueGetter
>::Fun
),
611 (Property
<CData::IsCData
, CData::ValueSetter
>::Fun
),
616 static const JSFunctionSpec sCDataFunctions
[] = {
617 JS_FN("address", CData::Address
, 0, CDATAFN_FLAGS
),
618 JS_FN("readString", CData::ReadString
, 0, CDATAFN_FLAGS
),
619 JS_FN("readStringReplaceMalformed", CData::ReadStringReplaceMalformed
, 0,
621 JS_FN("readTypedArray", CData::ReadTypedArray
, 0, CDATAFN_FLAGS
),
622 JS_FN("toSource", CData::ToSource
, 0, CDATAFN_FLAGS
),
623 JS_FN("toString", CData::ToSource
, 0, CDATAFN_FLAGS
),
627 static const JSFunctionSpec sCDataFinalizerFunctions
[] = {
628 JS_FN("dispose", CDataFinalizer::Methods::Dispose
, 0,
629 CDATAFINALIZERFN_FLAGS
),
630 JS_FN("forget", CDataFinalizer::Methods::Forget
, 0, CDATAFINALIZERFN_FLAGS
),
631 JS_FN("readString", CDataFinalizer::Methods::ReadString
, 0,
632 CDATAFINALIZERFN_FLAGS
),
633 JS_FN("readTypedArray", CDataFinalizer::Methods::ReadTypedArray
, 0,
634 CDATAFINALIZERFN_FLAGS
),
635 JS_FN("toString", CDataFinalizer::Methods::ToString
, 0,
636 CDATAFINALIZERFN_FLAGS
),
637 JS_FN("toSource", CDataFinalizer::Methods::ToSource
, 0,
638 CDATAFINALIZERFN_FLAGS
),
642 static const JSFunctionSpec sPointerFunction
=
643 JS_FN("PointerType", PointerType::Create
, 1, CTYPESCTOR_FLAGS
);
645 static const JSPropertySpec sPointerProps
[] = {
647 (Property
<PointerType::IsPointerType
,
648 PointerType::TargetTypeGetter
>::Fun
),
653 static const JSFunctionSpec sPointerInstanceFunctions
[] = {
654 JS_FN("isNull", PointerType::IsNull
, 0, CTYPESFN_FLAGS
),
655 JS_FN("increment", PointerType::Increment
, 0, CTYPESFN_FLAGS
),
656 JS_FN("decrement", PointerType::Decrement
, 0, CTYPESFN_FLAGS
),
660 static const JSPropertySpec sPointerInstanceProps
[] = {
663 (Property
<PointerType::IsPointer
, PointerType::ContentsGetter
>::Fun
),
664 (Property
<PointerType::IsPointer
, PointerType::ContentsSetter
>::Fun
),
669 static const JSFunctionSpec sArrayFunction
=
670 JS_FN("ArrayType", ArrayType::Create
, 1, CTYPESCTOR_FLAGS
);
672 static const JSPropertySpec sArrayProps
[] = {
675 (Property
<ArrayType::IsArrayType
, ArrayType::ElementTypeGetter
>::Fun
),
679 (Property
<ArrayType::IsArrayOrArrayType
, ArrayType::LengthGetter
>::Fun
),
684 static const JSFunctionSpec sArrayInstanceFunctions
[] = {
685 JS_FN("addressOfElement", ArrayType::AddressOfElement
, 1, CDATAFN_FLAGS
),
689 static const JSPropertySpec sArrayInstanceProps
[] = {
692 (Property
<ArrayType::IsArrayOrArrayType
, ArrayType::LengthGetter
>::Fun
),
697 static const JSFunctionSpec sStructFunction
=
698 JS_FN("StructType", StructType::Create
, 2, CTYPESCTOR_FLAGS
);
700 static const JSPropertySpec sStructProps
[] = {
702 (Property
<StructType::IsStruct
, StructType::FieldsArrayGetter
>::Fun
),
707 static const JSFunctionSpec sStructFunctions
[] = {
708 JS_FN("define", StructType::Define
, 1, CDATAFN_FLAGS
),
712 static const JSFunctionSpec sStructInstanceFunctions
[] = {
713 JS_FN("addressOfField", StructType::AddressOfField
, 1, CDATAFN_FLAGS
),
717 static const JSFunctionSpec sFunctionFunction
=
718 JS_FN("FunctionType", FunctionType::Create
, 2, CTYPESCTOR_FLAGS
);
720 static const JSPropertySpec sFunctionProps
[] = {
722 (Property
<FunctionType::IsFunctionType
,
723 FunctionType::ArgTypesGetter
>::Fun
),
726 (Property
<FunctionType::IsFunctionType
,
727 FunctionType::ReturnTypeGetter
>::Fun
),
731 (Property
<FunctionType::IsFunctionType
, FunctionType::ABIGetter
>::Fun
),
734 (Property
<FunctionType::IsFunctionType
,
735 FunctionType::IsVariadicGetter
>::Fun
),
740 static const JSFunctionSpec sFunctionInstanceFunctions
[] = {
741 JS_FN("call", js::fun_call
, 1, CDATAFN_FLAGS
),
742 JS_FN("apply", js::fun_apply
, 2, CDATAFN_FLAGS
),
746 static const JSClass sInt64ProtoClass
= {
751 static const JSClass sUInt64ProtoClass
= {
756 static const JSClassOps sInt64ClassOps
= {
757 nullptr, // addProperty
758 nullptr, // delProperty
759 nullptr, // enumerate
760 nullptr, // newEnumerate
762 nullptr, // mayResolve
763 Int64Base::Finalize
, // finalize
765 nullptr, // construct
769 static const JSClass sInt64Class
= {
771 JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS
) | JSCLASS_FOREGROUND_FINALIZE
,
775 static const JSClass sUInt64Class
= {
777 JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS
) | JSCLASS_FOREGROUND_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 // "join" is defined specially; see InitInt64Class.
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 // "join" is defined specially; see InitInt64Class.
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
[] = {
810 JS_PSG("errno", (Property
<IsCTypesGlobal
, CData::ErrnoGetter
>::Fun
),
813 JS_PSG("winLastError",
814 (Property
<IsCTypesGlobal
, CData::LastErrorGetter
>::Fun
),
816 #endif // defined(XP_WIN)
820 static const JSFunctionSpec sModuleFunctions
[] = {
821 JS_FN("CDataFinalizer", CDataFinalizer::Construct
, 2, CTYPESFN_FLAGS
),
822 JS_FN("open", Library::Open
, 1, CTYPESFN_FLAGS
),
823 JS_FN("cast", CData::Cast
, 2, CTYPESFN_FLAGS
),
824 JS_FN("getRuntime", CData::GetRuntime
, 1, CTYPESFN_FLAGS
),
825 JS_FN("libraryName", Library::Name
, 1, CTYPESFN_FLAGS
),
829 // Wrapper for arrays, to intercept indexed gets/sets.
830 class CDataArrayProxyHandler
: public ForwardingProxyHandler
{
832 static const CDataArrayProxyHandler singleton
;
833 static const char family
;
835 constexpr CDataArrayProxyHandler() : ForwardingProxyHandler(&family
) {}
837 bool get(JSContext
* cx
, HandleObject proxy
, HandleValue receiver
, HandleId id
,
838 MutableHandleValue vp
) const override
;
839 bool set(JSContext
* cx
, HandleObject proxy
, HandleId id
, HandleValue v
,
840 HandleValue receiver
, ObjectOpResult
& result
) const override
;
843 const CDataArrayProxyHandler
CDataArrayProxyHandler::singleton
;
844 const char CDataArrayProxyHandler::family
= 0;
846 bool CDataArrayProxyHandler::get(JSContext
* cx
, HandleObject proxy
,
847 HandleValue receiver
, HandleId id
,
848 MutableHandleValue vp
) const {
849 RootedObject
target(cx
, proxy
->as
<ProxyObject
>().target());
850 bool handled
= false;
851 if (!ArrayType::Getter(cx
, target
, id
, vp
, &handled
)) {
857 return ForwardingProxyHandler::get(cx
, proxy
, receiver
, id
, vp
);
860 bool CDataArrayProxyHandler::set(JSContext
* cx
, HandleObject proxy
, HandleId id
,
861 HandleValue v
, HandleValue receiver
,
862 ObjectOpResult
& result
) const {
863 RootedObject
target(cx
, proxy
->as
<ProxyObject
>().target());
864 bool handled
= false;
865 if (!ArrayType::Setter(cx
, target
, id
, v
, result
, &handled
)) {
871 return ForwardingProxyHandler::set(cx
, proxy
, id
, v
, receiver
, result
);
874 static JSObject
* MaybeUnwrapArrayWrapper(JSObject
* obj
) {
875 if (obj
->is
<ProxyObject
>() &&
876 obj
->as
<ProxyObject
>().handler() == &CDataArrayProxyHandler::singleton
) {
877 return obj
->as
<ProxyObject
>().target();
882 static MOZ_ALWAYS_INLINE JSString
* NewUCString(JSContext
* cx
,
883 const AutoStringChars
&& from
) {
884 return JS_NewUCStringCopyN(cx
, from
.begin(), from
.length());
888 * Return a size rounded up to a multiple of a power of two.
890 * Note: |align| must be a power of 2.
892 static MOZ_ALWAYS_INLINE
size_t Align(size_t val
, size_t align
) {
893 // Ensure that align is a power of two.
894 MOZ_ASSERT(align
!= 0 && (align
& (align
- 1)) == 0);
895 return ((val
- 1) | (align
- 1)) + 1;
898 static ABICode
GetABICode(JSObject
* obj
) {
899 // make sure we have an object representing a CABI class,
900 // and extract the enumerated class type from the reserved slot.
901 if (!obj
->hasClass(&sCABIClass
)) {
905 Value result
= JS::GetReservedSlot(obj
, SLOT_ABICODE
);
906 return ABICode(result
.toInt32());
909 static const JSErrorFormatString ErrorFormatString
[CTYPESERR_LIMIT
] = {
910 #define MSG_DEF(name, count, exception, format) \
911 {#name, format, count, exception},
912 #include "ctypes/ctypes.msg"
916 static const JSErrorFormatString
* GetErrorMessage(void* userRef
,
917 const unsigned errorNumber
) {
918 if (0 < errorNumber
&& errorNumber
< CTYPESERR_LIMIT
) {
919 return &ErrorFormatString
[errorNumber
];
924 static JS::UniqueChars
EncodeUTF8(JSContext
* cx
, AutoString
& str
) {
925 RootedString
string(cx
, NewUCString(cx
, str
.finish()));
929 return JS_EncodeStringToUTF8(cx
, string
);
932 static const char* CTypesToSourceForError(JSContext
* cx
, HandleValue val
,
933 JS::UniqueChars
& bytes
) {
934 if (val
.isObject()) {
935 RootedObject
obj(cx
, &val
.toObject());
936 if (CType::IsCType(obj
) || CData::IsCDataMaybeUnwrap(&obj
)) {
937 RootedValue
v(cx
, ObjectValue(*obj
));
938 RootedString
str(cx
, JS_ValueToSource(cx
, v
));
939 bytes
= JS_EncodeStringToUTF8(cx
, str
);
943 return ValueToSourceForError(cx
, val
, bytes
);
946 static void BuildCStyleFunctionTypeSource(JSContext
* cx
, HandleObject typeObj
,
947 HandleString nameStr
,
951 static void BuildCStyleTypeSource(JSContext
* cx
, JSObject
* typeObj_
,
952 AutoString
& source
) {
953 RootedObject
typeObj(cx
, typeObj_
);
955 MOZ_ASSERT(CType::IsCType(typeObj
));
957 switch (CType::GetTypeCode(typeObj
)) {
958 #define BUILD_SOURCE(name, fromType, ffiType) \
960 AppendString(cx, source, #name); \
962 CTYPES_FOR_EACH_TYPE(BUILD_SOURCE
)
965 AppendString(cx
, source
, "void");
968 unsigned ptrCount
= 0;
970 RootedObject
baseTypeObj(cx
, typeObj
);
972 baseTypeObj
= PointerType::GetBaseType(baseTypeObj
);
974 type
= CType::GetTypeCode(baseTypeObj
);
975 } while (type
== TYPE_pointer
|| type
== TYPE_array
);
976 if (type
== TYPE_function
) {
977 BuildCStyleFunctionTypeSource(cx
, baseTypeObj
, nullptr, ptrCount
,
981 BuildCStyleTypeSource(cx
, baseTypeObj
, source
);
982 AppendChars(source
, '*', ptrCount
);
986 RootedString
name(cx
, CType::GetName(cx
, typeObj
));
987 AppendString(cx
, source
, "struct ");
988 AppendString(cx
, source
, name
);
992 BuildCStyleFunctionTypeSource(cx
, typeObj
, nullptr, 0, source
);
995 MOZ_CRASH("TYPE_array shouldn't appear in function type");
999 static void BuildCStyleFunctionTypeSource(JSContext
* cx
, HandleObject typeObj
,
1000 HandleString nameStr
,
1002 AutoString
& source
) {
1003 MOZ_ASSERT(CType::IsCType(typeObj
));
1005 FunctionInfo
* fninfo
= FunctionType::GetFunctionInfo(typeObj
);
1006 BuildCStyleTypeSource(cx
, fninfo
->mReturnType
, source
);
1007 AppendString(cx
, source
, " ");
1009 MOZ_ASSERT(ptrCount
== 0);
1010 AppendString(cx
, source
, nameStr
);
1011 } else if (ptrCount
) {
1012 AppendString(cx
, source
, "(");
1013 AppendChars(source
, '*', ptrCount
);
1014 AppendString(cx
, source
, ")");
1016 AppendString(cx
, source
, "(");
1017 if (fninfo
->mArgTypes
.length() > 0) {
1018 for (size_t i
= 0; i
< fninfo
->mArgTypes
.length(); ++i
) {
1019 BuildCStyleTypeSource(cx
, fninfo
->mArgTypes
[i
], source
);
1020 if (i
!= fninfo
->mArgTypes
.length() - 1 || fninfo
->mIsVariadic
) {
1021 AppendString(cx
, source
, ", ");
1024 if (fninfo
->mIsVariadic
) {
1025 AppendString(cx
, source
, "...");
1028 AppendString(cx
, source
, ")");
1031 static void BuildFunctionTypeSource(JSContext
* cx
, HandleObject funObj
,
1032 AutoString
& source
) {
1033 MOZ_ASSERT(CData::IsCData(funObj
) || CType::IsCType(funObj
));
1035 if (CData::IsCData(funObj
)) {
1036 Value slot
= JS::GetReservedSlot(funObj
, SLOT_REFERENT
);
1037 if (!slot
.isUndefined() && Library::IsLibrary(&slot
.toObject())) {
1038 slot
= JS::GetReservedSlot(funObj
, SLOT_FUNNAME
);
1039 MOZ_ASSERT(!slot
.isUndefined());
1040 RootedObject
typeObj(cx
, CData::GetCType(funObj
));
1041 RootedObject
baseTypeObj(cx
, PointerType::GetBaseType(typeObj
));
1042 RootedString
nameStr(cx
, slot
.toString());
1043 BuildCStyleFunctionTypeSource(cx
, baseTypeObj
, nameStr
, 0, source
);
1048 RootedValue
funVal(cx
, ObjectValue(*funObj
));
1049 RootedString
funcStr(cx
, JS_ValueToSource(cx
, funVal
));
1051 JS_ClearPendingException(cx
);
1052 AppendString(cx
, source
, "<<error converting function to string>>");
1055 AppendString(cx
, source
, funcStr
);
1058 enum class ConversionType
{
1066 static void BuildConversionPosition(JSContext
* cx
, ConversionType convType
,
1067 HandleObject funObj
, unsigned argIndex
,
1068 AutoString
& source
) {
1070 case ConversionType::Argument
: {
1073 AppendString(cx
, source
, " at argument ");
1074 AppendUInt(source
, argIndex
+ 1);
1075 AppendString(cx
, source
, " of ");
1076 BuildFunctionTypeSource(cx
, funObj
, source
);
1079 case ConversionType::Finalizer
:
1082 AppendString(cx
, source
, " at argument 1 of ");
1083 BuildFunctionTypeSource(cx
, funObj
, source
);
1085 case ConversionType::Return
:
1088 AppendString(cx
, source
, " at the return value of ");
1089 BuildFunctionTypeSource(cx
, funObj
, source
);
1092 MOZ_ASSERT(!funObj
);
1097 static JSLinearString
* GetFieldName(HandleObject structObj
,
1098 unsigned fieldIndex
) {
1099 const FieldInfoHash
* fields
= StructType::GetFieldInfo(structObj
);
1100 for (FieldInfoHash::Range r
= fields
->all(); !r
.empty(); r
.popFront()) {
1101 if (r
.front().value().mIndex
== fieldIndex
) {
1102 return (&r
.front())->key();
1108 static void BuildTypeSource(JSContext
* cx
, JSObject
* typeObj_
, bool makeShort
,
1109 AutoString
& result
);
1111 static JS::UniqueChars
TypeSourceForError(JSContext
* cx
, JSObject
* typeObj
) {
1113 BuildTypeSource(cx
, typeObj
, true, source
);
1117 return EncodeUTF8(cx
, source
);
1120 static JS::UniqueChars
FunctionTypeSourceForError(JSContext
* cx
,
1121 HandleObject funObj
) {
1122 AutoString funSource
;
1123 BuildFunctionTypeSource(cx
, funObj
, funSource
);
1127 return EncodeUTF8(cx
, funSource
);
1130 static JS::UniqueChars
ConversionPositionForError(JSContext
* cx
,
1131 ConversionType convType
,
1132 HandleObject funObj
,
1133 unsigned argIndex
) {
1134 AutoString posSource
;
1135 BuildConversionPosition(cx
, convType
, funObj
, argIndex
, posSource
);
1139 return EncodeUTF8(cx
, posSource
);
1142 class IndexCString final
{
1143 char indexStr
[21]; // space for UINT64_MAX plus terminating null
1144 static_assert(sizeof(size_t) <= 8, "index array too small");
1147 explicit IndexCString(size_t index
) {
1148 SprintfLiteral(indexStr
, "%zu", index
);
1151 const char* get() const { return indexStr
; }
1154 static bool ConvError(JSContext
* cx
, const char* expectedStr
,
1155 HandleValue actual
, ConversionType convType
,
1156 HandleObject funObj
= nullptr, unsigned argIndex
= 0,
1157 HandleObject arrObj
= nullptr, unsigned arrIndex
= 0) {
1158 JS::UniqueChars valBytes
;
1159 const char* valStr
= CTypesToSourceForError(cx
, actual
, valBytes
);
1165 MOZ_ASSERT(CType::IsCType(arrObj
));
1167 switch (CType::GetTypeCode(arrObj
)) {
1169 MOZ_ASSERT(!funObj
);
1171 IndexCString
indexStr(arrIndex
);
1173 JS::UniqueChars arrStr
= TypeSourceForError(cx
, arrObj
);
1178 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1179 CTYPESMSG_CONV_ERROR_ARRAY
, valStr
,
1180 indexStr
.get(), arrStr
.get());
1184 RootedString
name(cx
, GetFieldName(arrObj
, arrIndex
));
1186 JS::UniqueChars nameStr
= JS_EncodeStringToUTF8(cx
, name
);
1191 JS::UniqueChars structStr
= TypeSourceForError(cx
, arrObj
);
1196 JS::UniqueChars posStr
;
1198 posStr
= ConversionPositionForError(cx
, convType
, funObj
, argIndex
);
1204 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1205 CTYPESMSG_CONV_ERROR_STRUCT
, valStr
,
1206 nameStr
.get(), expectedStr
, structStr
.get(),
1207 (posStr
? posStr
.get() : ""));
1211 MOZ_CRASH("invalid arrObj value");
1217 case ConversionType::Argument
: {
1220 IndexCString
indexStr(argIndex
+ 1);
1222 JS::UniqueChars funStr
= FunctionTypeSourceForError(cx
, funObj
);
1227 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1228 CTYPESMSG_CONV_ERROR_ARG
, valStr
, indexStr
.get(),
1232 case ConversionType::Finalizer
: {
1235 JS::UniqueChars funStr
= FunctionTypeSourceForError(cx
, funObj
);
1240 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1241 CTYPESMSG_CONV_ERROR_FIN
, valStr
, funStr
.get());
1244 case ConversionType::Return
: {
1247 JS::UniqueChars funStr
= FunctionTypeSourceForError(cx
, funObj
);
1252 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1253 CTYPESMSG_CONV_ERROR_RET
, valStr
, funStr
.get());
1256 case ConversionType::Setter
:
1257 case ConversionType::Construct
:
1258 MOZ_ASSERT(!funObj
);
1260 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1261 CTYPESMSG_CONV_ERROR_SET
, valStr
, expectedStr
);
1268 static bool ConvError(JSContext
* cx
, HandleObject expectedType
,
1269 HandleValue actual
, ConversionType convType
,
1270 HandleObject funObj
= nullptr, unsigned argIndex
= 0,
1271 HandleObject arrObj
= nullptr, unsigned arrIndex
= 0) {
1272 MOZ_ASSERT(CType::IsCType(expectedType
));
1274 JS::UniqueChars expectedStr
= TypeSourceForError(cx
, expectedType
);
1279 return ConvError(cx
, expectedStr
.get(), actual
, convType
, funObj
, argIndex
,
1283 static bool ArgumentConvError(JSContext
* cx
, HandleValue actual
,
1284 const char* funStr
, unsigned argIndex
) {
1285 MOZ_ASSERT(JS::StringIsASCII(funStr
));
1287 JS::UniqueChars valBytes
;
1288 const char* valStr
= CTypesToSourceForError(cx
, actual
, valBytes
);
1293 IndexCString
indexStr(argIndex
+ 1);
1295 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1296 CTYPESMSG_CONV_ERROR_ARG
, valStr
, indexStr
.get(),
1301 static bool ArgumentLengthError(JSContext
* cx
, const char* fun
,
1302 const char* count
, const char* s
) {
1303 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1304 CTYPESMSG_WRONG_ARG_LENGTH
, fun
, count
, s
);
1308 static bool ArrayLengthMismatch(JSContext
* cx
, size_t expectedLength
,
1309 HandleObject arrObj
, size_t actualLength
,
1310 HandleValue actual
, ConversionType convType
) {
1311 MOZ_ASSERT(arrObj
&& CType::IsCType(arrObj
));
1313 JS::UniqueChars valBytes
;
1314 const char* valStr
= CTypesToSourceForError(cx
, actual
, valBytes
);
1319 IndexCString
expectedLengthStr(expectedLength
);
1320 IndexCString
actualLengthStr(actualLength
);
1322 JS::UniqueChars arrStr
= TypeSourceForError(cx
, arrObj
);
1327 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1328 CTYPESMSG_ARRAY_MISMATCH
, valStr
, arrStr
.get(),
1329 expectedLengthStr
.get(), actualLengthStr
.get());
1333 static bool ArrayLengthOverflow(JSContext
* cx
, unsigned expectedLength
,
1334 HandleObject arrObj
, unsigned actualLength
,
1335 HandleValue actual
, ConversionType convType
) {
1336 MOZ_ASSERT(arrObj
&& CType::IsCType(arrObj
));
1338 JS::UniqueChars valBytes
;
1339 const char* valStr
= CTypesToSourceForError(cx
, actual
, valBytes
);
1344 IndexCString
expectedLengthStr(expectedLength
);
1345 IndexCString
actualLengthStr(actualLength
);
1347 JS::UniqueChars arrStr
= TypeSourceForError(cx
, arrObj
);
1352 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1353 CTYPESMSG_ARRAY_OVERFLOW
, valStr
, arrStr
.get(),
1354 expectedLengthStr
.get(), actualLengthStr
.get());
1358 static bool ArgumentRangeMismatch(JSContext
* cx
, const char* func
,
1359 const char* range
) {
1360 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1361 CTYPESMSG_ARG_RANGE_MISMATCH
, func
, range
);
1365 static bool ArgumentTypeMismatch(JSContext
* cx
, const char* arg
,
1366 const char* func
, const char* type
) {
1367 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1368 CTYPESMSG_ARG_TYPE_MISMATCH
, arg
, func
, type
);
1372 static bool CannotConstructError(JSContext
* cx
, const char* type
) {
1373 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1374 CTYPESMSG_CANNOT_CONSTRUCT
, type
);
1378 static bool DuplicateFieldError(JSContext
* cx
, Handle
<JSLinearString
*> name
) {
1379 JS::UniqueChars nameStr
= JS_EncodeStringToUTF8(cx
, name
);
1384 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1385 CTYPESMSG_DUPLICATE_FIELD
, nameStr
.get());
1389 static bool EmptyFinalizerCallError(JSContext
* cx
, const char* funName
) {
1390 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1391 CTYPESMSG_EMPTY_FIN_CALL
, funName
);
1395 static bool EmptyFinalizerError(JSContext
* cx
, ConversionType convType
,
1396 HandleObject funObj
= nullptr,
1397 unsigned argIndex
= 0) {
1398 JS::UniqueChars posStr
;
1400 posStr
= ConversionPositionForError(cx
, convType
, funObj
, argIndex
);
1406 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr, CTYPESMSG_EMPTY_FIN
,
1407 (posStr
? posStr
.get() : ""));
1411 static bool FieldCountMismatch(JSContext
* cx
, unsigned expectedCount
,
1412 HandleObject structObj
, unsigned actualCount
,
1413 HandleValue actual
, ConversionType convType
,
1414 HandleObject funObj
= nullptr,
1415 unsigned argIndex
= 0) {
1416 MOZ_ASSERT(structObj
&& CType::IsCType(structObj
));
1418 JS::UniqueChars valBytes
;
1419 const char* valStr
= CTypesToSourceForError(cx
, actual
, valBytes
);
1424 JS::UniqueChars structStr
= TypeSourceForError(cx
, structObj
);
1429 IndexCString
expectedCountStr(expectedCount
);
1430 IndexCString
actualCountStr(actualCount
);
1432 JS::UniqueChars posStr
;
1434 posStr
= ConversionPositionForError(cx
, convType
, funObj
, argIndex
);
1440 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1441 CTYPESMSG_FIELD_MISMATCH
, valStr
, structStr
.get(),
1442 expectedCountStr
.get(), actualCountStr
.get(),
1443 (posStr
? posStr
.get() : ""));
1447 static bool FieldDescriptorCountError(JSContext
* cx
, HandleValue typeVal
,
1449 JS::UniqueChars valBytes
;
1450 const char* valStr
= CTypesToSourceForError(cx
, typeVal
, valBytes
);
1455 IndexCString
lengthStr(length
);
1457 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1458 CTYPESMSG_FIELD_DESC_COUNT
, valStr
, lengthStr
.get());
1462 static bool FieldDescriptorNameError(JSContext
* cx
, HandleId id
) {
1463 JS::UniqueChars idBytes
;
1464 RootedValue
idVal(cx
, IdToValue(id
));
1465 const char* propStr
= CTypesToSourceForError(cx
, idVal
, idBytes
);
1470 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1471 CTYPESMSG_FIELD_DESC_NAME
, propStr
);
1475 static bool FieldDescriptorSizeError(JSContext
* cx
, HandleObject typeObj
,
1477 RootedValue
typeVal(cx
, ObjectValue(*typeObj
));
1478 JS::UniqueChars typeBytes
;
1479 const char* typeStr
= CTypesToSourceForError(cx
, typeVal
, typeBytes
);
1484 RootedString
idStr(cx
, IdToString(cx
, id
));
1485 JS::UniqueChars propStr
= JS_EncodeStringToUTF8(cx
, idStr
);
1490 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1491 CTYPESMSG_FIELD_DESC_SIZE
, typeStr
, propStr
.get());
1495 static bool FieldDescriptorNameTypeError(JSContext
* cx
, HandleValue typeVal
) {
1496 JS::UniqueChars valBytes
;
1497 const char* valStr
= CTypesToSourceForError(cx
, typeVal
, valBytes
);
1502 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1503 CTYPESMSG_FIELD_DESC_NAMETYPE
, valStr
);
1507 static bool FieldDescriptorTypeError(JSContext
* cx
, HandleValue poroVal
,
1509 JS::UniqueChars typeBytes
;
1510 const char* typeStr
= CTypesToSourceForError(cx
, poroVal
, typeBytes
);
1515 RootedString
idStr(cx
, IdToString(cx
, id
));
1516 JS::UniqueChars propStr
= JS_EncodeStringToUTF8(cx
, idStr
);
1521 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1522 CTYPESMSG_FIELD_DESC_TYPE
, typeStr
, propStr
.get());
1526 static bool FieldMissingError(JSContext
* cx
, JSObject
* typeObj
,
1527 JSLinearString
* name_
) {
1528 JS::UniqueChars typeBytes
;
1529 RootedString
name(cx
, name_
);
1530 RootedValue
typeVal(cx
, ObjectValue(*typeObj
));
1531 const char* typeStr
= CTypesToSourceForError(cx
, typeVal
, typeBytes
);
1536 JS::UniqueChars nameStr
= JS_EncodeStringToUTF8(cx
, name
);
1541 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1542 CTYPESMSG_FIELD_MISSING
, typeStr
, nameStr
.get());
1546 static bool FinalizerSizeError(JSContext
* cx
, HandleObject funObj
,
1547 HandleValue actual
) {
1548 MOZ_ASSERT(CType::IsCType(funObj
));
1550 JS::UniqueChars valBytes
;
1551 const char* valStr
= CTypesToSourceForError(cx
, actual
, valBytes
);
1556 JS::UniqueChars funStr
= FunctionTypeSourceForError(cx
, funObj
);
1561 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1562 CTYPESMSG_FIN_SIZE_ERROR
, funStr
.get(), valStr
);
1566 static bool FunctionArgumentLengthMismatch(
1567 JSContext
* cx
, unsigned expectedCount
, unsigned actualCount
,
1568 HandleObject funObj
, HandleObject typeObj
, bool isVariadic
) {
1569 JS::UniqueChars funStr
;
1570 Value slot
= JS::GetReservedSlot(funObj
, SLOT_REFERENT
);
1571 if (!slot
.isUndefined() && Library::IsLibrary(&slot
.toObject())) {
1572 funStr
= FunctionTypeSourceForError(cx
, funObj
);
1574 funStr
= FunctionTypeSourceForError(cx
, typeObj
);
1580 IndexCString
expectedCountStr(expectedCount
);
1581 IndexCString
actualCountStr(actualCount
);
1583 const char* variadicStr
= isVariadic
? " or more" : "";
1585 JS_ReportErrorNumberUTF8(
1586 cx
, GetErrorMessage
, nullptr, CTYPESMSG_ARG_COUNT_MISMATCH
, funStr
.get(),
1587 expectedCountStr
.get(), variadicStr
, actualCountStr
.get());
1591 static bool FunctionArgumentTypeError(JSContext
* cx
, uint32_t index
,
1592 HandleValue typeVal
, const char* reason
) {
1593 MOZ_ASSERT(JS::StringIsASCII(reason
));
1595 JS::UniqueChars valBytes
;
1596 const char* valStr
= CTypesToSourceForError(cx
, typeVal
, valBytes
);
1601 IndexCString
indexStr(index
+ 1);
1603 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1604 CTYPESMSG_ARG_TYPE_ERROR
, indexStr
.get(), reason
,
1609 static bool FunctionReturnTypeError(JSContext
* cx
, HandleValue type
,
1610 const char* reason
) {
1611 MOZ_ASSERT(JS::StringIsASCII(reason
));
1613 JS::UniqueChars valBytes
;
1614 const char* valStr
= CTypesToSourceForError(cx
, type
, valBytes
);
1619 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1620 CTYPESMSG_RET_TYPE_ERROR
, reason
, valStr
);
1624 static bool IncompatibleCallee(JSContext
* cx
, const char* funName
,
1625 HandleObject actualObj
) {
1626 MOZ_ASSERT(JS::StringIsASCII(funName
));
1628 JS::UniqueChars valBytes
;
1629 RootedValue
val(cx
, ObjectValue(*actualObj
));
1630 const char* valStr
= CTypesToSourceForError(cx
, val
, valBytes
);
1635 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1636 CTYPESMSG_INCOMPATIBLE_CALLEE
, funName
, valStr
);
1640 static bool IncompatibleThisProto(JSContext
* cx
, const char* funName
,
1641 const char* actualType
) {
1642 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1643 CTYPESMSG_INCOMPATIBLE_THIS
, funName
, actualType
);
1647 static bool IncompatibleThisProto(JSContext
* cx
, const char* funName
,
1648 HandleValue actualVal
) {
1649 MOZ_ASSERT(JS::StringIsASCII(funName
));
1651 JS::UniqueChars valBytes
;
1652 const char* valStr
= CTypesToSourceForError(cx
, actualVal
, valBytes
);
1657 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1658 CTYPESMSG_INCOMPATIBLE_THIS_VAL
, funName
,
1659 "incompatible object", valStr
);
1663 static bool IncompatibleThisType(JSContext
* cx
, const char* funName
,
1664 const char* actualType
) {
1665 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1666 CTYPESMSG_INCOMPATIBLE_THIS_TYPE
, funName
,
1671 static bool IncompatibleThisType(JSContext
* cx
, const char* funName
,
1672 const char* actualType
,
1673 HandleValue actualVal
) {
1674 MOZ_ASSERT(JS::StringIsASCII(funName
));
1675 MOZ_ASSERT(JS::StringIsASCII(actualType
));
1677 JS::UniqueChars valBytes
;
1678 const char* valStr
= CTypesToSourceForError(cx
, actualVal
, valBytes
);
1683 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1684 CTYPESMSG_INCOMPATIBLE_THIS_VAL
, funName
, actualType
,
1689 static bool InvalidIndexError(JSContext
* cx
, HandleValue val
) {
1690 JS::UniqueChars idBytes
;
1691 const char* indexStr
= CTypesToSourceForError(cx
, val
, idBytes
);
1696 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1697 CTYPESMSG_INVALID_INDEX
, indexStr
);
1701 static bool InvalidIndexError(JSContext
* cx
, HandleId id
) {
1702 RootedValue
idVal(cx
, IdToValue(id
));
1703 return InvalidIndexError(cx
, idVal
);
1706 static bool InvalidIndexRangeError(JSContext
* cx
, size_t index
, size_t length
) {
1707 IndexCString
indexStr(index
);
1708 IndexCString
lengthStr(length
);
1710 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1711 CTYPESMSG_INVALID_RANGE
, indexStr
.get(),
1716 static bool NonPrimitiveError(JSContext
* cx
, HandleObject typeObj
) {
1717 MOZ_ASSERT(CType::IsCType(typeObj
));
1719 JS::UniqueChars typeStr
= TypeSourceForError(cx
, typeObj
);
1724 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1725 CTYPESMSG_NON_PRIMITIVE
, typeStr
.get());
1729 static bool NonStringBaseError(JSContext
* cx
, HandleValue thisVal
) {
1730 JS::UniqueChars valBytes
;
1731 const char* valStr
= CTypesToSourceForError(cx
, thisVal
, valBytes
);
1736 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1737 CTYPESMSG_NON_STRING_BASE
, valStr
);
1741 static bool NonTypedArrayBaseError(JSContext
* cx
, HandleValue thisVal
) {
1742 JS::UniqueChars valBytes
;
1743 const char* valStr
= CTypesToSourceForError(cx
, thisVal
, valBytes
);
1748 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1749 CTYPESMSG_NON_TYPEDARRAY_BASE
, valStr
);
1753 static bool NullPointerError(JSContext
* cx
, const char* action
,
1755 MOZ_ASSERT(JS::StringIsASCII(action
));
1757 JS::UniqueChars valBytes
;
1758 RootedValue
val(cx
, ObjectValue(*obj
));
1759 const char* valStr
= CTypesToSourceForError(cx
, val
, valBytes
);
1764 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr, CTYPESMSG_NULL_POINTER
,
1769 static bool PropNameNonStringError(JSContext
* cx
, HandleId id
,
1770 HandleValue actual
, ConversionType convType
,
1771 HandleObject funObj
= nullptr,
1772 unsigned argIndex
= 0) {
1773 JS::UniqueChars valBytes
;
1774 const char* valStr
= CTypesToSourceForError(cx
, actual
, valBytes
);
1779 JS::UniqueChars idBytes
;
1780 RootedValue
idVal(cx
, IdToValue(id
));
1781 const char* propStr
= CTypesToSourceForError(cx
, idVal
, idBytes
);
1786 JS::UniqueChars posStr
;
1788 posStr
= ConversionPositionForError(cx
, convType
, funObj
, argIndex
);
1794 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1795 CTYPESMSG_PROP_NONSTRING
, propStr
, valStr
,
1796 (posStr
? posStr
.get() : ""));
1800 static bool SizeOverflow(JSContext
* cx
, const char* name
, const char* limit
) {
1801 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1802 CTYPESMSG_SIZE_OVERFLOW
, name
, limit
);
1806 static bool TypeError(JSContext
* cx
, const char* expected
, HandleValue actual
) {
1807 MOZ_ASSERT(JS::StringIsASCII(expected
));
1809 JS::UniqueChars bytes
;
1810 const char* src
= CTypesToSourceForError(cx
, actual
, bytes
);
1815 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr, CTYPESMSG_TYPE_ERROR
,
1820 static bool TypeOverflow(JSContext
* cx
, const char* expected
,
1821 HandleValue actual
) {
1822 MOZ_ASSERT(JS::StringIsASCII(expected
));
1824 JS::UniqueChars valBytes
;
1825 const char* valStr
= CTypesToSourceForError(cx
, actual
, valBytes
);
1830 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1831 CTYPESMSG_TYPE_OVERFLOW
, valStr
, expected
);
1835 static bool UndefinedSizeCastError(JSContext
* cx
, HandleObject targetTypeObj
) {
1836 JS::UniqueChars targetTypeStr
= TypeSourceForError(cx
, targetTypeObj
);
1837 if (!targetTypeStr
) {
1841 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1842 CTYPESMSG_UNDEFINED_SIZE_CAST
, targetTypeStr
.get());
1846 static bool SizeMismatchCastError(JSContext
* cx
, HandleObject sourceTypeObj
,
1847 HandleObject targetTypeObj
, size_t sourceSize
,
1848 size_t targetSize
) {
1849 JS::UniqueChars sourceTypeStr
= TypeSourceForError(cx
, sourceTypeObj
);
1850 if (!sourceTypeStr
) {
1854 JS::UniqueChars targetTypeStr
= TypeSourceForError(cx
, targetTypeObj
);
1855 if (!targetTypeStr
) {
1859 IndexCString
sourceSizeStr(sourceSize
);
1860 IndexCString
targetSizeStr(targetSize
);
1862 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1863 CTYPESMSG_SIZE_MISMATCH_CAST
, targetTypeStr
.get(),
1864 sourceTypeStr
.get(), targetSizeStr
.get(),
1865 sourceSizeStr
.get());
1869 static bool UndefinedSizePointerError(JSContext
* cx
, const char* action
,
1871 MOZ_ASSERT(JS::StringIsASCII(action
));
1873 JS::UniqueChars valBytes
;
1874 RootedValue
val(cx
, ObjectValue(*obj
));
1875 const char* valStr
= CTypesToSourceForError(cx
, val
, valBytes
);
1880 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1881 CTYPESMSG_UNDEFINED_SIZE
, action
, valStr
);
1885 static bool VariadicArgumentTypeError(JSContext
* cx
, uint32_t index
,
1886 HandleValue actual
) {
1887 JS::UniqueChars valBytes
;
1888 const char* valStr
= CTypesToSourceForError(cx
, actual
, valBytes
);
1893 IndexCString
indexStr(index
+ 1);
1895 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1896 CTYPESMSG_VARG_TYPE_ERROR
, indexStr
.get(), valStr
);
1900 [[nodiscard
]] JSObject
* GetThisObject(JSContext
* cx
, const CallArgs
& args
,
1902 if (!args
.thisv().isObject()) {
1903 IncompatibleThisProto(cx
, msg
, args
.thisv());
1907 return &args
.thisv().toObject();
1910 static bool DefineToStringTag(JSContext
* cx
, HandleObject obj
,
1911 const char* toStringTag
) {
1912 RootedString
toStringTagStr(cx
, JS_NewStringCopyZ(cx
, toStringTag
));
1913 if (!toStringTagStr
) {
1917 RootedId
toStringTagId(
1918 cx
, JS::GetWellKnownSymbolKey(cx
, JS::SymbolCode::toStringTag
));
1919 return JS_DefinePropertyById(cx
, obj
, toStringTagId
, toStringTagStr
,
1923 static JSObject
* InitCTypeClass(JSContext
* cx
, HandleObject ctypesObj
) {
1924 JSFunction
* fun
= JS_DefineFunction(cx
, ctypesObj
, "CType", ConstructAbstract
,
1925 0, CTYPESCTOR_FLAGS
);
1930 RootedObject
ctor(cx
, JS_GetFunctionObject(fun
));
1931 RootedObject
fnproto(cx
);
1932 if (!JS_GetPrototype(cx
, ctor
, &fnproto
)) {
1936 MOZ_ASSERT(fnproto
);
1938 // Set up ctypes.CType.prototype.
1939 RootedObject
prototype(
1940 cx
, JS_NewObjectWithGivenProto(cx
, &sCTypeProtoClass
, fnproto
));
1945 if (!JS_DefineProperty(cx
, ctor
, "prototype", prototype
,
1946 JSPROP_ENUMERATE
| JSPROP_READONLY
| JSPROP_PERMANENT
))
1949 if (!JS_DefineProperty(cx
, prototype
, "constructor", ctor
,
1950 JSPROP_ENUMERATE
| JSPROP_READONLY
| JSPROP_PERMANENT
))
1953 // Define properties and functions common to all CTypes.
1954 if (!JS_DefineProperties(cx
, prototype
, sCTypeProps
) ||
1955 !JS_DefineFunctions(cx
, prototype
, sCTypeFunctions
))
1958 if (!DefineToStringTag(cx
, prototype
, "CType")) {
1962 if (!JS_FreezeObject(cx
, ctor
) || !JS_FreezeObject(cx
, prototype
)) {
1969 static JSObject
* InitABIClass(JSContext
* cx
) {
1970 RootedObject
obj(cx
, JS_NewPlainObject(cx
));
1976 if (!JS_DefineFunctions(cx
, obj
, sCABIFunctions
)) {
1980 if (!DefineToStringTag(cx
, obj
, "CABI")) {
1987 static JSObject
* InitCDataClass(JSContext
* cx
, HandleObject parent
,
1988 HandleObject CTypeProto
) {
1989 JSFunction
* fun
= JS_DefineFunction(cx
, parent
, "CData", ConstructAbstract
, 0,
1995 RootedObject
ctor(cx
, JS_GetFunctionObject(fun
));
1998 // Set up ctypes.CData.__proto__ === ctypes.CType.prototype.
1999 // (Note that 'ctypes.CData instanceof Function' is still true, thanks to the
2000 // prototype chain.)
2001 if (!JS_SetPrototype(cx
, ctor
, CTypeProto
)) {
2005 // Set up ctypes.CData.prototype.
2006 RootedObject
prototype(cx
, JS_NewObject(cx
, &sCDataProtoClass
));
2011 if (!JS_DefineProperty(cx
, ctor
, "prototype", prototype
,
2012 JSPROP_ENUMERATE
| JSPROP_READONLY
| JSPROP_PERMANENT
))
2015 if (!JS_DefineProperty(cx
, prototype
, "constructor", ctor
,
2016 JSPROP_ENUMERATE
| JSPROP_READONLY
| JSPROP_PERMANENT
))
2019 // Define properties and functions common to all CDatas.
2020 if (!JS_DefineProperties(cx
, prototype
, sCDataProps
) ||
2021 !JS_DefineFunctions(cx
, prototype
, sCDataFunctions
))
2024 if (!DefineToStringTag(cx
, prototype
, "CData")) {
2028 if ( // !JS_FreezeObject(cx, prototype) || // XXX fixme - see bug 541212!
2029 !JS_FreezeObject(cx
, ctor
))
2035 static bool DefineABIConstant(JSContext
* cx
, HandleObject ctypesObj
,
2036 const char* name
, ABICode code
,
2037 HandleObject prototype
) {
2038 RootedObject
obj(cx
, JS_NewObjectWithGivenProto(cx
, &sCABIClass
, prototype
));
2042 JS_SetReservedSlot(obj
, SLOT_ABICODE
, Int32Value(code
));
2044 if (!JS_FreezeObject(cx
, obj
)) {
2048 return JS_DefineProperty(
2049 cx
, ctypesObj
, name
, obj
,
2050 JSPROP_ENUMERATE
| JSPROP_READONLY
| JSPROP_PERMANENT
);
2053 // Set up a single type constructor for
2054 // ctypes.{Pointer,Array,Struct,Function}Type.
2055 static bool InitTypeConstructor(
2056 JSContext
* cx
, HandleObject parent
, HandleObject CTypeProto
,
2057 HandleObject CDataProto
, const JSFunctionSpec spec
,
2058 const JSFunctionSpec
* fns
, const JSPropertySpec
* props
,
2059 const JSFunctionSpec
* instanceFns
, const JSPropertySpec
* instanceProps
,
2060 MutableHandleObject typeProto
, MutableHandleObject dataProto
) {
2061 JSFunction
* fun
= js::DefineFunctionWithReserved(
2062 cx
, parent
, spec
.name
.string(), spec
.call
.op
, spec
.nargs
, spec
.flags
);
2067 RootedObject
obj(cx
, JS_GetFunctionObject(fun
));
2072 // Set up the .prototype and .prototype.constructor properties.
2073 typeProto
.set(JS_NewObjectWithGivenProto(cx
, &sCTypeProtoClass
, CTypeProto
));
2078 // Define property before proceeding, for GC safety.
2079 if (!JS_DefineProperty(cx
, obj
, "prototype", typeProto
,
2080 JSPROP_ENUMERATE
| JSPROP_READONLY
| JSPROP_PERMANENT
))
2083 if (fns
&& !JS_DefineFunctions(cx
, typeProto
, fns
)) {
2087 if (!JS_DefineProperties(cx
, typeProto
, props
)) {
2091 if (!JS_DefineProperty(cx
, typeProto
, "constructor", obj
,
2092 JSPROP_ENUMERATE
| JSPROP_READONLY
| JSPROP_PERMANENT
))
2095 // Stash ctypes.{Pointer,Array,Struct}Type.prototype on a reserved slot of
2096 // the type constructor, for faster lookup.
2097 js::SetFunctionNativeReserved(obj
, SLOT_FN_CTORPROTO
,
2098 ObjectValue(*typeProto
));
2100 // Create an object to serve as the common ancestor for all CData objects
2101 // created from the given type constructor. This has ctypes.CData.prototype
2102 // as its prototype, such that it inherits the properties and functions
2103 // common to all CDatas.
2104 dataProto
.set(JS_NewObjectWithGivenProto(cx
, &sCDataProtoClass
, CDataProto
));
2109 // Define functions and properties on the 'dataProto' object that are common
2110 // to all CData objects created from this type constructor. (These will
2111 // become functions and properties on CData objects created from this type.)
2112 if (instanceFns
&& !JS_DefineFunctions(cx
, dataProto
, instanceFns
)) {
2116 if (instanceProps
&& !JS_DefineProperties(cx
, dataProto
, instanceProps
)) {
2120 // Link the type prototype to the data prototype.
2121 JS_SetReservedSlot(typeProto
, SLOT_OURDATAPROTO
, ObjectValue(*dataProto
));
2123 if (!JS_FreezeObject(cx
, obj
) ||
2124 // !JS_FreezeObject(cx, dataProto) || // XXX fixme - see bug 541212!
2125 !JS_FreezeObject(cx
, typeProto
))
2131 static JSObject
* InitInt64Class(JSContext
* cx
, HandleObject parent
,
2132 const JSClass
* clasp
, JSNative construct
,
2133 const JSFunctionSpec
* fs
,
2134 const JSFunctionSpec
* static_fs
) {
2135 // Init type class and constructor
2136 RootedObject
prototype(
2137 cx
, JS_InitClass(cx
, parent
, clasp
, nullptr, clasp
->name
, construct
, 0,
2138 nullptr, fs
, nullptr, static_fs
));
2143 if (clasp
== &sInt64ProtoClass
) {
2144 if (!DefineToStringTag(cx
, prototype
, "Int64")) {
2148 MOZ_ASSERT(clasp
== &sUInt64ProtoClass
);
2149 if (!DefineToStringTag(cx
, prototype
, "UInt64")) {
2154 RootedObject
ctor(cx
, JS_GetConstructor(cx
, prototype
));
2159 // Define the 'join' function as an extended native and stash
2160 // ctypes.{Int64,UInt64}.prototype in a reserved slot of the new function.
2161 JSNative native
= (clasp
== &sInt64ProtoClass
) ? Int64::Join
: UInt64::Join
;
2162 JSFunction
* fun
= js::DefineFunctionWithReserved(cx
, ctor
, "join", native
, 2,
2168 js::SetFunctionNativeReserved(fun
, SLOT_FN_INT64PROTO
,
2169 ObjectValue(*prototype
));
2171 if (!JS_FreezeObject(cx
, ctor
)) {
2174 if (!JS_FreezeObject(cx
, prototype
)) {
2181 static void AttachProtos(JSObject
* proto
, HandleObjectVector protos
) {
2182 // For a given 'proto' of [[Class]] "CTypeProto", attach each of the 'protos'
2183 // to the appropriate CTypeProtoSlot. (SLOT_CTYPES is the last slot
2184 // of [[Class]] "CTypeProto" that we fill in this automated manner.)
2185 for (uint32_t i
= 0; i
<= SLOT_CTYPES
; ++i
) {
2186 JS_SetReservedSlot(proto
, i
, ObjectOrNullValue(protos
[i
]));
2190 static bool InitTypeClasses(JSContext
* cx
, HandleObject ctypesObj
) {
2191 // Initialize the ctypes.CType class. This acts as an abstract base class for
2192 // the various types, and provides the common API functions. It has:
2193 // * [[Class]] "Function"
2194 // * __proto__ === Function.prototype
2195 // * A constructor that throws a TypeError. (You can't construct an
2197 // * 'prototype' property:
2198 // * [[Class]] "CTypeProto"
2199 // * __proto__ === Function.prototype
2200 // * A constructor that throws a TypeError. (You can't construct an
2201 // abstract type instance!)
2202 // * 'constructor' property === ctypes.CType
2203 // * Provides properties and functions common to all CTypes.
2204 RootedObject
CTypeProto(cx
, InitCTypeClass(cx
, ctypesObj
));
2209 // Initialize the ctypes.CData class. This acts as an abstract base class for
2210 // instances of the various types, and provides the common API functions.
2212 // * [[Class]] "Function"
2213 // * __proto__ === Function.prototype
2214 // * A constructor that throws a TypeError. (You can't construct an
2215 // abstract type instance!)
2216 // * 'prototype' property:
2217 // * [[Class]] "CDataProto"
2218 // * 'constructor' property === ctypes.CData
2219 // * Provides properties and functions common to all CDatas.
2220 RootedObject
CDataProto(cx
, InitCDataClass(cx
, ctypesObj
, CTypeProto
));
2225 // Link CTypeProto to CDataProto.
2226 JS_SetReservedSlot(CTypeProto
, SLOT_OURDATAPROTO
, ObjectValue(*CDataProto
));
2228 // Create and attach the special class constructors: ctypes.PointerType,
2229 // ctypes.ArrayType, ctypes.StructType, and ctypes.FunctionType.
2230 // Each of these constructors 'c' has, respectively:
2231 // * [[Class]] "Function"
2232 // * __proto__ === Function.prototype
2233 // * A constructor that creates a user-defined type.
2234 // * 'prototype' property:
2235 // * [[Class]] "CTypeProto"
2236 // * __proto__ === ctypes.CType.prototype
2237 // * 'constructor' property === 'c'
2238 // We also construct an object 'p' to serve, given a type object 't'
2239 // constructed from one of these type constructors, as
2240 // 't.prototype.__proto__'. This object has:
2241 // * [[Class]] "CDataProto"
2242 // * __proto__ === ctypes.CData.prototype
2243 // * Properties and functions common to all CDatas.
2244 // Therefore an instance 't' of ctypes.{Pointer,Array,Struct,Function}Type
2245 // will have, resp.:
2246 // * [[Class]] "CType"
2247 // * __proto__ === ctypes.{Pointer,Array,Struct,Function}Type.prototype
2248 // * A constructor which creates and returns a CData object, containing
2249 // binary data of the given type.
2250 // * 'prototype' property:
2251 // * [[Class]] "CDataProto"
2252 // * __proto__ === 'p', the prototype object from above
2253 // * 'constructor' property === 't'
2254 RootedObjectVector
protos(cx
);
2255 if (!protos
.resize(CTYPEPROTO_SLOTS
)) {
2258 if (!InitTypeConstructor(
2259 cx
, ctypesObj
, CTypeProto
, CDataProto
, sPointerFunction
, nullptr,
2260 sPointerProps
, sPointerInstanceFunctions
, sPointerInstanceProps
,
2261 protos
[SLOT_POINTERPROTO
], protos
[SLOT_POINTERDATAPROTO
]))
2264 if (!InitTypeConstructor(
2265 cx
, ctypesObj
, CTypeProto
, CDataProto
, sArrayFunction
, nullptr,
2266 sArrayProps
, sArrayInstanceFunctions
, sArrayInstanceProps
,
2267 protos
[SLOT_ARRAYPROTO
], protos
[SLOT_ARRAYDATAPROTO
]))
2270 if (!InitTypeConstructor(
2271 cx
, ctypesObj
, CTypeProto
, CDataProto
, sStructFunction
,
2272 sStructFunctions
, sStructProps
, sStructInstanceFunctions
, nullptr,
2273 protos
[SLOT_STRUCTPROTO
], protos
[SLOT_STRUCTDATAPROTO
]))
2276 if (!InitTypeConstructor(cx
, ctypesObj
, CTypeProto
,
2277 protos
[SLOT_POINTERDATAPROTO
], sFunctionFunction
,
2278 nullptr, sFunctionProps
, sFunctionInstanceFunctions
,
2279 nullptr, protos
[SLOT_FUNCTIONPROTO
],
2280 protos
[SLOT_FUNCTIONDATAPROTO
]))
2283 protos
[SLOT_CDATAPROTO
].set(CDataProto
);
2285 // Create and attach the ctypes.{Int64,UInt64} constructors.
2286 // Each of these has, respectively:
2287 // * [[Class]] "Function"
2288 // * __proto__ === Function.prototype
2289 // * A constructor that creates a ctypes.{Int64,UInt64} object,
2291 // * 'prototype' property:
2292 // * [[Class]] {"Int64Proto","UInt64Proto"}
2293 // * 'constructor' property === ctypes.{Int64,UInt64}
2294 protos
[SLOT_INT64PROTO
].set(InitInt64Class(cx
, ctypesObj
, &sInt64ProtoClass
,
2295 Int64::Construct
, sInt64Functions
,
2296 sInt64StaticFunctions
));
2297 if (!protos
[SLOT_INT64PROTO
]) {
2300 protos
[SLOT_UINT64PROTO
].set(
2301 InitInt64Class(cx
, ctypesObj
, &sUInt64ProtoClass
, UInt64::Construct
,
2302 sUInt64Functions
, sUInt64StaticFunctions
));
2303 if (!protos
[SLOT_UINT64PROTO
]) {
2307 // Finally, store a pointer to the global ctypes object.
2308 // Note that there is no other reliable manner of locating this object.
2309 protos
[SLOT_CTYPES
].set(ctypesObj
);
2311 // Attach the prototypes just created to each of ctypes.CType.prototype,
2312 // and the special type constructors, so we can access them when constructing
2313 // instances of those types.
2314 AttachProtos(CTypeProto
, protos
);
2315 AttachProtos(protos
[SLOT_POINTERPROTO
], protos
);
2316 AttachProtos(protos
[SLOT_ARRAYPROTO
], protos
);
2317 AttachProtos(protos
[SLOT_STRUCTPROTO
], protos
);
2318 AttachProtos(protos
[SLOT_FUNCTIONPROTO
], protos
);
2320 RootedObject
ABIProto(cx
, InitABIClass(cx
));
2325 // Attach objects representing ABI constants.
2326 if (!DefineABIConstant(cx
, ctypesObj
, "default_abi", ABI_DEFAULT
, ABIProto
) ||
2327 !DefineABIConstant(cx
, ctypesObj
, "stdcall_abi", ABI_STDCALL
, ABIProto
) ||
2328 !DefineABIConstant(cx
, ctypesObj
, "thiscall_abi", ABI_THISCALL
,
2330 !DefineABIConstant(cx
, ctypesObj
, "winapi_abi", ABI_WINAPI
, ABIProto
))
2333 // Create objects representing the builtin types, and attach them to the
2334 // ctypes object. Each type object 't' has:
2335 // * [[Class]] "CType"
2336 // * __proto__ === ctypes.CType.prototype
2337 // * A constructor which creates and returns a CData object, containing
2338 // binary data of the given type.
2339 // * 'prototype' property:
2340 // * [[Class]] "CDataProto"
2341 // * __proto__ === ctypes.CData.prototype
2342 // * 'constructor' property === 't'
2343 #define DEFINE_TYPE(name, type, ffiType) \
2344 RootedObject typeObj_##name(cx); \
2346 RootedValue typeVal(cx, Int32Value(sizeof(type))); \
2347 RootedValue alignVal(cx, Int32Value(ffiType.alignment)); \
2349 CType::DefineBuiltin(cx, ctypesObj, #name, CTypeProto, CDataProto, \
2350 #name, TYPE_##name, typeVal, alignVal, &ffiType); \
2351 if (!typeObj_##name) return false; \
2353 CTYPES_FOR_EACH_TYPE(DEFINE_TYPE
)
2356 // Alias 'ctypes.unsigned' as 'ctypes.unsigned_int', since they represent
2357 // the same type in C.
2358 if (!JS_DefineProperty(cx
, ctypesObj
, "unsigned", typeObj_unsigned_int
,
2359 JSPROP_ENUMERATE
| JSPROP_READONLY
| JSPROP_PERMANENT
))
2362 // Alias 'ctypes.jschar' as 'ctypes.char16_t' to prevent breaking addons
2363 // that are still using jschar (bug 1064935).
2364 if (!JS_DefineProperty(cx
, ctypesObj
, "jschar", typeObj_char16_t
,
2365 JSPROP_ENUMERATE
| JSPROP_READONLY
| JSPROP_PERMANENT
))
2368 // Create objects representing the special types void_t and voidptr_t.
2369 RootedObject
typeObj(
2370 cx
, CType::DefineBuiltin(cx
, ctypesObj
, "void_t", CTypeProto
, CDataProto
,
2371 "void", TYPE_void_t
, JS::UndefinedHandleValue
,
2372 JS::UndefinedHandleValue
, &ffi_type_void
));
2377 typeObj
= PointerType::CreateInternal(cx
, typeObj
);
2381 if (!JS_DefineProperty(cx
, ctypesObj
, "voidptr_t", typeObj
,
2382 JSPROP_ENUMERATE
| JSPROP_READONLY
| JSPROP_PERMANENT
))
2388 bool IsCTypesGlobal(JSObject
* obj
) {
2389 return obj
->hasClass(&sCTypesGlobalClass
);
2392 bool IsCTypesGlobal(HandleValue v
) {
2393 return v
.isObject() && IsCTypesGlobal(&v
.toObject());
2396 // Get the JS::CTypesCallbacks struct from the 'ctypes' object 'obj'.
2397 const JS::CTypesCallbacks
* GetCallbacks(JSObject
* obj
) {
2398 MOZ_ASSERT(IsCTypesGlobal(obj
));
2400 Value result
= JS::GetReservedSlot(obj
, SLOT_CALLBACKS
);
2401 if (result
.isUndefined()) {
2405 return static_cast<const JS::CTypesCallbacks
*>(result
.toPrivate());
2408 // Utility function to access a property of an object as an object
2409 // returns false and sets the error if the property does not exist
2410 // or is not an object
2411 static bool GetObjectProperty(JSContext
* cx
, HandleObject obj
,
2412 const char* property
,
2413 MutableHandleObject result
) {
2414 RootedValue
val(cx
);
2415 if (!JS_GetProperty(cx
, obj
, property
, &val
)) {
2419 if (val
.isPrimitive()) {
2420 JS_ReportErrorASCII(cx
, "missing or non-object field");
2424 result
.set(val
.toObjectOrNull());
2428 } // namespace js::ctypes
2431 using namespace js::ctypes
;
2433 JS_PUBLIC_API
bool JS::InitCTypesClass(JSContext
* cx
,
2434 Handle
<JSObject
*> global
) {
2435 // attach ctypes property to global object
2436 RootedObject
ctypes(cx
, JS_NewObject(cx
, &sCTypesGlobalClass
));
2441 if (!JS_DefineProperty(cx
, global
, "ctypes", ctypes
,
2442 JSPROP_READONLY
| JSPROP_PERMANENT
)) {
2446 if (!InitTypeClasses(cx
, ctypes
)) {
2450 // attach API functions and properties
2451 if (!JS_DefineFunctions(cx
, ctypes
, sModuleFunctions
) ||
2452 !JS_DefineProperties(cx
, ctypes
, sModuleProps
))
2455 if (!DefineToStringTag(cx
, ctypes
, "ctypes")) {
2459 // Set up ctypes.CDataFinalizer.prototype.
2460 RootedObject
ctor(cx
);
2461 if (!GetObjectProperty(cx
, ctypes
, "CDataFinalizer", &ctor
)) {
2465 RootedObject
prototype(cx
, JS_NewObject(cx
, &sCDataFinalizerProtoClass
));
2470 if (!JS_DefineFunctions(cx
, prototype
, sCDataFinalizerFunctions
)) {
2474 if (!DefineToStringTag(cx
, prototype
, "CDataFinalizer")) {
2478 if (!JS_DefineProperty(cx
, ctor
, "prototype", prototype
,
2479 JSPROP_ENUMERATE
| JSPROP_READONLY
| JSPROP_PERMANENT
))
2482 if (!JS_DefineProperty(cx
, prototype
, "constructor", ctor
,
2483 JSPROP_ENUMERATE
| JSPROP_READONLY
| JSPROP_PERMANENT
))
2486 // Seal the ctypes object, to prevent modification.
2487 return JS_FreezeObject(cx
, ctypes
);
2490 JS_PUBLIC_API
void JS::SetCTypesCallbacks(JSObject
* ctypesObj
,
2491 const CTypesCallbacks
* callbacks
) {
2492 MOZ_ASSERT(callbacks
);
2493 MOZ_ASSERT(IsCTypesGlobal(ctypesObj
));
2495 // Set the callbacks on a reserved slot.
2496 JS_SetReservedSlot(ctypesObj
, SLOT_CALLBACKS
,
2497 PrivateValue(const_cast<CTypesCallbacks
*>(callbacks
)));
2504 size_t SizeOfDataIfCDataObject(mozilla::MallocSizeOf mallocSizeOf
,
2506 if (!CData::IsCData(obj
)) {
2511 Value slot
= JS::GetReservedSlot(obj
, ctypes::SLOT_OWNS
);
2512 if (!slot
.isUndefined()) {
2513 bool owns
= slot
.toBoolean();
2514 slot
= JS::GetReservedSlot(obj
, ctypes::SLOT_DATA
);
2515 if (!slot
.isUndefined()) {
2516 char** buffer
= static_cast<char**>(slot
.toPrivate());
2517 n
+= mallocSizeOf(buffer
);
2519 n
+= mallocSizeOf(*buffer
);
2526 /*******************************************************************************
2527 ** Type conversion functions
2528 *******************************************************************************/
2530 // Enforce some sanity checks on type widths and properties.
2531 // Where the architecture is 64-bit, make sure it's LP64 or LLP64. (ctypes.int
2532 // autoconverts to a primitive JS number; to support ILP64 architectures, it
2533 // would need to autoconvert to an Int64 object instead. Therefore we enforce
2534 // this invariant here.)
2535 static_assert(sizeof(bool) == 1 || sizeof(bool) == 4);
2536 static_assert(sizeof(char) == 1);
2537 static_assert(sizeof(short) == 2);
2538 static_assert(sizeof(int) == 4);
2539 static_assert(sizeof(unsigned) == 4);
2540 static_assert(sizeof(long) == 4 || sizeof(long) == 8);
2541 static_assert(sizeof(long long) == 8);
2542 static_assert(sizeof(size_t) == sizeof(uintptr_t));
2543 static_assert(sizeof(float) == 4);
2544 static_assert(sizeof(PRFuncPtr
) == sizeof(void*));
2545 static_assert(numeric_limits
<double>::is_signed
);
2547 template <typename TargetType
, typename FromType
,
2548 bool FromIsIntegral
= std::is_integral_v
<FromType
>>
2551 template <typename TargetType
, typename FromType
>
2552 struct ConvertImpl
<TargetType
, FromType
, false> {
2553 static MOZ_ALWAYS_INLINE TargetType
Convert(FromType input
) {
2554 return JS::ToSignedOrUnsignedInteger
<TargetType
>(input
);
2558 template <typename TargetType
>
2559 struct ConvertUnsignedTargetTo
{
2560 static TargetType
convert(std::make_unsigned_t
<TargetType
> input
) {
2561 return std::is_signed_v
<TargetType
> ? mozilla::WrapToSigned(input
) : input
;
2566 struct ConvertUnsignedTargetTo
<char16_t
> {
2567 static char16_t
convert(char16_t input
) {
2568 // mozilla::WrapToSigned can't be used on char16_t.
2573 template <typename TargetType
, typename FromType
>
2574 struct ConvertImpl
<TargetType
, FromType
, true> {
2575 static MOZ_ALWAYS_INLINE TargetType
Convert(FromType input
) {
2576 using UnsignedTargetType
= std::make_unsigned_t
<TargetType
>;
2577 auto resultUnsigned
= static_cast<UnsignedTargetType
>(input
);
2579 return ConvertUnsignedTargetTo
<TargetType
>::convert(resultUnsigned
);
2583 template <class TargetType
, class FromType
>
2584 static MOZ_ALWAYS_INLINE TargetType
Convert(FromType d
) {
2586 std::is_integral_v
<FromType
> != std::is_floating_point_v
<FromType
>,
2587 "should only be converting from floating/integral type");
2589 return ConvertImpl
<TargetType
, FromType
>::Convert(d
);
2592 template <class TargetType
, class FromType
>
2593 static MOZ_ALWAYS_INLINE
bool IsAlwaysExact() {
2594 // Return 'true' if TargetType can always exactly represent FromType.
2596 // 1) TargetType must be the same or more bits wide as FromType. For integers
2597 // represented in 'n' bits, unsigned variants will have 'n' digits while
2598 // signed will have 'n - 1'. For floating point types, 'digits' is the
2600 // 2) If FromType is signed, TargetType must also be signed. (Floating point
2601 // types are always signed.)
2602 // 3) If TargetType is an exact integral type, FromType must be also.
2603 if (numeric_limits
<TargetType
>::digits
< numeric_limits
<FromType
>::digits
) {
2607 if (numeric_limits
<FromType
>::is_signed
&&
2608 !numeric_limits
<TargetType
>::is_signed
)
2611 if (!numeric_limits
<FromType
>::is_exact
&&
2612 numeric_limits
<TargetType
>::is_exact
)
2618 // Templated helper to determine if FromType 'i' converts losslessly to
2619 // TargetType 'j'. Default case where both types are the same signedness.
2620 template <class TargetType
, class FromType
, bool TargetSigned
, bool FromSigned
>
2621 struct IsExactImpl
{
2622 static MOZ_ALWAYS_INLINE
bool Test(FromType i
, TargetType j
) {
2623 static_assert(numeric_limits
<TargetType
>::is_exact
);
2624 return FromType(j
) == i
;
2628 // Specialization where TargetType is unsigned, FromType is signed.
2629 template <class TargetType
, class FromType
>
2630 struct IsExactImpl
<TargetType
, FromType
, false, true> {
2631 static MOZ_ALWAYS_INLINE
bool Test(FromType i
, TargetType j
) {
2632 static_assert(numeric_limits
<TargetType
>::is_exact
);
2633 return i
>= 0 && FromType(j
) == i
;
2637 // Specialization where TargetType is signed, FromType is unsigned.
2638 template <class TargetType
, class FromType
>
2639 struct IsExactImpl
<TargetType
, FromType
, true, false> {
2640 static MOZ_ALWAYS_INLINE
bool Test(FromType i
, TargetType j
) {
2641 static_assert(numeric_limits
<TargetType
>::is_exact
);
2642 return TargetType(i
) >= 0 && FromType(j
) == i
;
2646 // Convert FromType 'i' to TargetType 'result', returning true iff 'result'
2647 // is an exact representation of 'i'.
2648 template <class TargetType
, class FromType
>
2649 static MOZ_ALWAYS_INLINE
bool ConvertExact(FromType i
, TargetType
* result
) {
2650 static_assert(std::numeric_limits
<TargetType
>::is_exact
,
2651 "TargetType must be exact to simplify conversion");
2653 *result
= Convert
<TargetType
>(i
);
2655 // See if we can avoid a dynamic check.
2656 if (IsAlwaysExact
<TargetType
, FromType
>()) {
2660 // Return 'true' if 'i' is exactly representable in 'TargetType'.
2661 return IsExactImpl
<TargetType
, FromType
,
2662 numeric_limits
<TargetType
>::is_signed
,
2663 numeric_limits
<FromType
>::is_signed
>::Test(i
, *result
);
2666 // Templated helper to determine if Type 'i' is negative. Default case
2667 // where IntegerType is unsigned.
2668 template <class Type
, bool IsSigned
>
2669 struct IsNegativeImpl
{
2670 static MOZ_ALWAYS_INLINE
bool Test(Type i
) { return false; }
2673 // Specialization where Type is signed.
2674 template <class Type
>
2675 struct IsNegativeImpl
<Type
, true> {
2676 static MOZ_ALWAYS_INLINE
bool Test(Type i
) { return i
< 0; }
2679 // Determine whether Type 'i' is negative.
2680 template <class Type
>
2681 static MOZ_ALWAYS_INLINE
bool IsNegative(Type i
) {
2682 return IsNegativeImpl
<Type
, numeric_limits
<Type
>::is_signed
>::Test(i
);
2685 // Implicitly convert val to bool, allowing bool, int, and double
2686 // arguments numerically equal to 0 or 1.
2687 static bool jsvalToBool(JSContext
* cx
, HandleValue val
, bool* result
) {
2688 if (val
.isBoolean()) {
2689 *result
= val
.toBoolean();
2692 if (val
.isInt32()) {
2693 int32_t i
= val
.toInt32();
2695 return i
== 0 || i
== 1;
2697 if (val
.isDouble()) {
2698 double d
= val
.toDouble();
2701 return d
== 1 || d
== 0;
2703 // Don't silently convert null to bool. It's probably a mistake.
2707 // Implicitly convert val to IntegerType, allowing bool, int, double,
2708 // Int64, UInt64, and CData integer types 't' where all values of 't' are
2709 // representable by IntegerType.
2710 template <class IntegerType
>
2711 static bool jsvalToInteger(JSContext
* cx
, HandleValue val
,
2712 IntegerType
* result
) {
2713 static_assert(numeric_limits
<IntegerType
>::is_exact
);
2715 if (val
.isInt32()) {
2716 // Make sure the integer fits in the alotted precision, and has the right
2718 int32_t i
= val
.toInt32();
2719 return ConvertExact(i
, result
);
2721 if (val
.isDouble()) {
2722 // Don't silently lose bits here -- check that val really is an
2723 // integer value, and has the right sign.
2724 double d
= val
.toDouble();
2725 return ConvertExact(d
, result
);
2727 if (val
.isObject()) {
2728 RootedObject
obj(cx
, &val
.toObject());
2729 if (CData::IsCDataMaybeUnwrap(&obj
)) {
2730 JSObject
* typeObj
= CData::GetCType(obj
);
2731 void* data
= CData::GetData(obj
);
2733 // Check whether the source type is always representable, with exact
2734 // precision, by the target type. If it is, convert the value.
2735 switch (CType::GetTypeCode(typeObj
)) {
2736 #define INTEGER_CASE(name, fromType, ffiType) \
2738 if (!IsAlwaysExact<IntegerType, fromType>()) return false; \
2739 *result = IntegerType(*static_cast<fromType*>(data)); \
2741 CTYPES_FOR_EACH_INT_TYPE(INTEGER_CASE
)
2742 CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGER_CASE
)
2748 case TYPE_float32_t
:
2749 case TYPE_float64_t
:
2751 case TYPE_signed_char
:
2752 case TYPE_unsigned_char
:
2758 // Not a compatible number type.
2763 if (Int64::IsInt64(obj
)) {
2764 // Make sure the integer fits in IntegerType.
2765 int64_t i
= Int64Base::GetInt(obj
);
2766 return ConvertExact(i
, result
);
2769 if (UInt64::IsUInt64(obj
)) {
2770 // Make sure the integer fits in IntegerType.
2771 uint64_t i
= Int64Base::GetInt(obj
);
2772 return ConvertExact(i
, result
);
2775 if (CDataFinalizer::IsCDataFinalizer(obj
)) {
2776 RootedValue
innerData(cx
);
2777 if (!CDataFinalizer::GetValue(cx
, obj
, &innerData
)) {
2778 return false; // Nothing to convert
2780 return jsvalToInteger(cx
, innerData
, result
);
2785 if (val
.isBoolean()) {
2786 // Implicitly promote boolean values to 0 or 1, like C.
2787 *result
= val
.toBoolean();
2788 MOZ_ASSERT(*result
== 0 || *result
== 1);
2791 // Don't silently convert null to an integer. It's probably a mistake.
2795 // Implicitly convert val to FloatType, allowing int, double,
2796 // Int64, UInt64, and CData numeric types 't' where all values of 't' are
2797 // representable by FloatType.
2798 template <class FloatType
>
2799 static bool jsvalToFloat(JSContext
* cx
, HandleValue val
, FloatType
* result
) {
2800 static_assert(!numeric_limits
<FloatType
>::is_exact
);
2802 // The following casts may silently throw away some bits, but there's
2803 // no good way around it. Sternly requiring that the 64-bit double
2804 // argument be exactly representable as a 32-bit float is
2805 // unrealistic: it would allow 1/2 to pass but not 1/3.
2806 if (val
.isInt32()) {
2807 *result
= FloatType(val
.toInt32());
2810 if (val
.isDouble()) {
2811 *result
= FloatType(val
.toDouble());
2814 if (val
.isObject()) {
2815 RootedObject
obj(cx
, &val
.toObject());
2816 if (CData::IsCDataMaybeUnwrap(&obj
)) {
2817 JSObject
* typeObj
= CData::GetCType(obj
);
2818 void* data
= CData::GetData(obj
);
2820 // Check whether the source type is always representable, with exact
2821 // precision, by the target type. If it is, convert the value.
2822 switch (CType::GetTypeCode(typeObj
)) {
2823 #define NUMERIC_CASE(name, fromType, ffiType) \
2825 if (!IsAlwaysExact<FloatType, fromType>()) return false; \
2826 *result = FloatType(*static_cast<fromType*>(data)); \
2828 CTYPES_FOR_EACH_FLOAT_TYPE(NUMERIC_CASE
)
2829 CTYPES_FOR_EACH_INT_TYPE(NUMERIC_CASE
)
2830 CTYPES_FOR_EACH_WRAPPED_INT_TYPE(NUMERIC_CASE
)
2835 case TYPE_signed_char
:
2836 case TYPE_unsigned_char
:
2842 // Not a compatible number type.
2847 // Don't silently convert true to 1.0 or false to 0.0, even though C/C++
2848 // does it. It's likely to be a mistake.
2852 template <class IntegerType
, class CharT
>
2853 static bool StringToInteger(JSContext
* cx
, CharT
* cp
, size_t length
,
2854 IntegerType
* result
, bool* overflow
) {
2855 static_assert(numeric_limits
<IntegerType
>::is_exact
);
2857 const CharT
* end
= cp
+ length
;
2862 IntegerType sign
= 1;
2864 if (!numeric_limits
<IntegerType
>::is_signed
) {
2872 // Assume base-10, unless the string begins with '0x' or '0X'.
2873 IntegerType base
= 10;
2874 if (end
- cp
> 2 && cp
[0] == '0' && (cp
[1] == 'x' || cp
[1] == 'X')) {
2879 // Scan the string left to right and build the number,
2880 // checking for valid characters 0 - 9, a - f, A - F and overflow.
2885 if (IsAsciiDigit(c
)) {
2887 } else if (base
== 16 && c
>= 'a' && c
<= 'f') {
2888 digit
= c
- 'a' + 10;
2889 } else if (base
== 16 && c
>= 'A' && c
<= 'F') {
2890 digit
= c
- 'A' + 10;
2896 i
= ii
* base
+ sign
* digit
;
2897 if (i
/ base
!= ii
) {
2907 template <class IntegerType
>
2908 static bool StringToInteger(JSContext
* cx
, JSString
* string
,
2909 IntegerType
* result
, bool* overflow
) {
2910 JSLinearString
* linear
= string
->ensureLinear(cx
);
2915 AutoCheckCannotGC nogc
;
2916 size_t length
= linear
->length();
2917 return string
->hasLatin1Chars()
2918 ? StringToInteger
<IntegerType
>(cx
, linear
->latin1Chars(nogc
),
2919 length
, result
, overflow
)
2920 : StringToInteger
<IntegerType
>(cx
, linear
->twoByteChars(nogc
),
2921 length
, result
, overflow
);
2924 // Implicitly convert val to IntegerType, allowing int, double,
2925 // Int64, UInt64, and optionally a decimal or hexadecimal string argument.
2926 // (This is common code shared by jsvalToSize and the Int64/UInt64
2928 template <class IntegerType
>
2929 static bool jsvalToBigInteger(JSContext
* cx
, HandleValue val
, bool allowString
,
2930 IntegerType
* result
, bool* overflow
) {
2931 static_assert(numeric_limits
<IntegerType
>::is_exact
);
2933 if (val
.isInt32()) {
2934 // Make sure the integer fits in the alotted precision, and has the right
2936 int32_t i
= val
.toInt32();
2937 return ConvertExact(i
, result
);
2939 if (val
.isDouble()) {
2940 // Don't silently lose bits here -- check that val really is an
2941 // integer value, and has the right sign.
2942 double d
= val
.toDouble();
2943 return ConvertExact(d
, result
);
2945 if (allowString
&& val
.isString()) {
2946 // Allow conversion from base-10 or base-16 strings, provided the result
2947 // fits in IntegerType. (This allows an Int64 or UInt64 object to be passed
2948 // to the JS array element operator, which will automatically call
2949 // toString() on the object for us.)
2950 return StringToInteger(cx
, val
.toString(), result
, overflow
);
2952 if (val
.isObject()) {
2953 // Allow conversion from an Int64 or UInt64 object directly.
2954 JSObject
* obj
= &val
.toObject();
2956 if (UInt64::IsUInt64(obj
)) {
2957 // Make sure the integer fits in IntegerType.
2958 uint64_t i
= Int64Base::GetInt(obj
);
2959 return ConvertExact(i
, result
);
2962 if (Int64::IsInt64(obj
)) {
2963 // Make sure the integer fits in IntegerType.
2964 int64_t i
= Int64Base::GetInt(obj
);
2965 return ConvertExact(i
, result
);
2968 if (CDataFinalizer::IsCDataFinalizer(obj
)) {
2969 RootedValue
innerData(cx
);
2970 if (!CDataFinalizer::GetValue(cx
, obj
, &innerData
)) {
2971 return false; // Nothing to convert
2973 return jsvalToBigInteger(cx
, innerData
, allowString
, result
, overflow
);
2979 // Implicitly convert val to a size value, where the size value is represented
2980 // by size_t but must also fit in a double.
2981 static bool jsvalToSize(JSContext
* cx
, HandleValue val
, bool allowString
,
2984 if (!jsvalToBigInteger(cx
, val
, allowString
, result
, &dummy
)) {
2988 // Also check that the result fits in a double.
2989 return Convert
<size_t>(double(*result
)) == *result
;
2992 // Implicitly convert val to IntegerType, allowing int, double,
2993 // Int64, UInt64, and optionally a decimal or hexadecimal string argument.
2994 // (This is common code shared by jsvalToSize and the Int64/UInt64
2996 template <class IntegerType
>
2997 static bool jsidToBigInteger(JSContext
* cx
, jsid val
, bool allowString
,
2998 IntegerType
* result
) {
2999 static_assert(numeric_limits
<IntegerType
>::is_exact
);
3002 // Make sure the integer fits in the alotted precision, and has the right
3004 int32_t i
= val
.toInt();
3005 return ConvertExact(i
, result
);
3007 if (allowString
&& val
.isString()) {
3008 // Allow conversion from base-10 or base-16 strings, provided the result
3009 // fits in IntegerType. (This allows an Int64 or UInt64 object to be passed
3010 // to the JS array element operator, which will automatically call
3011 // toString() on the object for us.)
3013 return StringToInteger(cx
, val
.toString(), result
, &dummy
);
3018 // Implicitly convert val to a size value, where the size value is represented
3019 // by size_t but must also fit in a double.
3020 static bool jsidToSize(JSContext
* cx
, jsid val
, bool allowString
,
3022 if (!jsidToBigInteger(cx
, val
, allowString
, result
)) {
3026 // Also check that the result fits in a double.
3027 return Convert
<size_t>(double(*result
)) == *result
;
3030 // Implicitly convert a size value to a Value, ensuring that the size_t value
3031 // fits in a double.
3032 static bool SizeTojsval(JSContext
* cx
, size_t size
, MutableHandleValue result
) {
3033 if (Convert
<size_t>(double(size
)) != size
) {
3037 result
.setNumber(double(size
));
3041 // Forcefully convert val to IntegerType when explicitly requested.
3042 template <class IntegerType
>
3043 static bool jsvalToIntegerExplicit(HandleValue val
, IntegerType
* result
) {
3044 static_assert(numeric_limits
<IntegerType
>::is_exact
);
3046 if (val
.isDouble()) {
3047 // Convert using ToInt32-style semantics: non-finite numbers become 0, and
3048 // everything else rounds toward zero then maps into |IntegerType| with
3049 // wraparound semantics.
3050 double d
= val
.toDouble();
3051 *result
= JS::ToSignedOrUnsignedInteger
<IntegerType
>(d
);
3054 if (val
.isObject()) {
3055 // Convert Int64 and UInt64 values by C-style cast.
3056 JSObject
* obj
= &val
.toObject();
3057 if (Int64::IsInt64(obj
)) {
3058 int64_t i
= Int64Base::GetInt(obj
);
3059 *result
= IntegerType(i
);
3062 if (UInt64::IsUInt64(obj
)) {
3063 uint64_t i
= Int64Base::GetInt(obj
);
3064 *result
= IntegerType(i
);
3071 // Forcefully convert val to a pointer value when explicitly requested.
3072 static bool jsvalToPtrExplicit(JSContext
* cx
, HandleValue val
,
3073 uintptr_t* result
) {
3074 if (val
.isInt32()) {
3075 // int32_t always fits in intptr_t. If the integer is negative, cast through
3076 // an intptr_t intermediate to sign-extend.
3077 int32_t i
= val
.toInt32();
3078 *result
= i
< 0 ? uintptr_t(intptr_t(i
)) : uintptr_t(i
);
3081 if (val
.isDouble()) {
3082 double d
= val
.toDouble();
3084 // Cast through an intptr_t intermediate to sign-extend.
3085 intptr_t i
= Convert
<intptr_t>(d
);
3086 if (double(i
) != d
) {
3090 *result
= uintptr_t(i
);
3094 // Don't silently lose bits here -- check that val really is an
3095 // integer value, and has the right sign.
3096 *result
= Convert
<uintptr_t>(d
);
3097 return double(*result
) == d
;
3099 if (val
.isObject()) {
3100 JSObject
* obj
= &val
.toObject();
3101 if (Int64::IsInt64(obj
)) {
3102 int64_t i
= Int64Base::GetInt(obj
);
3103 intptr_t p
= intptr_t(i
);
3105 // Make sure the integer fits in the alotted precision.
3106 if (int64_t(p
) != i
) {
3109 *result
= uintptr_t(p
);
3113 if (UInt64::IsUInt64(obj
)) {
3114 uint64_t i
= Int64Base::GetInt(obj
);
3116 // Make sure the integer fits in the alotted precision.
3117 *result
= uintptr_t(i
);
3118 return uint64_t(*result
) == i
;
3124 template <class IntegerType
, class CharType
, size_t N
>
3125 void IntegerToString(IntegerType i
, int radix
,
3126 StringBuilder
<CharType
, N
>& result
) {
3127 static_assert(numeric_limits
<IntegerType
>::is_exact
);
3129 // The buffer must be big enough for all the bits of IntegerType to fit,
3130 // in base-2, including '-'.
3131 CharType buffer
[sizeof(IntegerType
) * 8 + 1];
3132 CharType
* end
= std::end(buffer
);
3135 // Build the string in reverse. We use multiplication and subtraction
3136 // instead of modulus because that's much faster.
3137 const bool isNegative
= IsNegative(i
);
3138 size_t sign
= isNegative
? -1 : 1;
3140 IntegerType ii
= i
/ IntegerType(radix
);
3141 size_t index
= sign
* size_t(i
- ii
* IntegerType(radix
));
3142 *--cp
= "0123456789abcdefghijklmnopqrstuvwxyz"[index
];
3150 MOZ_ASSERT(cp
>= buffer
);
3151 if (!result
.append(cp
, end
)) {
3156 // Convert C binary value 'data' of CType 'typeObj' to a JS primitive, where
3157 // possible; otherwise, construct and return a CData object. The following
3158 // semantics apply when constructing a CData object for return:
3159 // * If 'wantPrimitive' is true, the caller indicates that 'result' must be
3160 // a JS primitive, and ConvertToJS will fail if 'result' would be a CData
3161 // object. Otherwise:
3162 // * If a CData object 'parentObj' is supplied, the new CData object is
3163 // dependent on the given parent and its buffer refers to a slice of the
3165 // * If 'parentObj' is null, the new CData object may or may not own its
3166 // resulting buffer depending on the 'ownResult' argument.
3167 static bool ConvertToJS(JSContext
* cx
, HandleObject typeObj
,
3168 HandleObject parentObj
, void* data
, bool wantPrimitive
,
3169 bool ownResult
, MutableHandleValue result
) {
3170 MOZ_ASSERT(!parentObj
|| CData::IsCData(parentObj
));
3171 MOZ_ASSERT(!parentObj
|| !ownResult
);
3172 MOZ_ASSERT(!wantPrimitive
|| !ownResult
);
3174 TypeCode typeCode
= CType::GetTypeCode(typeObj
);
3178 result
.setUndefined();
3181 result
.setBoolean(*static_cast<bool*>(data
));
3183 #define INT_CASE(name, type, ffiType) \
3184 case TYPE_##name: { \
3185 type value = *static_cast<type*>(data); \
3186 if (sizeof(type) < 4) \
3187 result.setInt32(int32_t(value)); \
3189 result.setDouble(double(value)); \
3192 CTYPES_FOR_EACH_INT_TYPE(INT_CASE
)
3194 #define WRAPPED_INT_CASE(name, type, ffiType) \
3195 case TYPE_##name: { \
3196 /* Return an Int64 or UInt64 object - do not convert to a JS number. */ \
3198 RootedObject proto(cx); \
3199 if (!numeric_limits<type>::is_signed) { \
3200 value = *static_cast<type*>(data); \
3201 /* Get ctypes.UInt64.prototype from ctypes.CType.prototype. */ \
3202 proto = CType::GetProtoFromType(cx, typeObj, SLOT_UINT64PROTO); \
3203 if (!proto) return false; \
3205 value = int64_t(*static_cast<type*>(data)); \
3206 /* Get ctypes.Int64.prototype from ctypes.CType.prototype. */ \
3207 proto = CType::GetProtoFromType(cx, typeObj, SLOT_INT64PROTO); \
3208 if (!proto) return false; \
3211 JSObject* obj = Int64Base::Construct(cx, proto, value, \
3212 !numeric_limits<type>::is_signed); \
3213 if (!obj) return false; \
3214 result.setObject(*obj); \
3217 CTYPES_FOR_EACH_WRAPPED_INT_TYPE(WRAPPED_INT_CASE
)
3218 #undef WRAPPED_INT_CASE
3219 #define FLOAT_CASE(name, type, ffiType) \
3220 case TYPE_##name: { \
3221 type value = *static_cast<type*>(data); \
3222 result.setDouble(double(value)); \
3225 CTYPES_FOR_EACH_FLOAT_TYPE(FLOAT_CASE
)
3227 #define CHAR_CASE(name, type, ffiType) \
3229 /* Convert to an integer. We have no idea what character encoding to */ \
3230 /* use, if any. */ \
3231 result.setInt32(*static_cast<type*>(data)); \
3233 CTYPES_FOR_EACH_CHAR_TYPE(CHAR_CASE
)
3235 case TYPE_char16_t
: {
3236 // Convert the char16_t to a 1-character string.
3237 JSString
* str
= JS_NewUCStringCopyN(cx
, static_cast<char16_t
*>(data
), 1);
3242 result
.setString(str
);
3248 // We're about to create a new CData object to return. If the caller
3249 // doesn't want this, return early.
3250 if (wantPrimitive
) {
3251 return NonPrimitiveError(cx
, typeObj
);
3254 JSObject
* obj
= CData::Create(cx
, typeObj
, parentObj
, data
, ownResult
);
3259 result
.setObject(*obj
);
3263 MOZ_CRASH("cannot return a FunctionType");
3269 // Determine if the contents of a typed array can be converted without
3270 // ambiguity to a C type. Elements of a Int8Array are converted to
3271 // ctypes.int8_t, UInt8Array to ctypes.uint8_t, etc.
3272 bool CanConvertTypedArrayItemTo(JSObject
* baseType
, JSObject
* valObj
,
3274 TypeCode baseTypeCode
= CType::GetTypeCode(baseType
);
3275 if (baseTypeCode
== TYPE_void_t
|| baseTypeCode
== TYPE_char
) {
3278 TypeCode elementTypeCode
;
3279 switch (JS_GetArrayBufferViewType(valObj
)) {
3281 elementTypeCode
= TYPE_int8_t
;
3284 case Scalar::Uint8Clamped
:
3285 elementTypeCode
= TYPE_uint8_t
;
3288 elementTypeCode
= TYPE_int16_t
;
3290 case Scalar::Uint16
:
3291 elementTypeCode
= TYPE_uint16_t
;
3294 elementTypeCode
= TYPE_int32_t
;
3296 case Scalar::Uint32
:
3297 elementTypeCode
= TYPE_uint32_t
;
3299 case Scalar::Float32
:
3300 elementTypeCode
= TYPE_float32_t
;
3302 case Scalar::Float64
:
3303 elementTypeCode
= TYPE_float64_t
;
3309 return elementTypeCode
== baseTypeCode
;
3312 static CDataFinalizer::Private
* GetFinalizerPrivate(JSObject
* obj
) {
3313 MOZ_ASSERT(CDataFinalizer::IsCDataFinalizer(obj
));
3315 using T
= CDataFinalizer::Private
;
3316 return JS::GetMaybePtrFromReservedSlot
<T
>(obj
, SLOT_DATAFINALIZER_PRIVATE
);
3319 // Implicitly convert Value 'val' to a C binary representation of CType
3320 // 'targetType', storing the result in 'buffer'. Adequate space must be
3321 // provided in 'buffer' by the caller. This function generally does minimal
3322 // coercion between types. There are two cases in which this function is used:
3323 // 1) The target buffer is internal to a CData object; we simply write data
3325 // 2) We are converting an argument for an ffi call, in which case 'convType'
3326 // will be 'ConversionType::Argument'. This allows us to handle a special
3327 // case: if necessary, we can autoconvert a JS string primitive to a
3328 // pointer-to-character type. In this case, ownership of the allocated string
3329 // is handed off to the caller; 'freePointer' will be set to indicate this.
3330 static bool ImplicitConvert(JSContext
* cx
, HandleValue val
,
3331 JSObject
* targetType_
, void* buffer
,
3332 ConversionType convType
, bool* freePointer
,
3333 HandleObject funObj
= nullptr,
3334 unsigned argIndex
= 0,
3335 HandleObject arrObj
= nullptr,
3336 unsigned arrIndex
= 0) {
3337 RootedObject
targetType(cx
, targetType_
);
3338 MOZ_ASSERT(CType::IsSizeDefined(targetType
));
3340 // First, check if val is either a CData object or a CDataFinalizer
3341 // of type targetType.
3342 JSObject
* sourceData
= nullptr;
3343 JSObject
* sourceType
= nullptr;
3344 RootedObject
valObj(cx
, nullptr);
3345 if (val
.isObject()) {
3346 valObj
= &val
.toObject();
3347 if (CData::IsCDataMaybeUnwrap(&valObj
)) {
3348 sourceData
= valObj
;
3349 sourceType
= CData::GetCType(sourceData
);
3351 // If the types are equal, copy the buffer contained within the CData.
3352 // (Note that the buffers may overlap partially or completely.)
3353 if (CType::TypesEqual(sourceType
, targetType
)) {
3354 size_t size
= CType::GetSize(sourceType
);
3355 memmove(buffer
, CData::GetData(sourceData
), size
);
3358 } else if (CDataFinalizer::IsCDataFinalizer(valObj
)) {
3359 sourceData
= valObj
;
3360 sourceType
= CDataFinalizer::GetCType(cx
, sourceData
);
3362 CDataFinalizer::Private
* p
= GetFinalizerPrivate(sourceData
);
3365 // We have called |dispose| or |forget| already.
3366 return EmptyFinalizerError(cx
, convType
, funObj
, argIndex
);
3369 // If the types are equal, copy the buffer contained within the CData.
3370 if (CType::TypesEqual(sourceType
, targetType
)) {
3371 memmove(buffer
, p
->cargs
, p
->cargs_size
);
3377 TypeCode targetCode
= CType::GetTypeCode(targetType
);
3379 switch (targetCode
) {
3381 // Do not implicitly lose bits, but allow the values 0, 1, and -0.
3382 // Programs can convert explicitly, if needed, using `Boolean(v)` or
3385 if (!jsvalToBool(cx
, val
, &result
)) {
3386 return ConvError(cx
, "boolean", val
, convType
, funObj
, argIndex
, arrObj
,
3389 *static_cast<bool*>(buffer
) = result
;
3392 #define CHAR16_CASE(name, type, ffiType) \
3393 case TYPE_##name: { \
3394 /* Convert from a 1-character string, regardless of encoding, */ \
3395 /* or from an integer, provided the result fits in 'type'. */ \
3397 if (val.isString()) { \
3398 JSString* str = val.toString(); \
3399 if (str->length() != 1) \
3400 return ConvError(cx, #name, val, convType, funObj, argIndex, arrObj, \
3402 JSLinearString* linear = str->ensureLinear(cx); \
3403 if (!linear) return false; \
3404 result = linear->latin1OrTwoByteChar(0); \
3405 } else if (!jsvalToInteger(cx, val, &result)) { \
3406 return ConvError(cx, #name, val, convType, funObj, argIndex, arrObj, \
3409 *static_cast<type*>(buffer) = result; \
3412 CTYPES_FOR_EACH_CHAR16_TYPE(CHAR16_CASE
)
3414 #define INTEGRAL_CASE(name, type, ffiType) \
3415 case TYPE_##name: { \
3416 /* Do not implicitly lose bits. */ \
3418 if (!jsvalToInteger(cx, val, &result)) \
3419 return ConvError(cx, #name, val, convType, funObj, argIndex, arrObj, \
3421 *static_cast<type*>(buffer) = result; \
3424 CTYPES_FOR_EACH_INT_TYPE(INTEGRAL_CASE
)
3425 CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGRAL_CASE
)
3426 // It's hard to believe ctypes.char16_t("f") should work yet
3427 // ctypes.char("f") should not. Ditto for ctypes.{un,}signed_char. But
3428 // this is how ctypes has always worked, so preserve these semantics, and
3429 // don't switch to an algorithm similar to that in DEFINE_CHAR16_TYPE
3431 CTYPES_FOR_EACH_CHAR_TYPE(INTEGRAL_CASE
)
3432 #undef INTEGRAL_CASE
3433 #define FLOAT_CASE(name, type, ffiType) \
3434 case TYPE_##name: { \
3436 if (!jsvalToFloat(cx, val, &result)) \
3437 return ConvError(cx, #name, val, convType, funObj, argIndex, arrObj, \
3439 *static_cast<type*>(buffer) = result; \
3442 CTYPES_FOR_EACH_FLOAT_TYPE(FLOAT_CASE
)
3444 case TYPE_pointer
: {
3446 // Convert to a null pointer.
3447 *static_cast<void**>(buffer
) = nullptr;
3451 JS::Rooted
<JSObject
*> baseType(cx
, PointerType::GetBaseType(targetType
));
3453 // First, determine if the targetType is ctypes.void_t.ptr.
3454 TypeCode sourceCode
= CType::GetTypeCode(sourceType
);
3455 void* sourceBuffer
= CData::GetData(sourceData
);
3456 bool voidptrTarget
= CType::GetTypeCode(baseType
) == TYPE_void_t
;
3458 if (sourceCode
== TYPE_pointer
&& voidptrTarget
) {
3459 // Autoconvert if targetType is ctypes.voidptr_t.
3460 *static_cast<void**>(buffer
) = *static_cast<void**>(sourceBuffer
);
3463 if (sourceCode
== TYPE_array
) {
3464 // Autoconvert an array to a ctypes.void_t.ptr or to
3465 // sourceType.elementType.ptr, just like C.
3466 JSObject
* elementType
= ArrayType::GetBaseType(sourceType
);
3467 if (voidptrTarget
|| CType::TypesEqual(baseType
, elementType
)) {
3468 *static_cast<void**>(buffer
) = sourceBuffer
;
3473 } else if (convType
== ConversionType::Argument
&& val
.isString()) {
3474 // Convert the string for the ffi call. This requires allocating space
3475 // which the caller assumes ownership of.
3476 // TODO: Extend this so we can safely convert strings at other times
3478 JSString
* sourceString
= val
.toString();
3479 size_t sourceLength
= sourceString
->length();
3480 Rooted
<JSLinearString
*> sourceLinear(cx
,
3481 sourceString
->ensureLinear(cx
));
3482 if (!sourceLinear
) {
3486 switch (CType::GetTypeCode(baseType
)) {
3488 case TYPE_signed_char
:
3489 case TYPE_unsigned_char
: {
3490 // Reject if unpaired surrogate characters are present.
3491 if (!ReportErrorIfUnpairedSurrogatePresent(cx
, sourceLinear
)) {
3495 // Convert from UTF-16 to UTF-8.
3496 size_t nbytes
= JS::GetDeflatedUTF8StringLength(sourceLinear
);
3498 char** charBuffer
= static_cast<char**>(buffer
);
3499 *charBuffer
= cx
->pod_malloc
<char>(nbytes
+ 1);
3504 nbytes
= JS::DeflateStringToUTF8Buffer(
3505 sourceLinear
, mozilla::Span(*charBuffer
, nbytes
));
3506 (*charBuffer
)[nbytes
] = '\0';
3507 *freePointer
= true;
3510 case TYPE_char16_t
: {
3511 // Copy the char16_t string data. (We could provide direct access to
3512 // the JSString's buffer, but this approach is safer if the caller
3513 // happens to modify the string.)
3514 char16_t
** char16Buffer
= static_cast<char16_t
**>(buffer
);
3515 *char16Buffer
= cx
->pod_malloc
<char16_t
>(sourceLength
+ 1);
3516 if (!*char16Buffer
) {
3520 *freePointer
= true;
3522 CopyChars(*char16Buffer
, *sourceLinear
);
3523 (*char16Buffer
)[sourceLength
] = '\0';
3527 return ConvError(cx
, targetType
, val
, convType
, funObj
, argIndex
,
3531 } else if (val
.isObject() && JS::IsArrayBufferObject(valObj
)) {
3532 // Convert ArrayBuffer to pointer without any copy. This is only valid
3533 // when converting an argument to a function call, as it is possible for
3534 // the pointer to be invalidated by anything that runs JS code. (It is
3535 // invalid to invoke JS code from a ctypes function call.)
3536 if (convType
!= ConversionType::Argument
) {
3537 return ConvError(cx
, targetType
, val
, convType
, funObj
, argIndex
,
3542 JS::AutoCheckCannotGC nogc
;
3544 ptr
= JS::GetArrayBufferData(valObj
, &isShared
, nogc
);
3545 MOZ_ASSERT(!isShared
); // Because ArrayBuffer
3548 return ConvError(cx
, targetType
, val
, convType
, funObj
, argIndex
,
3551 *static_cast<void**>(buffer
) = ptr
;
3553 } else if (val
.isObject() && JS::IsSharedArrayBufferObject(valObj
)) {
3554 // CTypes has not yet opted in to allowing shared memory pointers
3555 // to escape. Exporting a pointer to the shared buffer without
3556 // indicating sharedness would expose client code to races.
3557 return ConvError(cx
, targetType
, val
, convType
, funObj
, argIndex
,
3559 } else if (val
.isObject() && JS_IsArrayBufferViewObject(valObj
)) {
3560 // Same as ArrayBuffer, above, though note that this will take the
3561 // offset of the view into account.
3562 if (!CanConvertTypedArrayItemTo(baseType
, valObj
, cx
)) {
3563 return ConvError(cx
, targetType
, val
, convType
, funObj
, argIndex
,
3566 if (convType
!= ConversionType::Argument
) {
3567 return ConvError(cx
, targetType
, val
, convType
, funObj
, argIndex
,
3572 JS::AutoCheckCannotGC nogc
;
3574 ptr
= JS_GetArrayBufferViewData(valObj
, &isShared
, nogc
);
3576 // Opt out of shared memory, for now. Exporting a
3577 // pointer to the shared buffer without indicating
3578 // sharedness would expose client code to races.
3583 return ConvError(cx
, targetType
, val
, convType
, funObj
, argIndex
,
3586 *static_cast<void**>(buffer
) = ptr
;
3589 return ConvError(cx
, targetType
, val
, convType
, funObj
, argIndex
, arrObj
,
3593 MOZ_ASSERT(!funObj
);
3595 RootedObject
baseType(cx
, ArrayType::GetBaseType(targetType
));
3596 size_t targetLength
= ArrayType::GetLength(targetType
);
3598 if (val
.isString()) {
3599 JSString
* sourceString
= val
.toString();
3600 size_t sourceLength
= sourceString
->length();
3601 Rooted
<JSLinearString
*> sourceLinear(cx
,
3602 sourceString
->ensureLinear(cx
));
3603 if (!sourceLinear
) {
3607 switch (CType::GetTypeCode(baseType
)) {
3609 case TYPE_signed_char
:
3610 case TYPE_unsigned_char
: {
3611 // Reject if unpaired surrogate characters are present.
3612 if (!ReportErrorIfUnpairedSurrogatePresent(cx
, sourceLinear
)) {
3616 // Convert from UTF-16 or Latin1 to UTF-8.
3617 size_t nbytes
= JS::GetDeflatedUTF8StringLength(sourceLinear
);
3619 if (targetLength
< nbytes
) {
3620 MOZ_ASSERT(!funObj
);
3621 return ArrayLengthOverflow(cx
, targetLength
, targetType
, nbytes
,
3625 char* charBuffer
= static_cast<char*>(buffer
);
3626 nbytes
= JS::DeflateStringToUTF8Buffer(
3627 sourceLinear
, mozilla::Span(charBuffer
, nbytes
));
3629 if (targetLength
> nbytes
) {
3630 charBuffer
[nbytes
] = '\0';
3635 case TYPE_char16_t
: {
3636 // Copy the string data, char16_t for char16_t, including the
3637 // terminator if there's space.
3638 if (targetLength
< sourceLength
) {
3639 MOZ_ASSERT(!funObj
);
3640 return ArrayLengthOverflow(cx
, targetLength
, targetType
,
3641 sourceLength
, val
, convType
);
3644 char16_t
* dest
= static_cast<char16_t
*>(buffer
);
3645 CopyChars(dest
, *sourceLinear
);
3647 if (targetLength
> sourceLength
) {
3648 dest
[sourceLength
] = '\0';
3654 return ConvError(cx
, targetType
, val
, convType
, funObj
, argIndex
,
3659 if (!GetClassOfValue(cx
, val
, &cls
)) {
3663 if (cls
== ESClass::Array
) {
3664 // Convert each element of the array by calling ImplicitConvert.
3665 uint32_t sourceLength
;
3666 if (!JS::GetArrayLength(cx
, valObj
, &sourceLength
) ||
3667 targetLength
!= size_t(sourceLength
)) {
3668 MOZ_ASSERT(!funObj
);
3669 return ArrayLengthMismatch(cx
, targetLength
, targetType
,
3670 size_t(sourceLength
), val
, convType
);
3673 // Convert into an intermediate, in case of failure.
3674 size_t elementSize
= CType::GetSize(baseType
);
3675 size_t arraySize
= elementSize
* targetLength
;
3676 auto intermediate
= cx
->make_pod_array
<char>(arraySize
);
3677 if (!intermediate
) {
3681 RootedValue
item(cx
);
3682 for (uint32_t i
= 0; i
< sourceLength
; ++i
) {
3683 if (!JS_GetElement(cx
, valObj
, i
, &item
)) {
3687 char* data
= intermediate
.get() + elementSize
* i
;
3688 if (!ImplicitConvert(cx
, item
, baseType
, data
, convType
, nullptr,
3689 funObj
, argIndex
, targetType
, i
))
3693 memcpy(buffer
, intermediate
.get(), arraySize
);
3694 } else if (cls
== ESClass::ArrayBuffer
||
3695 cls
== ESClass::SharedArrayBuffer
) {
3696 // Check that array is consistent with type, then
3698 const bool bufferShared
= cls
== ESClass::SharedArrayBuffer
;
3699 size_t sourceLength
= bufferShared
3700 ? JS::GetSharedArrayBufferByteLength(valObj
)
3701 : JS::GetArrayBufferByteLength(valObj
);
3702 size_t elementSize
= CType::GetSize(baseType
);
3703 size_t arraySize
= elementSize
* targetLength
;
3704 if (arraySize
!= sourceLength
) {
3705 MOZ_ASSERT(!funObj
);
3706 return ArrayLengthMismatch(cx
, arraySize
, targetType
, sourceLength
,
3709 SharedMem
<void*> target
= SharedMem
<void*>::unshared(buffer
);
3710 JS::AutoCheckCannotGC nogc
;
3712 SharedMem
<void*> src
=
3714 ? SharedMem
<void*>::shared(
3715 JS::GetSharedArrayBufferData(valObj
, &isShared
, nogc
))
3716 : SharedMem
<void*>::unshared(
3717 JS::GetArrayBufferData(valObj
, &isShared
, nogc
)));
3718 MOZ_ASSERT(isShared
== bufferShared
);
3719 jit::AtomicOperations::memcpySafeWhenRacy(target
, src
, sourceLength
);
3721 } else if (JS_IsTypedArrayObject(valObj
)) {
3722 // Check that array is consistent with type, then
3723 // copy the array. It is OK to copy from shared to unshared
3725 if (!CanConvertTypedArrayItemTo(baseType
, valObj
, cx
)) {
3726 return ConvError(cx
, targetType
, val
, convType
, funObj
, argIndex
,
3730 size_t sourceLength
= JS_GetTypedArrayByteLength(valObj
);
3731 size_t elementSize
= CType::GetSize(baseType
);
3732 size_t arraySize
= elementSize
* targetLength
;
3733 if (arraySize
!= sourceLength
) {
3734 MOZ_ASSERT(!funObj
);
3735 return ArrayLengthMismatch(cx
, arraySize
, targetType
, sourceLength
,
3738 SharedMem
<void*> target
= SharedMem
<void*>::unshared(buffer
);
3739 JS::AutoCheckCannotGC nogc
;
3741 SharedMem
<void*> src
= SharedMem
<void*>::shared(
3742 JS_GetArrayBufferViewData(valObj
, &isShared
, nogc
));
3743 jit::AtomicOperations::memcpySafeWhenRacy(target
, src
, sourceLength
);
3746 // Don't implicitly convert to string. Users can implicitly convert
3747 // with `String(x)` or `""+x`.
3748 return ConvError(cx
, targetType
, val
, convType
, funObj
, argIndex
,
3755 if (val
.isObject() && !sourceData
) {
3756 // Enumerate the properties of the object; if they match the struct
3757 // specification, convert the fields.
3758 Rooted
<IdVector
> props(cx
, IdVector(cx
));
3759 if (!JS_Enumerate(cx
, valObj
, &props
)) {
3763 // Convert into an intermediate, in case of failure.
3764 size_t structSize
= CType::GetSize(targetType
);
3765 auto intermediate
= cx
->make_pod_array
<char>(structSize
);
3766 if (!intermediate
) {
3770 const FieldInfoHash
* fields
= StructType::GetFieldInfo(targetType
);
3771 if (props
.length() != fields
->count()) {
3772 return FieldCountMismatch(cx
, fields
->count(), targetType
,
3773 props
.length(), val
, convType
, funObj
,
3778 for (size_t i
= 0; i
< props
.length(); ++i
) {
3781 if (!id
.isString()) {
3782 return PropNameNonStringError(cx
, id
, val
, convType
, funObj
,
3786 JSLinearString
* name
= id
.toLinearString();
3787 const FieldInfo
* field
=
3788 StructType::LookupField(cx
, targetType
, name
);
3793 RootedValue
prop(cx
);
3794 if (!JS_GetPropertyById(cx
, valObj
, id
, &prop
)) {
3798 // Convert the field via ImplicitConvert().
3799 char* fieldData
= intermediate
.get() + field
->mOffset
;
3800 if (!ImplicitConvert(cx
, prop
, field
->mType
, fieldData
, convType
,
3801 nullptr, funObj
, argIndex
, targetType
, i
))
3805 memcpy(buffer
, intermediate
.get(), structSize
);
3809 return ConvError(cx
, targetType
, val
, convType
, funObj
, argIndex
, arrObj
,
3814 MOZ_CRASH("invalid type");
3820 // Convert Value 'val' to a C binary representation of CType 'targetType',
3821 // storing the result in 'buffer'. This function is more forceful than
3823 static bool ExplicitConvert(JSContext
* cx
, HandleValue val
,
3824 HandleObject targetType
, void* buffer
,
3825 ConversionType convType
) {
3826 // If ImplicitConvert succeeds, use that result.
3827 if (ImplicitConvert(cx
, val
, targetType
, buffer
, convType
, nullptr)) {
3831 // If ImplicitConvert failed, and there is no pending exception, then assume
3832 // hard failure (out of memory, or some other similarly serious condition).
3833 // We store any pending exception in case we need to re-throw it.
3835 if (!JS_GetPendingException(cx
, &ex
)) {
3839 // Otherwise, assume soft failure. Clear the pending exception so that we
3840 // can throw a different one as required.
3841 JS_ClearPendingException(cx
);
3843 TypeCode type
= CType::GetTypeCode(targetType
);
3847 *static_cast<bool*>(buffer
) = ToBoolean(val
);
3850 #define INTEGRAL_CASE(name, type, ffiType) \
3851 case TYPE_##name: { \
3852 /* Convert numeric values with a C-style cast, and */ \
3853 /* allow conversion from a base-10 or base-16 string. */ \
3855 bool overflow = false; \
3856 if (!jsvalToIntegerExplicit(val, &result) && \
3857 (!val.isString() || \
3858 !StringToInteger(cx, val.toString(), &result, &overflow))) { \
3860 return TypeOverflow(cx, #name, val); \
3862 return ConvError(cx, #name, val, convType); \
3864 *static_cast<type*>(buffer) = result; \
3867 CTYPES_FOR_EACH_INT_TYPE(INTEGRAL_CASE
)
3868 CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGRAL_CASE
)
3869 CTYPES_FOR_EACH_CHAR_TYPE(INTEGRAL_CASE
)
3870 CTYPES_FOR_EACH_CHAR16_TYPE(INTEGRAL_CASE
)
3871 #undef INTEGRAL_CASE
3872 case TYPE_pointer
: {
3873 // Convert a number, Int64 object, or UInt64 object to a pointer.
3875 if (!jsvalToPtrExplicit(cx
, val
, &result
)) {
3876 return ConvError(cx
, targetType
, val
, convType
);
3878 *static_cast<uintptr_t*>(buffer
) = result
;
3881 case TYPE_float32_t
:
3882 case TYPE_float64_t
:
3887 // ImplicitConvert is sufficient. Re-throw the exception it generated.
3888 JS_SetPendingException(cx
, ex
);
3892 MOZ_CRASH("invalid type");
3897 // Given a CType 'typeObj', generate a string describing the C type declaration
3898 // corresponding to 'typeObj'. For instance, the CType constructed from
3899 // 'ctypes.int32_t.ptr.array(4).ptr.ptr' will result in the type string
3900 // 'int32_t*(**)[4]'.
3901 static JSString
* BuildTypeName(JSContext
* cx
, JSObject
* typeObj_
) {
3903 RootedObject
typeObj(cx
, typeObj_
);
3905 // Walk the hierarchy of types, outermost to innermost, building up the type
3906 // string. This consists of the base type, which goes on the left.
3907 // Derived type modifiers (* and []) build from the inside outward, with
3908 // pointers on the left and arrays on the right. An excellent description
3909 // of the rules for building C type declarations can be found at:
3910 // http://unixwiz.net/techtips/reading-cdecl.html
3911 TypeCode prevGrouping
= CType::GetTypeCode(typeObj
), currentGrouping
;
3913 currentGrouping
= CType::GetTypeCode(typeObj
);
3914 switch (currentGrouping
) {
3915 case TYPE_pointer
: {
3916 // Pointer types go on the left.
3917 PrependString(cx
, result
, "*");
3919 typeObj
= PointerType::GetBaseType(typeObj
);
3920 prevGrouping
= currentGrouping
;
3924 if (prevGrouping
== TYPE_pointer
) {
3925 // Outer type is pointer, inner type is array. Grouping is required.
3926 PrependString(cx
, result
, "(");
3927 AppendString(cx
, result
, ")");
3930 // Array types go on the right.
3931 AppendString(cx
, result
, "[");
3933 if (ArrayType::GetSafeLength(typeObj
, &length
)) {
3934 IntegerToString(length
, 10, result
);
3937 AppendString(cx
, result
, "]");
3939 typeObj
= ArrayType::GetBaseType(typeObj
);
3940 prevGrouping
= currentGrouping
;
3943 case TYPE_function
: {
3944 FunctionInfo
* fninfo
= FunctionType::GetFunctionInfo(typeObj
);
3946 // Add in the calling convention, if it's not cdecl.
3947 // There's no trailing or leading space needed here, as none of the
3948 // modifiers can produce a string beginning with an identifier ---
3949 // except for TYPE_function itself, which is fine because functions
3950 // can't return functions.
3951 ABICode abi
= GetABICode(fninfo
->mABI
);
3952 if (abi
== ABI_STDCALL
) {
3953 PrependString(cx
, result
, "__stdcall");
3954 } else if (abi
== ABI_THISCALL
) {
3955 PrependString(cx
, result
, "__thiscall");
3956 } else if (abi
== ABI_WINAPI
) {
3957 PrependString(cx
, result
, "WINAPI");
3960 // Function application binds more tightly than dereferencing, so
3961 // wrap pointer types in parens. Functions can't return functions
3962 // (only pointers to them), and arrays can't hold functions
3963 // (similarly), so we don't need to address those cases.
3964 if (prevGrouping
== TYPE_pointer
) {
3965 PrependString(cx
, result
, "(");
3966 AppendString(cx
, result
, ")");
3969 // Argument list goes on the right.
3970 AppendString(cx
, result
, "(");
3971 for (size_t i
= 0; i
< fninfo
->mArgTypes
.length(); ++i
) {
3972 RootedObject
argType(cx
, fninfo
->mArgTypes
[i
]);
3973 JSString
* argName
= CType::GetName(cx
, argType
);
3974 AppendString(cx
, result
, argName
);
3975 if (i
!= fninfo
->mArgTypes
.length() - 1 || fninfo
->mIsVariadic
)
3976 AppendString(cx
, result
, ", ");
3978 if (fninfo
->mIsVariadic
) {
3979 AppendString(cx
, result
, "...");
3981 AppendString(cx
, result
, ")");
3983 // Set 'typeObj' to the return type, and let the loop process it.
3984 // 'prevGrouping' doesn't matter here, because functions cannot return
3985 // arrays -- thus the parenthetical rules don't get tickled.
3986 typeObj
= fninfo
->mReturnType
;
3990 // Either a basic or struct type. Use the type's name as the base type.
3996 // If prepending the base type name directly would splice two
3997 // identifiers, insert a space.
3998 if (IsAsciiAlpha(result
[0]) || result
[0] == '_') {
3999 PrependString(cx
, result
, " ");
4002 // Stick the base type and derived type parts together.
4003 JSString
* baseName
= CType::GetName(cx
, typeObj
);
4004 PrependString(cx
, result
, baseName
);
4008 return NewUCString(cx
, result
.finish());
4011 // Given a CType 'typeObj', generate a string 'result' such that 'eval(result)'
4012 // would construct the same CType. If 'makeShort' is true, assume that any
4013 // StructType 't' is bound to an in-scope variable of name 't.name', and use
4014 // that variable in place of generating a string to construct the type 't'.
4015 // (This means the type comparison function CType::TypesEqual will return true
4016 // when comparing the input and output of AppendTypeSource, since struct
4017 // equality is determined by strict JSObject pointer equality.)
4018 static void BuildTypeSource(JSContext
* cx
, JSObject
* typeObj_
, bool makeShort
,
4019 AutoString
& result
) {
4020 RootedObject
typeObj(cx
, typeObj_
);
4022 // Walk the types, building up the toSource() string.
4023 switch (CType::GetTypeCode(typeObj
)) {
4025 #define CASE_FOR_TYPE(name, type, ffiType) case TYPE_##name:
4026 CTYPES_FOR_EACH_TYPE(CASE_FOR_TYPE
)
4027 #undef CASE_FOR_TYPE
4029 AppendString(cx
, result
, "ctypes.");
4030 JSString
* nameStr
= CType::GetName(cx
, typeObj
);
4031 AppendString(cx
, result
, nameStr
);
4034 case TYPE_pointer
: {
4035 RootedObject
baseType(cx
, PointerType::GetBaseType(typeObj
));
4037 // Specialcase ctypes.voidptr_t.
4038 if (CType::GetTypeCode(baseType
) == TYPE_void_t
) {
4039 AppendString(cx
, result
, "ctypes.voidptr_t");
4043 // Recursively build the source string, and append '.ptr'.
4044 BuildTypeSource(cx
, baseType
, makeShort
, result
);
4045 AppendString(cx
, result
, ".ptr");
4048 case TYPE_function
: {
4049 FunctionInfo
* fninfo
= FunctionType::GetFunctionInfo(typeObj
);
4051 AppendString(cx
, result
, "ctypes.FunctionType(");
4053 switch (GetABICode(fninfo
->mABI
)) {
4055 AppendString(cx
, result
, "ctypes.default_abi, ");
4058 AppendString(cx
, result
, "ctypes.stdcall_abi, ");
4061 AppendString(cx
, result
, "ctypes.thiscall_abi, ");
4064 AppendString(cx
, result
, "ctypes.winapi_abi, ");
4067 MOZ_CRASH("invalid abi");
4070 // Recursively build the source string describing the function return and
4072 BuildTypeSource(cx
, fninfo
->mReturnType
, true, result
);
4074 if (fninfo
->mArgTypes
.length() > 0) {
4075 AppendString(cx
, result
, ", [");
4076 for (size_t i
= 0; i
< fninfo
->mArgTypes
.length(); ++i
) {
4077 BuildTypeSource(cx
, fninfo
->mArgTypes
[i
], true, result
);
4078 if (i
!= fninfo
->mArgTypes
.length() - 1 || fninfo
->mIsVariadic
)
4079 AppendString(cx
, result
, ", ");
4081 if (fninfo
->mIsVariadic
) {
4082 AppendString(cx
, result
, "\"...\"");
4084 AppendString(cx
, result
, "]");
4087 AppendString(cx
, result
, ")");
4091 // Recursively build the source string, and append '.array(n)',
4092 // where n is the array length, or the empty string if the array length
4094 JSObject
* baseType
= ArrayType::GetBaseType(typeObj
);
4095 BuildTypeSource(cx
, baseType
, makeShort
, result
);
4096 AppendString(cx
, result
, ".array(");
4099 if (ArrayType::GetSafeLength(typeObj
, &length
)) {
4100 IntegerToString(length
, 10, result
);
4103 AppendString(cx
, result
, ")");
4107 JSString
* name
= CType::GetName(cx
, typeObj
);
4110 // Shorten the type declaration by assuming that StructType 't' is bound
4111 // to an in-scope variable of name 't.name'.
4112 AppendString(cx
, result
, name
);
4116 // Write the full struct declaration.
4117 AppendString(cx
, result
, "ctypes.StructType(\"");
4118 AppendString(cx
, result
, name
);
4119 AppendString(cx
, result
, "\"");
4121 // If it's an opaque struct, we're done.
4122 if (!CType::IsSizeDefined(typeObj
)) {
4123 AppendString(cx
, result
, ")");
4127 AppendString(cx
, result
, ", [");
4129 const FieldInfoHash
* fields
= StructType::GetFieldInfo(typeObj
);
4130 size_t length
= fields
->count();
4131 Vector
<const FieldInfoHash::Entry
*, 64, SystemAllocPolicy
> fieldsArray
;
4132 if (!fieldsArray
.resize(length
)) {
4136 for (FieldInfoHash::Range r
= fields
->all(); !r
.empty(); r
.popFront()) {
4137 fieldsArray
[r
.front().value().mIndex
] = &r
.front();
4140 for (size_t i
= 0; i
< length
; ++i
) {
4141 const FieldInfoHash::Entry
* entry
= fieldsArray
[i
];
4142 AppendString(cx
, result
, "{ \"");
4143 AppendString(cx
, result
, entry
->key());
4144 AppendString(cx
, result
, "\": ");
4145 BuildTypeSource(cx
, entry
->value().mType
, true, result
);
4146 AppendString(cx
, result
, " }");
4147 if (i
!= length
- 1) {
4148 AppendString(cx
, result
, ", ");
4152 AppendString(cx
, result
, "])");
4158 // Given a CData object of CType 'typeObj' with binary value 'data', generate a
4159 // string 'result' such that 'eval(result)' would construct a CData object with
4160 // the same CType and containing the same binary value. This assumes that any
4161 // StructType 't' is bound to an in-scope variable of name 't.name'. (This means
4162 // the type comparison function CType::TypesEqual will return true when
4163 // comparing the types, since struct equality is determined by strict JSObject
4164 // pointer equality.) Further, if 'isImplicit' is true, ensure that the
4165 // resulting string can ImplicitConvert successfully if passed to another data
4166 // constructor. (This is important when called recursively, since fields of
4167 // structs and arrays are converted with ImplicitConvert.)
4168 [[nodiscard
]] static bool BuildDataSource(JSContext
* cx
, HandleObject typeObj
,
4169 void* data
, bool isImplicit
,
4170 AutoString
& result
) {
4171 TypeCode type
= CType::GetTypeCode(typeObj
);
4174 if (*static_cast<bool*>(data
)) {
4175 AppendString(cx
, result
, "true");
4177 AppendString(cx
, result
, "false");
4180 #define INTEGRAL_CASE(name, type, ffiType) \
4182 /* Serialize as a primitive decimal integer. */ \
4183 IntegerToString(*static_cast<type*>(data), 10, result); \
4185 CTYPES_FOR_EACH_INT_TYPE(INTEGRAL_CASE
)
4186 #undef INTEGRAL_CASE
4187 #define WRAPPED_INT_CASE(name, type, ffiType) \
4189 /* Serialize as a wrapped decimal integer. */ \
4190 if (!numeric_limits<type>::is_signed) \
4191 AppendString(cx, result, "ctypes.UInt64(\""); \
4193 AppendString(cx, result, "ctypes.Int64(\""); \
4195 IntegerToString(*static_cast<type*>(data), 10, result); \
4196 AppendString(cx, result, "\")"); \
4198 CTYPES_FOR_EACH_WRAPPED_INT_TYPE(WRAPPED_INT_CASE
)
4199 #undef WRAPPED_INT_CASE
4200 #define FLOAT_CASE(name, type, ffiType) \
4201 case TYPE_##name: { \
4202 /* Serialize as a primitive double. */ \
4203 double fp = *static_cast<type*>(data); \
4204 ToCStringBuf cbuf; \
4206 char* str = NumberToCString(&cbuf, fp, &strLength); \
4208 if (!result.append(str, strLength)) { \
4209 JS_ReportOutOfMemory(cx); \
4214 CTYPES_FOR_EACH_FLOAT_TYPE(FLOAT_CASE
)
4216 #define CHAR_CASE(name, type, ffiType) \
4218 /* Serialize as an integer. */ \
4219 IntegerToString(*static_cast<type*>(data), 10, result); \
4221 CTYPES_FOR_EACH_CHAR_TYPE(CHAR_CASE
)
4223 case TYPE_char16_t
: {
4224 // Serialize as a 1-character JS string.
4225 JSString
* str
= JS_NewUCStringCopyN(cx
, static_cast<char16_t
*>(data
), 1);
4230 // Escape characters, and quote as necessary.
4231 RootedValue
valStr(cx
, StringValue(str
));
4232 JSString
* src
= JS_ValueToSource(cx
, valStr
);
4237 AppendString(cx
, result
, src
);
4241 case TYPE_function
: {
4243 // The result must be able to ImplicitConvert successfully.
4244 // Wrap in a type constructor, then serialize for ExplicitConvert.
4245 BuildTypeSource(cx
, typeObj
, true, result
);
4246 AppendString(cx
, result
, "(");
4249 // Serialize the pointer value as a wrapped hexadecimal integer.
4250 uintptr_t ptr
= *static_cast<uintptr_t*>(data
);
4251 AppendString(cx
, result
, "ctypes.UInt64(\"0x");
4252 IntegerToString(ptr
, 16, result
);
4253 AppendString(cx
, result
, "\")");
4256 AppendString(cx
, result
, ")");
4262 // Serialize each element of the array recursively. Each element must
4263 // be able to ImplicitConvert successfully.
4264 RootedObject
baseType(cx
, ArrayType::GetBaseType(typeObj
));
4265 AppendString(cx
, result
, "[");
4267 size_t length
= ArrayType::GetLength(typeObj
);
4268 size_t elementSize
= CType::GetSize(baseType
);
4269 for (size_t i
= 0; i
< length
; ++i
) {
4270 char* element
= static_cast<char*>(data
) + elementSize
* i
;
4271 if (!BuildDataSource(cx
, baseType
, element
, true, result
)) {
4275 if (i
+ 1 < length
) {
4276 AppendString(cx
, result
, ", ");
4279 AppendString(cx
, result
, "]");
4284 // The result must be able to ImplicitConvert successfully.
4285 // Serialize the data as an object with properties, rather than
4286 // a sequence of arguments to the StructType constructor.
4287 AppendString(cx
, result
, "{");
4290 // Serialize each field of the struct recursively. Each field must
4291 // be able to ImplicitConvert successfully.
4292 const FieldInfoHash
* fields
= StructType::GetFieldInfo(typeObj
);
4293 size_t length
= fields
->count();
4294 Vector
<const FieldInfoHash::Entry
*, 64, SystemAllocPolicy
> fieldsArray
;
4295 if (!fieldsArray
.resize(length
)) {
4299 for (FieldInfoHash::Range r
= fields
->all(); !r
.empty(); r
.popFront()) {
4300 fieldsArray
[r
.front().value().mIndex
] = &r
.front();
4303 for (size_t i
= 0; i
< length
; ++i
) {
4304 const FieldInfoHash::Entry
* entry
= fieldsArray
[i
];
4307 AppendString(cx
, result
, "\"");
4308 AppendString(cx
, result
, entry
->key());
4309 AppendString(cx
, result
, "\": ");
4312 char* fieldData
= static_cast<char*>(data
) + entry
->value().mOffset
;
4313 RootedObject
entryType(cx
, entry
->value().mType
);
4314 if (!BuildDataSource(cx
, entryType
, fieldData
, true, result
)) {
4318 if (i
+ 1 != length
) {
4319 AppendString(cx
, result
, ", ");
4324 AppendString(cx
, result
, "}");
4330 MOZ_CRASH("invalid type");
4336 /*******************************************************************************
4337 ** JSAPI callback function implementations
4338 *******************************************************************************/
4340 bool ConstructAbstract(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4341 // Calling an abstract base class constructor is disallowed.
4342 return CannotConstructError(cx
, "abstract type");
4345 /*******************************************************************************
4346 ** CType implementation
4347 *******************************************************************************/
4349 bool CType::ConstructData(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4350 CallArgs args
= CallArgsFromVp(argc
, vp
);
4351 // get the callee object...
4352 RootedObject
obj(cx
, &args
.callee());
4353 if (!CType::IsCType(obj
)) {
4354 return IncompatibleCallee(cx
, "CType constructor", obj
);
4357 // How we construct the CData object depends on what type we represent.
4358 // An instance 'd' of a CData object of type 't' has:
4359 // * [[Class]] "CData"
4360 // * __proto__ === t.prototype
4361 switch (GetTypeCode(obj
)) {
4363 return CannotConstructError(cx
, "void_t");
4365 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
4366 CTYPESMSG_FUNCTION_CONSTRUCT
);
4369 return PointerType::ConstructData(cx
, obj
, args
);
4371 return ArrayType::ConstructData(cx
, obj
, args
);
4373 return StructType::ConstructData(cx
, obj
, args
);
4375 return ConstructBasic(cx
, obj
, args
);
4379 bool CType::ConstructBasic(JSContext
* cx
, HandleObject obj
,
4380 const CallArgs
& args
) {
4381 if (args
.length() > 1) {
4382 return ArgumentLengthError(cx
, "CType constructor", "at most one", "");
4385 // construct a CData object
4386 RootedObject
result(cx
, CData::Create(cx
, obj
, nullptr, nullptr, true));
4391 if (args
.length() == 1) {
4392 if (!ExplicitConvert(cx
, args
[0], obj
, CData::GetData(result
),
4393 ConversionType::Construct
))
4397 args
.rval().setObject(*result
);
4401 JSObject
* CType::Create(JSContext
* cx
, HandleObject typeProto
,
4402 HandleObject dataProto
, TypeCode type
, JSString
* name_
,
4403 HandleValue size
, HandleValue align
,
4404 ffi_type
* ffiType
) {
4405 RootedString
name(cx
, name_
);
4407 // Create a CType object with the properties and slots common to all CTypes.
4408 // Each type object 't' has:
4409 // * [[Class]] "CType"
4410 // * __proto__ === 'typeProto'; one of ctypes.{CType,PointerType,ArrayType,
4411 // StructType}.prototype
4412 // * A constructor which creates and returns a CData object, containing
4413 // binary data of the given type.
4414 // * 'prototype' property:
4415 // * [[Class]] "CDataProto"
4416 // * __proto__ === 'dataProto'; an object containing properties and
4417 // functions common to all CData objects of types derived from
4418 // 'typeProto'. (For instance, this could be ctypes.CData.prototype
4419 // for simple types, or something representing structs for StructTypes.)
4420 // * 'constructor' property === 't'
4421 // * Additional properties specified by 'ps', as appropriate for the
4422 // specific type instance 't'.
4423 RootedObject
typeObj(cx
,
4424 JS_NewObjectWithGivenProto(cx
, &sCTypeClass
, typeProto
));
4429 // Set up the reserved slots.
4430 JS_SetReservedSlot(typeObj
, SLOT_TYPECODE
, Int32Value(type
));
4432 JS_SetReservedSlot(typeObj
, SLOT_FFITYPE
, PrivateValue(ffiType
));
4433 if (type
== TYPE_struct
|| type
== TYPE_array
) {
4434 AddCellMemory(typeObj
, sizeof(ffi_type
), MemoryUse::CTypeFFIType
);
4438 JS_SetReservedSlot(typeObj
, SLOT_NAME
, StringValue(name
));
4440 JS_SetReservedSlot(typeObj
, SLOT_SIZE
, size
);
4441 JS_SetReservedSlot(typeObj
, SLOT_ALIGN
, align
);
4444 // Set up the 'prototype' and 'prototype.constructor' properties.
4445 RootedObject
prototype(
4446 cx
, JS_NewObjectWithGivenProto(cx
, &sCDataProtoClass
, dataProto
));
4451 if (!JS_DefineProperty(cx
, prototype
, "constructor", typeObj
,
4452 JSPROP_READONLY
| JSPROP_PERMANENT
))
4455 // Set the 'prototype' object.
4456 // if (!JS_FreezeObject(cx, prototype)) // XXX fixme - see bug 541212!
4458 JS_SetReservedSlot(typeObj
, SLOT_PROTO
, ObjectValue(*prototype
));
4461 if (!JS_FreezeObject(cx
, typeObj
)) {
4465 // Assert a sanity check on size and alignment: size % alignment should always
4467 MOZ_ASSERT_IF(IsSizeDefined(typeObj
),
4468 GetSize(typeObj
) % GetAlignment(typeObj
) == 0);
4473 JSObject
* CType::DefineBuiltin(JSContext
* cx
, HandleObject ctypesObj
,
4474 const char* propName
, JSObject
* typeProto_
,
4475 JSObject
* dataProto_
, const char* name
,
4476 TypeCode type
, HandleValue size
,
4477 HandleValue align
, ffi_type
* ffiType
) {
4478 RootedObject
typeProto(cx
, typeProto_
);
4479 RootedObject
dataProto(cx
, dataProto_
);
4481 RootedString
nameStr(cx
, JS_NewStringCopyZ(cx
, name
));
4486 // Create a new CType object with the common properties and slots.
4487 RootedObject
typeObj(cx
, Create(cx
, typeProto
, dataProto
, type
, nameStr
, size
,
4493 // Define the CType as a 'propName' property on 'ctypesObj'.
4494 if (!JS_DefineProperty(cx
, ctypesObj
, propName
, typeObj
,
4495 JSPROP_ENUMERATE
| JSPROP_READONLY
| JSPROP_PERMANENT
))
4501 static void FinalizeFFIType(JS::GCContext
* gcx
, JSObject
* obj
,
4502 const Value
& slot
, size_t elementCount
) {
4503 ffi_type
* ffiType
= static_cast<ffi_type
*>(slot
.toPrivate());
4504 size_t size
= elementCount
* sizeof(ffi_type
*);
4505 gcx
->free_(obj
, ffiType
->elements
, size
, MemoryUse::CTypeFFITypeElements
);
4506 gcx
->delete_(obj
, ffiType
, MemoryUse::CTypeFFIType
);
4509 void CType::Finalize(JS::GCContext
* gcx
, JSObject
* obj
) {
4510 // Make sure our TypeCode slot is legit. If it's not, bail.
4511 Value slot
= JS::GetReservedSlot(obj
, SLOT_TYPECODE
);
4512 if (slot
.isUndefined()) {
4516 // The contents of our slots depends on what kind of type we are.
4517 switch (TypeCode(slot
.toInt32())) {
4518 case TYPE_function
: {
4519 // Free the FunctionInfo.
4520 slot
= JS::GetReservedSlot(obj
, SLOT_FNINFO
);
4521 if (!slot
.isUndefined()) {
4522 auto fninfo
= static_cast<FunctionInfo
*>(slot
.toPrivate());
4523 gcx
->delete_(obj
, fninfo
, MemoryUse::CTypeFunctionInfo
);
4529 size_t fieldCount
= 0;
4531 // Free the FieldInfoHash table.
4532 slot
= JS::GetReservedSlot(obj
, SLOT_FIELDINFO
);
4533 if (!slot
.isUndefined()) {
4534 auto info
= static_cast<FieldInfoHash
*>(slot
.toPrivate());
4535 fieldCount
= info
->count();
4536 gcx
->delete_(obj
, info
, MemoryUse::CTypeFieldInfo
);
4539 // Free the ffi_type info.
4540 Value slot
= JS::GetReservedSlot(obj
, SLOT_FFITYPE
);
4541 if (!slot
.isUndefined()) {
4542 size_t elementCount
= fieldCount
!= 0 ? fieldCount
+ 1 : 2;
4543 FinalizeFFIType(gcx
, obj
, slot
, elementCount
);
4546 // Free the ffi_type info.
4551 // Free the ffi_type info.
4552 Value slot
= JS::GetReservedSlot(obj
, SLOT_FFITYPE
);
4553 if (!slot
.isUndefined()) {
4554 size_t elementCount
= ArrayType::GetLength(obj
);
4555 FinalizeFFIType(gcx
, obj
, slot
, elementCount
);
4561 // Nothing to do here.
4566 void CType::Trace(JSTracer
* trc
, JSObject
* obj
) {
4567 // Make sure our TypeCode slot is legit. If it's not, bail.
4568 Value slot
= obj
->as
<NativeObject
>().getReservedSlot(SLOT_TYPECODE
);
4569 if (slot
.isUndefined()) {
4573 // The contents of our slots depends on what kind of type we are.
4574 switch (TypeCode(slot
.toInt32())) {
4576 slot
= obj
->as
<NativeObject
>().getReservedSlot(SLOT_FIELDINFO
);
4577 if (slot
.isUndefined()) {
4581 FieldInfoHash
* fields
= static_cast<FieldInfoHash
*>(slot
.toPrivate());
4585 case TYPE_function
: {
4586 // Check if we have a FunctionInfo.
4587 slot
= obj
->as
<NativeObject
>().getReservedSlot(SLOT_FNINFO
);
4588 if (slot
.isUndefined()) {
4592 FunctionInfo
* fninfo
= static_cast<FunctionInfo
*>(slot
.toPrivate());
4595 // Identify our objects to the tracer.
4596 TraceEdge(trc
, &fninfo
->mABI
, "abi");
4597 TraceEdge(trc
, &fninfo
->mReturnType
, "returnType");
4598 fninfo
->mArgTypes
.trace(trc
);
4603 // Nothing to do here.
4608 bool CType::IsCType(JSObject
* obj
) { return obj
->hasClass(&sCTypeClass
); }
4610 bool CType::IsCTypeProto(JSObject
* obj
) {
4611 return obj
->hasClass(&sCTypeProtoClass
);
4614 TypeCode
CType::GetTypeCode(JSObject
* typeObj
) {
4615 MOZ_ASSERT(IsCType(typeObj
));
4617 Value result
= JS::GetReservedSlot(typeObj
, SLOT_TYPECODE
);
4618 return TypeCode(result
.toInt32());
4621 bool CType::TypesEqual(JSObject
* t1
, JSObject
* t2
) {
4622 MOZ_ASSERT(IsCType(t1
) && IsCType(t2
));
4624 // Fast path: check for object equality.
4629 // First, perform shallow comparison.
4630 TypeCode c1
= GetTypeCode(t1
);
4631 TypeCode c2
= GetTypeCode(t2
);
4636 // Determine whether the types require shallow or deep comparison.
4638 case TYPE_pointer
: {
4639 // Compare base types.
4640 JSObject
* b1
= PointerType::GetBaseType(t1
);
4641 JSObject
* b2
= PointerType::GetBaseType(t2
);
4642 return TypesEqual(b1
, b2
);
4644 case TYPE_function
: {
4645 FunctionInfo
* f1
= FunctionType::GetFunctionInfo(t1
);
4646 FunctionInfo
* f2
= FunctionType::GetFunctionInfo(t2
);
4648 // Compare abi, return type, and argument types.
4649 if (f1
->mABI
!= f2
->mABI
) {
4653 if (!TypesEqual(f1
->mReturnType
, f2
->mReturnType
)) {
4657 if (f1
->mArgTypes
.length() != f2
->mArgTypes
.length()) {
4661 if (f1
->mIsVariadic
!= f2
->mIsVariadic
) {
4665 for (size_t i
= 0; i
< f1
->mArgTypes
.length(); ++i
) {
4666 if (!TypesEqual(f1
->mArgTypes
[i
], f2
->mArgTypes
[i
])) {
4674 // Compare length, then base types.
4675 // An undefined length array matches other undefined length arrays.
4676 size_t s1
= 0, s2
= 0;
4677 bool d1
= ArrayType::GetSafeLength(t1
, &s1
);
4678 bool d2
= ArrayType::GetSafeLength(t2
, &s2
);
4679 if (d1
!= d2
|| (d1
&& s1
!= s2
)) {
4683 JSObject
* b1
= ArrayType::GetBaseType(t1
);
4684 JSObject
* b2
= ArrayType::GetBaseType(t2
);
4685 return TypesEqual(b1
, b2
);
4688 // Require exact type object equality.
4691 // Shallow comparison is sufficient.
4696 bool CType::GetSafeSize(JSObject
* obj
, size_t* result
) {
4697 MOZ_ASSERT(CType::IsCType(obj
));
4699 Value size
= JS::GetReservedSlot(obj
, SLOT_SIZE
);
4701 // The "size" property can be an int, a double, or JS::UndefinedValue()
4702 // (for arrays of undefined length), and must always fit in a size_t.
4703 if (size
.isInt32()) {
4704 *result
= size
.toInt32();
4707 if (size
.isDouble()) {
4708 *result
= Convert
<size_t>(size
.toDouble());
4712 MOZ_ASSERT(size
.isUndefined());
4716 size_t CType::GetSize(JSObject
* obj
) {
4717 MOZ_ASSERT(CType::IsCType(obj
));
4719 Value size
= JS::GetReservedSlot(obj
, SLOT_SIZE
);
4721 MOZ_ASSERT(!size
.isUndefined());
4723 // The "size" property can be an int, a double, or JS::UndefinedValue()
4724 // (for arrays of undefined length), and must always fit in a size_t.
4725 // For callers who know it can never be JS::UndefinedValue(), return a size_t
4727 if (size
.isInt32()) {
4728 return size
.toInt32();
4730 return Convert
<size_t>(size
.toDouble());
4733 bool CType::IsSizeDefined(JSObject
* obj
) {
4734 MOZ_ASSERT(CType::IsCType(obj
));
4736 Value size
= JS::GetReservedSlot(obj
, SLOT_SIZE
);
4738 // The "size" property can be an int, a double, or JS::UndefinedValue()
4739 // (for arrays of undefined length), and must always fit in a size_t.
4740 MOZ_ASSERT(size
.isInt32() || size
.isDouble() || size
.isUndefined());
4741 return !size
.isUndefined();
4744 size_t CType::GetAlignment(JSObject
* obj
) {
4745 MOZ_ASSERT(CType::IsCType(obj
));
4747 Value slot
= JS::GetReservedSlot(obj
, SLOT_ALIGN
);
4748 return static_cast<size_t>(slot
.toInt32());
4751 ffi_type
* CType::GetFFIType(JSContext
* cx
, JSObject
* obj
) {
4752 MOZ_ASSERT(CType::IsCType(obj
));
4754 Value slot
= JS::GetReservedSlot(obj
, SLOT_FFITYPE
);
4756 if (!slot
.isUndefined()) {
4757 return static_cast<ffi_type
*>(slot
.toPrivate());
4760 UniquePtrFFIType result
;
4761 switch (CType::GetTypeCode(obj
)) {
4763 result
= ArrayType::BuildFFIType(cx
, obj
);
4767 result
= StructType::BuildFFIType(cx
, obj
);
4771 MOZ_CRASH("simple types must have an ffi_type");
4777 JS_InitReservedSlot(obj
, SLOT_FFITYPE
, result
.get(),
4778 JS::MemoryUse::CTypeFFIType
);
4779 return result
.release();
4782 JSString
* CType::GetName(JSContext
* cx
, HandleObject obj
) {
4783 MOZ_ASSERT(CType::IsCType(obj
));
4785 Value string
= JS::GetReservedSlot(obj
, SLOT_NAME
);
4786 if (!string
.isUndefined()) {
4787 return string
.toString();
4790 // Build the type name lazily.
4791 JSString
* name
= BuildTypeName(cx
, obj
);
4795 JS_SetReservedSlot(obj
, SLOT_NAME
, StringValue(name
));
4799 JSObject
* CType::GetProtoFromCtor(JSObject
* obj
, CTypeProtoSlot slot
) {
4800 // Get ctypes.{Pointer,Array,Struct}Type.prototype from a reserved slot
4801 // on the type constructor.
4802 Value protoslot
= js::GetFunctionNativeReserved(obj
, SLOT_FN_CTORPROTO
);
4803 JSObject
* proto
= &protoslot
.toObject();
4805 MOZ_ASSERT(CType::IsCTypeProto(proto
));
4807 // Get the desired prototype.
4808 Value result
= JS::GetReservedSlot(proto
, slot
);
4809 return &result
.toObject();
4812 JSObject
* CType::GetProtoFromType(JSContext
* cx
, JSObject
* objArg
,
4813 CTypeProtoSlot slot
) {
4814 MOZ_ASSERT(IsCType(objArg
));
4815 RootedObject
obj(cx
, objArg
);
4817 // Get the prototype of the type object.
4818 RootedObject
proto(cx
);
4819 if (!JS_GetPrototype(cx
, obj
, &proto
)) {
4823 MOZ_ASSERT(CType::IsCTypeProto(proto
));
4825 // Get the requested ctypes.{Pointer,Array,Struct,Function}Type.prototype.
4826 Value result
= JS::GetReservedSlot(proto
, slot
);
4827 MOZ_ASSERT(result
.isObject());
4828 return &result
.toObject();
4831 bool CType::IsCTypeOrProto(HandleValue v
) {
4832 if (!v
.isObject()) {
4835 JSObject
* obj
= &v
.toObject();
4836 return CType::IsCType(obj
) || CType::IsCTypeProto(obj
);
4839 bool CType::PrototypeGetter(JSContext
* cx
, const JS::CallArgs
& args
) {
4840 RootedObject
obj(cx
, &args
.thisv().toObject());
4841 unsigned slot
= CType::IsCTypeProto(obj
) ? (unsigned)SLOT_OURDATAPROTO
4842 : (unsigned)SLOT_PROTO
;
4843 args
.rval().set(JS::GetReservedSlot(obj
, slot
));
4844 MOZ_ASSERT(args
.rval().isObject() || args
.rval().isUndefined());
4848 bool CType::IsCType(HandleValue v
) {
4849 return v
.isObject() && CType::IsCType(&v
.toObject());
4852 bool CType::NameGetter(JSContext
* cx
, const JS::CallArgs
& args
) {
4853 RootedObject
obj(cx
, &args
.thisv().toObject());
4854 JSString
* name
= CType::GetName(cx
, obj
);
4859 args
.rval().setString(name
);
4863 bool CType::SizeGetter(JSContext
* cx
, const JS::CallArgs
& args
) {
4864 RootedObject
obj(cx
, &args
.thisv().toObject());
4865 args
.rval().set(JS::GetReservedSlot(obj
, SLOT_SIZE
));
4866 MOZ_ASSERT(args
.rval().isNumber() || args
.rval().isUndefined());
4870 bool CType::PtrGetter(JSContext
* cx
, const JS::CallArgs
& args
) {
4871 RootedObject
obj(cx
, &args
.thisv().toObject());
4872 JSObject
* pointerType
= PointerType::CreateInternal(cx
, obj
);
4877 args
.rval().setObject(*pointerType
);
4881 bool CType::CreateArray(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4882 CallArgs args
= CallArgsFromVp(argc
, vp
);
4883 RootedObject
baseType(cx
, GetThisObject(cx
, args
, "CType.prototype.array"));
4887 if (!CType::IsCType(baseType
)) {
4888 return IncompatibleThisProto(cx
, "CType.prototype.array", args
.thisv());
4891 // Construct and return a new ArrayType object.
4892 if (args
.length() > 1) {
4893 return ArgumentLengthError(cx
, "CType.prototype.array", "at most one", "");
4896 // Convert the length argument to a size_t.
4898 if (args
.length() == 1 && !jsvalToSize(cx
, args
[0], false, &length
)) {
4899 return ArgumentTypeMismatch(cx
, "", "CType.prototype.array",
4900 "a nonnegative integer");
4904 ArrayType::CreateInternal(cx
, baseType
, length
, args
.length() == 1);
4909 args
.rval().setObject(*result
);
4913 bool CType::ToString(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4914 CallArgs args
= CallArgsFromVp(argc
, vp
);
4915 RootedObject
obj(cx
, GetThisObject(cx
, args
, "CType.prototype.toString"));
4919 if (!CType::IsCType(obj
) && !CType::IsCTypeProto(obj
)) {
4920 return IncompatibleThisProto(cx
, "CType.prototype.toString",
4921 InformalValueTypeName(args
.thisv()));
4924 // Create the appropriate string depending on whether we're sCTypeClass or
4925 // sCTypeProtoClass.
4927 if (CType::IsCType(obj
)) {
4929 AppendString(cx
, type
, "type ");
4930 AppendString(cx
, type
, GetName(cx
, obj
));
4934 result
= NewUCString(cx
, type
.finish());
4936 result
= JS_NewStringCopyZ(cx
, "[CType proto object]");
4942 args
.rval().setString(result
);
4946 bool CType::ToSource(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4947 CallArgs args
= CallArgsFromVp(argc
, vp
);
4948 JSObject
* obj
= GetThisObject(cx
, args
, "CType.prototype.toSource");
4952 if (!CType::IsCType(obj
) && !CType::IsCTypeProto(obj
)) {
4953 return IncompatibleThisProto(cx
, "CType.prototype.toSource",
4954 InformalValueTypeName(args
.thisv()));
4957 // Create the appropriate string depending on whether we're sCTypeClass or
4958 // sCTypeProtoClass.
4960 if (CType::IsCType(obj
)) {
4962 BuildTypeSource(cx
, obj
, false, source
);
4966 result
= NewUCString(cx
, source
.finish());
4968 result
= JS_NewStringCopyZ(cx
, "[CType proto object]");
4974 args
.rval().setString(result
);
4978 static JSObject
* CType::GetGlobalCTypes(JSContext
* cx
, JSObject
* objArg
) {
4979 MOZ_ASSERT(CType::IsCType(objArg
));
4981 RootedObject
obj(cx
, objArg
);
4982 RootedObject
objTypeProto(cx
);
4983 if (!JS_GetPrototype(cx
, obj
, &objTypeProto
)) {
4986 MOZ_ASSERT(objTypeProto
);
4987 MOZ_ASSERT(CType::IsCTypeProto(objTypeProto
));
4989 Value valCTypes
= JS::GetReservedSlot(objTypeProto
, SLOT_CTYPES
);
4990 MOZ_ASSERT(valCTypes
.isObject());
4991 return &valCTypes
.toObject();
4994 /*******************************************************************************
4995 ** ABI implementation
4996 *******************************************************************************/
4998 bool ABI::IsABI(JSObject
* obj
) { return obj
->hasClass(&sCABIClass
); }
5000 bool ABI::ToSource(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5001 CallArgs args
= CallArgsFromVp(argc
, vp
);
5002 if (args
.length() != 0) {
5003 return ArgumentLengthError(cx
, "ABI.prototype.toSource", "no", "s");
5006 JSObject
* obj
= GetThisObject(cx
, args
, "ABI.prototype.toSource");
5010 if (!ABI::IsABI(obj
)) {
5011 return IncompatibleThisProto(cx
, "ABI.prototype.toSource",
5012 InformalValueTypeName(args
.thisv()));
5016 switch (GetABICode(obj
)) {
5018 result
= JS_NewStringCopyZ(cx
, "ctypes.default_abi");
5021 result
= JS_NewStringCopyZ(cx
, "ctypes.stdcall_abi");
5024 result
= JS_NewStringCopyZ(cx
, "ctypes.thiscall_abi");
5027 result
= JS_NewStringCopyZ(cx
, "ctypes.winapi_abi");
5030 JS_ReportErrorASCII(cx
, "not a valid ABICode");
5037 args
.rval().setString(result
);
5041 /*******************************************************************************
5042 ** PointerType implementation
5043 *******************************************************************************/
5045 bool PointerType::Create(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5046 CallArgs args
= CallArgsFromVp(argc
, vp
);
5047 // Construct and return a new PointerType object.
5048 if (args
.length() != 1) {
5049 return ArgumentLengthError(cx
, "PointerType", "one", "");
5052 Value arg
= args
[0];
5053 RootedObject
obj(cx
);
5054 if (arg
.isPrimitive() || !CType::IsCType(obj
= &arg
.toObject())) {
5055 return ArgumentTypeMismatch(cx
, "", "PointerType", "a CType");
5058 JSObject
* result
= CreateInternal(cx
, obj
);
5063 args
.rval().setObject(*result
);
5067 JSObject
* PointerType::CreateInternal(JSContext
* cx
, HandleObject baseType
) {
5068 // check if we have a cached PointerType on our base CType.
5069 Value slot
= JS::GetReservedSlot(baseType
, SLOT_PTR
);
5070 if (!slot
.isUndefined()) {
5071 return &slot
.toObject();
5074 // Get ctypes.PointerType.prototype and the common prototype for CData objects
5075 // of this type, or ctypes.FunctionType.prototype for function pointers.
5076 CTypeProtoSlot slotId
= CType::GetTypeCode(baseType
) == TYPE_function
5077 ? SLOT_FUNCTIONDATAPROTO
5078 : SLOT_POINTERDATAPROTO
;
5079 RootedObject
dataProto(cx
, CType::GetProtoFromType(cx
, baseType
, slotId
));
5083 RootedObject
typeProto(
5084 cx
, CType::GetProtoFromType(cx
, baseType
, SLOT_POINTERPROTO
));
5089 // Create a new CType object with the common properties and slots.
5090 RootedValue
sizeVal(cx
, Int32Value(sizeof(void*)));
5091 RootedValue
alignVal(cx
, Int32Value(ffi_type_pointer
.alignment
));
5093 CType::Create(cx
, typeProto
, dataProto
, TYPE_pointer
, nullptr, sizeVal
,
5094 alignVal
, &ffi_type_pointer
);
5099 // Set the target type. (This will be 'null' for an opaque pointer type.)
5100 JS_SetReservedSlot(typeObj
, SLOT_TARGET_T
, ObjectValue(*baseType
));
5102 // Finally, cache our newly-created PointerType on our pointed-to CType.
5103 JS_SetReservedSlot(baseType
, SLOT_PTR
, ObjectValue(*typeObj
));
5108 bool PointerType::ConstructData(JSContext
* cx
, HandleObject obj
,
5109 const CallArgs
& args
) {
5110 if (!CType::IsCType(obj
) || CType::GetTypeCode(obj
) != TYPE_pointer
) {
5111 return IncompatibleCallee(cx
, "PointerType constructor", obj
);
5114 if (args
.length() > 3) {
5115 return ArgumentLengthError(cx
, "PointerType constructor", "0, 1, 2, or 3",
5119 RootedObject
result(cx
, CData::Create(cx
, obj
, nullptr, nullptr, true));
5124 // Set return value early, must not observe *vp after
5125 args
.rval().setObject(*result
);
5127 // There are 3 things that we might be creating here:
5128 // 1 - A null pointer (no arguments)
5129 // 2 - An initialized pointer (1 argument)
5130 // 3 - A closure (1-3 arguments)
5132 // The API doesn't give us a perfect way to distinguish 2 and 3, but the
5133 // heuristics we use should be fine.
5136 // Case 1 - Null pointer
5138 if (args
.length() == 0) {
5142 // Analyze the arguments a bit to decide what to do next.
5143 RootedObject
baseObj(cx
, PointerType::GetBaseType(obj
));
5144 bool looksLikeClosure
= CType::GetTypeCode(baseObj
) == TYPE_function
&&
5145 args
[0].isObject() &&
5146 JS::IsCallable(&args
[0].toObject());
5149 // Case 2 - Initialized pointer
5151 if (!looksLikeClosure
) {
5152 if (args
.length() != 1) {
5153 return ArgumentLengthError(cx
, "FunctionType constructor", "one", "");
5155 return ExplicitConvert(cx
, args
[0], obj
, CData::GetData(result
),
5156 ConversionType::Construct
);
5163 // The second argument is an optional 'this' parameter with which to invoke
5164 // the given js function. Callers may leave this blank, or pass null if they
5165 // wish to pass the third argument.
5166 RootedObject
thisObj(cx
, nullptr);
5167 if (args
.length() >= 2) {
5168 if (args
[1].isNull()) {
5170 } else if (args
[1].isObject()) {
5171 thisObj
= &args
[1].toObject();
5172 } else if (!JS_ValueToObject(cx
, args
[1], &thisObj
)) {
5177 // The third argument is an optional error sentinel that js-ctypes will return
5178 // if an exception is raised while executing the closure. The type must match
5179 // the return type of the callback.
5180 RootedValue
errVal(cx
);
5181 if (args
.length() == 3) {
5185 RootedObject
fnObj(cx
, &args
[0].toObject());
5186 return FunctionType::ConstructData(cx
, baseObj
, result
, fnObj
, thisObj
,
5190 JSObject
* PointerType::GetBaseType(JSObject
* obj
) {
5191 MOZ_ASSERT(CType::GetTypeCode(obj
) == TYPE_pointer
);
5193 Value type
= JS::GetReservedSlot(obj
, SLOT_TARGET_T
);
5194 MOZ_ASSERT(!type
.isNull());
5195 return &type
.toObject();
5198 bool PointerType::IsPointerType(HandleValue v
) {
5199 if (!v
.isObject()) {
5202 JSObject
* obj
= &v
.toObject();
5203 return CType::IsCType(obj
) && CType::GetTypeCode(obj
) == TYPE_pointer
;
5206 bool PointerType::IsPointer(HandleValue v
) {
5207 if (!v
.isObject()) {
5210 JSObject
* obj
= MaybeUnwrapArrayWrapper(&v
.toObject());
5211 return CData::IsCData(obj
) &&
5212 CType::GetTypeCode(CData::GetCType(obj
)) == TYPE_pointer
;
5215 bool PointerType::TargetTypeGetter(JSContext
* cx
, const JS::CallArgs
& args
) {
5216 RootedObject
obj(cx
, &args
.thisv().toObject());
5217 args
.rval().set(JS::GetReservedSlot(obj
, SLOT_TARGET_T
));
5218 MOZ_ASSERT(args
.rval().isObject());
5222 bool PointerType::IsNull(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5223 CallArgs args
= CallArgsFromVp(argc
, vp
);
5224 RootedObject
obj(cx
, GetThisObject(cx
, args
, "PointerType.prototype.isNull"));
5228 if (!CData::IsCDataMaybeUnwrap(&obj
)) {
5229 return IncompatibleThisProto(cx
, "PointerType.prototype.isNull",
5233 // Get pointer type and base type.
5234 JSObject
* typeObj
= CData::GetCType(obj
);
5235 if (CType::GetTypeCode(typeObj
) != TYPE_pointer
) {
5236 return IncompatibleThisType(cx
, "PointerType.prototype.isNull",
5237 "non-PointerType CData", args
.thisv());
5240 void* data
= *static_cast<void**>(CData::GetData(obj
));
5241 args
.rval().setBoolean(data
== nullptr);
5245 bool PointerType::OffsetBy(JSContext
* cx
, const CallArgs
& args
, int offset
,
5247 RootedObject
obj(cx
, GetThisObject(cx
, args
, name
));
5251 if (!CData::IsCDataMaybeUnwrap(&obj
)) {
5252 return IncompatibleThisProto(cx
, name
, args
.thisv());
5255 RootedObject
typeObj(cx
, CData::GetCType(obj
));
5256 if (CType::GetTypeCode(typeObj
) != TYPE_pointer
) {
5257 return IncompatibleThisType(cx
, name
, "non-PointerType CData",
5261 RootedObject
baseType(cx
, PointerType::GetBaseType(typeObj
));
5262 if (!CType::IsSizeDefined(baseType
)) {
5263 return UndefinedSizePointerError(cx
, "modify", obj
);
5266 size_t elementSize
= CType::GetSize(baseType
);
5267 char* data
= static_cast<char*>(*static_cast<void**>(CData::GetData(obj
)));
5268 void* address
= data
+ offset
* ptrdiff_t(elementSize
);
5270 // Create a PointerType CData object containing the new address.
5271 JSObject
* result
= CData::Create(cx
, typeObj
, nullptr, &address
, true);
5276 args
.rval().setObject(*result
);
5280 bool PointerType::Increment(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5281 CallArgs args
= CallArgsFromVp(argc
, vp
);
5282 return OffsetBy(cx
, args
, 1, "PointerType.prototype.increment");
5285 bool PointerType::Decrement(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5286 CallArgs args
= CallArgsFromVp(argc
, vp
);
5287 return OffsetBy(cx
, args
, -1, "PointerType.prototype.decrement");
5290 bool PointerType::ContentsGetter(JSContext
* cx
, const JS::CallArgs
& args
) {
5291 RootedObject
obj(cx
, &args
.thisv().toObject());
5292 RootedObject
baseType(cx
, GetBaseType(CData::GetCType(obj
)));
5293 if (!CType::IsSizeDefined(baseType
)) {
5294 return UndefinedSizePointerError(cx
, "get contents of", obj
);
5297 void* data
= *static_cast<void**>(CData::GetData(obj
));
5298 if (data
== nullptr) {
5299 return NullPointerError(cx
, "read contents of", obj
);
5302 RootedValue
result(cx
);
5303 if (!ConvertToJS(cx
, baseType
, nullptr, data
, false, false, &result
)) {
5307 args
.rval().set(result
);
5311 bool PointerType::ContentsSetter(JSContext
* cx
, const JS::CallArgs
& args
) {
5312 RootedObject
obj(cx
, &args
.thisv().toObject());
5313 RootedObject
baseType(cx
, GetBaseType(CData::GetCType(obj
)));
5314 if (!CType::IsSizeDefined(baseType
)) {
5315 return UndefinedSizePointerError(cx
, "set contents of", obj
);
5318 void* data
= *static_cast<void**>(CData::GetData(obj
));
5319 if (data
== nullptr) {
5320 return NullPointerError(cx
, "write contents to", obj
);
5323 args
.rval().setUndefined();
5324 return ImplicitConvert(cx
, args
.get(0), baseType
, data
,
5325 ConversionType::Setter
, nullptr);
5328 /*******************************************************************************
5329 ** ArrayType implementation
5330 *******************************************************************************/
5332 bool ArrayType::Create(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5333 CallArgs args
= CallArgsFromVp(argc
, vp
);
5334 // Construct and return a new ArrayType object.
5335 if (args
.length() < 1 || args
.length() > 2) {
5336 return ArgumentLengthError(cx
, "ArrayType", "one or two", "s");
5339 if (args
[0].isPrimitive() || !CType::IsCType(&args
[0].toObject())) {
5340 return ArgumentTypeMismatch(cx
, "first ", "ArrayType", "a CType");
5343 // Convert the length argument to a size_t.
5345 if (args
.length() == 2 && !jsvalToSize(cx
, args
[1], false, &length
)) {
5346 return ArgumentTypeMismatch(cx
, "second ", "ArrayType",
5347 "a nonnegative integer");
5350 RootedObject
baseType(cx
, &args
[0].toObject());
5351 JSObject
* result
= CreateInternal(cx
, baseType
, length
, args
.length() == 2);
5356 args
.rval().setObject(*result
);
5360 JSObject
* ArrayType::CreateInternal(JSContext
* cx
, HandleObject baseType
,
5361 size_t length
, bool lengthDefined
) {
5362 // Get ctypes.ArrayType.prototype and the common prototype for CData objects
5363 // of this type, from ctypes.CType.prototype.
5364 RootedObject
typeProto(
5365 cx
, CType::GetProtoFromType(cx
, baseType
, SLOT_ARRAYPROTO
));
5369 RootedObject
dataProto(
5370 cx
, CType::GetProtoFromType(cx
, baseType
, SLOT_ARRAYDATAPROTO
));
5375 // Determine the size of the array from the base type, if possible.
5376 // The size of the base type must be defined.
5377 // If our length is undefined, both our size and length will be undefined.
5379 if (!CType::GetSafeSize(baseType
, &baseSize
)) {
5380 JS_ReportErrorASCII(cx
, "base size must be defined");
5384 RootedValue
sizeVal(cx
);
5385 RootedValue
lengthVal(cx
);
5386 if (lengthDefined
) {
5387 // Check for overflow, and convert to an int or double as required.
5388 size_t size
= length
* baseSize
;
5389 if (length
> 0 && size
/ length
!= baseSize
) {
5390 SizeOverflow(cx
, "array size", "size_t");
5393 if (!SizeTojsval(cx
, size
, &sizeVal
)) {
5394 SizeOverflow(cx
, "array size", "JavaScript number");
5397 if (!SizeTojsval(cx
, length
, &lengthVal
)) {
5398 SizeOverflow(cx
, "array length", "JavaScript number");
5403 RootedValue
alignVal(cx
, Int32Value(CType::GetAlignment(baseType
)));
5405 // Create a new CType object with the common properties and slots.
5406 JSObject
* typeObj
= CType::Create(cx
, typeProto
, dataProto
, TYPE_array
,
5407 nullptr, sizeVal
, alignVal
, nullptr);
5412 // Set the element type.
5413 JS_SetReservedSlot(typeObj
, SLOT_ELEMENT_T
, ObjectValue(*baseType
));
5416 JS_SetReservedSlot(typeObj
, SLOT_LENGTH
, lengthVal
);
5421 bool ArrayType::ConstructData(JSContext
* cx
, HandleObject obj_
,
5422 const CallArgs
& args
) {
5423 RootedObject
obj(cx
, obj_
); // Make a mutable version
5425 if (!CType::IsCType(obj
) || CType::GetTypeCode(obj
) != TYPE_array
) {
5426 return IncompatibleCallee(cx
, "ArrayType constructor", obj
);
5429 // Decide whether we have an object to initialize from. We'll override this
5430 // if we get a length argument instead.
5431 bool convertObject
= args
.length() == 1;
5433 // Check if we're an array of undefined length. If we are, allow construction
5434 // with a length argument, or with an actual JS array.
5435 if (CType::IsSizeDefined(obj
)) {
5436 if (args
.length() > 1) {
5437 return ArgumentLengthError(cx
, "size defined ArrayType constructor",
5442 if (args
.length() != 1) {
5443 return ArgumentLengthError(cx
, "size undefined ArrayType constructor",
5447 RootedObject
baseType(cx
, GetBaseType(obj
));
5450 if (jsvalToSize(cx
, args
[0], false, &length
)) {
5451 // Have a length, rather than an object to initialize from.
5452 convertObject
= false;
5454 } else if (args
[0].isObject()) {
5455 // We were given an object with a .length property.
5456 // This could be a JS array, or a CData array.
5457 RootedObject
arg(cx
, &args
[0].toObject());
5458 RootedValue
lengthVal(cx
);
5459 if (!JS_GetProperty(cx
, arg
, "length", &lengthVal
) ||
5460 !jsvalToSize(cx
, lengthVal
, false, &length
)) {
5461 return ArgumentTypeMismatch(cx
, "",
5462 "size undefined ArrayType constructor",
5463 "an array object or integer");
5466 } else if (args
[0].isString()) {
5467 // We were given a string. Size the array to the appropriate length,
5468 // including space for the terminator.
5469 JSString
* sourceString
= args
[0].toString();
5470 size_t sourceLength
= sourceString
->length();
5471 Rooted
<JSLinearString
*> sourceLinear(cx
, sourceString
->ensureLinear(cx
));
5472 if (!sourceLinear
) {
5476 switch (CType::GetTypeCode(baseType
)) {
5478 case TYPE_signed_char
:
5479 case TYPE_unsigned_char
: {
5480 // Reject if unpaired surrogate characters are present.
5481 if (!ReportErrorIfUnpairedSurrogatePresent(cx
, sourceLinear
)) {
5485 // Determine the UTF-8 length.
5486 length
= JS::GetDeflatedUTF8StringLength(sourceLinear
);
5492 length
= sourceLength
+ 1;
5495 return ConvError(cx
, obj
, args
[0], ConversionType::Construct
);
5499 return ArgumentTypeMismatch(cx
, "",
5500 "size undefined ArrayType constructor",
5501 "an array object or integer");
5504 // Construct a new ArrayType of defined length, for the new CData object.
5505 obj
= CreateInternal(cx
, baseType
, length
, true);
5511 JSObject
* result
= CData::Create(cx
, obj
, nullptr, nullptr, true);
5516 args
.rval().setObject(*result
);
5518 if (convertObject
) {
5519 if (!ExplicitConvert(cx
, args
[0], obj
, CData::GetData(result
),
5520 ConversionType::Construct
))
5527 JSObject
* ArrayType::GetBaseType(JSObject
* obj
) {
5528 MOZ_ASSERT(CType::IsCType(obj
));
5529 MOZ_ASSERT(CType::GetTypeCode(obj
) == TYPE_array
);
5531 Value type
= JS::GetReservedSlot(obj
, SLOT_ELEMENT_T
);
5532 MOZ_ASSERT(!type
.isNull());
5533 return &type
.toObject();
5536 bool ArrayType::GetSafeLength(JSObject
* obj
, size_t* result
) {
5537 MOZ_ASSERT(CType::IsCType(obj
));
5538 MOZ_ASSERT(CType::GetTypeCode(obj
) == TYPE_array
);
5540 Value length
= JS::GetReservedSlot(obj
, SLOT_LENGTH
);
5542 // The "length" property can be an int, a double, or JS::UndefinedValue()
5543 // (for arrays of undefined length), and must always fit in a size_t.
5544 if (length
.isInt32()) {
5545 *result
= length
.toInt32();
5548 if (length
.isDouble()) {
5549 *result
= Convert
<size_t>(length
.toDouble());
5553 MOZ_ASSERT(length
.isUndefined());
5557 size_t ArrayType::GetLength(JSObject
* obj
) {
5558 MOZ_ASSERT(CType::IsCType(obj
));
5559 MOZ_ASSERT(CType::GetTypeCode(obj
) == TYPE_array
);
5561 Value length
= JS::GetReservedSlot(obj
, SLOT_LENGTH
);
5563 MOZ_ASSERT(!length
.isUndefined());
5565 // The "length" property can be an int, a double, or JS::UndefinedValue()
5566 // (for arrays of undefined length), and must always fit in a size_t.
5567 // For callers who know it can never be JS::UndefinedValue(), return a size_t
5569 if (length
.isInt32()) {
5570 return length
.toInt32();
5572 return Convert
<size_t>(length
.toDouble());
5575 UniquePtrFFIType
ArrayType::BuildFFIType(JSContext
* cx
, JSObject
* obj
) {
5576 MOZ_ASSERT(CType::IsCType(obj
));
5577 MOZ_ASSERT(CType::GetTypeCode(obj
) == TYPE_array
);
5578 MOZ_ASSERT(CType::IsSizeDefined(obj
));
5580 JSObject
* baseType
= ArrayType::GetBaseType(obj
);
5581 ffi_type
* ffiBaseType
= CType::GetFFIType(cx
, baseType
);
5586 size_t length
= ArrayType::GetLength(obj
);
5588 // Create an ffi_type to represent the array. This is necessary for the case
5589 // where the array is part of a struct. Since libffi has no intrinsic
5590 // support for array types, we approximate it by creating a struct type
5591 // with elements of type 'baseType' and with appropriate size and alignment
5592 // values. It would be nice to not do all the work of setting up 'elements',
5593 // but some libffi platforms currently require that it be meaningful. I'm
5594 // looking at you, x86_64.
5595 auto ffiType
= cx
->make_unique
<ffi_type
>();
5600 ffiType
->type
= FFI_TYPE_STRUCT
;
5601 ffiType
->size
= CType::GetSize(obj
);
5602 ffiType
->alignment
= CType::GetAlignment(obj
);
5603 ffiType
->elements
= cx
->pod_malloc
<ffi_type
*>(length
+ 1);
5604 if (!ffiType
->elements
) {
5608 for (size_t i
= 0; i
< length
; ++i
) {
5609 ffiType
->elements
[i
] = ffiBaseType
;
5611 ffiType
->elements
[length
] = nullptr;
5616 bool ArrayType::IsArrayType(HandleValue v
) {
5617 if (!v
.isObject()) {
5620 JSObject
* obj
= &v
.toObject();
5621 return CType::IsCType(obj
) && CType::GetTypeCode(obj
) == TYPE_array
;
5624 bool ArrayType::IsArrayOrArrayType(HandleValue v
) {
5625 if (!v
.isObject()) {
5628 JSObject
* obj
= MaybeUnwrapArrayWrapper(&v
.toObject());
5630 // Allow both CTypes and CDatas of the ArrayType persuasion by extracting the
5631 // CType if we're dealing with a CData.
5632 if (CData::IsCData(obj
)) {
5633 obj
= CData::GetCType(obj
);
5635 return CType::IsCType(obj
) && CType::GetTypeCode(obj
) == TYPE_array
;
5638 bool ArrayType::ElementTypeGetter(JSContext
* cx
, const JS::CallArgs
& args
) {
5639 RootedObject
obj(cx
, &args
.thisv().toObject());
5640 args
.rval().set(JS::GetReservedSlot(obj
, SLOT_ELEMENT_T
));
5641 MOZ_ASSERT(args
.rval().isObject());
5645 bool ArrayType::LengthGetter(JSContext
* cx
, const JS::CallArgs
& args
) {
5646 RootedObject
obj(cx
, &args
.thisv().toObject());
5648 // This getter exists for both CTypes and CDatas of the ArrayType persuasion.
5649 // If we're dealing with a CData, get the CType from it.
5650 if (CData::IsCDataMaybeUnwrap(&obj
)) {
5651 obj
= CData::GetCType(obj
);
5654 args
.rval().set(JS::GetReservedSlot(obj
, SLOT_LENGTH
));
5655 MOZ_ASSERT(args
.rval().isNumber() || args
.rval().isUndefined());
5659 bool ArrayType::Getter(JSContext
* cx
, HandleObject obj
, HandleId idval
,
5660 MutableHandleValue vp
, bool* handled
) {
5663 // This should never happen, but we'll check to be safe.
5664 if (!CData::IsCData(obj
)) {
5665 RootedValue
objVal(cx
, ObjectValue(*obj
));
5666 return IncompatibleThisProto(cx
, "ArrayType property getter", objVal
);
5669 // Bail early if we're not an ArrayType. (This setter is present for all
5670 // CData, regardless of CType.)
5671 JSObject
* typeObj
= CData::GetCType(obj
);
5672 if (CType::GetTypeCode(typeObj
) != TYPE_array
) {
5676 // Convert the index to a size_t and bounds-check it.
5678 size_t length
= GetLength(typeObj
);
5679 bool ok
= jsidToSize(cx
, idval
, true, &index
);
5681 if (!ok
&& idval
.isSymbol()) {
5685 if (!ok
&& idval
.isString() &&
5686 !StringToInteger(cx
, idval
.toString(), &dummy
, &dummy2
)) {
5687 // String either isn't a number, or doesn't fit in size_t.
5688 // Chances are it's a regular property lookup, so return.
5692 return InvalidIndexError(cx
, idval
);
5694 if (index
>= length
) {
5695 return InvalidIndexRangeError(cx
, index
, length
);
5700 RootedObject
baseType(cx
, GetBaseType(typeObj
));
5701 size_t elementSize
= CType::GetSize(baseType
);
5702 char* data
= static_cast<char*>(CData::GetData(obj
)) + elementSize
* index
;
5703 return ConvertToJS(cx
, baseType
, obj
, data
, false, false, vp
);
5706 bool ArrayType::Setter(JSContext
* cx
, HandleObject obj
, HandleId idval
,
5707 HandleValue vp
, ObjectOpResult
& result
, bool* handled
) {
5710 // This should never happen, but we'll check to be safe.
5711 if (!CData::IsCData(obj
)) {
5712 RootedValue
objVal(cx
, ObjectValue(*obj
));
5713 return IncompatibleThisProto(cx
, "ArrayType property setter", objVal
);
5716 // Bail early if we're not an ArrayType. (This setter is present for all
5717 // CData, regardless of CType.)
5718 RootedObject
typeObj(cx
, CData::GetCType(obj
));
5719 if (CType::GetTypeCode(typeObj
) != TYPE_array
) {
5720 return result
.succeed();
5723 // Convert the index to a size_t and bounds-check it.
5725 size_t length
= GetLength(typeObj
);
5726 bool ok
= jsidToSize(cx
, idval
, true, &index
);
5728 if (!ok
&& idval
.isSymbol()) {
5732 if (!ok
&& idval
.isString() &&
5733 !StringToInteger(cx
, idval
.toString(), &dummy
, &dummy2
)) {
5734 // String either isn't a number, or doesn't fit in size_t.
5735 // Chances are it's a regular property lookup, so return.
5736 return result
.succeed();
5739 return InvalidIndexError(cx
, idval
);
5741 if (index
>= length
) {
5742 return InvalidIndexRangeError(cx
, index
, length
);
5747 RootedObject
baseType(cx
, GetBaseType(typeObj
));
5748 size_t elementSize
= CType::GetSize(baseType
);
5749 char* data
= static_cast<char*>(CData::GetData(obj
)) + elementSize
* index
;
5750 if (!ImplicitConvert(cx
, vp
, baseType
, data
, ConversionType::Setter
, nullptr,
5751 nullptr, 0, typeObj
, index
))
5753 return result
.succeed();
5756 bool ArrayType::AddressOfElement(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5757 CallArgs args
= CallArgsFromVp(argc
, vp
);
5759 cx
, GetThisObject(cx
, args
, "ArrayType.prototype.addressOfElement"));
5763 if (!CData::IsCDataMaybeUnwrap(&obj
)) {
5764 return IncompatibleThisProto(cx
, "ArrayType.prototype.addressOfElement",
5768 RootedObject
typeObj(cx
, CData::GetCType(obj
));
5769 if (CType::GetTypeCode(typeObj
) != TYPE_array
) {
5770 return IncompatibleThisType(cx
, "ArrayType.prototype.addressOfElement",
5771 "non-ArrayType CData", args
.thisv());
5774 if (args
.length() != 1) {
5775 return ArgumentLengthError(cx
, "ArrayType.prototype.addressOfElement",
5779 RootedObject
baseType(cx
, GetBaseType(typeObj
));
5780 RootedObject
pointerType(cx
, PointerType::CreateInternal(cx
, baseType
));
5785 // Create a PointerType CData object containing null.
5786 RootedObject
result(cx
,
5787 CData::Create(cx
, pointerType
, nullptr, nullptr, true));
5792 args
.rval().setObject(*result
);
5794 // Convert the index to a size_t and bounds-check it.
5796 size_t length
= GetLength(typeObj
);
5797 if (!jsvalToSize(cx
, args
[0], false, &index
)) {
5798 return InvalidIndexError(cx
, args
[0]);
5800 if (index
>= length
) {
5801 return InvalidIndexRangeError(cx
, index
, length
);
5804 // Manually set the pointer inside the object, so we skip the conversion step.
5805 void** data
= static_cast<void**>(CData::GetData(result
));
5806 size_t elementSize
= CType::GetSize(baseType
);
5807 *data
= static_cast<char*>(CData::GetData(obj
)) + elementSize
* index
;
5811 /*******************************************************************************
5812 ** StructType implementation
5813 *******************************************************************************/
5815 // For a struct field descriptor 'val' of the form { name : type }, extract
5816 // 'name' and 'type'.
5817 static JSLinearString
* ExtractStructField(JSContext
* cx
, HandleValue val
,
5818 MutableHandleObject typeObj
) {
5819 if (val
.isPrimitive()) {
5820 FieldDescriptorNameTypeError(cx
, val
);
5824 RootedObject
obj(cx
, &val
.toObject());
5825 Rooted
<IdVector
> props(cx
, IdVector(cx
));
5826 if (!JS_Enumerate(cx
, obj
, &props
)) {
5830 // make sure we have one, and only one, property
5831 if (props
.length() != 1) {
5832 FieldDescriptorCountError(cx
, val
, props
.length());
5836 RootedId
nameid(cx
, props
[0]);
5837 if (!nameid
.isString()) {
5838 FieldDescriptorNameError(cx
, nameid
);
5842 RootedValue
propVal(cx
);
5843 if (!JS_GetPropertyById(cx
, obj
, nameid
, &propVal
)) {
5847 if (propVal
.isPrimitive() || !CType::IsCType(&propVal
.toObject())) {
5848 FieldDescriptorTypeError(cx
, propVal
, nameid
);
5852 // Undefined size or zero size struct members are illegal.
5853 // (Zero-size arrays are legal as struct members in C++, but libffi will
5854 // choke on a zero-size struct, so we disallow them.)
5855 typeObj
.set(&propVal
.toObject());
5857 if (!CType::GetSafeSize(typeObj
, &size
) || size
== 0) {
5858 FieldDescriptorSizeError(cx
, typeObj
, nameid
);
5862 return nameid
.toLinearString();
5865 // For a struct field with 'name' and 'type', add an element of the form
5867 static bool AddFieldToArray(JSContext
* cx
, MutableHandleValue element
,
5868 JSLinearString
* name_
, JSObject
* typeObj_
) {
5869 RootedObject
typeObj(cx
, typeObj_
);
5870 Rooted
<JSLinearString
*> name(cx
, name_
);
5871 RootedObject
fieldObj(cx
, JS_NewPlainObject(cx
));
5876 element
.setObject(*fieldObj
);
5878 AutoStableStringChars
nameChars(cx
);
5879 if (!nameChars
.initTwoByte(cx
, name
)) {
5883 if (!JS_DefineUCProperty(
5884 cx
, fieldObj
, nameChars
.twoByteChars(), name
->length(), typeObj
,
5885 JSPROP_ENUMERATE
| JSPROP_READONLY
| JSPROP_PERMANENT
))
5888 return JS_FreezeObject(cx
, fieldObj
);
5891 bool StructType::Create(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5892 CallArgs args
= CallArgsFromVp(argc
, vp
);
5894 // Construct and return a new StructType object.
5895 if (args
.length() < 1 || args
.length() > 2) {
5896 return ArgumentLengthError(cx
, "StructType", "one or two", "s");
5899 Value name
= args
[0];
5900 if (!name
.isString()) {
5901 return ArgumentTypeMismatch(cx
, "first ", "StructType", "a string");
5904 // Get ctypes.StructType.prototype from the ctypes.StructType constructor.
5905 RootedObject
typeProto(
5906 cx
, CType::GetProtoFromCtor(&args
.callee(), SLOT_STRUCTPROTO
));
5908 // Create a simple StructType with no defined fields. The result will be
5909 // non-instantiable as CData, will have no 'prototype' property, and will
5910 // have undefined size and alignment and no ffi_type.
5911 RootedObject
result(
5912 cx
, CType::Create(cx
, typeProto
, nullptr, TYPE_struct
, name
.toString(),
5913 JS::UndefinedHandleValue
, JS::UndefinedHandleValue
,
5919 if (args
.length() == 2) {
5920 RootedObject
arr(cx
, args
[1].isObject() ? &args
[1].toObject() : nullptr);
5925 if (!JS::IsArrayObject(cx
, arr
, &isArray
)) {
5930 return ArgumentTypeMismatch(cx
, "second ", "StructType", "an array");
5933 // Define the struct fields.
5934 if (!DefineInternal(cx
, result
, arr
)) {
5939 args
.rval().setObject(*result
);
5943 bool StructType::DefineInternal(JSContext
* cx
, JSObject
* typeObj_
,
5944 JSObject
* fieldsObj_
) {
5945 RootedObject
typeObj(cx
, typeObj_
);
5946 RootedObject
fieldsObj(cx
, fieldsObj_
);
5949 MOZ_ALWAYS_TRUE(JS::GetArrayLength(cx
, fieldsObj
, &len
));
5951 // Get the common prototype for CData objects of this type from
5952 // ctypes.CType.prototype.
5953 RootedObject
dataProto(
5954 cx
, CType::GetProtoFromType(cx
, typeObj
, SLOT_STRUCTDATAPROTO
));
5959 // Set up the 'prototype' and 'prototype.constructor' properties.
5960 // The prototype will reflect the struct fields as properties on CData objects
5961 // created from this type.
5962 RootedObject
prototype(
5963 cx
, JS_NewObjectWithGivenProto(cx
, &sCDataProtoClass
, dataProto
));
5968 if (!JS_DefineProperty(cx
, prototype
, "constructor", typeObj
,
5969 JSPROP_READONLY
| JSPROP_PERMANENT
))
5972 // Create a FieldInfoHash to stash on the type object.
5973 Rooted
<FieldInfoHash
> fields(cx
, FieldInfoHash(cx
->zone(), len
));
5975 // Process the field types.
5976 size_t structSize
, structAlign
;
5981 for (uint32_t i
= 0; i
< len
; ++i
) {
5982 RootedValue
item(cx
);
5983 if (!JS_GetElement(cx
, fieldsObj
, i
, &item
)) {
5987 RootedObject
fieldType(cx
, nullptr);
5988 Rooted
<JSLinearString
*> name(cx
,
5989 ExtractStructField(cx
, item
, &fieldType
));
5994 // Make sure each field name is unique
5995 FieldInfoHash::AddPtr entryPtr
= fields
.lookupForAdd(name
);
5997 return DuplicateFieldError(cx
, name
);
6000 // Add the field to the StructType's 'prototype' property.
6001 AutoStableStringChars
nameChars(cx
);
6002 if (!nameChars
.initTwoByte(cx
, name
)) {
6006 RootedFunction
getter(
6008 NewFunctionWithReserved(cx
, StructType::FieldGetter
, 0, 0, nullptr));
6012 SetFunctionNativeReserved(getter
, StructType::SLOT_FIELDNAME
,
6013 StringValue(JS_FORGET_STRING_LINEARNESS(name
)));
6014 RootedObject
getterObj(cx
, JS_GetFunctionObject(getter
));
6016 RootedFunction
setter(
6018 NewFunctionWithReserved(cx
, StructType::FieldSetter
, 1, 0, nullptr));
6022 SetFunctionNativeReserved(setter
, StructType::SLOT_FIELDNAME
,
6023 StringValue(JS_FORGET_STRING_LINEARNESS(name
)));
6024 RootedObject
setterObj(cx
, JS_GetFunctionObject(setter
));
6026 if (!JS_DefineUCProperty(cx
, prototype
, nameChars
.twoByteChars(),
6027 name
->length(), getterObj
, setterObj
,
6028 JSPROP_ENUMERATE
| JSPROP_PERMANENT
)) {
6032 size_t fieldSize
= CType::GetSize(fieldType
);
6033 size_t fieldAlign
= CType::GetAlignment(fieldType
);
6034 size_t fieldOffset
= Align(structSize
, fieldAlign
);
6035 // Check for overflow. Since we hold invariant that fieldSize % fieldAlign
6036 // be zero, we can safely check fieldOffset + fieldSize without first
6037 // checking fieldOffset for overflow.
6038 if (fieldOffset
+ fieldSize
< structSize
) {
6039 SizeOverflow(cx
, "struct size", "size_t");
6043 // Add field name to the hash
6045 info
.mType
= fieldType
;
6047 info
.mOffset
= fieldOffset
;
6048 if (!fields
.add(entryPtr
, name
, info
)) {
6049 JS_ReportOutOfMemory(cx
);
6053 structSize
= fieldOffset
+ fieldSize
;
6055 if (fieldAlign
> structAlign
) {
6056 structAlign
= fieldAlign
;
6060 // Pad the struct tail according to struct alignment.
6061 size_t structTail
= Align(structSize
, structAlign
);
6062 if (structTail
< structSize
) {
6063 SizeOverflow(cx
, "struct size", "size_t");
6066 structSize
= structTail
;
6069 // Empty structs are illegal in C, but are legal and have a size of
6070 // 1 byte in C++. We're going to allow them, and trick libffi into
6071 // believing this by adding a char member. The resulting struct will have
6072 // no getters or setters, and will be initialized to zero.
6077 RootedValue
sizeVal(cx
);
6078 if (!SizeTojsval(cx
, structSize
, &sizeVal
)) {
6079 SizeOverflow(cx
, "struct size", "double");
6083 // Move the field hash to the heap and store it in the typeObj.
6084 FieldInfoHash
* heapHash
= cx
->new_
<FieldInfoHash
>(std::move(fields
.get()));
6086 JS_ReportOutOfMemory(cx
);
6089 JS_InitReservedSlot(typeObj
, SLOT_FIELDINFO
, heapHash
,
6090 JS::MemoryUse::CTypeFieldInfo
);
6091 JS_SetReservedSlot(typeObj
, SLOT_SIZE
, sizeVal
);
6092 JS_SetReservedSlot(typeObj
, SLOT_ALIGN
, Int32Value(structAlign
));
6093 // if (!JS_FreezeObject(cx, prototype)0 // XXX fixme - see bug 541212!
6095 JS_SetReservedSlot(typeObj
, SLOT_PROTO
, ObjectValue(*prototype
));
6099 UniquePtrFFIType
StructType::BuildFFIType(JSContext
* cx
, JSObject
* obj
) {
6100 MOZ_ASSERT(CType::IsCType(obj
));
6101 MOZ_ASSERT(CType::GetTypeCode(obj
) == TYPE_struct
);
6102 MOZ_ASSERT(CType::IsSizeDefined(obj
));
6104 const FieldInfoHash
* fields
= GetFieldInfo(obj
);
6105 size_t len
= fields
->count();
6107 size_t structSize
= CType::GetSize(obj
);
6108 size_t structAlign
= CType::GetAlignment(obj
);
6110 auto ffiType
= cx
->make_unique
<ffi_type
>();
6114 ffiType
->type
= FFI_TYPE_STRUCT
;
6116 size_t count
= len
!= 0 ? len
+ 1 : 2;
6117 auto elements
= cx
->make_pod_array
<ffi_type
*>(count
);
6123 elements
[len
] = nullptr;
6125 for (FieldInfoHash::Range r
= fields
->all(); !r
.empty(); r
.popFront()) {
6126 const FieldInfoHash::Entry
& entry
= r
.front();
6127 ffi_type
* fieldType
= CType::GetFFIType(cx
, entry
.value().mType
);
6131 elements
[entry
.value().mIndex
] = fieldType
;
6134 // Represent an empty struct as having a size of 1 byte, just like C++.
6135 MOZ_ASSERT(structSize
== 1);
6136 MOZ_ASSERT(structAlign
== 1);
6137 elements
[0] = &ffi_type_uint8
;
6138 elements
[1] = nullptr;
6141 ffiType
->elements
= elements
.release();
6142 AddCellMemory(obj
, count
* sizeof(ffi_type
*),
6143 MemoryUse::CTypeFFITypeElements
);
6146 // Perform a sanity check: the result of our struct size and alignment
6147 // calculations should match libffi's. We force it to do this calculation
6148 // by calling ffi_prep_cif.
6151 ffiType
->alignment
= 0;
6153 ffi_prep_cif(&cif
, FFI_DEFAULT_ABI
, 0, ffiType
.get(), nullptr);
6154 MOZ_ASSERT(status
== FFI_OK
);
6155 MOZ_ASSERT(structSize
== ffiType
->size
);
6156 MOZ_ASSERT(structAlign
== ffiType
->alignment
);
6158 // Fill in the ffi_type's size and align fields. This makes libffi treat the
6159 // type as initialized; it will not recompute the values. (We assume
6160 // everything agrees; if it doesn't, we really want to know about it, which
6161 // is the purpose of the above debug-only check.)
6162 ffiType
->size
= structSize
;
6163 ffiType
->alignment
= structAlign
;
6169 bool StructType::Define(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6170 CallArgs args
= CallArgsFromVp(argc
, vp
);
6171 RootedObject
obj(cx
, GetThisObject(cx
, args
, "StructType.prototype.define"));
6175 if (!CType::IsCType(obj
)) {
6176 return IncompatibleThisProto(cx
, "StructType.prototype.define",
6179 if (CType::GetTypeCode(obj
) != TYPE_struct
) {
6180 return IncompatibleThisType(cx
, "StructType.prototype.define",
6181 "non-StructType", args
.thisv());
6184 if (CType::IsSizeDefined(obj
)) {
6185 JS_ReportErrorASCII(cx
, "StructType has already been defined");
6189 if (args
.length() != 1) {
6190 return ArgumentLengthError(cx
, "StructType.prototype.define", "one", "");
6193 HandleValue arg
= args
[0];
6194 if (arg
.isPrimitive()) {
6195 return ArgumentTypeMismatch(cx
, "", "StructType.prototype.define",
6200 if (!arg
.isObject()) {
6203 if (!JS::IsArrayObject(cx
, arg
, &isArray
)) {
6209 return ArgumentTypeMismatch(cx
, "", "StructType.prototype.define",
6213 RootedObject
arr(cx
, &arg
.toObject());
6214 return DefineInternal(cx
, obj
, arr
);
6217 bool StructType::ConstructData(JSContext
* cx
, HandleObject obj
,
6218 const CallArgs
& args
) {
6219 if (!CType::IsCType(obj
) || CType::GetTypeCode(obj
) != TYPE_struct
) {
6220 return IncompatibleCallee(cx
, "StructType constructor", obj
);
6223 if (!CType::IsSizeDefined(obj
)) {
6224 JS_ReportErrorASCII(cx
, "cannot construct an opaque StructType");
6228 JSObject
* result
= CData::Create(cx
, obj
, nullptr, nullptr, true);
6233 args
.rval().setObject(*result
);
6235 if (args
.length() == 0) {
6239 char* buffer
= static_cast<char*>(CData::GetData(result
));
6240 const FieldInfoHash
* fields
= GetFieldInfo(obj
);
6242 if (args
.length() == 1) {
6243 // There are two possible interpretations of the argument:
6244 // 1) It may be an object '{ ... }' with properties representing the
6245 // struct fields intended to ExplicitConvert wholesale to our StructType.
6246 // 2) If the struct contains one field, the arg may be intended to
6247 // ImplicitConvert directly to that arg's CType.
6248 // Thankfully, the conditions for these two possibilities to succeed
6249 // are mutually exclusive, so we can pick the right one.
6251 // Try option 1) first.
6252 if (ExplicitConvert(cx
, args
[0], obj
, buffer
, ConversionType::Construct
)) {
6256 if (fields
->count() != 1) {
6260 // If ExplicitConvert failed, and there is no pending exception, then assume
6261 // hard failure (out of memory, or some other similarly serious condition).
6262 if (!JS_IsExceptionPending(cx
)) {
6266 // Otherwise, assume soft failure, and clear the pending exception so that
6267 // we can throw a different one as required.
6268 JS_ClearPendingException(cx
);
6270 // Fall through to try option 2).
6273 // We have a type constructor of the form 'ctypes.StructType(a, b, c, ...)'.
6274 // ImplicitConvert each field.
6275 if (args
.length() == fields
->count()) {
6276 for (FieldInfoHash::Range r
= fields
->all(); !r
.empty(); r
.popFront()) {
6277 const FieldInfo
& field
= r
.front().value();
6278 MOZ_ASSERT(field
.mIndex
< fields
->count()); /* Quantified invariant */
6279 if (!ImplicitConvert(cx
, args
[field
.mIndex
], field
.mType
,
6280 buffer
+ field
.mOffset
, ConversionType::Construct
,
6281 nullptr, nullptr, 0, obj
, field
.mIndex
))
6288 size_t count
= fields
->count();
6290 char fieldLengthStr
[32];
6291 SprintfLiteral(fieldLengthStr
, "0, 1, or %zu", count
);
6292 return ArgumentLengthError(cx
, "StructType constructor", fieldLengthStr
,
6295 return ArgumentLengthError(cx
, "StructType constructor", "at most one", "");
6298 const FieldInfoHash
* StructType::GetFieldInfo(JSObject
* obj
) {
6299 MOZ_ASSERT(CType::IsCType(obj
));
6300 MOZ_ASSERT(CType::GetTypeCode(obj
) == TYPE_struct
);
6302 Value slot
= JS::GetReservedSlot(obj
, SLOT_FIELDINFO
);
6303 MOZ_ASSERT(!slot
.isUndefined() && slot
.toPrivate());
6305 return static_cast<const FieldInfoHash
*>(slot
.toPrivate());
6308 const FieldInfo
* StructType::LookupField(JSContext
* cx
, JSObject
* obj
,
6309 JSLinearString
* name
) {
6310 MOZ_ASSERT(CType::IsCType(obj
));
6311 MOZ_ASSERT(CType::GetTypeCode(obj
) == TYPE_struct
);
6313 FieldInfoHash::Ptr ptr
= GetFieldInfo(obj
)->lookup(name
);
6315 return &ptr
->value();
6318 FieldMissingError(cx
, obj
, name
);
6322 JSObject
* StructType::BuildFieldsArray(JSContext
* cx
, JSObject
* obj
) {
6323 MOZ_ASSERT(CType::IsCType(obj
));
6324 MOZ_ASSERT(CType::GetTypeCode(obj
) == TYPE_struct
);
6325 MOZ_ASSERT(CType::IsSizeDefined(obj
));
6327 const FieldInfoHash
* fields
= GetFieldInfo(obj
);
6328 size_t len
= fields
->count();
6330 // Prepare a new array for the 'fields' property of the StructType.
6331 JS::RootedValueVector
fieldsVec(cx
);
6332 if (!fieldsVec
.resize(len
)) {
6336 for (FieldInfoHash::Range r
= fields
->all(); !r
.empty(); r
.popFront()) {
6337 const FieldInfoHash::Entry
& entry
= r
.front();
6338 // Add the field descriptor to the array.
6339 if (!AddFieldToArray(cx
, fieldsVec
[entry
.value().mIndex
], entry
.key(),
6340 entry
.value().mType
))
6344 RootedObject
fieldsProp(cx
, JS::NewArrayObject(cx
, fieldsVec
));
6349 // Seal the fields array.
6350 if (!JS_FreezeObject(cx
, fieldsProp
)) {
6358 bool StructType::IsStruct(HandleValue v
) {
6359 if (!v
.isObject()) {
6362 JSObject
* obj
= &v
.toObject();
6363 return CType::IsCType(obj
) && CType::GetTypeCode(obj
) == TYPE_struct
;
6366 bool StructType::FieldsArrayGetter(JSContext
* cx
, const JS::CallArgs
& args
) {
6367 RootedObject
obj(cx
, &args
.thisv().toObject());
6369 args
.rval().set(JS::GetReservedSlot(obj
, SLOT_FIELDS
));
6371 if (!CType::IsSizeDefined(obj
)) {
6372 MOZ_ASSERT(args
.rval().isUndefined());
6376 if (args
.rval().isUndefined()) {
6377 // Build the 'fields' array lazily.
6378 JSObject
* fields
= BuildFieldsArray(cx
, obj
);
6382 JS_SetReservedSlot(obj
, SLOT_FIELDS
, ObjectValue(*fields
));
6384 args
.rval().setObject(*fields
);
6387 MOZ_ASSERT(args
.rval().isObject());
6391 bool StructType::FieldGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6392 CallArgs args
= CallArgsFromVp(argc
, vp
);
6394 if (!args
.thisv().isObject()) {
6395 return IncompatibleThisProto(cx
, "StructType property getter",
6399 RootedObject
obj(cx
, &args
.thisv().toObject());
6400 if (!CData::IsCDataMaybeUnwrap(&obj
)) {
6401 return IncompatibleThisProto(cx
, "StructType property getter",
6405 JSObject
* typeObj
= CData::GetCType(obj
);
6406 if (CType::GetTypeCode(typeObj
) != TYPE_struct
) {
6407 return IncompatibleThisType(cx
, "StructType property getter",
6408 "non-StructType CData", args
.thisv());
6411 RootedValue
nameVal(
6412 cx
, GetFunctionNativeReserved(&args
.callee(), SLOT_FIELDNAME
));
6413 Rooted
<JSLinearString
*> name(cx
,
6414 JS_EnsureLinearString(cx
, nameVal
.toString()));
6419 const FieldInfo
* field
= LookupField(cx
, typeObj
, name
);
6424 char* data
= static_cast<char*>(CData::GetData(obj
)) + field
->mOffset
;
6425 RootedObject
fieldType(cx
, field
->mType
);
6426 return ConvertToJS(cx
, fieldType
, obj
, data
, false, false, args
.rval());
6429 bool StructType::FieldSetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6430 CallArgs args
= CallArgsFromVp(argc
, vp
);
6432 if (!args
.thisv().isObject()) {
6433 return IncompatibleThisProto(cx
, "StructType property setter",
6437 RootedObject
obj(cx
, &args
.thisv().toObject());
6438 if (!CData::IsCDataMaybeUnwrap(&obj
)) {
6439 return IncompatibleThisProto(cx
, "StructType property setter",
6443 RootedObject
typeObj(cx
, CData::GetCType(obj
));
6444 if (CType::GetTypeCode(typeObj
) != TYPE_struct
) {
6445 return IncompatibleThisType(cx
, "StructType property setter",
6446 "non-StructType CData", args
.thisv());
6449 RootedValue
nameVal(
6450 cx
, GetFunctionNativeReserved(&args
.callee(), SLOT_FIELDNAME
));
6451 Rooted
<JSLinearString
*> name(cx
,
6452 JS_EnsureLinearString(cx
, nameVal
.toString()));
6457 const FieldInfo
* field
= LookupField(cx
, typeObj
, name
);
6462 args
.rval().setUndefined();
6464 char* data
= static_cast<char*>(CData::GetData(obj
)) + field
->mOffset
;
6465 return ImplicitConvert(cx
, args
.get(0), field
->mType
, data
,
6466 ConversionType::Setter
, nullptr, nullptr, 0, typeObj
,
6470 bool StructType::AddressOfField(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6471 CallArgs args
= CallArgsFromVp(argc
, vp
);
6473 cx
, GetThisObject(cx
, args
, "StructType.prototype.addressOfField"));
6478 if (!CData::IsCDataMaybeUnwrap(&obj
)) {
6479 return IncompatibleThisProto(cx
, "StructType.prototype.addressOfField",
6483 JSObject
* typeObj
= CData::GetCType(obj
);
6484 if (CType::GetTypeCode(typeObj
) != TYPE_struct
) {
6485 return IncompatibleThisType(cx
, "StructType.prototype.addressOfField",
6486 "non-StructType CData", args
.thisv());
6489 if (args
.length() != 1) {
6490 return ArgumentLengthError(cx
, "StructType.prototype.addressOfField", "one",
6494 if (!args
[0].isString()) {
6495 return ArgumentTypeMismatch(cx
, "", "StructType.prototype.addressOfField",
6499 JSLinearString
* str
= JS_EnsureLinearString(cx
, args
[0].toString());
6504 const FieldInfo
* field
= LookupField(cx
, typeObj
, str
);
6509 RootedObject
baseType(cx
, field
->mType
);
6510 RootedObject
pointerType(cx
, PointerType::CreateInternal(cx
, baseType
));
6515 // Create a PointerType CData object containing null.
6516 JSObject
* result
= CData::Create(cx
, pointerType
, nullptr, nullptr, true);
6521 args
.rval().setObject(*result
);
6523 // Manually set the pointer inside the object, so we skip the conversion step.
6524 void** data
= static_cast<void**>(CData::GetData(result
));
6525 *data
= static_cast<char*>(CData::GetData(obj
)) + field
->mOffset
;
6529 /*******************************************************************************
6530 ** FunctionType implementation
6531 *******************************************************************************/
6533 // Helper class for handling allocation of function arguments.
6535 AutoValue() : mData(nullptr) {}
6537 ~AutoValue() { js_free(mData
); }
6539 bool SizeToType(JSContext
* cx
, JSObject
* type
) {
6540 // Allocate a minimum of sizeof(ffi_arg) to handle small integers.
6541 size_t size
= Align(CType::GetSize(type
), sizeof(ffi_arg
));
6542 mData
= js_calloc(size
);
6543 return mData
!= nullptr;
6549 static bool GetABI(JSContext
* cx
, HandleValue abiType
, ffi_abi
* result
) {
6550 if (abiType
.isPrimitive()) {
6554 ABICode abi
= GetABICode(abiType
.toObjectOrNull());
6556 // determine the ABI from the subset of those available on the
6557 // given platform. ABI_DEFAULT specifies the default
6558 // C calling convention (cdecl) on each platform.
6561 *result
= FFI_DEFAULT_ABI
;
6565 # if defined(_M_X64)
6566 *result
= FFI_WIN64
;
6567 # elif defined(_M_ARM64)
6570 # error unknown 64-bit Windows platform
6573 #elif defined(_WIN32)
6574 *result
= FFI_THISCALL
;
6581 #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
6582 *result
= FFI_STDCALL
;
6584 #elif (defined(_WIN64))
6585 // We'd like the same code to work across Win32 and Win64, so stdcall_api
6586 // and winapi_abi become aliases to the lone Win64 ABI.
6587 # if defined(_M_X64)
6588 *result
= FFI_WIN64
;
6589 # elif defined(_M_ARM64)
6592 # error unknown 64-bit Windows platform
6602 static JSObject
* PrepareType(JSContext
* cx
, uint32_t index
, HandleValue type
) {
6603 if (type
.isPrimitive() || !CType::IsCType(type
.toObjectOrNull())) {
6604 FunctionArgumentTypeError(cx
, index
, type
, "is not a ctypes type");
6608 JSObject
* result
= type
.toObjectOrNull();
6609 TypeCode typeCode
= CType::GetTypeCode(result
);
6611 if (typeCode
== TYPE_array
) {
6612 // convert array argument types to pointers, just like C.
6613 // ImplicitConvert will do the same, when passing an array as data.
6614 RootedObject
baseType(cx
, ArrayType::GetBaseType(result
));
6615 result
= PointerType::CreateInternal(cx
, baseType
);
6620 } else if (typeCode
== TYPE_void_t
|| typeCode
== TYPE_function
) {
6621 // disallow void or function argument types
6622 FunctionArgumentTypeError(cx
, index
, type
, "cannot be void or function");
6626 if (!CType::IsSizeDefined(result
)) {
6627 FunctionArgumentTypeError(cx
, index
, type
, "must have defined size");
6631 // libffi cannot pass types of zero size by value.
6632 MOZ_ASSERT(CType::GetSize(result
) != 0);
6637 static JSObject
* PrepareReturnType(JSContext
* cx
, HandleValue type
) {
6638 if (type
.isPrimitive() || !CType::IsCType(type
.toObjectOrNull())) {
6639 FunctionReturnTypeError(cx
, type
, "is not a ctypes type");
6643 JSObject
* result
= type
.toObjectOrNull();
6644 TypeCode typeCode
= CType::GetTypeCode(result
);
6646 // Arrays and functions can never be return types.
6647 if (typeCode
== TYPE_array
|| typeCode
== TYPE_function
) {
6648 FunctionReturnTypeError(cx
, type
, "cannot be an array or function");
6652 if (typeCode
!= TYPE_void_t
&& !CType::IsSizeDefined(result
)) {
6653 FunctionReturnTypeError(cx
, type
, "must have defined size");
6657 // libffi cannot pass types of zero size by value.
6658 MOZ_ASSERT(typeCode
== TYPE_void_t
|| CType::GetSize(result
) != 0);
6663 static MOZ_ALWAYS_INLINE
bool IsEllipsis(JSContext
* cx
, HandleValue v
,
6665 *isEllipsis
= false;
6666 if (!v
.isString()) {
6669 JSString
* str
= v
.toString();
6670 if (str
->length() != 3) {
6673 JSLinearString
* linear
= str
->ensureLinear(cx
);
6678 *isEllipsis
= (linear
->latin1OrTwoByteChar(0) == dot
&&
6679 linear
->latin1OrTwoByteChar(1) == dot
&&
6680 linear
->latin1OrTwoByteChar(2) == dot
);
6684 static bool PrepareCIF(JSContext
* cx
, FunctionInfo
* fninfo
) {
6686 RootedValue
abiType(cx
, ObjectOrNullValue(fninfo
->mABI
));
6687 if (!GetABI(cx
, abiType
, &abi
)) {
6688 JS_ReportErrorASCII(cx
, "Invalid ABI specification");
6692 ffi_type
* rtype
= CType::GetFFIType(cx
, fninfo
->mReturnType
);
6698 if (fninfo
->mIsVariadic
) {
6699 status
= ffi_prep_cif_var(&fninfo
->mCIF
, abi
, fninfo
->mArgTypes
.length(),
6700 fninfo
->mFFITypes
.length(), rtype
,
6701 fninfo
->mFFITypes
.begin());
6703 status
= ffi_prep_cif(&fninfo
->mCIF
, abi
, fninfo
->mFFITypes
.length(), rtype
,
6704 fninfo
->mFFITypes
.begin());
6711 JS_ReportErrorASCII(cx
, "Invalid ABI specification");
6713 case FFI_BAD_TYPEDEF
:
6714 JS_ReportErrorASCII(cx
, "Invalid type specification");
6717 JS_ReportErrorASCII(cx
, "Unknown libffi error");
6722 void FunctionType::BuildSymbolName(JSContext
* cx
, JSString
* name
,
6723 JSObject
* typeObj
, AutoCString
& result
) {
6724 FunctionInfo
* fninfo
= GetFunctionInfo(typeObj
);
6726 switch (GetABICode(fninfo
->mABI
)) {
6730 // For cdecl or WINAPI functions, no mangling is necessary.
6731 AppendString(cx
, result
, name
);
6735 #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
6736 // On WIN32, stdcall functions look like:
6738 // where 'foo' is the function name, and '40' is the aligned size of the
6740 AppendString(cx
, result
, "_");
6741 AppendString(cx
, result
, name
);
6742 AppendString(cx
, result
, "@");
6744 // Compute the suffix by aligning each argument to sizeof(ffi_arg).
6746 for (size_t i
= 0; i
< fninfo
->mArgTypes
.length(); ++i
) {
6747 JSObject
* argType
= fninfo
->mArgTypes
[i
];
6748 size
+= Align(CType::GetSize(argType
), sizeof(ffi_arg
));
6751 IntegerToString(size
, 10, result
);
6752 #elif defined(_WIN64)
6753 // On Win64, stdcall is an alias to the default ABI for compatibility, so
6754 // no mangling is done.
6755 AppendString(cx
, result
, name
);
6761 MOZ_CRASH("invalid abi");
6765 static bool CreateFunctionInfo(JSContext
* cx
, HandleObject typeObj
,
6766 HandleValue abiType
, HandleObject returnType
,
6767 const HandleValueArray
& args
) {
6768 FunctionInfo
* fninfo(cx
->new_
<FunctionInfo
>(cx
->zone()));
6773 // Stash the FunctionInfo in a reserved slot.
6774 JS_InitReservedSlot(typeObj
, SLOT_FNINFO
, fninfo
,
6775 JS::MemoryUse::CTypeFunctionInfo
);
6778 if (!GetABI(cx
, abiType
, &abi
)) {
6779 JS_ReportErrorASCII(cx
, "Invalid ABI specification");
6782 fninfo
->mABI
= abiType
.toObjectOrNull();
6784 fninfo
->mReturnType
= returnType
;
6786 // prepare the argument types
6787 if (!fninfo
->mArgTypes
.reserve(args
.length()) ||
6788 !fninfo
->mFFITypes
.reserve(args
.length())) {
6789 JS_ReportOutOfMemory(cx
);
6793 fninfo
->mIsVariadic
= false;
6795 for (uint32_t i
= 0; i
< args
.length(); ++i
) {
6797 if (!IsEllipsis(cx
, args
[i
], &isEllipsis
)) {
6801 fninfo
->mIsVariadic
= true;
6803 JS_ReportErrorASCII(cx
,
6804 "\"...\" may not be the first and only parameter "
6805 "type of a variadic function declaration");
6808 if (i
< args
.length() - 1) {
6809 JS_ReportErrorASCII(cx
,
6810 "\"...\" must be the last parameter type of a "
6811 "variadic function declaration");
6814 if (GetABICode(fninfo
->mABI
) != ABI_DEFAULT
) {
6815 JS_ReportErrorASCII(cx
,
6816 "Variadic functions must use the __cdecl calling "
6823 JSObject
* argType
= PrepareType(cx
, i
, args
[i
]);
6828 ffi_type
* ffiType
= CType::GetFFIType(cx
, argType
);
6833 fninfo
->mArgTypes
.infallibleAppend(argType
);
6834 fninfo
->mFFITypes
.infallibleAppend(ffiType
);
6837 if (fninfo
->mIsVariadic
) {
6838 // wait to PrepareCIF until function is called
6842 if (!PrepareCIF(cx
, fninfo
)) {
6849 bool FunctionType::Create(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6850 // Construct and return a new FunctionType object.
6851 CallArgs args
= CallArgsFromVp(argc
, vp
);
6852 if (args
.length() < 2 || args
.length() > 3) {
6853 return ArgumentLengthError(cx
, "FunctionType", "two or three", "s");
6856 JS::RootedValueVector
argTypes(cx
);
6857 RootedObject
arrayObj(cx
, nullptr);
6859 if (args
.length() == 3) {
6860 // Prepare an array of Values for the arguments.
6862 if (!args
[2].isObject()) {
6865 if (!JS::IsArrayObject(cx
, args
[2], &isArray
)) {
6871 return ArgumentTypeMismatch(cx
, "third ", "FunctionType", "an array");
6874 arrayObj
= &args
[2].toObject();
6877 MOZ_ALWAYS_TRUE(JS::GetArrayLength(cx
, arrayObj
, &len
));
6879 if (!argTypes
.resize(len
)) {
6880 JS_ReportOutOfMemory(cx
);
6885 // Pull out the argument types from the array, if any.
6886 MOZ_ASSERT_IF(argTypes
.length(), arrayObj
);
6887 for (uint32_t i
= 0; i
< argTypes
.length(); ++i
) {
6888 if (!JS_GetElement(cx
, arrayObj
, i
, argTypes
[i
])) {
6893 JSObject
* result
= CreateInternal(cx
, args
[0], args
[1], argTypes
);
6898 args
.rval().setObject(*result
);
6902 JSObject
* FunctionType::CreateInternal(JSContext
* cx
, HandleValue abi
,
6904 const HandleValueArray
& args
) {
6905 // Prepare the result type
6906 RootedObject
returnType(cx
, PrepareReturnType(cx
, rtype
));
6911 // Get ctypes.FunctionType.prototype and the common prototype for CData
6912 // objects of this type, from ctypes.CType.prototype.
6913 RootedObject
typeProto(
6914 cx
, CType::GetProtoFromType(cx
, returnType
, SLOT_FUNCTIONPROTO
));
6918 RootedObject
dataProto(
6919 cx
, CType::GetProtoFromType(cx
, returnType
, SLOT_FUNCTIONDATAPROTO
));
6924 // Create a new CType object with the common properties and slots.
6925 RootedObject
typeObj(
6926 cx
, CType::Create(cx
, typeProto
, dataProto
, TYPE_function
, nullptr,
6927 JS::UndefinedHandleValue
, JS::UndefinedHandleValue
,
6933 // Determine and check the types, and prepare the function CIF.
6934 if (!CreateFunctionInfo(cx
, typeObj
, abi
, returnType
, args
)) {
6941 // Construct a function pointer to a JS function (see CClosure::Create()).
6942 // Regular function pointers are constructed directly in
6943 // PointerType::ConstructData().
6944 bool FunctionType::ConstructData(JSContext
* cx
, HandleObject typeObj
,
6945 HandleObject dataObj
, HandleObject fnObj
,
6946 HandleObject thisObj
, HandleValue errVal
) {
6947 MOZ_ASSERT(CType::GetTypeCode(typeObj
) == TYPE_function
);
6949 PRFuncPtr
* data
= static_cast<PRFuncPtr
*>(CData::GetData(dataObj
));
6951 FunctionInfo
* fninfo
= FunctionType::GetFunctionInfo(typeObj
);
6952 if (fninfo
->mIsVariadic
) {
6953 JS_ReportErrorASCII(cx
, "Can't declare a variadic callback function");
6956 if (GetABICode(fninfo
->mABI
) == ABI_WINAPI
) {
6957 JS_ReportErrorASCII(cx
,
6958 "Can't declare a ctypes.winapi_abi callback function, "
6959 "use ctypes.stdcall_abi instead");
6963 RootedObject
closureObj(
6964 cx
, CClosure::Create(cx
, typeObj
, fnObj
, thisObj
, errVal
, data
));
6969 // Set the closure object as the referent of the new CData object.
6970 JS_SetReservedSlot(dataObj
, SLOT_REFERENT
, ObjectValue(*closureObj
));
6972 // Seal the CData object, to prevent modification of the function pointer.
6973 // This permanently associates this object with the closure, and avoids
6974 // having to do things like reset SLOT_REFERENT when someone tries to
6975 // change the pointer value.
6976 // XXX This will need to change when bug 541212 is fixed -- CData::ValueSetter
6977 // could be called on a frozen object.
6978 return JS_FreezeObject(cx
, dataObj
);
6981 typedef Vector
<AutoValue
, 16, SystemAllocPolicy
> AutoValueAutoArray
;
6983 static bool ConvertArgument(JSContext
* cx
, HandleObject funObj
,
6984 unsigned argIndex
, HandleValue arg
, JSObject
* type
,
6985 AutoValue
* value
, AutoValueAutoArray
* strings
) {
6986 if (!value
->SizeToType(cx
, type
)) {
6987 JS_ReportAllocationOverflow(cx
);
6991 bool freePointer
= false;
6992 if (!ImplicitConvert(cx
, arg
, type
, value
->mData
, ConversionType::Argument
,
6993 &freePointer
, funObj
, argIndex
))
6997 // ImplicitConvert converted a string for us, which we have to free.
6998 // Keep track of it.
6999 if (!strings
->growBy(1)) {
7000 JS_ReportOutOfMemory(cx
);
7003 strings
->back().mData
= *static_cast<char**>(value
->mData
);
7009 bool FunctionType::Call(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7010 CallArgs args
= CallArgsFromVp(argc
, vp
);
7011 // get the callee object...
7012 RootedObject
obj(cx
, &args
.callee());
7013 if (!CData::IsCDataMaybeUnwrap(&obj
)) {
7014 return IncompatibleThisProto(cx
, "FunctionType.prototype.call",
7018 RootedObject
typeObj(cx
, CData::GetCType(obj
));
7019 if (CType::GetTypeCode(typeObj
) != TYPE_pointer
) {
7020 return IncompatibleThisType(cx
, "FunctionType.prototype.call",
7021 "non-PointerType CData", args
.calleev());
7024 typeObj
= PointerType::GetBaseType(typeObj
);
7025 if (CType::GetTypeCode(typeObj
) != TYPE_function
) {
7026 return IncompatibleThisType(cx
, "FunctionType.prototype.call",
7027 "non-FunctionType pointer", args
.calleev());
7030 FunctionInfo
* fninfo
= GetFunctionInfo(typeObj
);
7031 uint32_t argcFixed
= fninfo
->mArgTypes
.length();
7033 if ((!fninfo
->mIsVariadic
&& args
.length() != argcFixed
) ||
7034 (fninfo
->mIsVariadic
&& args
.length() < argcFixed
)) {
7035 return FunctionArgumentLengthMismatch(cx
, argcFixed
, args
.length(), obj
,
7036 typeObj
, fninfo
->mIsVariadic
);
7039 // Check if we have a Library object. If we do, make sure it's open.
7040 Value slot
= JS::GetReservedSlot(obj
, SLOT_REFERENT
);
7041 if (!slot
.isUndefined() && Library::IsLibrary(&slot
.toObject())) {
7042 PRLibrary
* library
= Library::GetLibrary(&slot
.toObject());
7044 JS_ReportErrorASCII(cx
, "library is not open");
7049 // prepare the values for each argument
7050 AutoValueAutoArray values
;
7051 AutoValueAutoArray strings
;
7052 if (!values
.resize(args
.length())) {
7053 JS_ReportOutOfMemory(cx
);
7057 for (unsigned i
= 0; i
< argcFixed
; ++i
) {
7058 if (!ConvertArgument(cx
, obj
, i
, args
[i
], fninfo
->mArgTypes
[i
], &values
[i
],
7064 if (fninfo
->mIsVariadic
) {
7065 if (!fninfo
->mFFITypes
.resize(args
.length())) {
7066 JS_ReportOutOfMemory(cx
);
7070 RootedObject
obj(cx
); // Could reuse obj instead of declaring a second
7071 RootedObject
type(cx
); // RootedObject, but readability would suffer.
7072 RootedValue
arg(cx
);
7074 for (uint32_t i
= argcFixed
; i
< args
.length(); ++i
) {
7075 obj
= args
[i
].isObject() ? &args
[i
].toObject() : nullptr;
7076 if (!obj
|| !CData::IsCDataMaybeUnwrap(&obj
)) {
7077 // Since we know nothing about the CTypes of the ... arguments,
7078 // they absolutely must be CData objects already.
7079 return VariadicArgumentTypeError(cx
, i
, args
[i
]);
7081 type
= CData::GetCType(obj
);
7083 // These functions report their own errors.
7086 RootedValue
typeVal(cx
, ObjectValue(*type
));
7087 type
= PrepareType(cx
, i
, typeVal
);
7091 // Relying on ImplicitConvert only for the limited purpose of
7092 // converting one CType to another (e.g., T[] to T*).
7093 arg
= ObjectValue(*obj
);
7094 if (!ConvertArgument(cx
, obj
, i
, arg
, type
, &values
[i
], &strings
)) {
7097 fninfo
->mFFITypes
[i
] = CType::GetFFIType(cx
, type
);
7098 if (!fninfo
->mFFITypes
[i
]) {
7102 if (!PrepareCIF(cx
, fninfo
)) {
7107 // initialize a pointer to an appropriate location, for storing the result
7108 AutoValue returnValue
;
7109 TypeCode typeCode
= CType::GetTypeCode(fninfo
->mReturnType
);
7110 if (typeCode
!= TYPE_void_t
&&
7111 !returnValue
.SizeToType(cx
, fninfo
->mReturnType
)) {
7112 JS_ReportAllocationOverflow(cx
);
7116 // Let the runtime callback know that we are about to call into C.
7117 AutoCTypesActivityCallback
autoCallback(cx
, CTypesActivityType::BeginCall
,
7118 CTypesActivityType::EndCall
);
7120 uintptr_t fn
= *reinterpret_cast<uintptr_t*>(CData::GetData(obj
));
7123 int32_t lastErrorStatus
; // The status as defined by |GetLastError|
7124 int32_t savedLastError
= GetLastError();
7126 #endif // defined(XP_WIN)
7127 int errnoStatus
; // The status as defined by |errno|
7128 int savedErrno
= errno
;
7131 ffi_call(&fninfo
->mCIF
, FFI_FN(fn
), returnValue
.mData
,
7132 reinterpret_cast<void**>(values
.begin()));
7134 // Save error value.
7135 // We need to save it before leaving the scope of |suspend| as destructing
7136 // |suspend| has the side-effect of clearing |GetLastError|
7137 // (see bug 684017).
7139 errnoStatus
= errno
;
7141 lastErrorStatus
= GetLastError();
7142 SetLastError(savedLastError
);
7143 #endif // defined(XP_WIN)
7147 // We're no longer calling into C.
7148 autoCallback
.DoEndCallback();
7150 // Store the error value for later consultation with |ctypes.getStatus|
7151 JSObject
* objCTypes
= CType::GetGlobalCTypes(cx
, typeObj
);
7156 JS_SetReservedSlot(objCTypes
, SLOT_ERRNO
, Int32Value(errnoStatus
));
7158 JS_SetReservedSlot(objCTypes
, SLOT_LASTERROR
, Int32Value(lastErrorStatus
));
7159 #endif // defined(XP_WIN)
7161 // Small integer types get returned as a word-sized ffi_arg. Coerce it back
7162 // into the correct size for ConvertToJS.
7164 #define INTEGRAL_CASE(name, type, ffiType) \
7166 if (sizeof(type) < sizeof(ffi_arg)) { \
7167 ffi_arg data = *static_cast<ffi_arg*>(returnValue.mData); \
7168 *static_cast<type*>(returnValue.mData) = static_cast<type>(data); \
7171 CTYPES_FOR_EACH_INT_TYPE(INTEGRAL_CASE
)
7172 CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGRAL_CASE
)
7173 CTYPES_FOR_EACH_BOOL_TYPE(INTEGRAL_CASE
)
7174 CTYPES_FOR_EACH_CHAR_TYPE(INTEGRAL_CASE
)
7175 CTYPES_FOR_EACH_CHAR16_TYPE(INTEGRAL_CASE
)
7176 #undef INTEGRAL_CASE
7181 // prepare a JS object from the result
7182 RootedObject
returnType(cx
, fninfo
->mReturnType
);
7183 return ConvertToJS(cx
, returnType
, nullptr, returnValue
.mData
, false, true,
7187 FunctionInfo
* FunctionType::GetFunctionInfo(JSObject
* obj
) {
7188 MOZ_ASSERT(CType::IsCType(obj
));
7189 MOZ_ASSERT(CType::GetTypeCode(obj
) == TYPE_function
);
7191 Value slot
= JS::GetReservedSlot(obj
, SLOT_FNINFO
);
7192 MOZ_ASSERT(!slot
.isUndefined() && slot
.toPrivate());
7194 return static_cast<FunctionInfo
*>(slot
.toPrivate());
7197 bool FunctionType::IsFunctionType(HandleValue v
) {
7198 if (!v
.isObject()) {
7201 JSObject
* obj
= &v
.toObject();
7202 return CType::IsCType(obj
) && CType::GetTypeCode(obj
) == TYPE_function
;
7205 bool FunctionType::ArgTypesGetter(JSContext
* cx
, const JS::CallArgs
& args
) {
7206 JS::Rooted
<JSObject
*> obj(cx
, &args
.thisv().toObject());
7208 args
.rval().set(JS::GetReservedSlot(obj
, SLOT_ARGS_T
));
7209 if (!args
.rval().isUndefined()) {
7213 FunctionInfo
* fninfo
= GetFunctionInfo(obj
);
7214 size_t len
= fninfo
->mArgTypes
.length();
7216 // Prepare a new array.
7217 JS::Rooted
<JSObject
*> argTypes(cx
);
7219 JS::RootedValueVector
vec(cx
);
7220 if (!vec
.resize(len
)) {
7224 for (size_t i
= 0; i
< len
; ++i
) {
7225 vec
[i
].setObject(*fninfo
->mArgTypes
[i
]);
7228 argTypes
= JS::NewArrayObject(cx
, vec
);
7234 // Seal and cache it.
7235 if (!JS_FreezeObject(cx
, argTypes
)) {
7238 JS_SetReservedSlot(obj
, SLOT_ARGS_T
, JS::ObjectValue(*argTypes
));
7240 args
.rval().setObject(*argTypes
);
7244 bool FunctionType::ReturnTypeGetter(JSContext
* cx
, const JS::CallArgs
& args
) {
7245 // Get the returnType object from the FunctionInfo.
7246 args
.rval().setObject(
7247 *GetFunctionInfo(&args
.thisv().toObject())->mReturnType
);
7251 bool FunctionType::ABIGetter(JSContext
* cx
, const JS::CallArgs
& args
) {
7252 // Get the abi object from the FunctionInfo.
7253 args
.rval().setObject(*GetFunctionInfo(&args
.thisv().toObject())->mABI
);
7257 bool FunctionType::IsVariadicGetter(JSContext
* cx
, const JS::CallArgs
& args
) {
7258 args
.rval().setBoolean(
7259 GetFunctionInfo(&args
.thisv().toObject())->mIsVariadic
);
7263 /*******************************************************************************
7264 ** CClosure implementation
7265 *******************************************************************************/
7267 JSObject
* CClosure::Create(JSContext
* cx
, HandleObject typeObj
,
7268 HandleObject fnObj
, HandleObject thisObj
,
7269 HandleValue errVal
, PRFuncPtr
* fnptr
) {
7272 RootedObject
result(cx
, JS_NewObject(cx
, &sCClosureClass
));
7277 // Get the FunctionInfo from the FunctionType.
7278 FunctionInfo
* fninfo
= FunctionType::GetFunctionInfo(typeObj
);
7279 MOZ_ASSERT(!fninfo
->mIsVariadic
);
7280 MOZ_ASSERT(GetABICode(fninfo
->mABI
) != ABI_WINAPI
);
7282 // Get the prototype of the FunctionType object, of class CTypeProto,
7283 // which stores our JSContext for use with the closure.
7284 RootedObject
proto(cx
);
7285 if (!JS_GetPrototype(cx
, typeObj
, &proto
)) {
7289 MOZ_ASSERT(CType::IsCTypeProto(proto
));
7291 // Prepare the error sentinel value. It's important to do this now, because
7292 // we might be unable to convert the value to the proper type. If so, we want
7293 // the caller to know about it _now_, rather than some uncertain time in the
7294 // future when the error sentinel is actually needed.
7295 UniquePtr
<uint8_t[], JS::FreePolicy
> errResult
;
7296 if (!errVal
.isUndefined()) {
7297 // Make sure the callback returns something.
7298 if (CType::GetTypeCode(fninfo
->mReturnType
) == TYPE_void_t
) {
7299 JS_ReportErrorASCII(cx
, "A void callback can't pass an error sentinel");
7303 // With the exception of void, the FunctionType constructor ensures that
7304 // the return type has a defined size.
7305 MOZ_ASSERT(CType::IsSizeDefined(fninfo
->mReturnType
));
7307 // Allocate a buffer for the return value.
7308 size_t rvSize
= CType::GetSize(fninfo
->mReturnType
);
7309 errResult
= cx
->make_pod_array
<uint8_t>(rvSize
);
7314 // Do the value conversion. This might fail, in which case we throw.
7315 if (!ImplicitConvert(cx
, errVal
, fninfo
->mReturnType
, errResult
.get(),
7316 ConversionType::Return
, nullptr, typeObj
))
7320 ClosureInfo
* cinfo
= cx
->new_
<ClosureInfo
>(cx
);
7322 JS_ReportOutOfMemory(cx
);
7326 // Copy the important bits of context into cinfo.
7327 cinfo
->errResult
= errResult
.release();
7328 cinfo
->closureObj
= result
;
7329 cinfo
->typeObj
= typeObj
;
7330 cinfo
->thisObj
= thisObj
;
7331 cinfo
->jsfnObj
= fnObj
;
7333 // Stash the ClosureInfo struct on our new object.
7334 JS_InitReservedSlot(result
, SLOT_CLOSUREINFO
, cinfo
,
7335 JS::MemoryUse::CClosureInfo
);
7337 // Create an ffi_closure object and initialize it.
7340 static_cast<ffi_closure
*>(ffi_closure_alloc(sizeof(ffi_closure
), &code
));
7341 if (!cinfo
->closure
|| !code
) {
7342 JS_ReportErrorASCII(cx
, "couldn't create closure - libffi error");
7346 ffi_status status
= ffi_prep_closure_loc(cinfo
->closure
, &fninfo
->mCIF
,
7347 CClosure::ClosureStub
, cinfo
, code
);
7348 if (status
!= FFI_OK
) {
7349 JS_ReportErrorASCII(cx
, "couldn't create closure - libffi error");
7353 // Casting between void* and a function pointer is forbidden in C and C++.
7354 // Do it via an integral type.
7355 *fnptr
= reinterpret_cast<PRFuncPtr
>(reinterpret_cast<uintptr_t>(code
));
7359 void CClosure::Trace(JSTracer
* trc
, JSObject
* obj
) {
7360 // Make sure our ClosureInfo slot is legit. If it's not, bail.
7361 Value slot
= JS::GetReservedSlot(obj
, SLOT_CLOSUREINFO
);
7362 if (slot
.isUndefined()) {
7366 ClosureInfo
* cinfo
= static_cast<ClosureInfo
*>(slot
.toPrivate());
7368 TraceEdge(trc
, &cinfo
->closureObj
, "closureObj");
7369 TraceEdge(trc
, &cinfo
->typeObj
, "typeObj");
7370 TraceEdge(trc
, &cinfo
->jsfnObj
, "jsfnObj");
7371 TraceNullableEdge(trc
, &cinfo
->thisObj
, "thisObj");
7374 void CClosure::Finalize(JS::GCContext
* gcx
, JSObject
* obj
) {
7375 // Make sure our ClosureInfo slot is legit. If it's not, bail.
7376 Value slot
= JS::GetReservedSlot(obj
, SLOT_CLOSUREINFO
);
7377 if (slot
.isUndefined()) {
7381 ClosureInfo
* cinfo
= static_cast<ClosureInfo
*>(slot
.toPrivate());
7382 gcx
->delete_(obj
, cinfo
, MemoryUse::CClosureInfo
);
7385 void CClosure::ClosureStub(ffi_cif
* cif
, void* result
, void** args
,
7390 MOZ_ASSERT(userData
);
7392 // Retrieve the essentials from our closure object.
7393 ArgClosure
argClosure(cif
, result
, args
, static_cast<ClosureInfo
*>(userData
));
7394 JSContext
* cx
= argClosure
.cinfo
->cx
;
7396 js::AssertSameCompartment(cx
, argClosure
.cinfo
->jsfnObj
);
7398 RootedObject
global(cx
, JS::CurrentGlobalOrNull(cx
));
7401 js::PrepareScriptEnvironmentAndInvoke(cx
, global
, argClosure
);
7404 bool CClosure::ArgClosure::operator()(JSContext
* cx
) {
7405 // Let the runtime callback know that we are about to call into JS again. The
7406 // end callback will fire automatically when we exit this function.
7407 AutoCTypesActivityCallback
autoCallback(cx
, CTypesActivityType::BeginCallback
,
7408 CTypesActivityType::EndCallback
);
7410 RootedObject
typeObj(cx
, cinfo
->typeObj
);
7411 RootedObject
thisObj(cx
, cinfo
->thisObj
);
7412 RootedValue
jsfnVal(cx
, ObjectValue(*cinfo
->jsfnObj
));
7413 AssertSameCompartment(cx
, cinfo
->jsfnObj
);
7415 JS_AbortIfWrongThread(cx
);
7417 // Assert that our CIFs agree.
7418 FunctionInfo
* fninfo
= FunctionType::GetFunctionInfo(typeObj
);
7419 MOZ_ASSERT(cif
== &fninfo
->mCIF
);
7421 TypeCode typeCode
= CType::GetTypeCode(fninfo
->mReturnType
);
7423 // Initialize the result to zero, in case something fails. Small integer types
7424 // are promoted to a word-sized ffi_arg, so we must be careful to zero the
7427 if (cif
->rtype
!= &ffi_type_void
) {
7428 rvSize
= cif
->rtype
->size
;
7430 #define INTEGRAL_CASE(name, type, ffiType) case TYPE_##name:
7431 CTYPES_FOR_EACH_INT_TYPE(INTEGRAL_CASE
)
7432 CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGRAL_CASE
)
7433 CTYPES_FOR_EACH_BOOL_TYPE(INTEGRAL_CASE
)
7434 CTYPES_FOR_EACH_CHAR_TYPE(INTEGRAL_CASE
)
7435 CTYPES_FOR_EACH_CHAR16_TYPE(INTEGRAL_CASE
)
7436 #undef INTEGRAL_CASE
7437 rvSize
= Align(rvSize
, sizeof(ffi_arg
));
7442 memset(result
, 0, rvSize
);
7445 // Set up an array for converted arguments.
7446 JS::RootedValueVector
argv(cx
);
7447 if (!argv
.resize(cif
->nargs
)) {
7448 JS_ReportOutOfMemory(cx
);
7452 for (uint32_t i
= 0; i
< cif
->nargs
; ++i
) {
7453 // Convert each argument, and have any CData objects created depend on
7454 // the existing buffers.
7455 RootedObject
argType(cx
, fninfo
->mArgTypes
[i
]);
7456 if (!ConvertToJS(cx
, argType
, nullptr, args
[i
], false, false, argv
[i
])) {
7461 // Call the JS function. 'thisObj' may be nullptr, in which case the JS
7462 // engine will find an appropriate object to use.
7463 RootedValue
rval(cx
);
7464 bool success
= JS_CallFunctionValue(cx
, thisObj
, jsfnVal
, argv
, &rval
);
7466 // Convert the result. Note that we pass 'ConversionType::Return', such that
7467 // ImplicitConvert will *not* autoconvert a JS string into a pointer-to-char
7468 // type, which would require an allocation that we can't track. The JS
7469 // function must perform this conversion itself and return a PointerType
7470 // CData; thusly, the burden of freeing the data is left to the user.
7471 if (success
&& cif
->rtype
!= &ffi_type_void
) {
7472 success
= ImplicitConvert(cx
, rval
, fninfo
->mReturnType
, result
,
7473 ConversionType::Return
, nullptr, typeObj
);
7477 // Something failed. The callee may have thrown, or it may not have
7478 // returned a value that ImplicitConvert() was happy with. Depending on how
7479 // prudent the consumer has been, we may or may not have a recovery plan.
7481 // Note that PrepareScriptEnvironmentAndInvoke should take care of reporting
7484 if (cinfo
->errResult
) {
7485 // Good case: we have a sentinel that we can return. Copy it in place of
7486 // the actual return value, and then proceed.
7488 // The buffer we're returning might be larger than the size of the return
7489 // type, due to libffi alignment issues (see above). But it should never
7491 size_t copySize
= CType::GetSize(fninfo
->mReturnType
);
7492 MOZ_ASSERT(copySize
<= rvSize
);
7493 memcpy(result
, cinfo
->errResult
, copySize
);
7495 // We still want to return false here, so that
7496 // PrepareScriptEnvironmentAndInvoke will report the exception.
7498 // Bad case: not much we can do here. The rv is already zeroed out, so we
7499 // just return and hope for the best.
7504 // Small integer types must be returned as a word-sized ffi_arg. Coerce it
7505 // back into the size libffi expects.
7507 #define INTEGRAL_CASE(name, type, ffiType) \
7509 if (sizeof(type) < sizeof(ffi_arg)) { \
7510 ffi_arg data = *static_cast<type*>(result); \
7511 *static_cast<ffi_arg*>(result) = data; \
7514 CTYPES_FOR_EACH_INT_TYPE(INTEGRAL_CASE
)
7515 CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGRAL_CASE
)
7516 CTYPES_FOR_EACH_BOOL_TYPE(INTEGRAL_CASE
)
7517 CTYPES_FOR_EACH_CHAR_TYPE(INTEGRAL_CASE
)
7518 CTYPES_FOR_EACH_CHAR16_TYPE(INTEGRAL_CASE
)
7519 #undef INTEGRAL_CASE
7527 /*******************************************************************************
7528 ** CData implementation
7529 *******************************************************************************/
7531 // Create a new CData object of type 'typeObj' containing binary data supplied
7532 // in 'source', optionally with a referent object 'refObj'.
7534 // * 'typeObj' must be a CType of defined (but possibly zero) size.
7536 // * If an object 'refObj' is supplied, the new CData object stores the
7537 // referent object in a reserved slot for GC safety, such that 'refObj' will
7538 // be held alive by the resulting CData object. 'refObj' may or may not be
7539 // a CData object; merely an object we want to keep alive.
7540 // * If 'refObj' is a CData object, 'ownResult' must be false.
7541 // * Otherwise, 'refObj' is a Library or CClosure object, and 'ownResult'
7542 // may be true or false.
7543 // * Otherwise 'refObj' is nullptr. In this case, 'ownResult' may be true or
7546 // * If 'ownResult' is true, the CData object will allocate an appropriately
7547 // sized buffer, and free it upon finalization. If 'source' data is
7548 // supplied, the data will be copied from 'source' into the buffer;
7549 // otherwise, the entirety of the new buffer will be initialized to zero.
7550 // * If 'ownResult' is false, the new CData's buffer refers to a slice of
7551 // another buffer kept alive by 'refObj'. 'source' data must be provided,
7552 // and the new CData's buffer will refer to 'source'.
7553 JSObject
* CData::Create(JSContext
* cx
, HandleObject typeObj
,
7554 HandleObject refObj
, void* source
, bool ownResult
) {
7555 MOZ_ASSERT(typeObj
);
7556 MOZ_ASSERT(CType::IsCType(typeObj
));
7557 MOZ_ASSERT(CType::IsSizeDefined(typeObj
));
7558 MOZ_ASSERT(ownResult
|| source
);
7559 MOZ_ASSERT_IF(refObj
&& CData::IsCData(refObj
), !ownResult
);
7561 // Get the 'prototype' property from the type.
7562 Value slot
= JS::GetReservedSlot(typeObj
, SLOT_PROTO
);
7563 MOZ_ASSERT(slot
.isObject());
7565 RootedObject
proto(cx
, &slot
.toObject());
7567 RootedObject
dataObj(cx
, JS_NewObjectWithGivenProto(cx
, &sCDataClass
, proto
));
7572 // set the CData's associated type
7573 JS_SetReservedSlot(dataObj
, SLOT_CTYPE
, ObjectValue(*typeObj
));
7575 // Stash the referent object, if any, for GC safety.
7577 JS_SetReservedSlot(dataObj
, SLOT_REFERENT
, ObjectValue(*refObj
));
7580 // Set our ownership flag.
7581 JS_SetReservedSlot(dataObj
, SLOT_OWNS
, BooleanValue(ownResult
));
7583 // attach the buffer. since it might not be 2-byte aligned, we need to
7584 // allocate an aligned space for it and store it there. :(
7585 UniquePtr
<char*, JS::FreePolicy
> buffer(cx
->new_
<char*>());
7592 data
= static_cast<char*>(source
);
7594 // Initialize our own buffer.
7595 size_t size
= CType::GetSize(typeObj
);
7596 data
= cx
->pod_malloc
<char>(size
);
7602 memset(data
, 0, size
);
7604 memcpy(data
, source
, size
);
7607 AddCellMemory(dataObj
, size
, MemoryUse::CDataBuffer
);
7610 *buffer
.get() = data
;
7611 JS_InitReservedSlot(dataObj
, SLOT_DATA
, buffer
.release(),
7612 JS::MemoryUse::CDataBufferPtr
);
7614 // If this is an array, wrap it in a proxy so we can intercept element
7617 if (CType::GetTypeCode(typeObj
) != TYPE_array
) {
7621 RootedValue
priv(cx
, ObjectValue(*dataObj
));
7622 ProxyOptions options
;
7623 options
.setLazyProto(true);
7624 return NewProxyObject(cx
, &CDataArrayProxyHandler::singleton
, priv
, nullptr,
7628 void CData::Finalize(JS::GCContext
* gcx
, JSObject
* obj
) {
7629 // Delete our buffer, and the data it contains if we own it.
7630 Value slot
= JS::GetReservedSlot(obj
, SLOT_OWNS
);
7631 if (slot
.isUndefined()) {
7635 bool owns
= slot
.toBoolean();
7637 slot
= JS::GetReservedSlot(obj
, SLOT_DATA
);
7638 if (slot
.isUndefined()) {
7641 char** buffer
= static_cast<char**>(slot
.toPrivate());
7644 JSObject
* typeObj
= &JS::GetReservedSlot(obj
, SLOT_CTYPE
).toObject();
7645 size_t size
= CType::GetSize(typeObj
);
7646 gcx
->free_(obj
, *buffer
, size
, MemoryUse::CDataBuffer
);
7648 gcx
->delete_(obj
, buffer
, MemoryUse::CDataBufferPtr
);
7651 JSObject
* CData::GetCType(JSObject
* dataObj
) {
7652 dataObj
= MaybeUnwrapArrayWrapper(dataObj
);
7653 MOZ_ASSERT(CData::IsCData(dataObj
));
7655 Value slot
= JS::GetReservedSlot(dataObj
, SLOT_CTYPE
);
7656 JSObject
* typeObj
= slot
.toObjectOrNull();
7657 MOZ_ASSERT(CType::IsCType(typeObj
));
7661 void* CData::GetData(JSObject
* dataObj
) {
7662 dataObj
= MaybeUnwrapArrayWrapper(dataObj
);
7663 MOZ_ASSERT(CData::IsCData(dataObj
));
7665 Value slot
= JS::GetReservedSlot(dataObj
, SLOT_DATA
);
7667 void** buffer
= static_cast<void**>(slot
.toPrivate());
7669 MOZ_ASSERT(*buffer
);
7673 bool CData::IsCData(JSObject
* obj
) {
7674 // Assert we don't have an array wrapper.
7675 MOZ_ASSERT(MaybeUnwrapArrayWrapper(obj
) == obj
);
7677 return obj
->hasClass(&sCDataClass
);
7680 bool CData::IsCDataMaybeUnwrap(MutableHandleObject obj
) {
7681 obj
.set(MaybeUnwrapArrayWrapper(obj
));
7682 return IsCData(obj
);
7685 bool CData::IsCData(HandleValue v
) {
7686 return v
.isObject() && CData::IsCData(MaybeUnwrapArrayWrapper(&v
.toObject()));
7689 bool CData::IsCDataProto(JSObject
* obj
) {
7690 return obj
->hasClass(&sCDataProtoClass
);
7693 bool CData::ValueGetter(JSContext
* cx
, const JS::CallArgs
& args
) {
7694 RootedObject
obj(cx
, &args
.thisv().toObject());
7696 // Convert the value to a primitive; do not create a new CData object.
7697 RootedObject
ctype(cx
, GetCType(obj
));
7698 return ConvertToJS(cx
, ctype
, nullptr, GetData(obj
), true, false,
7702 bool CData::ValueSetter(JSContext
* cx
, const JS::CallArgs
& args
) {
7703 RootedObject
obj(cx
, &args
.thisv().toObject());
7704 args
.rval().setUndefined();
7705 return ImplicitConvert(cx
, args
.get(0), GetCType(obj
), GetData(obj
),
7706 ConversionType::Setter
, nullptr);
7709 bool CData::Address(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7710 CallArgs args
= CallArgsFromVp(argc
, vp
);
7711 if (args
.length() != 0) {
7712 return ArgumentLengthError(cx
, "CData.prototype.address", "no", "s");
7715 RootedObject
obj(cx
, GetThisObject(cx
, args
, "CData.prototype.address"));
7719 if (!IsCDataMaybeUnwrap(&obj
)) {
7720 return IncompatibleThisProto(cx
, "CData.prototype.address", args
.thisv());
7723 RootedObject
typeObj(cx
, CData::GetCType(obj
));
7724 RootedObject
pointerType(cx
, PointerType::CreateInternal(cx
, typeObj
));
7729 // Create a PointerType CData object containing null.
7730 JSObject
* result
= CData::Create(cx
, pointerType
, nullptr, nullptr, true);
7735 args
.rval().setObject(*result
);
7737 // Manually set the pointer inside the object, so we skip the conversion step.
7738 void** data
= static_cast<void**>(GetData(result
));
7739 *data
= GetData(obj
);
7743 bool CData::Cast(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7744 CallArgs args
= CallArgsFromVp(argc
, vp
);
7745 if (args
.length() != 2) {
7746 return ArgumentLengthError(cx
, "ctypes.cast", "two", "s");
7749 RootedObject
sourceData(cx
);
7750 if (args
[0].isObject()) {
7751 sourceData
= &args
[0].toObject();
7754 if (!sourceData
|| !CData::IsCDataMaybeUnwrap(&sourceData
)) {
7755 return ArgumentTypeMismatch(cx
, "first ", "ctypes.cast", "a CData");
7757 RootedObject
sourceType(cx
, CData::GetCType(sourceData
));
7759 if (args
[1].isPrimitive() || !CType::IsCType(&args
[1].toObject())) {
7760 return ArgumentTypeMismatch(cx
, "second ", "ctypes.cast", "a CType");
7763 RootedObject
targetType(cx
, &args
[1].toObject());
7765 if (!CType::GetSafeSize(targetType
, &targetSize
)) {
7766 return UndefinedSizeCastError(cx
, targetType
);
7768 if (targetSize
> CType::GetSize(sourceType
)) {
7769 return SizeMismatchCastError(cx
, sourceType
, targetType
,
7770 CType::GetSize(sourceType
), targetSize
);
7773 // Construct a new CData object with a type of 'targetType' and a referent
7775 void* data
= CData::GetData(sourceData
);
7776 JSObject
* result
= CData::Create(cx
, targetType
, sourceData
, data
, false);
7781 args
.rval().setObject(*result
);
7785 bool CData::GetRuntime(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7786 CallArgs args
= CallArgsFromVp(argc
, vp
);
7787 if (args
.length() != 1) {
7788 return ArgumentLengthError(cx
, "ctypes.getRuntime", "one", "");
7791 if (args
[0].isPrimitive() || !CType::IsCType(&args
[0].toObject())) {
7792 return ArgumentTypeMismatch(cx
, "", "ctypes.getRuntime", "a CType");
7795 RootedObject
targetType(cx
, &args
[0].toObject());
7797 if (!CType::GetSafeSize(targetType
, &targetSize
) ||
7798 targetSize
!= sizeof(void*)) {
7799 JS_ReportErrorASCII(cx
, "target CType has non-pointer size");
7803 void* data
= static_cast<void*>(cx
->runtime());
7804 JSObject
* result
= CData::Create(cx
, targetType
, nullptr, &data
, true);
7809 args
.rval().setObject(*result
);
7813 // Unwrap the `this` object to a CData object, or extract a CData object from a
7815 static bool GetThisDataObject(JSContext
* cx
, const CallArgs
& args
,
7816 const char* funName
, MutableHandleObject obj
) {
7817 obj
.set(GetThisObject(cx
, args
, funName
));
7819 return IncompatibleThisProto(cx
, funName
, args
.thisv());
7821 if (!CData::IsCDataMaybeUnwrap(obj
)) {
7822 if (!CDataFinalizer::IsCDataFinalizer(obj
)) {
7823 return IncompatibleThisProto(cx
, funName
, args
.thisv());
7826 CDataFinalizer::Private
* p
= GetFinalizerPrivate(obj
);
7828 return EmptyFinalizerCallError(cx
, funName
);
7831 RootedValue
dataVal(cx
);
7832 if (!CDataFinalizer::GetValue(cx
, obj
, &dataVal
)) {
7833 return IncompatibleThisProto(cx
, funName
, args
.thisv());
7836 if (dataVal
.isPrimitive()) {
7837 return IncompatibleThisProto(cx
, funName
, args
.thisv());
7840 obj
.set(dataVal
.toObjectOrNull());
7841 if (!obj
|| !CData::IsCDataMaybeUnwrap(obj
)) {
7842 return IncompatibleThisProto(cx
, funName
, args
.thisv());
7849 typedef JS::TwoByteCharsZ (*InflateUTF8Method
)(JSContext
*, const JS::UTF8Chars
&,
7850 size_t*, arena_id_t
);
7852 static bool ReadStringCommon(JSContext
* cx
, InflateUTF8Method inflateUTF8
,
7853 unsigned argc
, Value
* vp
, const char* funName
,
7854 arena_id_t destArenaId
) {
7855 CallArgs args
= CallArgsFromVp(argc
, vp
);
7856 if (args
.length() != 0) {
7857 return ArgumentLengthError(cx
, funName
, "no", "s");
7860 RootedObject
obj(cx
);
7861 if (!GetThisDataObject(cx
, args
, funName
, &obj
)) {
7865 // Make sure we are a pointer to, or an array of, an 8-bit or 16-bit
7866 // character or integer type.
7868 JSObject
* typeObj
= CData::GetCType(obj
);
7869 TypeCode typeCode
= CType::GetTypeCode(typeObj
);
7871 size_t maxLength
= -1;
7874 baseType
= PointerType::GetBaseType(typeObj
);
7875 data
= *static_cast<void**>(CData::GetData(obj
));
7876 if (data
== nullptr) {
7877 return NullPointerError(cx
, "read contents of", obj
);
7881 baseType
= ArrayType::GetBaseType(typeObj
);
7882 data
= CData::GetData(obj
);
7883 maxLength
= ArrayType::GetLength(typeObj
);
7886 return TypeError(cx
, "PointerType or ArrayType", args
.thisv());
7889 // Convert the string buffer, taking care to determine the correct string
7890 // length in the case of arrays (which may contain embedded nulls).
7892 switch (CType::GetTypeCode(baseType
)) {
7896 case TYPE_signed_char
:
7897 case TYPE_unsigned_char
: {
7898 char* bytes
= static_cast<char*>(data
);
7899 size_t length
= js_strnlen(bytes
, maxLength
);
7901 // Determine the length.
7902 UniqueTwoByteChars
dst(
7903 inflateUTF8(cx
, JS::UTF8Chars(bytes
, length
), &length
, destArenaId
)
7909 result
= JS_NewUCString(cx
, std::move(dst
), length
);
7919 case TYPE_unsigned_short
:
7920 case TYPE_char16_t
: {
7921 char16_t
* chars
= static_cast<char16_t
*>(data
);
7922 size_t length
= js_strnlen(chars
, maxLength
);
7923 result
= JS_NewUCStringCopyN(cx
, chars
, length
);
7927 return NonStringBaseError(cx
, args
.thisv());
7934 args
.rval().setString(result
);
7938 bool CData::ReadString(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7939 return ReadStringCommon(cx
, JS::UTF8CharsToNewTwoByteCharsZ
, argc
, vp
,
7940 "CData.prototype.readString", js::StringBufferArena
);
7943 bool CDataFinalizer::Methods::ReadString(JSContext
* cx
, unsigned argc
,
7945 return ReadStringCommon(cx
, JS::UTF8CharsToNewTwoByteCharsZ
, argc
, vp
,
7946 "CDataFinalizer.prototype.readString",
7947 js::StringBufferArena
);
7950 bool CData::ReadStringReplaceMalformed(JSContext
* cx
, unsigned argc
,
7952 return ReadStringCommon(cx
, JS::LossyUTF8CharsToNewTwoByteCharsZ
, argc
, vp
,
7953 "CData.prototype.readStringReplaceMalformed",
7954 js::StringBufferArena
);
7957 using TypedArrayConstructor
= JSObject
* (*)(JSContext
*, size_t);
7959 template <typename Type
>
7960 TypedArrayConstructor
GetTypedArrayConstructorImpl() {
7961 if (std::is_floating_point_v
<Type
>) {
7962 switch (sizeof(Type
)) {
7964 return JS_NewFloat32Array
;
7966 return JS_NewFloat64Array
;
7972 constexpr bool isSigned
= std::is_signed_v
<Type
>;
7973 switch (sizeof(Type
)) {
7975 return isSigned
? JS_NewInt8Array
: JS_NewUint8Array
;
7977 return isSigned
? JS_NewInt16Array
: JS_NewUint16Array
;
7979 return isSigned
? JS_NewInt32Array
: JS_NewUint32Array
;
7985 static TypedArrayConstructor
GetTypedArrayConstructor(TypeCode baseType
) {
7987 #define MACRO(name, ctype, _) \
7989 return GetTypedArrayConstructorImpl<ctype>();
7990 CTYPES_FOR_EACH_TYPE(MACRO
)
7997 static bool ReadTypedArrayCommon(JSContext
* cx
, unsigned argc
, Value
* vp
,
7998 const char* funName
) {
7999 CallArgs args
= CallArgsFromVp(argc
, vp
);
8000 if (args
.length() != 0) {
8001 return ArgumentLengthError(cx
, funName
, "no", "s");
8004 RootedObject
obj(cx
);
8005 if (!GetThisDataObject(cx
, args
, funName
, &obj
)) {
8009 // Make sure we are a pointer to, or an array of, a type that is compatible
8010 // with a typed array base type.
8012 JSObject
* typeObj
= CData::GetCType(obj
);
8013 TypeCode typeCode
= CType::GetTypeCode(typeObj
);
8015 mozilla::Maybe
<size_t> length
;
8018 baseType
= PointerType::GetBaseType(typeObj
);
8019 data
= *static_cast<void**>(CData::GetData(obj
));
8020 if (data
== nullptr) {
8021 return NullPointerError(cx
, "read contents of", obj
);
8025 baseType
= ArrayType::GetBaseType(typeObj
);
8026 data
= CData::GetData(obj
);
8027 length
.emplace(ArrayType::GetLength(typeObj
));
8030 return TypeError(cx
, "PointerType or ArrayType", args
.thisv());
8033 TypeCode baseTypeCode
= CType::GetTypeCode(baseType
);
8035 // For string inputs only, use strlen to determine the length.
8036 switch (baseTypeCode
) {
8038 case TYPE_signed_char
:
8039 case TYPE_unsigned_char
:
8041 length
.emplace(js_strnlen(static_cast<char*>(data
), INT32_MAX
));
8047 length
.emplace(js_strlen(static_cast<char16_t
*>(data
)));
8056 return NonStringBaseError(cx
, args
.thisv());
8059 auto makeTypedArray
= GetTypedArrayConstructor(baseTypeCode
);
8060 if (!makeTypedArray
) {
8061 return NonTypedArrayBaseError(cx
, args
.thisv());
8064 CheckedInt
<size_t> size
= *length
;
8065 size
*= CType::GetSize(baseType
);
8066 if (!size
.isValid() || size
.value() > ArrayBufferObject::ByteLengthLimit
) {
8067 return SizeOverflow(cx
, "data", "typed array");
8070 JSObject
* result
= makeTypedArray(cx
, *length
);
8075 AutoCheckCannotGC
nogc(cx
);
8077 void* buffer
= JS_GetArrayBufferViewData(&result
->as
<ArrayBufferViewObject
>(),
8079 MOZ_ASSERT(!isShared
);
8080 memcpy(buffer
, data
, size
.value());
8082 args
.rval().setObject(*result
);
8086 bool CData::ReadTypedArray(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8087 return ReadTypedArrayCommon(cx
, argc
, vp
, "CData.prototype.readTypedArray");
8090 bool CDataFinalizer::Methods::ReadTypedArray(JSContext
* cx
, unsigned argc
,
8092 return ReadTypedArrayCommon(cx
, argc
, vp
,
8093 "CDataFinalizer.prototype.readTypedArray");
8096 JSString
* CData::GetSourceString(JSContext
* cx
, HandleObject typeObj
,
8098 // Walk the types, building up the toSource() string.
8099 // First, we build up the type expression:
8100 // 't.ptr' for pointers;
8101 // 't.array([n])' for arrays;
8102 // 'n' for structs, where n = t.name, the struct's name. (We assume this is
8103 // bound to a variable in the current scope.)
8105 BuildTypeSource(cx
, typeObj
, true, source
);
8106 AppendString(cx
, source
, "(");
8107 if (!BuildDataSource(cx
, typeObj
, data
, false, source
)) {
8108 source
.handle(false);
8110 AppendString(cx
, source
, ")");
8115 return NewUCString(cx
, source
.finish());
8118 bool CData::ToSource(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8119 CallArgs args
= CallArgsFromVp(argc
, vp
);
8120 if (args
.length() != 0) {
8121 return ArgumentLengthError(cx
, "CData.prototype.toSource", "no", "s");
8124 RootedObject
obj(cx
, GetThisObject(cx
, args
, "CData.prototype.toSource"));
8128 if (!CData::IsCDataMaybeUnwrap(&obj
) && !CData::IsCDataProto(obj
)) {
8129 return IncompatibleThisProto(cx
, "CData.prototype.toSource",
8130 InformalValueTypeName(args
.thisv()));
8134 if (CData::IsCData(obj
)) {
8135 RootedObject
typeObj(cx
, CData::GetCType(obj
));
8136 void* data
= CData::GetData(obj
);
8138 result
= CData::GetSourceString(cx
, typeObj
, data
);
8140 result
= JS_NewStringCopyZ(cx
, "[CData proto object]");
8147 args
.rval().setString(result
);
8151 bool CData::ErrnoGetter(JSContext
* cx
, const JS::CallArgs
& args
) {
8152 args
.rval().set(JS::GetReservedSlot(&args
.thisv().toObject(), SLOT_ERRNO
));
8157 bool CData::LastErrorGetter(JSContext
* cx
, const JS::CallArgs
& args
) {
8159 JS::GetReservedSlot(&args
.thisv().toObject(), SLOT_LASTERROR
));
8162 #endif // defined(XP_WIN)
8164 bool CDataFinalizer::Methods::ToSource(JSContext
* cx
, unsigned argc
,
8166 CallArgs args
= CallArgsFromVp(argc
, vp
);
8167 RootedObject
objThis(
8168 cx
, GetThisObject(cx
, args
, "CDataFinalizer.prototype.toSource"));
8172 if (!CDataFinalizer::IsCDataFinalizer(objThis
)) {
8173 return IncompatibleThisProto(cx
, "CDataFinalizer.prototype.toSource",
8174 InformalValueTypeName(args
.thisv()));
8177 CDataFinalizer::Private
* p
= GetFinalizerPrivate(objThis
);
8179 JSString
* strMessage
;
8181 strMessage
= JS_NewStringCopyZ(cx
, "ctypes.CDataFinalizer()");
8183 RootedObject
objType(cx
, CDataFinalizer::GetCType(cx
, objThis
));
8185 JS_ReportErrorASCII(cx
, "CDataFinalizer has no type");
8190 AppendString(cx
, source
, "ctypes.CDataFinalizer(");
8191 JSString
* srcValue
= CData::GetSourceString(cx
, objType
, p
->cargs
);
8195 AppendString(cx
, source
, srcValue
);
8196 AppendString(cx
, source
, ", ");
8197 Value valCodePtrType
=
8198 JS::GetReservedSlot(objThis
, SLOT_DATAFINALIZER_CODETYPE
);
8199 if (valCodePtrType
.isPrimitive()) {
8203 RootedObject
typeObj(cx
, valCodePtrType
.toObjectOrNull());
8204 JSString
* srcDispose
= CData::GetSourceString(cx
, typeObj
, &(p
->code
));
8209 AppendString(cx
, source
, srcDispose
);
8210 AppendString(cx
, source
, ")");
8214 strMessage
= NewUCString(cx
, source
.finish());
8218 // This is a memory issue, no error message
8222 args
.rval().setString(strMessage
);
8226 bool CDataFinalizer::Methods::ToString(JSContext
* cx
, unsigned argc
,
8228 CallArgs args
= CallArgsFromVp(argc
, vp
);
8230 GetThisObject(cx
, args
, "CDataFinalizer.prototype.toString");
8234 if (!CDataFinalizer::IsCDataFinalizer(objThis
)) {
8235 return IncompatibleThisProto(cx
, "CDataFinalizer.prototype.toString",
8236 InformalValueTypeName(args
.thisv()));
8239 JSString
* strMessage
;
8240 RootedValue
value(cx
);
8241 if (!GetFinalizerPrivate(objThis
)) {
8242 // Pre-check whether CDataFinalizer::GetValue can fail
8243 // to avoid reporting an error when not appropriate.
8244 strMessage
= JS_NewStringCopyZ(cx
, "[CDataFinalizer - empty]");
8248 } else if (!CDataFinalizer::GetValue(cx
, objThis
, &value
)) {
8249 MOZ_CRASH("Could not convert an empty CDataFinalizer");
8251 strMessage
= ToString(cx
, value
);
8256 args
.rval().setString(strMessage
);
8260 bool CDataFinalizer::IsCDataFinalizer(JSObject
* obj
) {
8261 return obj
->hasClass(&sCDataFinalizerClass
);
8264 JSObject
* CDataFinalizer::GetCType(JSContext
* cx
, JSObject
* obj
) {
8265 MOZ_ASSERT(IsCDataFinalizer(obj
));
8267 Value valData
= JS::GetReservedSlot(obj
, SLOT_DATAFINALIZER_VALTYPE
);
8268 if (valData
.isUndefined()) {
8272 return valData
.toObjectOrNull();
8275 bool CDataFinalizer::GetValue(JSContext
* cx
, JSObject
* obj
,
8276 MutableHandleValue aResult
) {
8277 MOZ_ASSERT(IsCDataFinalizer(obj
));
8279 CDataFinalizer::Private
* p
= GetFinalizerPrivate(obj
);
8282 // We have called |dispose| or |forget| already.
8283 JS_ReportErrorASCII(
8284 cx
, "Attempting to get the value of an empty CDataFinalizer");
8288 RootedObject
ctype(cx
, GetCType(cx
, obj
));
8289 return ConvertToJS(cx
, ctype
, /*parent*/ nullptr, p
->cargs
, false, true,
8294 * Attach a C function as a finalizer to a JS object.
8296 * Pseudo-JS signature:
8297 * function(CData<T>, CData<T -> U>): CDataFinalizer<T>
8300 * This function attaches strong references to the following values:
8301 * - the CType of |value|
8303 * Note: This function takes advantage of the fact that non-variadic
8304 * CData functions are initialized during creation.
8306 bool CDataFinalizer::Construct(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8307 CallArgs args
= CallArgsFromVp(argc
, vp
);
8308 RootedObject
objSelf(cx
, &args
.callee());
8309 RootedObject
objProto(cx
);
8310 if (!GetObjectProperty(cx
, objSelf
, "prototype", &objProto
)) {
8311 JS_ReportErrorASCII(cx
, "CDataFinalizer.prototype does not exist");
8316 if (args
.length() ==
8317 0) { // Special case: the empty (already finalized) object
8318 JSObject
* objResult
=
8319 JS_NewObjectWithGivenProto(cx
, &sCDataFinalizerClass
, objProto
);
8320 args
.rval().setObject(*objResult
);
8324 if (args
.length() != 2) {
8325 return ArgumentLengthError(cx
, "CDataFinalizer constructor", "two", "s");
8328 JS::HandleValue valCodePtr
= args
[1];
8329 if (!valCodePtr
.isObject()) {
8330 return TypeError(cx
, "_a CData object_ of a function pointer type",
8333 RootedObject
objCodePtr(cx
, &valCodePtr
.toObject());
8335 // Note: Using a custom argument formatter here would be awkward (requires
8336 // a destructor just to uninstall the formatter).
8338 // 2. Extract argument type of |objCodePtr|
8339 if (!CData::IsCDataMaybeUnwrap(&objCodePtr
)) {
8340 return TypeError(cx
, "a _CData_ object of a function pointer type",
8343 RootedObject
objCodePtrType(cx
, CData::GetCType(objCodePtr
));
8344 RootedValue
valCodePtrType(cx
, ObjectValue(*objCodePtrType
));
8345 MOZ_ASSERT(objCodePtrType
);
8347 TypeCode typCodePtr
= CType::GetTypeCode(objCodePtrType
);
8348 if (typCodePtr
!= TYPE_pointer
) {
8349 return TypeError(cx
, "a CData object of a function _pointer_ type",
8353 JSObject
* objCodeType
= PointerType::GetBaseType(objCodePtrType
);
8354 MOZ_ASSERT(objCodeType
);
8356 TypeCode typCode
= CType::GetTypeCode(objCodeType
);
8357 if (typCode
!= TYPE_function
) {
8358 return TypeError(cx
, "a CData object of a _function_ pointer type",
8361 uintptr_t code
= *reinterpret_cast<uintptr_t*>(CData::GetData(objCodePtr
));
8363 return TypeError(cx
, "a CData object of a _non-NULL_ function pointer type",
8367 FunctionInfo
* funInfoFinalizer
= FunctionType::GetFunctionInfo(objCodeType
);
8368 MOZ_ASSERT(funInfoFinalizer
);
8370 if ((funInfoFinalizer
->mArgTypes
.length() != 1) ||
8371 (funInfoFinalizer
->mIsVariadic
)) {
8372 RootedValue
valCodeType(cx
, ObjectValue(*objCodeType
));
8373 return TypeError(cx
, "a function accepting exactly one argument",
8376 RootedObject
objArgType(cx
, funInfoFinalizer
->mArgTypes
[0]);
8377 RootedObject
returnType(cx
, funInfoFinalizer
->mReturnType
);
8379 // Invariant: At this stage, we know that funInfoFinalizer->mIsVariadic
8380 // is |false|. Therefore, funInfoFinalizer->mCIF has already been initialized.
8382 bool freePointer
= false;
8384 // 3. Perform dynamic cast of |args[0]| into |objType|, store it in |cargs|
8387 RootedValue
valData(cx
, args
[0]);
8388 if (!CType::GetSafeSize(objArgType
, &sizeArg
)) {
8389 RootedValue
valCodeType(cx
, ObjectValue(*objCodeType
));
8390 return TypeError(cx
, "a function with one known size argument",
8394 UniquePtr
<void, JS::FreePolicy
> cargs(malloc(sizeArg
));
8396 if (!ImplicitConvert(cx
, valData
, objArgType
, cargs
.get(),
8397 ConversionType::Finalizer
, &freePointer
, objCodePtrType
,
8402 // Note: We could handle that case, if necessary.
8403 JS_ReportErrorASCII(
8405 "Internal Error during CDataFinalizer. Object cannot be represented");
8409 // 4. Prepare buffer for holding return value
8411 UniquePtr
<void, JS::FreePolicy
> rvalue
;
8412 if (CType::GetTypeCode(returnType
) != TYPE_void_t
) {
8413 rvalue
.reset(malloc(Align(CType::GetSize(returnType
), sizeof(ffi_arg
))));
8414 } // Otherwise, simply do not allocate
8416 // 5. Create |objResult|
8418 JSObject
* objResult
=
8419 JS_NewObjectWithGivenProto(cx
, &sCDataFinalizerClass
, objProto
);
8424 // If our argument is a CData, it holds a type.
8425 // This is the type that we should capture, not that
8426 // of the function, which may be less precise.
8427 JSObject
* objBestArgType
= objArgType
;
8428 if (valData
.isObject()) {
8429 RootedObject
objData(cx
, &valData
.toObject());
8430 if (CData::IsCDataMaybeUnwrap(&objData
)) {
8431 objBestArgType
= CData::GetCType(objData
);
8433 if (!CType::GetSafeSize(objBestArgType
, &sizeBestArg
)) {
8434 MOZ_CRASH("object with unknown size");
8436 if (sizeBestArg
!= sizeArg
) {
8437 return FinalizerSizeError(cx
, objCodePtrType
, valData
);
8443 JS_SetReservedSlot(objResult
, SLOT_DATAFINALIZER_VALTYPE
,
8444 ObjectOrNullValue(objBestArgType
));
8447 JS_SetReservedSlot(objResult
, SLOT_DATAFINALIZER_CODETYPE
,
8448 ObjectValue(*objCodePtrType
));
8450 RootedValue
abiType(cx
, ObjectOrNullValue(funInfoFinalizer
->mABI
));
8452 if (!GetABI(cx
, abiType
, &abi
)) {
8453 JS_ReportErrorASCII(cx
,
8455 "Invalid ABI specification in CDataFinalizer");
8459 ffi_type
* rtype
= CType::GetFFIType(cx
, funInfoFinalizer
->mReturnType
);
8461 JS_ReportErrorASCII(cx
,
8463 "Could not access ffi type of CDataFinalizer");
8467 // 7. Store C information as private
8468 UniquePtr
<CDataFinalizer::Private
, JS::FreePolicy
> p(
8469 (CDataFinalizer::Private
*)malloc(sizeof(CDataFinalizer::Private
)));
8471 memmove(&p
->CIF
, &funInfoFinalizer
->mCIF
, sizeof(ffi_cif
));
8473 p
->cargs
= cargs
.release();
8474 p
->rvalue
= rvalue
.release();
8475 p
->cargs_size
= sizeArg
;
8478 JS::SetReservedSlot(objResult
, SLOT_DATAFINALIZER_PRIVATE
,
8479 JS::PrivateValue(p
.release()));
8480 args
.rval().setObject(*objResult
);
8485 * Actually call the finalizer. Does not perform any cleanup on the object.
8487 * Preconditions: |this| must be a |CDataFinalizer|, |p| must be non-null.
8488 * The function fails if |this| has gone through |Forget|/|Dispose|
8491 * This function does not alter the value of |errno|/|GetLastError|.
8493 * If argument |errnoStatus| is non-nullptr, it receives the value of |errno|
8494 * immediately after the call. Under Windows, if argument |lastErrorStatus|
8495 * is non-nullptr, it receives the value of |GetLastError| immediately after
8496 * the call. On other platforms, |lastErrorStatus| is ignored.
8498 void CDataFinalizer::CallFinalizer(CDataFinalizer::Private
* p
, int* errnoStatus
,
8499 int32_t* lastErrorStatus
) {
8500 int savedErrno
= errno
;
8503 int32_t savedLastError
= GetLastError();
8505 #endif // defined(XP_WIN)
8507 void* args
[1] = {p
->cargs
};
8508 ffi_call(&p
->CIF
, FFI_FN(p
->code
), p
->rvalue
, args
);
8511 *errnoStatus
= errno
;
8515 if (lastErrorStatus
) {
8516 *lastErrorStatus
= GetLastError();
8518 SetLastError(savedLastError
);
8519 #endif // defined(XP_WIN)
8525 * Preconditions: |this| must be a |CDataFinalizer|.
8526 * The function fails if |this| has gone through |Forget|/|Dispose|
8529 * Does not call the finalizer. Cleans up the Private memory and releases all
8530 * strong references.
8532 bool CDataFinalizer::Methods::Forget(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8533 CallArgs args
= CallArgsFromVp(argc
, vp
);
8534 if (args
.length() != 0) {
8535 return ArgumentLengthError(cx
, "CDataFinalizer.prototype.forget", "no",
8539 RootedObject
obj(cx
,
8540 GetThisObject(cx
, args
, "CDataFinalizer.prototype.forget"));
8544 if (!CDataFinalizer::IsCDataFinalizer(obj
)) {
8545 return IncompatibleThisProto(cx
, "CDataFinalizer.prototype.forget",
8549 CDataFinalizer::Private
* p
= GetFinalizerPrivate(obj
);
8552 return EmptyFinalizerCallError(cx
, "CDataFinalizer.prototype.forget");
8555 RootedValue
valJSData(cx
);
8556 RootedObject
ctype(cx
, GetCType(cx
, obj
));
8557 if (!ConvertToJS(cx
, ctype
, nullptr, p
->cargs
, false, true, &valJSData
)) {
8558 JS_ReportErrorASCII(cx
, "CDataFinalizer value cannot be represented");
8562 CDataFinalizer::Cleanup(p
, obj
);
8564 args
.rval().set(valJSData
);
8569 * Clean up the value.
8571 * Preconditions: |this| must be a |CDataFinalizer|.
8572 * The function fails if |this| has gone through |Forget|/|Dispose|
8575 * Calls the finalizer, cleans up the Private memory and releases all
8576 * strong references.
8578 bool CDataFinalizer::Methods::Dispose(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8579 CallArgs args
= CallArgsFromVp(argc
, vp
);
8580 if (args
.length() != 0) {
8581 return ArgumentLengthError(cx
, "CDataFinalizer.prototype.dispose", "no",
8585 RootedObject
obj(cx
,
8586 GetThisObject(cx
, args
, "CDataFinalizer.prototype.dispose"));
8590 if (!CDataFinalizer::IsCDataFinalizer(obj
)) {
8591 return IncompatibleThisProto(cx
, "CDataFinalizer.prototype.dispose",
8595 CDataFinalizer::Private
* p
= GetFinalizerPrivate(obj
);
8598 return EmptyFinalizerCallError(cx
, "CDataFinalizer.prototype.dispose");
8601 Value valType
= JS::GetReservedSlot(obj
, SLOT_DATAFINALIZER_VALTYPE
);
8602 MOZ_ASSERT(valType
.isObject());
8604 RootedObject
objCTypes(cx
, CType::GetGlobalCTypes(cx
, &valType
.toObject()));
8609 Value valCodePtrType
= JS::GetReservedSlot(obj
, SLOT_DATAFINALIZER_CODETYPE
);
8610 MOZ_ASSERT(valCodePtrType
.isObject());
8611 JSObject
* objCodePtrType
= &valCodePtrType
.toObject();
8613 JSObject
* objCodeType
= PointerType::GetBaseType(objCodePtrType
);
8614 MOZ_ASSERT(objCodeType
);
8615 MOZ_ASSERT(CType::GetTypeCode(objCodeType
) == TYPE_function
);
8617 RootedObject
resultType(
8618 cx
, FunctionType::GetFunctionInfo(objCodeType
)->mReturnType
);
8619 RootedValue
result(cx
);
8623 int32_t lastErrorStatus
;
8624 CDataFinalizer::CallFinalizer(p
, &errnoStatus
, &lastErrorStatus
);
8626 CDataFinalizer::CallFinalizer(p
, &errnoStatus
, nullptr);
8627 #endif // defined(XP_WIN)
8629 JS_SetReservedSlot(objCTypes
, SLOT_ERRNO
, Int32Value(errnoStatus
));
8631 JS_SetReservedSlot(objCTypes
, SLOT_LASTERROR
, Int32Value(lastErrorStatus
));
8632 #endif // defined(XP_WIN)
8634 if (ConvertToJS(cx
, resultType
, nullptr, p
->rvalue
, false, true, &result
)) {
8635 CDataFinalizer::Cleanup(p
, obj
);
8636 args
.rval().set(result
);
8639 CDataFinalizer::Cleanup(p
, obj
);
8644 * Perform finalization.
8646 * Preconditions: |this| must be the result of |CDataFinalizer|.
8647 * It may have gone through |Forget|/|Dispose|.
8649 * If |this| has not gone through |Forget|/|Dispose|, calls the
8650 * finalizer, cleans up the Private memory and releases all
8651 * strong references.
8653 void CDataFinalizer::Finalize(JS::GCContext
* gcx
, JSObject
* obj
) {
8654 CDataFinalizer::Private
* p
= GetFinalizerPrivate(obj
);
8660 CDataFinalizer::CallFinalizer(p
, nullptr, nullptr);
8661 CDataFinalizer::Cleanup(p
, nullptr);
8665 * Perform cleanup of a CDataFinalizer
8667 * Release strong references, cleanup |Private|.
8669 * Argument |p| contains the private information of the CDataFinalizer. If
8670 * nullptr, this function does nothing.
8671 * Argument |obj| should contain |nullptr| during finalization (or in any
8672 * context in which the object itself should not be cleaned up), or a
8673 * CDataFinalizer object otherwise.
8675 void CDataFinalizer::Cleanup(CDataFinalizer::Private
* p
, JSObject
* obj
) {
8677 return; // We have already cleaned up
8685 return; // No slots to clean up
8688 MOZ_ASSERT(CDataFinalizer::IsCDataFinalizer(obj
));
8690 static_assert(CDATAFINALIZER_SLOTS
== 3, "Code below must clear all slots");
8692 JS::SetReservedSlot(obj
, SLOT_DATAFINALIZER_PRIVATE
, JS::UndefinedValue());
8693 JS::SetReservedSlot(obj
, SLOT_DATAFINALIZER_VALTYPE
, JS::NullValue());
8694 JS::SetReservedSlot(obj
, SLOT_DATAFINALIZER_CODETYPE
, JS::NullValue());
8697 /*******************************************************************************
8698 ** Int64 and UInt64 implementation
8699 *******************************************************************************/
8701 JSObject
* Int64Base::Construct(JSContext
* cx
, HandleObject proto
, uint64_t data
,
8703 const JSClass
* clasp
= isUnsigned
? &sUInt64Class
: &sInt64Class
;
8704 RootedObject
result(cx
, JS_NewObjectWithGivenProto(cx
, clasp
, proto
));
8709 // attach the Int64's data
8710 uint64_t* buffer
= cx
->new_
<uint64_t>(data
);
8715 JS_InitReservedSlot(result
, SLOT_INT64
, buffer
, JS::MemoryUse::CTypesInt64
);
8717 if (!JS_FreezeObject(cx
, result
)) {
8724 void Int64Base::Finalize(JS::GCContext
* gcx
, JSObject
* obj
) {
8725 Value slot
= JS::GetReservedSlot(obj
, SLOT_INT64
);
8726 if (slot
.isUndefined()) {
8730 uint64_t* buffer
= static_cast<uint64_t*>(slot
.toPrivate());
8731 gcx
->delete_(obj
, buffer
, MemoryUse::CTypesInt64
);
8734 uint64_t Int64Base::GetInt(JSObject
* obj
) {
8735 MOZ_ASSERT(Int64::IsInt64(obj
) || UInt64::IsUInt64(obj
));
8737 Value slot
= JS::GetReservedSlot(obj
, SLOT_INT64
);
8738 return *static_cast<uint64_t*>(slot
.toPrivate());
8741 bool Int64Base::ToString(JSContext
* cx
, JSObject
* obj
, const CallArgs
& args
,
8743 if (args
.length() > 1) {
8745 return ArgumentLengthError(cx
, "UInt64.prototype.toString", "at most one",
8748 return ArgumentLengthError(cx
, "Int64.prototype.toString", "at most one",
8753 if (args
.length() == 1) {
8754 Value arg
= args
[0];
8755 if (arg
.isInt32()) {
8756 radix
= arg
.toInt32();
8758 if (!arg
.isInt32() || radix
< 2 || radix
> 36) {
8760 return ArgumentRangeMismatch(
8761 cx
, "UInt64.prototype.toString",
8762 "an integer at least 2 and no greater than 36");
8764 return ArgumentRangeMismatch(
8765 cx
, "Int64.prototype.toString",
8766 "an integer at least 2 and no greater than 36");
8770 AutoString intString
;
8772 IntegerToString(GetInt(obj
), radix
, intString
);
8774 IntegerToString(static_cast<int64_t>(GetInt(obj
)), radix
, intString
);
8780 JSString
* result
= NewUCString(cx
, intString
.finish());
8785 args
.rval().setString(result
);
8789 bool Int64Base::ToSource(JSContext
* cx
, JSObject
* obj
, const CallArgs
& args
,
8791 if (args
.length() != 0) {
8793 return ArgumentLengthError(cx
, "UInt64.prototype.toSource", "no", "s");
8795 return ArgumentLengthError(cx
, "Int64.prototype.toSource", "no", "s");
8798 // Return a decimal string suitable for constructing the number.
8801 AppendString(cx
, source
, "ctypes.UInt64(\"");
8802 IntegerToString(GetInt(obj
), 10, source
);
8804 AppendString(cx
, source
, "ctypes.Int64(\"");
8805 IntegerToString(static_cast<int64_t>(GetInt(obj
)), 10, source
);
8807 AppendString(cx
, source
, "\")");
8812 JSString
* result
= NewUCString(cx
, source
.finish());
8817 args
.rval().setString(result
);
8821 bool Int64::Construct(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8822 CallArgs args
= CallArgsFromVp(argc
, vp
);
8824 // Construct and return a new Int64 object.
8825 if (args
.length() != 1) {
8826 return ArgumentLengthError(cx
, "Int64 constructor", "one", "");
8830 bool overflow
= false;
8831 if (!jsvalToBigInteger(cx
, args
[0], true, &i
, &overflow
)) {
8833 return TypeOverflow(cx
, "int64", args
[0]);
8835 return ArgumentConvError(cx
, args
[0], "Int64", 0);
8838 // Get ctypes.Int64.prototype from the 'prototype' property of the ctor.
8839 RootedValue
slot(cx
);
8840 RootedObject
callee(cx
, &args
.callee());
8841 MOZ_ALWAYS_TRUE(JS_GetProperty(cx
, callee
, "prototype", &slot
));
8842 RootedObject
proto(cx
, slot
.toObjectOrNull());
8843 MOZ_ASSERT(proto
->hasClass(&sInt64ProtoClass
));
8845 JSObject
* result
= Int64Base::Construct(cx
, proto
, i
, false);
8850 args
.rval().setObject(*result
);
8854 bool Int64::IsInt64(JSObject
* obj
) { return obj
->hasClass(&sInt64Class
); }
8856 bool Int64::ToString(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8857 CallArgs args
= CallArgsFromVp(argc
, vp
);
8858 RootedObject
obj(cx
, GetThisObject(cx
, args
, "Int64.prototype.toString"));
8862 if (!Int64::IsInt64(obj
)) {
8863 if (!CData::IsCDataMaybeUnwrap(&obj
)) {
8864 return IncompatibleThisProto(cx
, "Int64.prototype.toString",
8865 InformalValueTypeName(args
.thisv()));
8867 return IncompatibleThisType(cx
, "Int64.prototype.toString",
8871 return Int64Base::ToString(cx
, obj
, args
, false);
8874 bool Int64::ToSource(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8875 CallArgs args
= CallArgsFromVp(argc
, vp
);
8876 RootedObject
obj(cx
, GetThisObject(cx
, args
, "Int64.prototype.toSource"));
8880 if (!Int64::IsInt64(obj
)) {
8881 if (!CData::IsCDataMaybeUnwrap(&obj
)) {
8882 return IncompatibleThisProto(cx
, "Int64.prototype.toSource",
8883 InformalValueTypeName(args
.thisv()));
8885 return IncompatibleThisType(cx
, "Int64.prototype.toSource",
8889 return Int64Base::ToSource(cx
, obj
, args
, false);
8892 bool Int64::Compare(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8893 CallArgs args
= CallArgsFromVp(argc
, vp
);
8894 if (args
.length() != 2) {
8895 return ArgumentLengthError(cx
, "Int64.compare", "two", "s");
8897 if (args
[0].isPrimitive() || !Int64::IsInt64(&args
[0].toObject())) {
8898 return ArgumentTypeMismatch(cx
, "first ", "Int64.compare", "a Int64");
8900 if (args
[1].isPrimitive() || !Int64::IsInt64(&args
[1].toObject())) {
8901 return ArgumentTypeMismatch(cx
, "second ", "Int64.compare", "a Int64");
8904 JSObject
* obj1
= &args
[0].toObject();
8905 JSObject
* obj2
= &args
[1].toObject();
8907 int64_t i1
= Int64Base::GetInt(obj1
);
8908 int64_t i2
= Int64Base::GetInt(obj2
);
8911 args
.rval().setInt32(0);
8912 } else if (i1
< i2
) {
8913 args
.rval().setInt32(-1);
8915 args
.rval().setInt32(1);
8921 #define LO_MASK ((uint64_t(1) << 32) - 1)
8922 #define INT64_LO(i) ((i) & LO_MASK)
8923 #define INT64_HI(i) ((i) >> 32)
8925 bool Int64::Lo(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8926 CallArgs args
= CallArgsFromVp(argc
, vp
);
8927 if (args
.length() != 1) {
8928 return ArgumentLengthError(cx
, "Int64.lo", "one", "");
8930 if (args
[0].isPrimitive() || !Int64::IsInt64(&args
[0].toObject())) {
8931 return ArgumentTypeMismatch(cx
, "", "Int64.lo", "a Int64");
8934 JSObject
* obj
= &args
[0].toObject();
8935 int64_t u
= Int64Base::GetInt(obj
);
8936 double d
= uint32_t(INT64_LO(u
));
8938 args
.rval().setNumber(d
);
8942 bool Int64::Hi(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8943 CallArgs args
= CallArgsFromVp(argc
, vp
);
8944 if (args
.length() != 1) {
8945 return ArgumentLengthError(cx
, "Int64.hi", "one", "");
8947 if (args
[0].isPrimitive() || !Int64::IsInt64(&args
[0].toObject())) {
8948 return ArgumentTypeMismatch(cx
, "", "Int64.hi", "a Int64");
8951 JSObject
* obj
= &args
[0].toObject();
8952 int64_t u
= Int64Base::GetInt(obj
);
8953 double d
= int32_t(INT64_HI(u
));
8955 args
.rval().setDouble(d
);
8959 bool Int64::Join(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8960 CallArgs args
= CallArgsFromVp(argc
, vp
);
8961 if (args
.length() != 2) {
8962 return ArgumentLengthError(cx
, "Int64.join", "two", "s");
8967 if (!jsvalToInteger(cx
, args
[0], &hi
)) {
8968 return ArgumentConvError(cx
, args
[0], "Int64.join", 0);
8970 if (!jsvalToInteger(cx
, args
[1], &lo
)) {
8971 return ArgumentConvError(cx
, args
[1], "Int64.join", 1);
8974 int64_t i
= mozilla::WrapToSigned((uint64_t(hi
) << 32) + lo
);
8976 // Get Int64.prototype from the function's reserved slot.
8977 JSObject
* callee
= &args
.callee();
8979 Value slot
= js::GetFunctionNativeReserved(callee
, SLOT_FN_INT64PROTO
);
8980 RootedObject
proto(cx
, &slot
.toObject());
8981 MOZ_ASSERT(proto
->hasClass(&sInt64ProtoClass
));
8983 JSObject
* result
= Int64Base::Construct(cx
, proto
, i
, false);
8988 args
.rval().setObject(*result
);
8992 bool UInt64::Construct(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8993 CallArgs args
= CallArgsFromVp(argc
, vp
);
8995 // Construct and return a new UInt64 object.
8996 if (args
.length() != 1) {
8997 return ArgumentLengthError(cx
, "UInt64 constructor", "one", "");
9001 bool overflow
= false;
9002 if (!jsvalToBigInteger(cx
, args
[0], true, &u
, &overflow
)) {
9004 return TypeOverflow(cx
, "uint64", args
[0]);
9006 return ArgumentConvError(cx
, args
[0], "UInt64", 0);
9009 // Get ctypes.UInt64.prototype from the 'prototype' property of the ctor.
9010 RootedValue
slot(cx
);
9011 RootedObject
callee(cx
, &args
.callee());
9012 MOZ_ALWAYS_TRUE(JS_GetProperty(cx
, callee
, "prototype", &slot
));
9013 RootedObject
proto(cx
, &slot
.toObject());
9014 MOZ_ASSERT(proto
->hasClass(&sUInt64ProtoClass
));
9016 JSObject
* result
= Int64Base::Construct(cx
, proto
, u
, true);
9021 args
.rval().setObject(*result
);
9025 bool UInt64::IsUInt64(JSObject
* obj
) { return obj
->hasClass(&sUInt64Class
); }
9027 bool UInt64::ToString(JSContext
* cx
, unsigned argc
, Value
* vp
) {
9028 CallArgs args
= CallArgsFromVp(argc
, vp
);
9029 RootedObject
obj(cx
, GetThisObject(cx
, args
, "UInt64.prototype.toString"));
9033 if (!UInt64::IsUInt64(obj
)) {
9034 if (!CData::IsCDataMaybeUnwrap(&obj
)) {
9035 return IncompatibleThisProto(cx
, "UInt64.prototype.toString",
9036 InformalValueTypeName(args
.thisv()));
9038 return IncompatibleThisType(cx
, "UInt64.prototype.toString",
9039 "non-UInt64 CData");
9042 return Int64Base::ToString(cx
, obj
, args
, true);
9045 bool UInt64::ToSource(JSContext
* cx
, unsigned argc
, Value
* vp
) {
9046 CallArgs args
= CallArgsFromVp(argc
, vp
);
9047 RootedObject
obj(cx
, GetThisObject(cx
, args
, "UInt64.prototype.toSource"));
9051 if (!UInt64::IsUInt64(obj
)) {
9052 if (!CData::IsCDataMaybeUnwrap(&obj
)) {
9053 return IncompatibleThisProto(cx
, "UInt64.prototype.toSource",
9054 InformalValueTypeName(args
.thisv()));
9056 return IncompatibleThisType(cx
, "UInt64.prototype.toSource",
9057 "non-UInt64 CData");
9060 return Int64Base::ToSource(cx
, obj
, args
, true);
9063 bool UInt64::Compare(JSContext
* cx
, unsigned argc
, Value
* vp
) {
9064 CallArgs args
= CallArgsFromVp(argc
, vp
);
9065 if (args
.length() != 2) {
9066 return ArgumentLengthError(cx
, "UInt64.compare", "two", "s");
9068 if (args
[0].isPrimitive() || !UInt64::IsUInt64(&args
[0].toObject())) {
9069 return ArgumentTypeMismatch(cx
, "first ", "UInt64.compare", "a UInt64");
9071 if (args
[1].isPrimitive() || !UInt64::IsUInt64(&args
[1].toObject())) {
9072 return ArgumentTypeMismatch(cx
, "second ", "UInt64.compare", "a UInt64");
9075 JSObject
* obj1
= &args
[0].toObject();
9076 JSObject
* obj2
= &args
[1].toObject();
9078 uint64_t u1
= Int64Base::GetInt(obj1
);
9079 uint64_t u2
= Int64Base::GetInt(obj2
);
9082 args
.rval().setInt32(0);
9083 } else if (u1
< u2
) {
9084 args
.rval().setInt32(-1);
9086 args
.rval().setInt32(1);
9092 bool UInt64::Lo(JSContext
* cx
, unsigned argc
, Value
* vp
) {
9093 CallArgs args
= CallArgsFromVp(argc
, vp
);
9094 if (args
.length() != 1) {
9095 return ArgumentLengthError(cx
, "UInt64.lo", "one", "");
9097 if (args
[0].isPrimitive() || !UInt64::IsUInt64(&args
[0].toObject())) {
9098 return ArgumentTypeMismatch(cx
, "", "UInt64.lo", "a UInt64");
9101 JSObject
* obj
= &args
[0].toObject();
9102 uint64_t u
= Int64Base::GetInt(obj
);
9103 double d
= uint32_t(INT64_LO(u
));
9105 args
.rval().setDouble(d
);
9109 bool UInt64::Hi(JSContext
* cx
, unsigned argc
, Value
* vp
) {
9110 CallArgs args
= CallArgsFromVp(argc
, vp
);
9111 if (args
.length() != 1) {
9112 return ArgumentLengthError(cx
, "UInt64.hi", "one", "");
9114 if (args
[0].isPrimitive() || !UInt64::IsUInt64(&args
[0].toObject())) {
9115 return ArgumentTypeMismatch(cx
, "", "UInt64.hi", "a UInt64");
9118 JSObject
* obj
= &args
[0].toObject();
9119 uint64_t u
= Int64Base::GetInt(obj
);
9120 double d
= uint32_t(INT64_HI(u
));
9122 args
.rval().setDouble(d
);
9126 bool UInt64::Join(JSContext
* cx
, unsigned argc
, Value
* vp
) {
9127 CallArgs args
= CallArgsFromVp(argc
, vp
);
9128 if (args
.length() != 2) {
9129 return ArgumentLengthError(cx
, "UInt64.join", "two", "s");
9134 if (!jsvalToInteger(cx
, args
[0], &hi
)) {
9135 return ArgumentConvError(cx
, args
[0], "UInt64.join", 0);
9137 if (!jsvalToInteger(cx
, args
[1], &lo
)) {
9138 return ArgumentConvError(cx
, args
[1], "UInt64.join", 1);
9141 uint64_t u
= (uint64_t(hi
) << 32) + uint64_t(lo
);
9143 // Get UInt64.prototype from the function's reserved slot.
9144 JSObject
* callee
= &args
.callee();
9146 Value slot
= js::GetFunctionNativeReserved(callee
, SLOT_FN_INT64PROTO
);
9147 RootedObject
proto(cx
, &slot
.toObject());
9148 MOZ_ASSERT(proto
->hasClass(&sUInt64ProtoClass
));
9150 JSObject
* result
= Int64Base::Construct(cx
, proto
, u
, true);
9155 args
.rval().setObject(*result
);
9159 } // namespace ctypes