Backed out changeset 2fc34d798e24 (bug 1917771) for causing failures at baseline...
[gecko.git] / js / src / ctypes / CTypes.cpp
blobc23aa0b3a1173e36f9d336d0cf6cd1e36f0d0dba
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"
17 #if defined(XP_UNIX)
18 # include <errno.h>
19 #endif
20 #if defined(XP_WIN)
21 # include <float.h>
22 #endif
23 #if defined(SOLARIS)
24 # include <ieeefp.h>
25 #endif
26 #include <iterator>
27 #include <limits>
28 #include <stdint.h>
29 #include <sys/types.h>
30 #include <type_traits>
32 #include "jsapi.h"
33 #include "jsexn.h"
34 #include "jsnum.h"
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,
78 char16_t* unpaired) {
79 for (const char16_t* end = chars + nchars; chars != end; chars++) {
80 char16_t c = *chars;
81 if (unicode::IsSurrogate(c)) {
82 chars++;
83 if (unicode::IsTrailSurrogate(c) || chars == end) {
84 *unpaired = c;
85 return true;
87 char16_t c2 = *chars;
88 if (!unicode::IsTrailSurrogate(c2)) {
89 *unpaired = c;
90 return true;
94 return false;
97 bool ReportErrorIfUnpairedSurrogatePresent(JSContext* cx, JSLinearString* str) {
98 if (str->hasLatin1Chars()) {
99 return true;
102 char16_t unpaired;
104 JS::AutoCheckCannotGC nogc;
105 if (!HasUnpairedSurrogate(str->twoByteChars(nogc), str->length(),
106 &unpaired)) {
107 return true;
111 char buffer[10];
112 SprintfLiteral(buffer, "0x%x", unpaired);
113 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
114 JSMSG_BAD_SURROGATE_CHAR, buffer);
115 return false;
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>
127 struct Property {
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);
136 namespace CType {
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);
165 } // namespace CType
167 namespace ABI {
168 bool IsABI(JSObject* obj);
169 static bool ToSource(JSContext* cx, unsigned argc, Value* vp);
170 } // namespace ABI
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,
190 const char* name);
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
244 namespace CClosure {
245 static void Trace(JSTracer* trc, JSObject* obj);
246 static void Finalize(JS::GCContext* gcx, JSObject* obj);
248 // libffi callback
249 static void ClosureStub(ffi_cif* cif, void* result, void** args,
250 void* userData);
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;
259 ffi_cif* cif;
260 void* result;
261 void** args;
262 ClosureInfo* cinfo;
264 } // namespace CClosure
266 namespace CData {
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,
278 void* data);
280 bool ErrnoGetter(JSContext* cx, const JS::CallArgs& args);
282 #if defined(XP_WIN)
283 bool LastErrorGetter(JSContext* cx, const JS::CallArgs& args);
284 #endif // defined(XP_WIN)
285 } // namespace CData
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
306 * |CDataFinalizer|.
308 * Note: the private data may be nullptr, if |dispose|, |forget| or the
309 * finalizer has already been called.
311 struct Private {
313 * The C data to pass to the code.
314 * Finalization/|dispose|/|forget| release this memory.
316 void* cargs;
319 * The total size of the buffer pointed by |cargs|
321 size_t cargs_size;
324 * Low-level signature information.
325 * Finalization/|dispose|/|forget| release this memory.
327 ffi_cif CIF;
330 * The C function to invoke during finalization.
331 * Do not deallocate this.
333 uintptr_t code;
336 * A buffer for holding the return value.
337 * Finalization/|dispose|/|forget| release this memory.
339 void* rvalue;
343 * Methods of instances of |CDataFinalizer|
345 namespace Methods {
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
355 * Utility functions
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,
403 bool isUnsigned);
405 uint64_t GetInt(JSObject* obj);
407 bool ToString(JSContext* cx, JSObject* obj, const CallArgs& args,
408 bool isUnsigned);
410 bool ToSource(JSContext* cx, JSObject* obj, const CallArgs& args,
411 bool isUnsigned);
413 static void Finalize(JS::GCContext* gcx, JSObject* obj);
414 } // namespace Int64Base
416 namespace Int64 {
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);
426 } // namespace Int64
428 namespace UInt64 {
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 = {
447 "ctypes",
448 JSCLASS_HAS_RESERVED_SLOTS(CTYPESGLOBAL_SLOTS),
451 static const JSClass sCABIClass = {
452 "CABI",
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
464 nullptr, // resolve
465 nullptr, // mayResolve
466 nullptr, // finalize
467 ConstructAbstract, // call
468 ConstructAbstract, // construct
469 nullptr, // trace
471 static const JSClass sCTypeProtoClass = {
472 "CType",
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 = {
480 "CData",
484 static const JSClassOps sCTypeClassOps = {
485 nullptr, // addProperty
486 nullptr, // delProperty
487 nullptr, // enumerate
488 nullptr, // newEnumerate
489 nullptr, // resolve
490 nullptr, // mayResolve
491 CType::Finalize, // finalize
492 CType::ConstructData, // call
493 CType::ConstructData, // construct
494 CType::Trace, // trace
496 static const JSClass sCTypeClass = {
497 "CType",
498 JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS) | JSCLASS_FOREGROUND_FINALIZE,
499 &sCTypeClassOps,
502 static const JSClassOps sCDataClassOps = {
503 nullptr, // addProperty
504 nullptr, // delProperty
505 nullptr, // enumerate
506 nullptr, // newEnumerate
507 nullptr, // resolve
508 nullptr, // mayResolve
509 CData::Finalize, // finalize
510 FunctionType::Call, // call
511 FunctionType::Call, // construct
512 nullptr, // trace
514 static const JSClass sCDataClass = {
515 "CData",
516 JSCLASS_HAS_RESERVED_SLOTS(CDATA_SLOTS) | JSCLASS_FOREGROUND_FINALIZE,
517 &sCDataClassOps,
520 static const JSClassOps sCClosureClassOps = {
521 nullptr, // addProperty
522 nullptr, // delProperty
523 nullptr, // enumerate
524 nullptr, // newEnumerate
525 nullptr, // resolve
526 nullptr, // mayResolve
527 CClosure::Finalize, // finalize
528 nullptr, // call
529 nullptr, // construct
530 CClosure::Trace, // trace
532 static const JSClass sCClosureClass = {
533 "CClosure",
534 JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS) | JSCLASS_FOREGROUND_FINALIZE,
535 &sCClosureClassOps,
539 * Class representing the prototype of CDataFinalizer.
541 static const JSClass sCDataFinalizerProtoClass = {
542 "CDataFinalizer",
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
557 nullptr, // resolve
558 nullptr, // mayResolve
559 CDataFinalizer::Finalize, // finalize
560 nullptr, // call
561 nullptr, // construct
562 nullptr, // trace
564 static const JSClass sCDataFinalizerClass = {
565 "CDataFinalizer",
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),
585 CTYPESACC_FLAGS),
586 JS_PSG("size", (Property<CType::IsCType, CType::SizeGetter>::Fun),
587 CTYPESACC_FLAGS),
588 JS_PSG("ptr", (Property<CType::IsCType, CType::PtrGetter>::Fun),
589 CTYPESACC_FLAGS),
590 JS_PSG("prototype",
591 (Property<CType::IsCTypeOrProto, CType::PrototypeGetter>::Fun),
592 CTYPESACC_FLAGS),
593 JS_PS_END,
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),
600 JS_FS_END,
603 static const JSFunctionSpec sCABIFunctions[] = {
604 JS_FN("toSource", ABI::ToSource, 0, CABIFN_FLAGS),
605 JS_FN("toString", ABI::ToSource, 0, CABIFN_FLAGS),
606 JS_FS_END,
609 static const JSPropertySpec sCDataProps[] = {
610 JS_PSGS("value", (Property<CData::IsCData, CData::ValueGetter>::Fun),
611 (Property<CData::IsCData, CData::ValueSetter>::Fun),
612 JSPROP_PERMANENT),
613 JS_PS_END,
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,
620 CDATAFN_FLAGS),
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),
624 JS_FS_END,
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),
639 JS_FS_END,
642 static const JSFunctionSpec sPointerFunction =
643 JS_FN("PointerType", PointerType::Create, 1, CTYPESCTOR_FLAGS);
645 static const JSPropertySpec sPointerProps[] = {
646 JS_PSG("targetType",
647 (Property<PointerType::IsPointerType,
648 PointerType::TargetTypeGetter>::Fun),
649 CTYPESACC_FLAGS),
650 JS_PS_END,
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),
657 JS_FS_END,
660 static const JSPropertySpec sPointerInstanceProps[] = {
661 JS_PSGS(
662 "contents",
663 (Property<PointerType::IsPointer, PointerType::ContentsGetter>::Fun),
664 (Property<PointerType::IsPointer, PointerType::ContentsSetter>::Fun),
665 JSPROP_PERMANENT),
666 JS_PS_END,
669 static const JSFunctionSpec sArrayFunction =
670 JS_FN("ArrayType", ArrayType::Create, 1, CTYPESCTOR_FLAGS);
672 static const JSPropertySpec sArrayProps[] = {
673 JS_PSG(
674 "elementType",
675 (Property<ArrayType::IsArrayType, ArrayType::ElementTypeGetter>::Fun),
676 CTYPESACC_FLAGS),
677 JS_PSG(
678 "length",
679 (Property<ArrayType::IsArrayOrArrayType, ArrayType::LengthGetter>::Fun),
680 CTYPESACC_FLAGS),
681 JS_PS_END,
684 static const JSFunctionSpec sArrayInstanceFunctions[] = {
685 JS_FN("addressOfElement", ArrayType::AddressOfElement, 1, CDATAFN_FLAGS),
686 JS_FS_END,
689 static const JSPropertySpec sArrayInstanceProps[] = {
690 JS_PSG(
691 "length",
692 (Property<ArrayType::IsArrayOrArrayType, ArrayType::LengthGetter>::Fun),
693 JSPROP_PERMANENT),
694 JS_PS_END,
697 static const JSFunctionSpec sStructFunction =
698 JS_FN("StructType", StructType::Create, 2, CTYPESCTOR_FLAGS);
700 static const JSPropertySpec sStructProps[] = {
701 JS_PSG("fields",
702 (Property<StructType::IsStruct, StructType::FieldsArrayGetter>::Fun),
703 CTYPESACC_FLAGS),
704 JS_PS_END,
707 static const JSFunctionSpec sStructFunctions[] = {
708 JS_FN("define", StructType::Define, 1, CDATAFN_FLAGS),
709 JS_FS_END,
712 static const JSFunctionSpec sStructInstanceFunctions[] = {
713 JS_FN("addressOfField", StructType::AddressOfField, 1, CDATAFN_FLAGS),
714 JS_FS_END,
717 static const JSFunctionSpec sFunctionFunction =
718 JS_FN("FunctionType", FunctionType::Create, 2, CTYPESCTOR_FLAGS);
720 static const JSPropertySpec sFunctionProps[] = {
721 JS_PSG("argTypes",
722 (Property<FunctionType::IsFunctionType,
723 FunctionType::ArgTypesGetter>::Fun),
724 CTYPESACC_FLAGS),
725 JS_PSG("returnType",
726 (Property<FunctionType::IsFunctionType,
727 FunctionType::ReturnTypeGetter>::Fun),
728 CTYPESACC_FLAGS),
729 JS_PSG(
730 "abi",
731 (Property<FunctionType::IsFunctionType, FunctionType::ABIGetter>::Fun),
732 CTYPESACC_FLAGS),
733 JS_PSG("isVariadic",
734 (Property<FunctionType::IsFunctionType,
735 FunctionType::IsVariadicGetter>::Fun),
736 CTYPESACC_FLAGS),
737 JS_PS_END,
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),
743 JS_FS_END,
746 static const JSClass sInt64ProtoClass = {
747 "Int64",
751 static const JSClass sUInt64ProtoClass = {
752 "UInt64",
756 static const JSClassOps sInt64ClassOps = {
757 nullptr, // addProperty
758 nullptr, // delProperty
759 nullptr, // enumerate
760 nullptr, // newEnumerate
761 nullptr, // resolve
762 nullptr, // mayResolve
763 Int64Base::Finalize, // finalize
764 nullptr, // call
765 nullptr, // construct
766 nullptr, // trace
769 static const JSClass sInt64Class = {
770 "Int64",
771 JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS) | JSCLASS_FOREGROUND_FINALIZE,
772 &sInt64ClassOps,
775 static const JSClass sUInt64Class = {
776 "UInt64",
777 JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS) | JSCLASS_FOREGROUND_FINALIZE,
778 &sInt64ClassOps,
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.
786 JS_FS_END,
789 static const JSFunctionSpec sUInt64StaticFunctions[] = {
790 JS_FN("compare", UInt64::Compare, 2, CTYPESFN_FLAGS),
791 JS_FN("lo", UInt64::Lo, 1, CTYPESFN_FLAGS),
792 JS_FN("hi", UInt64::Hi, 1, CTYPESFN_FLAGS),
793 // "join" is defined specially; see InitInt64Class.
794 JS_FS_END,
797 static const JSFunctionSpec sInt64Functions[] = {
798 JS_FN("toString", Int64::ToString, 0, CTYPESFN_FLAGS),
799 JS_FN("toSource", Int64::ToSource, 0, CTYPESFN_FLAGS),
800 JS_FS_END,
803 static const JSFunctionSpec sUInt64Functions[] = {
804 JS_FN("toString", UInt64::ToString, 0, CTYPESFN_FLAGS),
805 JS_FN("toSource", UInt64::ToSource, 0, CTYPESFN_FLAGS),
806 JS_FS_END,
809 static const JSPropertySpec sModuleProps[] = {
810 JS_PSG("errno", (Property<IsCTypesGlobal, CData::ErrnoGetter>::Fun),
811 JSPROP_PERMANENT),
812 #if defined(XP_WIN)
813 JS_PSG("winLastError",
814 (Property<IsCTypesGlobal, CData::LastErrorGetter>::Fun),
815 JSPROP_PERMANENT),
816 #endif // defined(XP_WIN)
817 JS_PS_END,
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),
826 JS_FS_END,
829 // Wrapper for arrays, to intercept indexed gets/sets.
830 class CDataArrayProxyHandler : public ForwardingProxyHandler {
831 public:
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)) {
852 return false;
854 if (handled) {
855 return true;
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)) {
866 return false;
868 if (handled) {
869 return true;
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();
879 return obj;
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)) {
902 return INVALID_ABI;
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"
913 #undef MSG_DEF
916 static const JSErrorFormatString* GetErrorMessage(void* userRef,
917 const unsigned errorNumber) {
918 if (0 < errorNumber && errorNumber < CTYPESERR_LIMIT) {
919 return &ErrorFormatString[errorNumber];
921 return nullptr;
924 static JS::UniqueChars EncodeUTF8(JSContext* cx, AutoString& str) {
925 RootedString string(cx, NewUCString(cx, str.finish()));
926 if (!string) {
927 return nullptr;
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);
940 return bytes.get();
943 return ValueToSourceForError(cx, val, bytes);
946 static void BuildCStyleFunctionTypeSource(JSContext* cx, HandleObject typeObj,
947 HandleString nameStr,
948 unsigned ptrCount,
949 AutoString& source);
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) \
959 case TYPE_##name: \
960 AppendString(cx, source, #name); \
961 break;
962 CTYPES_FOR_EACH_TYPE(BUILD_SOURCE)
963 #undef BUILD_SOURCE
964 case TYPE_void_t:
965 AppendString(cx, source, "void");
966 break;
967 case TYPE_pointer: {
968 unsigned ptrCount = 0;
969 TypeCode type;
970 RootedObject baseTypeObj(cx, typeObj);
971 do {
972 baseTypeObj = PointerType::GetBaseType(baseTypeObj);
973 ptrCount++;
974 type = CType::GetTypeCode(baseTypeObj);
975 } while (type == TYPE_pointer || type == TYPE_array);
976 if (type == TYPE_function) {
977 BuildCStyleFunctionTypeSource(cx, baseTypeObj, nullptr, ptrCount,
978 source);
979 break;
981 BuildCStyleTypeSource(cx, baseTypeObj, source);
982 AppendChars(source, '*', ptrCount);
983 break;
985 case TYPE_struct: {
986 RootedString name(cx, CType::GetName(cx, typeObj));
987 AppendString(cx, source, "struct ");
988 AppendString(cx, source, name);
989 break;
991 case TYPE_function:
992 BuildCStyleFunctionTypeSource(cx, typeObj, nullptr, 0, source);
993 break;
994 case TYPE_array:
995 MOZ_CRASH("TYPE_array shouldn't appear in function type");
999 static void BuildCStyleFunctionTypeSource(JSContext* cx, HandleObject typeObj,
1000 HandleString nameStr,
1001 unsigned ptrCount,
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, " ");
1008 if (nameStr) {
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);
1044 return;
1048 RootedValue funVal(cx, ObjectValue(*funObj));
1049 RootedString funcStr(cx, JS_ValueToSource(cx, funVal));
1050 if (!funcStr) {
1051 JS_ClearPendingException(cx);
1052 AppendString(cx, source, "<<error converting function to string>>");
1053 return;
1055 AppendString(cx, source, funcStr);
1058 enum class ConversionType {
1059 Argument = 0,
1060 Construct,
1061 Finalizer,
1062 Return,
1063 Setter
1066 static void BuildConversionPosition(JSContext* cx, ConversionType convType,
1067 HandleObject funObj, unsigned argIndex,
1068 AutoString& source) {
1069 switch (convType) {
1070 case ConversionType::Argument: {
1071 MOZ_ASSERT(funObj);
1073 AppendString(cx, source, " at argument ");
1074 AppendUInt(source, argIndex + 1);
1075 AppendString(cx, source, " of ");
1076 BuildFunctionTypeSource(cx, funObj, source);
1077 break;
1079 case ConversionType::Finalizer:
1080 MOZ_ASSERT(funObj);
1082 AppendString(cx, source, " at argument 1 of ");
1083 BuildFunctionTypeSource(cx, funObj, source);
1084 break;
1085 case ConversionType::Return:
1086 MOZ_ASSERT(funObj);
1088 AppendString(cx, source, " at the return value of ");
1089 BuildFunctionTypeSource(cx, funObj, source);
1090 break;
1091 default:
1092 MOZ_ASSERT(!funObj);
1093 break;
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();
1105 return nullptr;
1108 static void BuildTypeSource(JSContext* cx, JSObject* typeObj_, bool makeShort,
1109 AutoString& result);
1111 static JS::UniqueChars TypeSourceForError(JSContext* cx, JSObject* typeObj) {
1112 AutoString source;
1113 BuildTypeSource(cx, typeObj, true, source);
1114 if (!source) {
1115 return nullptr;
1117 return EncodeUTF8(cx, source);
1120 static JS::UniqueChars FunctionTypeSourceForError(JSContext* cx,
1121 HandleObject funObj) {
1122 AutoString funSource;
1123 BuildFunctionTypeSource(cx, funObj, funSource);
1124 if (!funSource) {
1125 return nullptr;
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);
1136 if (!posSource) {
1137 return nullptr;
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");
1146 public:
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);
1160 if (!valStr) {
1161 return false;
1164 if (arrObj) {
1165 MOZ_ASSERT(CType::IsCType(arrObj));
1167 switch (CType::GetTypeCode(arrObj)) {
1168 case TYPE_array: {
1169 MOZ_ASSERT(!funObj);
1171 IndexCString indexStr(arrIndex);
1173 JS::UniqueChars arrStr = TypeSourceForError(cx, arrObj);
1174 if (!arrStr) {
1175 return false;
1178 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1179 CTYPESMSG_CONV_ERROR_ARRAY, valStr,
1180 indexStr.get(), arrStr.get());
1181 break;
1183 case TYPE_struct: {
1184 RootedString name(cx, GetFieldName(arrObj, arrIndex));
1185 MOZ_ASSERT(name);
1186 JS::UniqueChars nameStr = JS_EncodeStringToUTF8(cx, name);
1187 if (!nameStr) {
1188 return false;
1191 JS::UniqueChars structStr = TypeSourceForError(cx, arrObj);
1192 if (!structStr) {
1193 return false;
1196 JS::UniqueChars posStr;
1197 if (funObj) {
1198 posStr = ConversionPositionForError(cx, convType, funObj, argIndex);
1199 if (!posStr) {
1200 return false;
1204 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1205 CTYPESMSG_CONV_ERROR_STRUCT, valStr,
1206 nameStr.get(), expectedStr, structStr.get(),
1207 (posStr ? posStr.get() : ""));
1208 break;
1210 default:
1211 MOZ_CRASH("invalid arrObj value");
1213 return false;
1216 switch (convType) {
1217 case ConversionType::Argument: {
1218 MOZ_ASSERT(funObj);
1220 IndexCString indexStr(argIndex + 1);
1222 JS::UniqueChars funStr = FunctionTypeSourceForError(cx, funObj);
1223 if (!funStr) {
1224 return false;
1227 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1228 CTYPESMSG_CONV_ERROR_ARG, valStr, indexStr.get(),
1229 funStr.get());
1230 break;
1232 case ConversionType::Finalizer: {
1233 MOZ_ASSERT(funObj);
1235 JS::UniqueChars funStr = FunctionTypeSourceForError(cx, funObj);
1236 if (!funStr) {
1237 return false;
1240 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1241 CTYPESMSG_CONV_ERROR_FIN, valStr, funStr.get());
1242 break;
1244 case ConversionType::Return: {
1245 MOZ_ASSERT(funObj);
1247 JS::UniqueChars funStr = FunctionTypeSourceForError(cx, funObj);
1248 if (!funStr) {
1249 return false;
1252 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1253 CTYPESMSG_CONV_ERROR_RET, valStr, funStr.get());
1254 break;
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);
1262 break;
1265 return false;
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);
1275 if (!expectedStr) {
1276 return false;
1279 return ConvError(cx, expectedStr.get(), actual, convType, funObj, argIndex,
1280 arrObj, arrIndex);
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);
1289 if (!valStr) {
1290 return false;
1293 IndexCString indexStr(argIndex + 1);
1295 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1296 CTYPESMSG_CONV_ERROR_ARG, valStr, indexStr.get(),
1297 funStr);
1298 return false;
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);
1305 return false;
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);
1315 if (!valStr) {
1316 return false;
1319 IndexCString expectedLengthStr(expectedLength);
1320 IndexCString actualLengthStr(actualLength);
1322 JS::UniqueChars arrStr = TypeSourceForError(cx, arrObj);
1323 if (!arrStr) {
1324 return false;
1327 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1328 CTYPESMSG_ARRAY_MISMATCH, valStr, arrStr.get(),
1329 expectedLengthStr.get(), actualLengthStr.get());
1330 return false;
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);
1340 if (!valStr) {
1341 return false;
1344 IndexCString expectedLengthStr(expectedLength);
1345 IndexCString actualLengthStr(actualLength);
1347 JS::UniqueChars arrStr = TypeSourceForError(cx, arrObj);
1348 if (!arrStr) {
1349 return false;
1352 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1353 CTYPESMSG_ARRAY_OVERFLOW, valStr, arrStr.get(),
1354 expectedLengthStr.get(), actualLengthStr.get());
1355 return false;
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);
1362 return false;
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);
1369 return false;
1372 static bool CannotConstructError(JSContext* cx, const char* type) {
1373 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1374 CTYPESMSG_CANNOT_CONSTRUCT, type);
1375 return false;
1378 static bool DuplicateFieldError(JSContext* cx, Handle<JSLinearString*> name) {
1379 JS::UniqueChars nameStr = JS_EncodeStringToUTF8(cx, name);
1380 if (!nameStr) {
1381 return false;
1384 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1385 CTYPESMSG_DUPLICATE_FIELD, nameStr.get());
1386 return false;
1389 static bool EmptyFinalizerCallError(JSContext* cx, const char* funName) {
1390 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1391 CTYPESMSG_EMPTY_FIN_CALL, funName);
1392 return false;
1395 static bool EmptyFinalizerError(JSContext* cx, ConversionType convType,
1396 HandleObject funObj = nullptr,
1397 unsigned argIndex = 0) {
1398 JS::UniqueChars posStr;
1399 if (funObj) {
1400 posStr = ConversionPositionForError(cx, convType, funObj, argIndex);
1401 if (!posStr) {
1402 return false;
1406 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, CTYPESMSG_EMPTY_FIN,
1407 (posStr ? posStr.get() : ""));
1408 return false;
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);
1420 if (!valStr) {
1421 return false;
1424 JS::UniqueChars structStr = TypeSourceForError(cx, structObj);
1425 if (!structStr) {
1426 return false;
1429 IndexCString expectedCountStr(expectedCount);
1430 IndexCString actualCountStr(actualCount);
1432 JS::UniqueChars posStr;
1433 if (funObj) {
1434 posStr = ConversionPositionForError(cx, convType, funObj, argIndex);
1435 if (!posStr) {
1436 return false;
1440 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1441 CTYPESMSG_FIELD_MISMATCH, valStr, structStr.get(),
1442 expectedCountStr.get(), actualCountStr.get(),
1443 (posStr ? posStr.get() : ""));
1444 return false;
1447 static bool FieldDescriptorCountError(JSContext* cx, HandleValue typeVal,
1448 size_t length) {
1449 JS::UniqueChars valBytes;
1450 const char* valStr = CTypesToSourceForError(cx, typeVal, valBytes);
1451 if (!valStr) {
1452 return false;
1455 IndexCString lengthStr(length);
1457 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1458 CTYPESMSG_FIELD_DESC_COUNT, valStr, lengthStr.get());
1459 return false;
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);
1466 if (!propStr) {
1467 return false;
1470 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1471 CTYPESMSG_FIELD_DESC_NAME, propStr);
1472 return false;
1475 static bool FieldDescriptorSizeError(JSContext* cx, HandleObject typeObj,
1476 HandleId id) {
1477 RootedValue typeVal(cx, ObjectValue(*typeObj));
1478 JS::UniqueChars typeBytes;
1479 const char* typeStr = CTypesToSourceForError(cx, typeVal, typeBytes);
1480 if (!typeStr) {
1481 return false;
1484 RootedString idStr(cx, IdToString(cx, id));
1485 JS::UniqueChars propStr = JS_EncodeStringToUTF8(cx, idStr);
1486 if (!propStr) {
1487 return false;
1490 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1491 CTYPESMSG_FIELD_DESC_SIZE, typeStr, propStr.get());
1492 return false;
1495 static bool FieldDescriptorNameTypeError(JSContext* cx, HandleValue typeVal) {
1496 JS::UniqueChars valBytes;
1497 const char* valStr = CTypesToSourceForError(cx, typeVal, valBytes);
1498 if (!valStr) {
1499 return false;
1502 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1503 CTYPESMSG_FIELD_DESC_NAMETYPE, valStr);
1504 return false;
1507 static bool FieldDescriptorTypeError(JSContext* cx, HandleValue poroVal,
1508 HandleId id) {
1509 JS::UniqueChars typeBytes;
1510 const char* typeStr = CTypesToSourceForError(cx, poroVal, typeBytes);
1511 if (!typeStr) {
1512 return false;
1515 RootedString idStr(cx, IdToString(cx, id));
1516 JS::UniqueChars propStr = JS_EncodeStringToUTF8(cx, idStr);
1517 if (!propStr) {
1518 return false;
1521 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1522 CTYPESMSG_FIELD_DESC_TYPE, typeStr, propStr.get());
1523 return false;
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);
1532 if (!typeStr) {
1533 return false;
1536 JS::UniqueChars nameStr = JS_EncodeStringToUTF8(cx, name);
1537 if (!nameStr) {
1538 return false;
1541 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1542 CTYPESMSG_FIELD_MISSING, typeStr, nameStr.get());
1543 return false;
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);
1552 if (!valStr) {
1553 return false;
1556 JS::UniqueChars funStr = FunctionTypeSourceForError(cx, funObj);
1557 if (!funStr) {
1558 return false;
1561 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1562 CTYPESMSG_FIN_SIZE_ERROR, funStr.get(), valStr);
1563 return false;
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);
1573 } else {
1574 funStr = FunctionTypeSourceForError(cx, typeObj);
1576 if (!funStr) {
1577 return false;
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());
1588 return false;
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);
1597 if (!valStr) {
1598 return false;
1601 IndexCString indexStr(index + 1);
1603 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1604 CTYPESMSG_ARG_TYPE_ERROR, indexStr.get(), reason,
1605 valStr);
1606 return false;
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);
1615 if (!valStr) {
1616 return false;
1619 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1620 CTYPESMSG_RET_TYPE_ERROR, reason, valStr);
1621 return false;
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);
1631 if (!valStr) {
1632 return false;
1635 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1636 CTYPESMSG_INCOMPATIBLE_CALLEE, funName, valStr);
1637 return false;
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);
1644 return false;
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);
1653 if (!valStr) {
1654 return false;
1657 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1658 CTYPESMSG_INCOMPATIBLE_THIS_VAL, funName,
1659 "incompatible object", valStr);
1660 return false;
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,
1667 actualType);
1668 return false;
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);
1679 if (!valStr) {
1680 return false;
1683 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1684 CTYPESMSG_INCOMPATIBLE_THIS_VAL, funName, actualType,
1685 valStr);
1686 return false;
1689 static bool InvalidIndexError(JSContext* cx, HandleValue val) {
1690 JS::UniqueChars idBytes;
1691 const char* indexStr = CTypesToSourceForError(cx, val, idBytes);
1692 if (!indexStr) {
1693 return false;
1696 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1697 CTYPESMSG_INVALID_INDEX, indexStr);
1698 return false;
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(),
1712 lengthStr.get());
1713 return false;
1716 static bool NonPrimitiveError(JSContext* cx, HandleObject typeObj) {
1717 MOZ_ASSERT(CType::IsCType(typeObj));
1719 JS::UniqueChars typeStr = TypeSourceForError(cx, typeObj);
1720 if (!typeStr) {
1721 return false;
1724 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1725 CTYPESMSG_NON_PRIMITIVE, typeStr.get());
1726 return false;
1729 static bool NonStringBaseError(JSContext* cx, HandleValue thisVal) {
1730 JS::UniqueChars valBytes;
1731 const char* valStr = CTypesToSourceForError(cx, thisVal, valBytes);
1732 if (!valStr) {
1733 return false;
1736 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1737 CTYPESMSG_NON_STRING_BASE, valStr);
1738 return false;
1741 static bool NonTypedArrayBaseError(JSContext* cx, HandleValue thisVal) {
1742 JS::UniqueChars valBytes;
1743 const char* valStr = CTypesToSourceForError(cx, thisVal, valBytes);
1744 if (!valStr) {
1745 return false;
1748 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1749 CTYPESMSG_NON_TYPEDARRAY_BASE, valStr);
1750 return false;
1753 static bool NullPointerError(JSContext* cx, const char* action,
1754 HandleObject obj) {
1755 MOZ_ASSERT(JS::StringIsASCII(action));
1757 JS::UniqueChars valBytes;
1758 RootedValue val(cx, ObjectValue(*obj));
1759 const char* valStr = CTypesToSourceForError(cx, val, valBytes);
1760 if (!valStr) {
1761 return false;
1764 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, CTYPESMSG_NULL_POINTER,
1765 action, valStr);
1766 return false;
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);
1775 if (!valStr) {
1776 return false;
1779 JS::UniqueChars idBytes;
1780 RootedValue idVal(cx, IdToValue(id));
1781 const char* propStr = CTypesToSourceForError(cx, idVal, idBytes);
1782 if (!propStr) {
1783 return false;
1786 JS::UniqueChars posStr;
1787 if (funObj) {
1788 posStr = ConversionPositionForError(cx, convType, funObj, argIndex);
1789 if (!posStr) {
1790 return false;
1794 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1795 CTYPESMSG_PROP_NONSTRING, propStr, valStr,
1796 (posStr ? posStr.get() : ""));
1797 return false;
1800 static bool SizeOverflow(JSContext* cx, const char* name, const char* limit) {
1801 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1802 CTYPESMSG_SIZE_OVERFLOW, name, limit);
1803 return false;
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);
1811 if (!src) {
1812 return false;
1815 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, CTYPESMSG_TYPE_ERROR,
1816 expected, src);
1817 return false;
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);
1826 if (!valStr) {
1827 return false;
1830 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1831 CTYPESMSG_TYPE_OVERFLOW, valStr, expected);
1832 return false;
1835 static bool UndefinedSizeCastError(JSContext* cx, HandleObject targetTypeObj) {
1836 JS::UniqueChars targetTypeStr = TypeSourceForError(cx, targetTypeObj);
1837 if (!targetTypeStr) {
1838 return false;
1841 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1842 CTYPESMSG_UNDEFINED_SIZE_CAST, targetTypeStr.get());
1843 return false;
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) {
1851 return false;
1854 JS::UniqueChars targetTypeStr = TypeSourceForError(cx, targetTypeObj);
1855 if (!targetTypeStr) {
1856 return false;
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());
1866 return false;
1869 static bool UndefinedSizePointerError(JSContext* cx, const char* action,
1870 HandleObject obj) {
1871 MOZ_ASSERT(JS::StringIsASCII(action));
1873 JS::UniqueChars valBytes;
1874 RootedValue val(cx, ObjectValue(*obj));
1875 const char* valStr = CTypesToSourceForError(cx, val, valBytes);
1876 if (!valStr) {
1877 return false;
1880 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1881 CTYPESMSG_UNDEFINED_SIZE, action, valStr);
1882 return false;
1885 static bool VariadicArgumentTypeError(JSContext* cx, uint32_t index,
1886 HandleValue actual) {
1887 JS::UniqueChars valBytes;
1888 const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
1889 if (!valStr) {
1890 return false;
1893 IndexCString indexStr(index + 1);
1895 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1896 CTYPESMSG_VARG_TYPE_ERROR, indexStr.get(), valStr);
1897 return false;
1900 [[nodiscard]] JSObject* GetThisObject(JSContext* cx, const CallArgs& args,
1901 const char* msg) {
1902 if (!args.thisv().isObject()) {
1903 IncompatibleThisProto(cx, msg, args.thisv());
1904 return nullptr;
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) {
1914 return false;
1917 RootedId toStringTagId(
1918 cx, JS::GetWellKnownSymbolKey(cx, JS::SymbolCode::toStringTag));
1919 return JS_DefinePropertyById(cx, obj, toStringTagId, toStringTagStr,
1920 JSPROP_READONLY);
1923 static JSObject* InitCTypeClass(JSContext* cx, HandleObject ctypesObj) {
1924 JSFunction* fun = JS_DefineFunction(cx, ctypesObj, "CType", ConstructAbstract,
1925 0, CTYPESCTOR_FLAGS);
1926 if (!fun) {
1927 return nullptr;
1930 RootedObject ctor(cx, JS_GetFunctionObject(fun));
1931 RootedObject fnproto(cx);
1932 if (!JS_GetPrototype(cx, ctor, &fnproto)) {
1933 return nullptr;
1935 MOZ_ASSERT(ctor);
1936 MOZ_ASSERT(fnproto);
1938 // Set up ctypes.CType.prototype.
1939 RootedObject prototype(
1940 cx, JS_NewObjectWithGivenProto(cx, &sCTypeProtoClass, fnproto));
1941 if (!prototype) {
1942 return nullptr;
1945 if (!JS_DefineProperty(cx, ctor, "prototype", prototype,
1946 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
1947 return nullptr;
1949 if (!JS_DefineProperty(cx, prototype, "constructor", ctor,
1950 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
1951 return nullptr;
1953 // Define properties and functions common to all CTypes.
1954 if (!JS_DefineProperties(cx, prototype, sCTypeProps) ||
1955 !JS_DefineFunctions(cx, prototype, sCTypeFunctions))
1956 return nullptr;
1958 if (!DefineToStringTag(cx, prototype, "CType")) {
1959 return nullptr;
1962 if (!JS_FreezeObject(cx, ctor) || !JS_FreezeObject(cx, prototype)) {
1963 return nullptr;
1966 return prototype;
1969 static JSObject* InitABIClass(JSContext* cx) {
1970 RootedObject obj(cx, JS_NewPlainObject(cx));
1972 if (!obj) {
1973 return nullptr;
1976 if (!JS_DefineFunctions(cx, obj, sCABIFunctions)) {
1977 return nullptr;
1980 if (!DefineToStringTag(cx, obj, "CABI")) {
1981 return nullptr;
1984 return obj;
1987 static JSObject* InitCDataClass(JSContext* cx, HandleObject parent,
1988 HandleObject CTypeProto) {
1989 JSFunction* fun = JS_DefineFunction(cx, parent, "CData", ConstructAbstract, 0,
1990 CTYPESCTOR_FLAGS);
1991 if (!fun) {
1992 return nullptr;
1995 RootedObject ctor(cx, JS_GetFunctionObject(fun));
1996 MOZ_ASSERT(ctor);
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)) {
2002 return nullptr;
2005 // Set up ctypes.CData.prototype.
2006 RootedObject prototype(cx, JS_NewObject(cx, &sCDataProtoClass));
2007 if (!prototype) {
2008 return nullptr;
2011 if (!JS_DefineProperty(cx, ctor, "prototype", prototype,
2012 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
2013 return nullptr;
2015 if (!JS_DefineProperty(cx, prototype, "constructor", ctor,
2016 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
2017 return nullptr;
2019 // Define properties and functions common to all CDatas.
2020 if (!JS_DefineProperties(cx, prototype, sCDataProps) ||
2021 !JS_DefineFunctions(cx, prototype, sCDataFunctions))
2022 return nullptr;
2024 if (!DefineToStringTag(cx, prototype, "CData")) {
2025 return nullptr;
2028 if ( // !JS_FreezeObject(cx, prototype) || // XXX fixme - see bug 541212!
2029 !JS_FreezeObject(cx, ctor))
2030 return nullptr;
2032 return prototype;
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));
2039 if (!obj) {
2040 return false;
2042 JS_SetReservedSlot(obj, SLOT_ABICODE, Int32Value(code));
2044 if (!JS_FreezeObject(cx, obj)) {
2045 return false;
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);
2063 if (!fun) {
2064 return false;
2067 RootedObject obj(cx, JS_GetFunctionObject(fun));
2068 if (!obj) {
2069 return false;
2072 // Set up the .prototype and .prototype.constructor properties.
2073 typeProto.set(JS_NewObjectWithGivenProto(cx, &sCTypeProtoClass, CTypeProto));
2074 if (!typeProto) {
2075 return false;
2078 // Define property before proceeding, for GC safety.
2079 if (!JS_DefineProperty(cx, obj, "prototype", typeProto,
2080 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
2081 return false;
2083 if (fns && !JS_DefineFunctions(cx, typeProto, fns)) {
2084 return false;
2087 if (!JS_DefineProperties(cx, typeProto, props)) {
2088 return false;
2091 if (!JS_DefineProperty(cx, typeProto, "constructor", obj,
2092 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
2093 return false;
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));
2105 if (!dataProto) {
2106 return false;
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)) {
2113 return false;
2116 if (instanceProps && !JS_DefineProperties(cx, dataProto, instanceProps)) {
2117 return false;
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))
2126 return false;
2128 return true;
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));
2139 if (!prototype) {
2140 return nullptr;
2143 if (clasp == &sInt64ProtoClass) {
2144 if (!DefineToStringTag(cx, prototype, "Int64")) {
2145 return nullptr;
2147 } else {
2148 MOZ_ASSERT(clasp == &sUInt64ProtoClass);
2149 if (!DefineToStringTag(cx, prototype, "UInt64")) {
2150 return nullptr;
2154 RootedObject ctor(cx, JS_GetConstructor(cx, prototype));
2155 if (!ctor) {
2156 return nullptr;
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,
2163 CTYPESFN_FLAGS);
2164 if (!fun) {
2165 return nullptr;
2168 js::SetFunctionNativeReserved(fun, SLOT_FN_INT64PROTO,
2169 ObjectValue(*prototype));
2171 if (!JS_FreezeObject(cx, ctor)) {
2172 return nullptr;
2174 if (!JS_FreezeObject(cx, prototype)) {
2175 return nullptr;
2178 return 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
2196 // abstract type!)
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));
2205 if (!CTypeProto) {
2206 return false;
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.
2211 // It has:
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));
2221 if (!CDataProto) {
2222 return false;
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)) {
2256 return false;
2258 if (!InitTypeConstructor(
2259 cx, ctypesObj, CTypeProto, CDataProto, sPointerFunction, nullptr,
2260 sPointerProps, sPointerInstanceFunctions, sPointerInstanceProps,
2261 protos[SLOT_POINTERPROTO], protos[SLOT_POINTERDATAPROTO]))
2262 return false;
2264 if (!InitTypeConstructor(
2265 cx, ctypesObj, CTypeProto, CDataProto, sArrayFunction, nullptr,
2266 sArrayProps, sArrayInstanceFunctions, sArrayInstanceProps,
2267 protos[SLOT_ARRAYPROTO], protos[SLOT_ARRAYDATAPROTO]))
2268 return false;
2270 if (!InitTypeConstructor(
2271 cx, ctypesObj, CTypeProto, CDataProto, sStructFunction,
2272 sStructFunctions, sStructProps, sStructInstanceFunctions, nullptr,
2273 protos[SLOT_STRUCTPROTO], protos[SLOT_STRUCTDATAPROTO]))
2274 return false;
2276 if (!InitTypeConstructor(cx, ctypesObj, CTypeProto,
2277 protos[SLOT_POINTERDATAPROTO], sFunctionFunction,
2278 nullptr, sFunctionProps, sFunctionInstanceFunctions,
2279 nullptr, protos[SLOT_FUNCTIONPROTO],
2280 protos[SLOT_FUNCTIONDATAPROTO]))
2281 return false;
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,
2290 // respectively.
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]) {
2298 return false;
2300 protos[SLOT_UINT64PROTO].set(
2301 InitInt64Class(cx, ctypesObj, &sUInt64ProtoClass, UInt64::Construct,
2302 sUInt64Functions, sUInt64StaticFunctions));
2303 if (!protos[SLOT_UINT64PROTO]) {
2304 return false;
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));
2321 if (!ABIProto) {
2322 return false;
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,
2329 ABIProto) ||
2330 !DefineABIConstant(cx, ctypesObj, "winapi_abi", ABI_WINAPI, ABIProto))
2331 return false;
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)); \
2348 typeObj_##name = \
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)
2354 #undef 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))
2360 return false;
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))
2366 return false;
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));
2373 if (!typeObj) {
2374 return false;
2377 typeObj = PointerType::CreateInternal(cx, typeObj);
2378 if (!typeObj) {
2379 return false;
2381 if (!JS_DefineProperty(cx, ctypesObj, "voidptr_t", typeObj,
2382 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
2383 return false;
2385 return true;
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()) {
2402 return nullptr;
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)) {
2416 return false;
2419 if (val.isPrimitive()) {
2420 JS_ReportErrorASCII(cx, "missing or non-object field");
2421 return false;
2424 result.set(val.toObjectOrNull());
2425 return true;
2428 } // namespace js::ctypes
2430 using namespace js;
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));
2437 if (!ctypes) {
2438 return false;
2441 if (!JS_DefineProperty(cx, global, "ctypes", ctypes,
2442 JSPROP_READONLY | JSPROP_PERMANENT)) {
2443 return false;
2446 if (!InitTypeClasses(cx, ctypes)) {
2447 return false;
2450 // attach API functions and properties
2451 if (!JS_DefineFunctions(cx, ctypes, sModuleFunctions) ||
2452 !JS_DefineProperties(cx, ctypes, sModuleProps))
2453 return false;
2455 if (!DefineToStringTag(cx, ctypes, "ctypes")) {
2456 return false;
2459 // Set up ctypes.CDataFinalizer.prototype.
2460 RootedObject ctor(cx);
2461 if (!GetObjectProperty(cx, ctypes, "CDataFinalizer", &ctor)) {
2462 return false;
2465 RootedObject prototype(cx, JS_NewObject(cx, &sCDataFinalizerProtoClass));
2466 if (!prototype) {
2467 return false;
2470 if (!JS_DefineFunctions(cx, prototype, sCDataFinalizerFunctions)) {
2471 return false;
2474 if (!DefineToStringTag(cx, prototype, "CDataFinalizer")) {
2475 return false;
2478 if (!JS_DefineProperty(cx, ctor, "prototype", prototype,
2479 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
2480 return false;
2482 if (!JS_DefineProperty(cx, prototype, "constructor", ctor,
2483 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
2484 return false;
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)));
2500 namespace js {
2502 namespace ctypes {
2504 size_t SizeOfDataIfCDataObject(mozilla::MallocSizeOf mallocSizeOf,
2505 JSObject* obj) {
2506 if (!CData::IsCData(obj)) {
2507 return 0;
2510 size_t n = 0;
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);
2518 if (owns) {
2519 n += mallocSizeOf(*buffer);
2523 return n;
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>>
2549 struct ConvertImpl;
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;
2565 template <>
2566 struct ConvertUnsignedTargetTo<char16_t> {
2567 static char16_t convert(char16_t input) {
2568 // mozilla::WrapToSigned can't be used on char16_t.
2569 return input;
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) {
2585 static_assert(
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.
2595 // This means that:
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
2599 // mantissa width.
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) {
2604 return false;
2607 if (numeric_limits<FromType>::is_signed &&
2608 !numeric_limits<TargetType>::is_signed)
2609 return false;
2611 if (!numeric_limits<FromType>::is_exact &&
2612 numeric_limits<TargetType>::is_exact)
2613 return false;
2615 return true;
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>()) {
2657 return true;
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();
2690 return true;
2692 if (val.isInt32()) {
2693 int32_t i = val.toInt32();
2694 *result = i != 0;
2695 return i == 0 || i == 1;
2697 if (val.isDouble()) {
2698 double d = val.toDouble();
2699 *result = d != 0;
2700 // Allow -0.
2701 return d == 1 || d == 0;
2703 // Don't silently convert null to bool. It's probably a mistake.
2704 return false;
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
2717 // sign.
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) \
2737 case TYPE_##name: \
2738 if (!IsAlwaysExact<IntegerType, fromType>()) return false; \
2739 *result = IntegerType(*static_cast<fromType*>(data)); \
2740 return true;
2741 CTYPES_FOR_EACH_INT_TYPE(INTEGER_CASE)
2742 CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGER_CASE)
2743 #undef INTEGER_CASE
2744 case TYPE_void_t:
2745 case TYPE_bool:
2746 case TYPE_float:
2747 case TYPE_double:
2748 case TYPE_float32_t:
2749 case TYPE_float64_t:
2750 case TYPE_char:
2751 case TYPE_signed_char:
2752 case TYPE_unsigned_char:
2753 case TYPE_char16_t:
2754 case TYPE_pointer:
2755 case TYPE_function:
2756 case TYPE_array:
2757 case TYPE_struct:
2758 // Not a compatible number type.
2759 return false;
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);
2783 return false;
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);
2789 return true;
2791 // Don't silently convert null to an integer. It's probably a mistake.
2792 return false;
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());
2808 return true;
2810 if (val.isDouble()) {
2811 *result = FloatType(val.toDouble());
2812 return true;
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) \
2824 case TYPE_##name: \
2825 if (!IsAlwaysExact<FloatType, fromType>()) return false; \
2826 *result = FloatType(*static_cast<fromType*>(data)); \
2827 return true;
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)
2831 #undef NUMERIC_CASE
2832 case TYPE_void_t:
2833 case TYPE_bool:
2834 case TYPE_char:
2835 case TYPE_signed_char:
2836 case TYPE_unsigned_char:
2837 case TYPE_char16_t:
2838 case TYPE_pointer:
2839 case TYPE_function:
2840 case TYPE_array:
2841 case TYPE_struct:
2842 // Not a compatible number type.
2843 return false;
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.
2849 return false;
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;
2858 if (cp == end) {
2859 return false;
2862 IntegerType sign = 1;
2863 if (cp[0] == '-') {
2864 if (!numeric_limits<IntegerType>::is_signed) {
2865 return false;
2868 sign = -1;
2869 ++cp;
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')) {
2875 cp += 2;
2876 base = 16;
2879 // Scan the string left to right and build the number,
2880 // checking for valid characters 0 - 9, a - f, A - F and overflow.
2881 IntegerType i = 0;
2882 while (cp != end) {
2883 char16_t c = *cp++;
2884 uint8_t digit;
2885 if (IsAsciiDigit(c)) {
2886 digit = c - '0';
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;
2891 } else {
2892 return false;
2895 IntegerType ii = i;
2896 i = ii * base + sign * digit;
2897 if (i / base != ii) {
2898 *overflow = true;
2899 return false;
2903 *result = i;
2904 return true;
2907 template <class IntegerType>
2908 static bool StringToInteger(JSContext* cx, JSString* string,
2909 IntegerType* result, bool* overflow) {
2910 JSLinearString* linear = string->ensureLinear(cx);
2911 if (!linear) {
2912 return false;
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
2927 // constructors.)
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
2935 // sign.
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);
2976 return false;
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,
2982 size_t* result) {
2983 bool dummy;
2984 if (!jsvalToBigInteger(cx, val, allowString, result, &dummy)) {
2985 return false;
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
2995 // constructors.)
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);
3001 if (val.isInt()) {
3002 // Make sure the integer fits in the alotted precision, and has the right
3003 // sign.
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.)
3012 bool dummy;
3013 return StringToInteger(cx, val.toString(), result, &dummy);
3015 return false;
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,
3021 size_t* result) {
3022 if (!jsidToBigInteger(cx, val, allowString, result)) {
3023 return false;
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) {
3034 return false;
3037 result.setNumber(double(size));
3038 return true;
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);
3052 return true;
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);
3060 return true;
3062 if (UInt64::IsUInt64(obj)) {
3063 uint64_t i = Int64Base::GetInt(obj);
3064 *result = IntegerType(i);
3065 return true;
3068 return false;
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);
3079 return true;
3081 if (val.isDouble()) {
3082 double d = val.toDouble();
3083 if (d < 0) {
3084 // Cast through an intptr_t intermediate to sign-extend.
3085 intptr_t i = Convert<intptr_t>(d);
3086 if (double(i) != d) {
3087 return false;
3090 *result = uintptr_t(i);
3091 return true;
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) {
3107 return false;
3109 *result = uintptr_t(p);
3110 return true;
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;
3121 return false;
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);
3133 CharType* cp = end;
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;
3139 do {
3140 IntegerType ii = i / IntegerType(radix);
3141 size_t index = sign * size_t(i - ii * IntegerType(radix));
3142 *--cp = "0123456789abcdefghijklmnopqrstuvwxyz"[index];
3143 i = ii;
3144 } while (i != 0);
3146 if (isNegative) {
3147 *--cp = '-';
3150 MOZ_ASSERT(cp >= buffer);
3151 if (!result.append(cp, end)) {
3152 return;
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
3164 // parent's buffer.
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);
3176 switch (typeCode) {
3177 case TYPE_void_t:
3178 result.setUndefined();
3179 break;
3180 case TYPE_bool:
3181 result.setBoolean(*static_cast<bool*>(data));
3182 break;
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)); \
3188 else \
3189 result.setDouble(double(value)); \
3190 break; \
3192 CTYPES_FOR_EACH_INT_TYPE(INT_CASE)
3193 #undef 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. */ \
3197 uint64_t value; \
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; \
3204 } else { \
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); \
3215 break; \
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)); \
3223 break; \
3225 CTYPES_FOR_EACH_FLOAT_TYPE(FLOAT_CASE)
3226 #undef FLOAT_CASE
3227 #define CHAR_CASE(name, type, ffiType) \
3228 case TYPE_##name: \
3229 /* Convert to an integer. We have no idea what character encoding to */ \
3230 /* use, if any. */ \
3231 result.setInt32(*static_cast<type*>(data)); \
3232 break;
3233 CTYPES_FOR_EACH_CHAR_TYPE(CHAR_CASE)
3234 #undef 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);
3238 if (!str) {
3239 return false;
3242 result.setString(str);
3243 break;
3245 case TYPE_pointer:
3246 case TYPE_array:
3247 case TYPE_struct: {
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);
3255 if (!obj) {
3256 return false;
3259 result.setObject(*obj);
3260 break;
3262 case TYPE_function:
3263 MOZ_CRASH("cannot return a FunctionType");
3266 return true;
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,
3273 JSContext* cx) {
3274 TypeCode baseTypeCode = CType::GetTypeCode(baseType);
3275 if (baseTypeCode == TYPE_void_t || baseTypeCode == TYPE_char) {
3276 return true;
3278 TypeCode elementTypeCode;
3279 switch (JS_GetArrayBufferViewType(valObj)) {
3280 case Scalar::Int8:
3281 elementTypeCode = TYPE_int8_t;
3282 break;
3283 case Scalar::Uint8:
3284 case Scalar::Uint8Clamped:
3285 elementTypeCode = TYPE_uint8_t;
3286 break;
3287 case Scalar::Int16:
3288 elementTypeCode = TYPE_int16_t;
3289 break;
3290 case Scalar::Uint16:
3291 elementTypeCode = TYPE_uint16_t;
3292 break;
3293 case Scalar::Int32:
3294 elementTypeCode = TYPE_int32_t;
3295 break;
3296 case Scalar::Uint32:
3297 elementTypeCode = TYPE_uint32_t;
3298 break;
3299 case Scalar::Float32:
3300 elementTypeCode = TYPE_float32_t;
3301 break;
3302 case Scalar::Float64:
3303 elementTypeCode = TYPE_float64_t;
3304 break;
3305 default:
3306 return false;
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
3324 // into it.
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);
3356 return true;
3358 } else if (CDataFinalizer::IsCDataFinalizer(valObj)) {
3359 sourceData = valObj;
3360 sourceType = CDataFinalizer::GetCType(cx, sourceData);
3362 CDataFinalizer::Private* p = GetFinalizerPrivate(sourceData);
3364 if (!p) {
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);
3372 return true;
3377 TypeCode targetCode = CType::GetTypeCode(targetType);
3379 switch (targetCode) {
3380 case TYPE_bool: {
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
3383 // `!!v`.
3384 bool result;
3385 if (!jsvalToBool(cx, val, &result)) {
3386 return ConvError(cx, "boolean", val, convType, funObj, argIndex, arrObj,
3387 arrIndex);
3389 *static_cast<bool*>(buffer) = result;
3390 break;
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'. */ \
3396 type result = 0; \
3397 if (val.isString()) { \
3398 JSString* str = val.toString(); \
3399 if (str->length() != 1) \
3400 return ConvError(cx, #name, val, convType, funObj, argIndex, arrObj, \
3401 arrIndex); \
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, \
3407 arrIndex); \
3409 *static_cast<type*>(buffer) = result; \
3410 break; \
3412 CTYPES_FOR_EACH_CHAR16_TYPE(CHAR16_CASE)
3413 #undef CHAR16_CASE
3414 #define INTEGRAL_CASE(name, type, ffiType) \
3415 case TYPE_##name: { \
3416 /* Do not implicitly lose bits. */ \
3417 type result; \
3418 if (!jsvalToInteger(cx, val, &result)) \
3419 return ConvError(cx, #name, val, convType, funObj, argIndex, arrObj, \
3420 arrIndex); \
3421 *static_cast<type*>(buffer) = result; \
3422 break; \
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
3430 // above, just yet.
3431 CTYPES_FOR_EACH_CHAR_TYPE(INTEGRAL_CASE)
3432 #undef INTEGRAL_CASE
3433 #define FLOAT_CASE(name, type, ffiType) \
3434 case TYPE_##name: { \
3435 type result; \
3436 if (!jsvalToFloat(cx, val, &result)) \
3437 return ConvError(cx, #name, val, convType, funObj, argIndex, arrObj, \
3438 arrIndex); \
3439 *static_cast<type*>(buffer) = result; \
3440 break; \
3442 CTYPES_FOR_EACH_FLOAT_TYPE(FLOAT_CASE)
3443 #undef FLOAT_CASE
3444 case TYPE_pointer: {
3445 if (val.isNull()) {
3446 // Convert to a null pointer.
3447 *static_cast<void**>(buffer) = nullptr;
3448 break;
3451 JS::Rooted<JSObject*> baseType(cx, PointerType::GetBaseType(targetType));
3452 if (sourceData) {
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);
3461 break;
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;
3469 break;
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
3477 // also.
3478 JSString* sourceString = val.toString();
3479 size_t sourceLength = sourceString->length();
3480 Rooted<JSLinearString*> sourceLinear(cx,
3481 sourceString->ensureLinear(cx));
3482 if (!sourceLinear) {
3483 return false;
3486 switch (CType::GetTypeCode(baseType)) {
3487 case TYPE_char:
3488 case TYPE_signed_char:
3489 case TYPE_unsigned_char: {
3490 // Reject if unpaired surrogate characters are present.
3491 if (!ReportErrorIfUnpairedSurrogatePresent(cx, sourceLinear)) {
3492 return false;
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);
3500 if (!*charBuffer) {
3501 return false;
3504 nbytes = JS::DeflateStringToUTF8Buffer(
3505 sourceLinear, mozilla::Span(*charBuffer, nbytes));
3506 (*charBuffer)[nbytes] = '\0';
3507 *freePointer = true;
3508 break;
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) {
3517 return false;
3520 *freePointer = true;
3522 CopyChars(*char16Buffer, *sourceLinear);
3523 (*char16Buffer)[sourceLength] = '\0';
3524 break;
3526 default:
3527 return ConvError(cx, targetType, val, convType, funObj, argIndex,
3528 arrObj, arrIndex);
3530 break;
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,
3538 arrObj, arrIndex);
3540 void* ptr;
3542 JS::AutoCheckCannotGC nogc;
3543 bool isShared;
3544 ptr = JS::GetArrayBufferData(valObj, &isShared, nogc);
3545 MOZ_ASSERT(!isShared); // Because ArrayBuffer
3547 if (!ptr) {
3548 return ConvError(cx, targetType, val, convType, funObj, argIndex,
3549 arrObj, arrIndex);
3551 *static_cast<void**>(buffer) = ptr;
3552 break;
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,
3558 arrObj, arrIndex);
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,
3564 arrObj, arrIndex);
3566 if (convType != ConversionType::Argument) {
3567 return ConvError(cx, targetType, val, convType, funObj, argIndex,
3568 arrObj, arrIndex);
3570 void* ptr;
3572 JS::AutoCheckCannotGC nogc;
3573 bool isShared;
3574 ptr = JS_GetArrayBufferViewData(valObj, &isShared, nogc);
3575 if (isShared) {
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.
3579 ptr = nullptr;
3582 if (!ptr) {
3583 return ConvError(cx, targetType, val, convType, funObj, argIndex,
3584 arrObj, arrIndex);
3586 *static_cast<void**>(buffer) = ptr;
3587 break;
3589 return ConvError(cx, targetType, val, convType, funObj, argIndex, arrObj,
3590 arrIndex);
3592 case TYPE_array: {
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) {
3604 return false;
3607 switch (CType::GetTypeCode(baseType)) {
3608 case TYPE_char:
3609 case TYPE_signed_char:
3610 case TYPE_unsigned_char: {
3611 // Reject if unpaired surrogate characters are present.
3612 if (!ReportErrorIfUnpairedSurrogatePresent(cx, sourceLinear)) {
3613 return false;
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,
3622 val, convType);
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';
3633 break;
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';
3651 break;
3653 default:
3654 return ConvError(cx, targetType, val, convType, funObj, argIndex,
3655 arrObj, arrIndex);
3657 } else {
3658 ESClass cls;
3659 if (!GetClassOfValue(cx, val, &cls)) {
3660 return false;
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) {
3678 return false;
3681 RootedValue item(cx);
3682 for (uint32_t i = 0; i < sourceLength; ++i) {
3683 if (!JS_GetElement(cx, valObj, i, &item)) {
3684 return false;
3687 char* data = intermediate.get() + elementSize * i;
3688 if (!ImplicitConvert(cx, item, baseType, data, convType, nullptr,
3689 funObj, argIndex, targetType, i))
3690 return false;
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
3697 // copy the array.
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,
3707 val, convType);
3709 SharedMem<void*> target = SharedMem<void*>::unshared(buffer);
3710 JS::AutoCheckCannotGC nogc;
3711 bool isShared;
3712 SharedMem<void*> src =
3713 (bufferShared
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);
3720 break;
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
3724 // or vice versa.
3725 if (!CanConvertTypedArrayItemTo(baseType, valObj, cx)) {
3726 return ConvError(cx, targetType, val, convType, funObj, argIndex,
3727 arrObj, arrIndex);
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,
3736 val, convType);
3738 SharedMem<void*> target = SharedMem<void*>::unshared(buffer);
3739 JS::AutoCheckCannotGC nogc;
3740 bool isShared;
3741 SharedMem<void*> src = SharedMem<void*>::shared(
3742 JS_GetArrayBufferViewData(valObj, &isShared, nogc));
3743 jit::AtomicOperations::memcpySafeWhenRacy(target, src, sourceLength);
3744 break;
3745 } else {
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,
3749 arrObj, arrIndex);
3752 break;
3754 case TYPE_struct: {
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)) {
3760 return false;
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) {
3767 return false;
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,
3774 argIndex);
3777 RootedId id(cx);
3778 for (size_t i = 0; i < props.length(); ++i) {
3779 id = props[i];
3781 if (!id.isString()) {
3782 return PropNameNonStringError(cx, id, val, convType, funObj,
3783 argIndex);
3786 JSLinearString* name = id.toLinearString();
3787 const FieldInfo* field =
3788 StructType::LookupField(cx, targetType, name);
3789 if (!field) {
3790 return false;
3793 RootedValue prop(cx);
3794 if (!JS_GetPropertyById(cx, valObj, id, &prop)) {
3795 return false;
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))
3802 return false;
3805 memcpy(buffer, intermediate.get(), structSize);
3806 break;
3809 return ConvError(cx, targetType, val, convType, funObj, argIndex, arrObj,
3810 arrIndex);
3812 case TYPE_void_t:
3813 case TYPE_function:
3814 MOZ_CRASH("invalid type");
3817 return true;
3820 // Convert Value 'val' to a C binary representation of CType 'targetType',
3821 // storing the result in 'buffer'. This function is more forceful than
3822 // ImplicitConvert.
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)) {
3828 return true;
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.
3834 RootedValue ex(cx);
3835 if (!JS_GetPendingException(cx, &ex)) {
3836 return false;
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);
3845 switch (type) {
3846 case TYPE_bool: {
3847 *static_cast<bool*>(buffer) = ToBoolean(val);
3848 break;
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. */ \
3854 type result; \
3855 bool overflow = false; \
3856 if (!jsvalToIntegerExplicit(val, &result) && \
3857 (!val.isString() || \
3858 !StringToInteger(cx, val.toString(), &result, &overflow))) { \
3859 if (overflow) { \
3860 return TypeOverflow(cx, #name, val); \
3862 return ConvError(cx, #name, val, convType); \
3864 *static_cast<type*>(buffer) = result; \
3865 break; \
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.
3874 uintptr_t result;
3875 if (!jsvalToPtrExplicit(cx, val, &result)) {
3876 return ConvError(cx, targetType, val, convType);
3878 *static_cast<uintptr_t*>(buffer) = result;
3879 break;
3881 case TYPE_float32_t:
3882 case TYPE_float64_t:
3883 case TYPE_float:
3884 case TYPE_double:
3885 case TYPE_array:
3886 case TYPE_struct:
3887 // ImplicitConvert is sufficient. Re-throw the exception it generated.
3888 JS_SetPendingException(cx, ex);
3889 return false;
3890 case TYPE_void_t:
3891 case TYPE_function:
3892 MOZ_CRASH("invalid type");
3894 return true;
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_) {
3902 AutoString result;
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;
3912 while (true) {
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;
3921 continue;
3923 case TYPE_array: {
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, "[");
3932 size_t length;
3933 if (ArrayType::GetSafeLength(typeObj, &length)) {
3934 IntegerToString(length, 10, result);
3937 AppendString(cx, result, "]");
3939 typeObj = ArrayType::GetBaseType(typeObj);
3940 prevGrouping = currentGrouping;
3941 continue;
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;
3987 continue;
3989 default:
3990 // Either a basic or struct type. Use the type's name as the base type.
3991 break;
3993 break;
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);
4005 if (!result) {
4006 return nullptr;
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)) {
4024 case TYPE_void_t:
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);
4032 break;
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");
4040 break;
4043 // Recursively build the source string, and append '.ptr'.
4044 BuildTypeSource(cx, baseType, makeShort, result);
4045 AppendString(cx, result, ".ptr");
4046 break;
4048 case TYPE_function: {
4049 FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
4051 AppendString(cx, result, "ctypes.FunctionType(");
4053 switch (GetABICode(fninfo->mABI)) {
4054 case ABI_DEFAULT:
4055 AppendString(cx, result, "ctypes.default_abi, ");
4056 break;
4057 case ABI_STDCALL:
4058 AppendString(cx, result, "ctypes.stdcall_abi, ");
4059 break;
4060 case ABI_THISCALL:
4061 AppendString(cx, result, "ctypes.thiscall_abi, ");
4062 break;
4063 case ABI_WINAPI:
4064 AppendString(cx, result, "ctypes.winapi_abi, ");
4065 break;
4066 case INVALID_ABI:
4067 MOZ_CRASH("invalid abi");
4070 // Recursively build the source string describing the function return and
4071 // argument types.
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, ")");
4088 break;
4090 case TYPE_array: {
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
4093 // is undefined.
4094 JSObject* baseType = ArrayType::GetBaseType(typeObj);
4095 BuildTypeSource(cx, baseType, makeShort, result);
4096 AppendString(cx, result, ".array(");
4098 size_t length;
4099 if (ArrayType::GetSafeLength(typeObj, &length)) {
4100 IntegerToString(length, 10, result);
4103 AppendString(cx, result, ")");
4104 break;
4106 case TYPE_struct: {
4107 JSString* name = CType::GetName(cx, typeObj);
4109 if (makeShort) {
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);
4113 break;
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, ")");
4124 break;
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)) {
4133 break;
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, "])");
4153 break;
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);
4172 switch (type) {
4173 case TYPE_bool:
4174 if (*static_cast<bool*>(data)) {
4175 AppendString(cx, result, "true");
4176 } else {
4177 AppendString(cx, result, "false");
4179 break;
4180 #define INTEGRAL_CASE(name, type, ffiType) \
4181 case TYPE_##name: \
4182 /* Serialize as a primitive decimal integer. */ \
4183 IntegerToString(*static_cast<type*>(data), 10, result); \
4184 break;
4185 CTYPES_FOR_EACH_INT_TYPE(INTEGRAL_CASE)
4186 #undef INTEGRAL_CASE
4187 #define WRAPPED_INT_CASE(name, type, ffiType) \
4188 case TYPE_##name: \
4189 /* Serialize as a wrapped decimal integer. */ \
4190 if (!numeric_limits<type>::is_signed) \
4191 AppendString(cx, result, "ctypes.UInt64(\""); \
4192 else \
4193 AppendString(cx, result, "ctypes.Int64(\""); \
4195 IntegerToString(*static_cast<type*>(data), 10, result); \
4196 AppendString(cx, result, "\")"); \
4197 break;
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; \
4205 size_t strLength; \
4206 char* str = NumberToCString(&cbuf, fp, &strLength); \
4207 MOZ_ASSERT(str); \
4208 if (!result.append(str, strLength)) { \
4209 JS_ReportOutOfMemory(cx); \
4210 return false; \
4212 break; \
4214 CTYPES_FOR_EACH_FLOAT_TYPE(FLOAT_CASE)
4215 #undef FLOAT_CASE
4216 #define CHAR_CASE(name, type, ffiType) \
4217 case TYPE_##name: \
4218 /* Serialize as an integer. */ \
4219 IntegerToString(*static_cast<type*>(data), 10, result); \
4220 break;
4221 CTYPES_FOR_EACH_CHAR_TYPE(CHAR_CASE)
4222 #undef 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);
4226 if (!str) {
4227 return false;
4230 // Escape characters, and quote as necessary.
4231 RootedValue valStr(cx, StringValue(str));
4232 JSString* src = JS_ValueToSource(cx, valStr);
4233 if (!src) {
4234 return false;
4237 AppendString(cx, result, src);
4238 break;
4240 case TYPE_pointer:
4241 case TYPE_function: {
4242 if (isImplicit) {
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, "\")");
4255 if (isImplicit) {
4256 AppendString(cx, result, ")");
4259 break;
4261 case TYPE_array: {
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)) {
4272 return false;
4275 if (i + 1 < length) {
4276 AppendString(cx, result, ", ");
4279 AppendString(cx, result, "]");
4280 break;
4282 case TYPE_struct: {
4283 if (isImplicit) {
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)) {
4296 return false;
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];
4306 if (isImplicit) {
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)) {
4315 return false;
4318 if (i + 1 != length) {
4319 AppendString(cx, result, ", ");
4323 if (isImplicit) {
4324 AppendString(cx, result, "}");
4327 break;
4329 case TYPE_void_t:
4330 MOZ_CRASH("invalid type");
4333 return true;
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)) {
4362 case TYPE_void_t:
4363 return CannotConstructError(cx, "void_t");
4364 case TYPE_function:
4365 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
4366 CTYPESMSG_FUNCTION_CONSTRUCT);
4367 return false;
4368 case TYPE_pointer:
4369 return PointerType::ConstructData(cx, obj, args);
4370 case TYPE_array:
4371 return ArrayType::ConstructData(cx, obj, args);
4372 case TYPE_struct:
4373 return StructType::ConstructData(cx, obj, args);
4374 default:
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));
4387 if (!result) {
4388 return false;
4391 if (args.length() == 1) {
4392 if (!ExplicitConvert(cx, args[0], obj, CData::GetData(result),
4393 ConversionType::Construct))
4394 return false;
4397 args.rval().setObject(*result);
4398 return true;
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));
4425 if (!typeObj) {
4426 return nullptr;
4429 // Set up the reserved slots.
4430 JS_SetReservedSlot(typeObj, SLOT_TYPECODE, Int32Value(type));
4431 if (ffiType) {
4432 JS_SetReservedSlot(typeObj, SLOT_FFITYPE, PrivateValue(ffiType));
4433 if (type == TYPE_struct || type == TYPE_array) {
4434 AddCellMemory(typeObj, sizeof(ffi_type), MemoryUse::CTypeFFIType);
4437 if (name) {
4438 JS_SetReservedSlot(typeObj, SLOT_NAME, StringValue(name));
4440 JS_SetReservedSlot(typeObj, SLOT_SIZE, size);
4441 JS_SetReservedSlot(typeObj, SLOT_ALIGN, align);
4443 if (dataProto) {
4444 // Set up the 'prototype' and 'prototype.constructor' properties.
4445 RootedObject prototype(
4446 cx, JS_NewObjectWithGivenProto(cx, &sCDataProtoClass, dataProto));
4447 if (!prototype) {
4448 return nullptr;
4451 if (!JS_DefineProperty(cx, prototype, "constructor", typeObj,
4452 JSPROP_READONLY | JSPROP_PERMANENT))
4453 return nullptr;
4455 // Set the 'prototype' object.
4456 // if (!JS_FreezeObject(cx, prototype)) // XXX fixme - see bug 541212!
4457 // return nullptr;
4458 JS_SetReservedSlot(typeObj, SLOT_PROTO, ObjectValue(*prototype));
4461 if (!JS_FreezeObject(cx, typeObj)) {
4462 return nullptr;
4465 // Assert a sanity check on size and alignment: size % alignment should always
4466 // be zero.
4467 MOZ_ASSERT_IF(IsSizeDefined(typeObj),
4468 GetSize(typeObj) % GetAlignment(typeObj) == 0);
4470 return typeObj;
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));
4482 if (!nameStr) {
4483 return nullptr;
4486 // Create a new CType object with the common properties and slots.
4487 RootedObject typeObj(cx, Create(cx, typeProto, dataProto, type, nameStr, size,
4488 align, ffiType));
4489 if (!typeObj) {
4490 return nullptr;
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))
4496 return nullptr;
4498 return typeObj;
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()) {
4513 return;
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);
4525 break;
4528 case TYPE_struct: {
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.
4547 break;
4550 case TYPE_array: {
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);
4557 break;
4560 default:
4561 // Nothing to do here.
4562 break;
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()) {
4570 return;
4573 // The contents of our slots depends on what kind of type we are.
4574 switch (TypeCode(slot.toInt32())) {
4575 case TYPE_struct: {
4576 slot = obj->as<NativeObject>().getReservedSlot(SLOT_FIELDINFO);
4577 if (slot.isUndefined()) {
4578 return;
4581 FieldInfoHash* fields = static_cast<FieldInfoHash*>(slot.toPrivate());
4582 fields->trace(trc);
4583 break;
4585 case TYPE_function: {
4586 // Check if we have a FunctionInfo.
4587 slot = obj->as<NativeObject>().getReservedSlot(SLOT_FNINFO);
4588 if (slot.isUndefined()) {
4589 return;
4592 FunctionInfo* fninfo = static_cast<FunctionInfo*>(slot.toPrivate());
4593 MOZ_ASSERT(fninfo);
4595 // Identify our objects to the tracer.
4596 TraceEdge(trc, &fninfo->mABI, "abi");
4597 TraceEdge(trc, &fninfo->mReturnType, "returnType");
4598 fninfo->mArgTypes.trace(trc);
4600 break;
4602 default:
4603 // Nothing to do here.
4604 break;
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.
4625 if (t1 == t2) {
4626 return true;
4629 // First, perform shallow comparison.
4630 TypeCode c1 = GetTypeCode(t1);
4631 TypeCode c2 = GetTypeCode(t2);
4632 if (c1 != c2) {
4633 return false;
4636 // Determine whether the types require shallow or deep comparison.
4637 switch (c1) {
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) {
4650 return false;
4653 if (!TypesEqual(f1->mReturnType, f2->mReturnType)) {
4654 return false;
4657 if (f1->mArgTypes.length() != f2->mArgTypes.length()) {
4658 return false;
4661 if (f1->mIsVariadic != f2->mIsVariadic) {
4662 return false;
4665 for (size_t i = 0; i < f1->mArgTypes.length(); ++i) {
4666 if (!TypesEqual(f1->mArgTypes[i], f2->mArgTypes[i])) {
4667 return false;
4671 return true;
4673 case TYPE_array: {
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)) {
4680 return false;
4683 JSObject* b1 = ArrayType::GetBaseType(t1);
4684 JSObject* b2 = ArrayType::GetBaseType(t2);
4685 return TypesEqual(b1, b2);
4687 case TYPE_struct:
4688 // Require exact type object equality.
4689 return false;
4690 default:
4691 // Shallow comparison is sufficient.
4692 return true;
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();
4705 return true;
4707 if (size.isDouble()) {
4708 *result = Convert<size_t>(size.toDouble());
4709 return true;
4712 MOZ_ASSERT(size.isUndefined());
4713 return false;
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
4726 // directly.
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)) {
4762 case TYPE_array:
4763 result = ArrayType::BuildFFIType(cx, obj);
4764 break;
4766 case TYPE_struct:
4767 result = StructType::BuildFFIType(cx, obj);
4768 break;
4770 default:
4771 MOZ_CRASH("simple types must have an ffi_type");
4774 if (!result) {
4775 return nullptr;
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);
4792 if (!name) {
4793 return nullptr;
4795 JS_SetReservedSlot(obj, SLOT_NAME, StringValue(name));
4796 return 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();
4804 MOZ_ASSERT(proto);
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)) {
4820 return nullptr;
4822 MOZ_ASSERT(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()) {
4833 return false;
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());
4845 return true;
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);
4855 if (!name) {
4856 return false;
4859 args.rval().setString(name);
4860 return true;
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());
4867 return true;
4870 bool CType::PtrGetter(JSContext* cx, const JS::CallArgs& args) {
4871 RootedObject obj(cx, &args.thisv().toObject());
4872 JSObject* pointerType = PointerType::CreateInternal(cx, obj);
4873 if (!pointerType) {
4874 return false;
4877 args.rval().setObject(*pointerType);
4878 return true;
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"));
4884 if (!baseType) {
4885 return false;
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.
4897 size_t length = 0;
4898 if (args.length() == 1 && !jsvalToSize(cx, args[0], false, &length)) {
4899 return ArgumentTypeMismatch(cx, "", "CType.prototype.array",
4900 "a nonnegative integer");
4903 JSObject* result =
4904 ArrayType::CreateInternal(cx, baseType, length, args.length() == 1);
4905 if (!result) {
4906 return false;
4909 args.rval().setObject(*result);
4910 return true;
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"));
4916 if (!obj) {
4917 return false;
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.
4926 JSString* result;
4927 if (CType::IsCType(obj)) {
4928 AutoString type;
4929 AppendString(cx, type, "type ");
4930 AppendString(cx, type, GetName(cx, obj));
4931 if (!type) {
4932 return false;
4934 result = NewUCString(cx, type.finish());
4935 } else {
4936 result = JS_NewStringCopyZ(cx, "[CType proto object]");
4938 if (!result) {
4939 return false;
4942 args.rval().setString(result);
4943 return true;
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");
4949 if (!obj) {
4950 return false;
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.
4959 JSString* result;
4960 if (CType::IsCType(obj)) {
4961 AutoString source;
4962 BuildTypeSource(cx, obj, false, source);
4963 if (!source) {
4964 return false;
4966 result = NewUCString(cx, source.finish());
4967 } else {
4968 result = JS_NewStringCopyZ(cx, "[CType proto object]");
4970 if (!result) {
4971 return false;
4974 args.rval().setString(result);
4975 return true;
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)) {
4984 return nullptr;
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");
5007 if (!obj) {
5008 return false;
5010 if (!ABI::IsABI(obj)) {
5011 return IncompatibleThisProto(cx, "ABI.prototype.toSource",
5012 InformalValueTypeName(args.thisv()));
5015 JSString* result;
5016 switch (GetABICode(obj)) {
5017 case ABI_DEFAULT:
5018 result = JS_NewStringCopyZ(cx, "ctypes.default_abi");
5019 break;
5020 case ABI_STDCALL:
5021 result = JS_NewStringCopyZ(cx, "ctypes.stdcall_abi");
5022 break;
5023 case ABI_THISCALL:
5024 result = JS_NewStringCopyZ(cx, "ctypes.thiscall_abi");
5025 break;
5026 case ABI_WINAPI:
5027 result = JS_NewStringCopyZ(cx, "ctypes.winapi_abi");
5028 break;
5029 default:
5030 JS_ReportErrorASCII(cx, "not a valid ABICode");
5031 return false;
5033 if (!result) {
5034 return false;
5037 args.rval().setString(result);
5038 return true;
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);
5059 if (!result) {
5060 return false;
5063 args.rval().setObject(*result);
5064 return true;
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));
5080 if (!dataProto) {
5081 return nullptr;
5083 RootedObject typeProto(
5084 cx, CType::GetProtoFromType(cx, baseType, SLOT_POINTERPROTO));
5085 if (!typeProto) {
5086 return nullptr;
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));
5092 JSObject* typeObj =
5093 CType::Create(cx, typeProto, dataProto, TYPE_pointer, nullptr, sizeVal,
5094 alignVal, &ffi_type_pointer);
5095 if (!typeObj) {
5096 return nullptr;
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));
5105 return 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",
5116 "s");
5119 RootedObject result(cx, CData::Create(cx, obj, nullptr, nullptr, true));
5120 if (!result) {
5121 return false;
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) {
5139 return true;
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);
5160 // Case 3 - Closure
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()) {
5169 thisObj = nullptr;
5170 } else if (args[1].isObject()) {
5171 thisObj = &args[1].toObject();
5172 } else if (!JS_ValueToObject(cx, args[1], &thisObj)) {
5173 return false;
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) {
5182 errVal = args[2];
5185 RootedObject fnObj(cx, &args[0].toObject());
5186 return FunctionType::ConstructData(cx, baseObj, result, fnObj, thisObj,
5187 errVal);
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()) {
5200 return false;
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()) {
5208 return false;
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());
5219 return true;
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"));
5225 if (!obj) {
5226 return false;
5228 if (!CData::IsCDataMaybeUnwrap(&obj)) {
5229 return IncompatibleThisProto(cx, "PointerType.prototype.isNull",
5230 args.thisv());
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);
5242 return true;
5245 bool PointerType::OffsetBy(JSContext* cx, const CallArgs& args, int offset,
5246 const char* name) {
5247 RootedObject obj(cx, GetThisObject(cx, args, name));
5248 if (!obj) {
5249 return false;
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",
5258 args.thisv());
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);
5272 if (!result) {
5273 return false;
5276 args.rval().setObject(*result);
5277 return true;
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)) {
5304 return false;
5307 args.rval().set(result);
5308 return true;
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.
5344 size_t length = 0;
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);
5352 if (!result) {
5353 return false;
5356 args.rval().setObject(*result);
5357 return true;
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));
5366 if (!typeProto) {
5367 return nullptr;
5369 RootedObject dataProto(
5370 cx, CType::GetProtoFromType(cx, baseType, SLOT_ARRAYDATAPROTO));
5371 if (!dataProto) {
5372 return nullptr;
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.
5378 size_t baseSize;
5379 if (!CType::GetSafeSize(baseType, &baseSize)) {
5380 JS_ReportErrorASCII(cx, "base size must be defined");
5381 return nullptr;
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");
5391 return nullptr;
5393 if (!SizeTojsval(cx, size, &sizeVal)) {
5394 SizeOverflow(cx, "array size", "JavaScript number");
5395 return nullptr;
5397 if (!SizeTojsval(cx, length, &lengthVal)) {
5398 SizeOverflow(cx, "array length", "JavaScript number");
5399 return nullptr;
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);
5408 if (!typeObj) {
5409 return nullptr;
5412 // Set the element type.
5413 JS_SetReservedSlot(typeObj, SLOT_ELEMENT_T, ObjectValue(*baseType));
5415 // Set the length.
5416 JS_SetReservedSlot(typeObj, SLOT_LENGTH, lengthVal);
5418 return typeObj;
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",
5438 "at most one", "");
5441 } else {
5442 if (args.length() != 1) {
5443 return ArgumentLengthError(cx, "size undefined ArrayType constructor",
5444 "one", "");
5447 RootedObject baseType(cx, GetBaseType(obj));
5449 size_t length;
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) {
5473 return false;
5476 switch (CType::GetTypeCode(baseType)) {
5477 case TYPE_char:
5478 case TYPE_signed_char:
5479 case TYPE_unsigned_char: {
5480 // Reject if unpaired surrogate characters are present.
5481 if (!ReportErrorIfUnpairedSurrogatePresent(cx, sourceLinear)) {
5482 return false;
5485 // Determine the UTF-8 length.
5486 length = JS::GetDeflatedUTF8StringLength(sourceLinear);
5488 ++length;
5489 break;
5491 case TYPE_char16_t:
5492 length = sourceLength + 1;
5493 break;
5494 default:
5495 return ConvError(cx, obj, args[0], ConversionType::Construct);
5498 } else {
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);
5506 if (!obj) {
5507 return false;
5511 JSObject* result = CData::Create(cx, obj, nullptr, nullptr, true);
5512 if (!result) {
5513 return false;
5516 args.rval().setObject(*result);
5518 if (convertObject) {
5519 if (!ExplicitConvert(cx, args[0], obj, CData::GetData(result),
5520 ConversionType::Construct))
5521 return false;
5524 return true;
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();
5546 return true;
5548 if (length.isDouble()) {
5549 *result = Convert<size_t>(length.toDouble());
5550 return true;
5553 MOZ_ASSERT(length.isUndefined());
5554 return false;
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
5568 // directly.
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);
5582 if (!ffiBaseType) {
5583 return nullptr;
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>();
5596 if (!ffiType) {
5597 return nullptr;
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) {
5605 return nullptr;
5608 for (size_t i = 0; i < length; ++i) {
5609 ffiType->elements[i] = ffiBaseType;
5611 ffiType->elements[length] = nullptr;
5613 return ffiType;
5616 bool ArrayType::IsArrayType(HandleValue v) {
5617 if (!v.isObject()) {
5618 return false;
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()) {
5626 return false;
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());
5642 return true;
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());
5656 return true;
5659 bool ArrayType::Getter(JSContext* cx, HandleObject obj, HandleId idval,
5660 MutableHandleValue vp, bool* handled) {
5661 *handled = false;
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) {
5673 return true;
5676 // Convert the index to a size_t and bounds-check it.
5677 size_t index;
5678 size_t length = GetLength(typeObj);
5679 bool ok = jsidToSize(cx, idval, true, &index);
5680 int32_t dummy;
5681 if (!ok && idval.isSymbol()) {
5682 return true;
5684 bool dummy2;
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.
5689 return true;
5691 if (!ok) {
5692 return InvalidIndexError(cx, idval);
5694 if (index >= length) {
5695 return InvalidIndexRangeError(cx, index, length);
5698 *handled = true;
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) {
5708 *handled = false;
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.
5724 size_t index;
5725 size_t length = GetLength(typeObj);
5726 bool ok = jsidToSize(cx, idval, true, &index);
5727 int32_t dummy;
5728 if (!ok && idval.isSymbol()) {
5729 return true;
5731 bool dummy2;
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();
5738 if (!ok) {
5739 return InvalidIndexError(cx, idval);
5741 if (index >= length) {
5742 return InvalidIndexRangeError(cx, index, length);
5745 *handled = true;
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))
5752 return false;
5753 return result.succeed();
5756 bool ArrayType::AddressOfElement(JSContext* cx, unsigned argc, Value* vp) {
5757 CallArgs args = CallArgsFromVp(argc, vp);
5758 RootedObject obj(
5759 cx, GetThisObject(cx, args, "ArrayType.prototype.addressOfElement"));
5760 if (!obj) {
5761 return false;
5763 if (!CData::IsCDataMaybeUnwrap(&obj)) {
5764 return IncompatibleThisProto(cx, "ArrayType.prototype.addressOfElement",
5765 args.thisv());
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",
5776 "one", "");
5779 RootedObject baseType(cx, GetBaseType(typeObj));
5780 RootedObject pointerType(cx, PointerType::CreateInternal(cx, baseType));
5781 if (!pointerType) {
5782 return false;
5785 // Create a PointerType CData object containing null.
5786 RootedObject result(cx,
5787 CData::Create(cx, pointerType, nullptr, nullptr, true));
5788 if (!result) {
5789 return false;
5792 args.rval().setObject(*result);
5794 // Convert the index to a size_t and bounds-check it.
5795 size_t index;
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;
5808 return true;
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);
5821 return nullptr;
5824 RootedObject obj(cx, &val.toObject());
5825 Rooted<IdVector> props(cx, IdVector(cx));
5826 if (!JS_Enumerate(cx, obj, &props)) {
5827 return nullptr;
5830 // make sure we have one, and only one, property
5831 if (props.length() != 1) {
5832 FieldDescriptorCountError(cx, val, props.length());
5833 return nullptr;
5836 RootedId nameid(cx, props[0]);
5837 if (!nameid.isString()) {
5838 FieldDescriptorNameError(cx, nameid);
5839 return nullptr;
5842 RootedValue propVal(cx);
5843 if (!JS_GetPropertyById(cx, obj, nameid, &propVal)) {
5844 return nullptr;
5847 if (propVal.isPrimitive() || !CType::IsCType(&propVal.toObject())) {
5848 FieldDescriptorTypeError(cx, propVal, nameid);
5849 return nullptr;
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());
5856 size_t size;
5857 if (!CType::GetSafeSize(typeObj, &size) || size == 0) {
5858 FieldDescriptorSizeError(cx, typeObj, nameid);
5859 return nullptr;
5862 return nameid.toLinearString();
5865 // For a struct field with 'name' and 'type', add an element of the form
5866 // { name : type }.
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));
5872 if (!fieldObj) {
5873 return false;
5876 element.setObject(*fieldObj);
5878 AutoStableStringChars nameChars(cx);
5879 if (!nameChars.initTwoByte(cx, name)) {
5880 return false;
5883 if (!JS_DefineUCProperty(
5884 cx, fieldObj, nameChars.twoByteChars(), name->length(), typeObj,
5885 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
5886 return false;
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,
5914 nullptr));
5915 if (!result) {
5916 return false;
5919 if (args.length() == 2) {
5920 RootedObject arr(cx, args[1].isObject() ? &args[1].toObject() : nullptr);
5921 bool isArray;
5922 if (!arr) {
5923 isArray = false;
5924 } else {
5925 if (!JS::IsArrayObject(cx, arr, &isArray)) {
5926 return false;
5929 if (!isArray) {
5930 return ArgumentTypeMismatch(cx, "second ", "StructType", "an array");
5933 // Define the struct fields.
5934 if (!DefineInternal(cx, result, arr)) {
5935 return false;
5939 args.rval().setObject(*result);
5940 return true;
5943 bool StructType::DefineInternal(JSContext* cx, JSObject* typeObj_,
5944 JSObject* fieldsObj_) {
5945 RootedObject typeObj(cx, typeObj_);
5946 RootedObject fieldsObj(cx, fieldsObj_);
5948 uint32_t len;
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));
5955 if (!dataProto) {
5956 return false;
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));
5964 if (!prototype) {
5965 return false;
5968 if (!JS_DefineProperty(cx, prototype, "constructor", typeObj,
5969 JSPROP_READONLY | JSPROP_PERMANENT))
5970 return false;
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;
5977 if (len != 0) {
5978 structSize = 0;
5979 structAlign = 0;
5981 for (uint32_t i = 0; i < len; ++i) {
5982 RootedValue item(cx);
5983 if (!JS_GetElement(cx, fieldsObj, i, &item)) {
5984 return false;
5987 RootedObject fieldType(cx, nullptr);
5988 Rooted<JSLinearString*> name(cx,
5989 ExtractStructField(cx, item, &fieldType));
5990 if (!name) {
5991 return false;
5994 // Make sure each field name is unique
5995 FieldInfoHash::AddPtr entryPtr = fields.lookupForAdd(name);
5996 if (entryPtr) {
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)) {
6003 return false;
6006 RootedFunction getter(
6008 NewFunctionWithReserved(cx, StructType::FieldGetter, 0, 0, nullptr));
6009 if (!getter) {
6010 return false;
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));
6019 if (!setter) {
6020 return false;
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)) {
6029 return false;
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");
6040 return false;
6043 // Add field name to the hash
6044 FieldInfo info;
6045 info.mType = fieldType;
6046 info.mIndex = i;
6047 info.mOffset = fieldOffset;
6048 if (!fields.add(entryPtr, name, info)) {
6049 JS_ReportOutOfMemory(cx);
6050 return false;
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");
6064 return false;
6066 structSize = structTail;
6068 } else {
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.
6073 structSize = 1;
6074 structAlign = 1;
6077 RootedValue sizeVal(cx);
6078 if (!SizeTojsval(cx, structSize, &sizeVal)) {
6079 SizeOverflow(cx, "struct size", "double");
6080 return false;
6083 // Move the field hash to the heap and store it in the typeObj.
6084 FieldInfoHash* heapHash = cx->new_<FieldInfoHash>(std::move(fields.get()));
6085 if (!heapHash) {
6086 JS_ReportOutOfMemory(cx);
6087 return false;
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!
6094 // return false;
6095 JS_SetReservedSlot(typeObj, SLOT_PROTO, ObjectValue(*prototype));
6096 return true;
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>();
6111 if (!ffiType) {
6112 return nullptr;
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);
6118 if (!elements) {
6119 return nullptr;
6122 if (len != 0) {
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);
6128 if (!fieldType) {
6129 return nullptr;
6131 elements[entry.value().mIndex] = fieldType;
6133 } else {
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);
6145 #ifdef DEBUG
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.
6149 ffi_cif cif;
6150 ffiType->size = 0;
6151 ffiType->alignment = 0;
6152 ffi_status status =
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);
6157 #else
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;
6164 #endif
6166 return ffiType;
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"));
6172 if (!obj) {
6173 return false;
6175 if (!CType::IsCType(obj)) {
6176 return IncompatibleThisProto(cx, "StructType.prototype.define",
6177 args.thisv());
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");
6186 return false;
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",
6196 "an array");
6199 bool isArray;
6200 if (!arg.isObject()) {
6201 isArray = false;
6202 } else {
6203 if (!JS::IsArrayObject(cx, arg, &isArray)) {
6204 return false;
6208 if (!isArray) {
6209 return ArgumentTypeMismatch(cx, "", "StructType.prototype.define",
6210 "an array");
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");
6225 return false;
6228 JSObject* result = CData::Create(cx, obj, nullptr, nullptr, true);
6229 if (!result) {
6230 return false;
6233 args.rval().setObject(*result);
6235 if (args.length() == 0) {
6236 return true;
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)) {
6253 return true;
6256 if (fields->count() != 1) {
6257 return false;
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)) {
6263 return false;
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))
6282 return false;
6285 return true;
6288 size_t count = fields->count();
6289 if (count >= 2) {
6290 char fieldLengthStr[32];
6291 SprintfLiteral(fieldLengthStr, "0, 1, or %zu", count);
6292 return ArgumentLengthError(cx, "StructType constructor", fieldLengthStr,
6293 "s");
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);
6314 if (ptr) {
6315 return &ptr->value();
6318 FieldMissingError(cx, obj, name);
6319 return nullptr;
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)) {
6333 return nullptr;
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))
6341 return nullptr;
6344 RootedObject fieldsProp(cx, JS::NewArrayObject(cx, fieldsVec));
6345 if (!fieldsProp) {
6346 return nullptr;
6349 // Seal the fields array.
6350 if (!JS_FreezeObject(cx, fieldsProp)) {
6351 return nullptr;
6354 return fieldsProp;
6357 /* static */
6358 bool StructType::IsStruct(HandleValue v) {
6359 if (!v.isObject()) {
6360 return false;
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());
6373 return true;
6376 if (args.rval().isUndefined()) {
6377 // Build the 'fields' array lazily.
6378 JSObject* fields = BuildFieldsArray(cx, obj);
6379 if (!fields) {
6380 return false;
6382 JS_SetReservedSlot(obj, SLOT_FIELDS, ObjectValue(*fields));
6384 args.rval().setObject(*fields);
6387 MOZ_ASSERT(args.rval().isObject());
6388 return true;
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",
6396 args.thisv());
6399 RootedObject obj(cx, &args.thisv().toObject());
6400 if (!CData::IsCDataMaybeUnwrap(&obj)) {
6401 return IncompatibleThisProto(cx, "StructType property getter",
6402 args.thisv());
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()));
6415 if (!name) {
6416 return false;
6419 const FieldInfo* field = LookupField(cx, typeObj, name);
6420 if (!field) {
6421 return false;
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",
6434 args.thisv());
6437 RootedObject obj(cx, &args.thisv().toObject());
6438 if (!CData::IsCDataMaybeUnwrap(&obj)) {
6439 return IncompatibleThisProto(cx, "StructType property setter",
6440 args.thisv());
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()));
6453 if (!name) {
6454 return false;
6457 const FieldInfo* field = LookupField(cx, typeObj, name);
6458 if (!field) {
6459 return false;
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,
6467 field->mIndex);
6470 bool StructType::AddressOfField(JSContext* cx, unsigned argc, Value* vp) {
6471 CallArgs args = CallArgsFromVp(argc, vp);
6472 RootedObject obj(
6473 cx, GetThisObject(cx, args, "StructType.prototype.addressOfField"));
6474 if (!obj) {
6475 return false;
6478 if (!CData::IsCDataMaybeUnwrap(&obj)) {
6479 return IncompatibleThisProto(cx, "StructType.prototype.addressOfField",
6480 args.thisv());
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",
6491 "");
6494 if (!args[0].isString()) {
6495 return ArgumentTypeMismatch(cx, "", "StructType.prototype.addressOfField",
6496 "a string");
6499 JSLinearString* str = JS_EnsureLinearString(cx, args[0].toString());
6500 if (!str) {
6501 return false;
6504 const FieldInfo* field = LookupField(cx, typeObj, str);
6505 if (!field) {
6506 return false;
6509 RootedObject baseType(cx, field->mType);
6510 RootedObject pointerType(cx, PointerType::CreateInternal(cx, baseType));
6511 if (!pointerType) {
6512 return false;
6515 // Create a PointerType CData object containing null.
6516 JSObject* result = CData::Create(cx, pointerType, nullptr, nullptr, true);
6517 if (!result) {
6518 return false;
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;
6526 return true;
6529 /*******************************************************************************
6530 ** FunctionType implementation
6531 *******************************************************************************/
6533 // Helper class for handling allocation of function arguments.
6534 struct AutoValue {
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;
6546 void* mData;
6549 static bool GetABI(JSContext* cx, HandleValue abiType, ffi_abi* result) {
6550 if (abiType.isPrimitive()) {
6551 return false;
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.
6559 switch (abi) {
6560 case ABI_DEFAULT:
6561 *result = FFI_DEFAULT_ABI;
6562 return true;
6563 case ABI_THISCALL:
6564 #if defined(_WIN64)
6565 # if defined(_M_X64)
6566 *result = FFI_WIN64;
6567 # elif defined(_M_ARM64)
6568 *result = FFI_SYSV;
6569 # else
6570 # error unknown 64-bit Windows platform
6571 # endif
6572 return true;
6573 #elif defined(_WIN32)
6574 *result = FFI_THISCALL;
6575 return true;
6576 #else
6577 break;
6578 #endif
6579 case ABI_STDCALL:
6580 case ABI_WINAPI:
6581 #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
6582 *result = FFI_STDCALL;
6583 return true;
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)
6590 *result = FFI_SYSV;
6591 # else
6592 # error unknown 64-bit Windows platform
6593 # endif
6594 return true;
6595 #endif
6596 case INVALID_ABI:
6597 break;
6599 return false;
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");
6605 return nullptr;
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);
6616 if (!result) {
6617 return nullptr;
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");
6623 return nullptr;
6626 if (!CType::IsSizeDefined(result)) {
6627 FunctionArgumentTypeError(cx, index, type, "must have defined size");
6628 return nullptr;
6631 // libffi cannot pass types of zero size by value.
6632 MOZ_ASSERT(CType::GetSize(result) != 0);
6634 return result;
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");
6640 return nullptr;
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");
6649 return nullptr;
6652 if (typeCode != TYPE_void_t && !CType::IsSizeDefined(result)) {
6653 FunctionReturnTypeError(cx, type, "must have defined size");
6654 return nullptr;
6657 // libffi cannot pass types of zero size by value.
6658 MOZ_ASSERT(typeCode == TYPE_void_t || CType::GetSize(result) != 0);
6660 return result;
6663 static MOZ_ALWAYS_INLINE bool IsEllipsis(JSContext* cx, HandleValue v,
6664 bool* isEllipsis) {
6665 *isEllipsis = false;
6666 if (!v.isString()) {
6667 return true;
6669 JSString* str = v.toString();
6670 if (str->length() != 3) {
6671 return true;
6673 JSLinearString* linear = str->ensureLinear(cx);
6674 if (!linear) {
6675 return false;
6677 char16_t dot = '.';
6678 *isEllipsis = (linear->latin1OrTwoByteChar(0) == dot &&
6679 linear->latin1OrTwoByteChar(1) == dot &&
6680 linear->latin1OrTwoByteChar(2) == dot);
6681 return true;
6684 static bool PrepareCIF(JSContext* cx, FunctionInfo* fninfo) {
6685 ffi_abi abi;
6686 RootedValue abiType(cx, ObjectOrNullValue(fninfo->mABI));
6687 if (!GetABI(cx, abiType, &abi)) {
6688 JS_ReportErrorASCII(cx, "Invalid ABI specification");
6689 return false;
6692 ffi_type* rtype = CType::GetFFIType(cx, fninfo->mReturnType);
6693 if (!rtype) {
6694 return false;
6697 ffi_status status;
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());
6702 } else {
6703 status = ffi_prep_cif(&fninfo->mCIF, abi, fninfo->mFFITypes.length(), rtype,
6704 fninfo->mFFITypes.begin());
6707 switch (status) {
6708 case FFI_OK:
6709 return true;
6710 case FFI_BAD_ABI:
6711 JS_ReportErrorASCII(cx, "Invalid ABI specification");
6712 return false;
6713 case FFI_BAD_TYPEDEF:
6714 JS_ReportErrorASCII(cx, "Invalid type specification");
6715 return false;
6716 default:
6717 JS_ReportErrorASCII(cx, "Unknown libffi error");
6718 return false;
6722 void FunctionType::BuildSymbolName(JSContext* cx, JSString* name,
6723 JSObject* typeObj, AutoCString& result) {
6724 FunctionInfo* fninfo = GetFunctionInfo(typeObj);
6726 switch (GetABICode(fninfo->mABI)) {
6727 case ABI_DEFAULT:
6728 case ABI_THISCALL:
6729 case ABI_WINAPI:
6730 // For cdecl or WINAPI functions, no mangling is necessary.
6731 AppendString(cx, result, name);
6732 break;
6734 case ABI_STDCALL: {
6735 #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
6736 // On WIN32, stdcall functions look like:
6737 // _foo@40
6738 // where 'foo' is the function name, and '40' is the aligned size of the
6739 // arguments.
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).
6745 size_t size = 0;
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);
6756 #endif
6757 break;
6760 case INVALID_ABI:
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()));
6769 if (!fninfo) {
6770 return false;
6773 // Stash the FunctionInfo in a reserved slot.
6774 JS_InitReservedSlot(typeObj, SLOT_FNINFO, fninfo,
6775 JS::MemoryUse::CTypeFunctionInfo);
6777 ffi_abi abi;
6778 if (!GetABI(cx, abiType, &abi)) {
6779 JS_ReportErrorASCII(cx, "Invalid ABI specification");
6780 return false;
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);
6790 return false;
6793 fninfo->mIsVariadic = false;
6795 for (uint32_t i = 0; i < args.length(); ++i) {
6796 bool isEllipsis;
6797 if (!IsEllipsis(cx, args[i], &isEllipsis)) {
6798 return false;
6800 if (isEllipsis) {
6801 fninfo->mIsVariadic = true;
6802 if (i < 1) {
6803 JS_ReportErrorASCII(cx,
6804 "\"...\" may not be the first and only parameter "
6805 "type of a variadic function declaration");
6806 return false;
6808 if (i < args.length() - 1) {
6809 JS_ReportErrorASCII(cx,
6810 "\"...\" must be the last parameter type of a "
6811 "variadic function declaration");
6812 return false;
6814 if (GetABICode(fninfo->mABI) != ABI_DEFAULT) {
6815 JS_ReportErrorASCII(cx,
6816 "Variadic functions must use the __cdecl calling "
6817 "convention");
6818 return false;
6820 break;
6823 JSObject* argType = PrepareType(cx, i, args[i]);
6824 if (!argType) {
6825 return false;
6828 ffi_type* ffiType = CType::GetFFIType(cx, argType);
6829 if (!ffiType) {
6830 return false;
6833 fninfo->mArgTypes.infallibleAppend(argType);
6834 fninfo->mFFITypes.infallibleAppend(ffiType);
6837 if (fninfo->mIsVariadic) {
6838 // wait to PrepareCIF until function is called
6839 return true;
6842 if (!PrepareCIF(cx, fninfo)) {
6843 return false;
6846 return true;
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.
6861 bool isArray;
6862 if (!args[2].isObject()) {
6863 isArray = false;
6864 } else {
6865 if (!JS::IsArrayObject(cx, args[2], &isArray)) {
6866 return false;
6870 if (!isArray) {
6871 return ArgumentTypeMismatch(cx, "third ", "FunctionType", "an array");
6874 arrayObj = &args[2].toObject();
6876 uint32_t len;
6877 MOZ_ALWAYS_TRUE(JS::GetArrayLength(cx, arrayObj, &len));
6879 if (!argTypes.resize(len)) {
6880 JS_ReportOutOfMemory(cx);
6881 return false;
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])) {
6889 return false;
6893 JSObject* result = CreateInternal(cx, args[0], args[1], argTypes);
6894 if (!result) {
6895 return false;
6898 args.rval().setObject(*result);
6899 return true;
6902 JSObject* FunctionType::CreateInternal(JSContext* cx, HandleValue abi,
6903 HandleValue rtype,
6904 const HandleValueArray& args) {
6905 // Prepare the result type
6906 RootedObject returnType(cx, PrepareReturnType(cx, rtype));
6907 if (!returnType) {
6908 return nullptr;
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));
6915 if (!typeProto) {
6916 return nullptr;
6918 RootedObject dataProto(
6919 cx, CType::GetProtoFromType(cx, returnType, SLOT_FUNCTIONDATAPROTO));
6920 if (!dataProto) {
6921 return nullptr;
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,
6928 nullptr));
6929 if (!typeObj) {
6930 return nullptr;
6933 // Determine and check the types, and prepare the function CIF.
6934 if (!CreateFunctionInfo(cx, typeObj, abi, returnType, args)) {
6935 return nullptr;
6938 return typeObj;
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");
6954 return false;
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");
6960 return false;
6963 RootedObject closureObj(
6964 cx, CClosure::Create(cx, typeObj, fnObj, thisObj, errVal, data));
6965 if (!closureObj) {
6966 return false;
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);
6988 return false;
6991 bool freePointer = false;
6992 if (!ImplicitConvert(cx, arg, type, value->mData, ConversionType::Argument,
6993 &freePointer, funObj, argIndex))
6994 return false;
6996 if (freePointer) {
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);
7001 return false;
7003 strings->back().mData = *static_cast<char**>(value->mData);
7006 return true;
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",
7015 args.calleev());
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());
7043 if (!library) {
7044 JS_ReportErrorASCII(cx, "library is not open");
7045 return false;
7049 // prepare the values for each argument
7050 AutoValueAutoArray values;
7051 AutoValueAutoArray strings;
7052 if (!values.resize(args.length())) {
7053 JS_ReportOutOfMemory(cx);
7054 return false;
7057 for (unsigned i = 0; i < argcFixed; ++i) {
7058 if (!ConvertArgument(cx, obj, i, args[i], fninfo->mArgTypes[i], &values[i],
7059 &strings)) {
7060 return false;
7064 if (fninfo->mIsVariadic) {
7065 if (!fninfo->mFFITypes.resize(args.length())) {
7066 JS_ReportOutOfMemory(cx);
7067 return false;
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);
7082 if (!type) {
7083 // These functions report their own errors.
7084 return false;
7086 RootedValue typeVal(cx, ObjectValue(*type));
7087 type = PrepareType(cx, i, typeVal);
7088 if (!type) {
7089 return false;
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)) {
7095 return false;
7097 fninfo->mFFITypes[i] = CType::GetFFIType(cx, type);
7098 if (!fninfo->mFFITypes[i]) {
7099 return false;
7102 if (!PrepareCIF(cx, fninfo)) {
7103 return false;
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);
7113 return false;
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));
7122 #if defined(XP_WIN)
7123 int32_t lastErrorStatus; // The status as defined by |GetLastError|
7124 int32_t savedLastError = GetLastError();
7125 SetLastError(0);
7126 #endif // defined(XP_WIN)
7127 int errnoStatus; // The status as defined by |errno|
7128 int savedErrno = errno;
7129 errno = 0;
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;
7140 #if defined(XP_WIN)
7141 lastErrorStatus = GetLastError();
7142 SetLastError(savedLastError);
7143 #endif // defined(XP_WIN)
7145 errno = savedErrno;
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);
7152 if (!objCTypes) {
7153 return false;
7156 JS_SetReservedSlot(objCTypes, SLOT_ERRNO, Int32Value(errnoStatus));
7157 #if defined(XP_WIN)
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.
7163 switch (typeCode) {
7164 #define INTEGRAL_CASE(name, type, ffiType) \
7165 case TYPE_##name: \
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); \
7170 break;
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
7177 default:
7178 break;
7181 // prepare a JS object from the result
7182 RootedObject returnType(cx, fninfo->mReturnType);
7183 return ConvertToJS(cx, returnType, nullptr, returnValue.mData, false, true,
7184 args.rval());
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()) {
7199 return false;
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()) {
7210 return true;
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)) {
7221 return false;
7224 for (size_t i = 0; i < len; ++i) {
7225 vec[i].setObject(*fninfo->mArgTypes[i]);
7228 argTypes = JS::NewArrayObject(cx, vec);
7229 if (!argTypes) {
7230 return false;
7234 // Seal and cache it.
7235 if (!JS_FreezeObject(cx, argTypes)) {
7236 return false;
7238 JS_SetReservedSlot(obj, SLOT_ARGS_T, JS::ObjectValue(*argTypes));
7240 args.rval().setObject(*argTypes);
7241 return true;
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);
7248 return true;
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);
7254 return true;
7257 bool FunctionType::IsVariadicGetter(JSContext* cx, const JS::CallArgs& args) {
7258 args.rval().setBoolean(
7259 GetFunctionInfo(&args.thisv().toObject())->mIsVariadic);
7260 return true;
7263 /*******************************************************************************
7264 ** CClosure implementation
7265 *******************************************************************************/
7267 JSObject* CClosure::Create(JSContext* cx, HandleObject typeObj,
7268 HandleObject fnObj, HandleObject thisObj,
7269 HandleValue errVal, PRFuncPtr* fnptr) {
7270 MOZ_ASSERT(fnObj);
7272 RootedObject result(cx, JS_NewObject(cx, &sCClosureClass));
7273 if (!result) {
7274 return nullptr;
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)) {
7286 return nullptr;
7288 MOZ_ASSERT(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");
7300 return nullptr;
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);
7310 if (!errResult) {
7311 return nullptr;
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))
7317 return nullptr;
7320 ClosureInfo* cinfo = cx->new_<ClosureInfo>(cx);
7321 if (!cinfo) {
7322 JS_ReportOutOfMemory(cx);
7323 return nullptr;
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.
7338 void* code;
7339 cinfo->closure =
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");
7343 return nullptr;
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");
7350 return nullptr;
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));
7356 return result;
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()) {
7363 return;
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()) {
7378 return;
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,
7386 void* userData) {
7387 MOZ_ASSERT(cif);
7388 MOZ_ASSERT(result);
7389 MOZ_ASSERT(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));
7399 MOZ_ASSERT(global);
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
7425 // whole word.
7426 size_t rvSize = 0;
7427 if (cif->rtype != &ffi_type_void) {
7428 rvSize = cif->rtype->size;
7429 switch (typeCode) {
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));
7438 break;
7439 default:
7440 break;
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);
7449 return false;
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])) {
7457 return false;
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);
7476 if (!success) {
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
7482 // the exception.
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
7490 // be smaller.
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.
7497 } else {
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.
7501 return false;
7504 // Small integer types must be returned as a word-sized ffi_arg. Coerce it
7505 // back into the size libffi expects.
7506 switch (typeCode) {
7507 #define INTEGRAL_CASE(name, type, ffiType) \
7508 case TYPE_##name: \
7509 if (sizeof(type) < sizeof(ffi_arg)) { \
7510 ffi_arg data = *static_cast<type*>(result); \
7511 *static_cast<ffi_arg*>(result) = data; \
7513 break;
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
7520 default:
7521 break;
7524 return true;
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
7544 // false.
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));
7568 if (!dataObj) {
7569 return nullptr;
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.
7576 if (refObj) {
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*>());
7586 if (!buffer) {
7587 return nullptr;
7590 char* data;
7591 if (!ownResult) {
7592 data = static_cast<char*>(source);
7593 } else {
7594 // Initialize our own buffer.
7595 size_t size = CType::GetSize(typeObj);
7596 data = cx->pod_malloc<char>(size);
7597 if (!data) {
7598 return nullptr;
7601 if (!source) {
7602 memset(data, 0, size);
7603 } else {
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
7615 // gets/sets.
7617 if (CType::GetTypeCode(typeObj) != TYPE_array) {
7618 return dataObj;
7621 RootedValue priv(cx, ObjectValue(*dataObj));
7622 ProxyOptions options;
7623 options.setLazyProto(true);
7624 return NewProxyObject(cx, &CDataArrayProxyHandler::singleton, priv, nullptr,
7625 options);
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()) {
7632 return;
7635 bool owns = slot.toBoolean();
7637 slot = JS::GetReservedSlot(obj, SLOT_DATA);
7638 if (slot.isUndefined()) {
7639 return;
7641 char** buffer = static_cast<char**>(slot.toPrivate());
7643 if (owns) {
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));
7658 return 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());
7668 MOZ_ASSERT(buffer);
7669 MOZ_ASSERT(*buffer);
7670 return *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,
7699 args.rval());
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"));
7716 if (!obj) {
7717 return false;
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));
7725 if (!pointerType) {
7726 return false;
7729 // Create a PointerType CData object containing null.
7730 JSObject* result = CData::Create(cx, pointerType, nullptr, nullptr, true);
7731 if (!result) {
7732 return false;
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);
7740 return true;
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());
7764 size_t targetSize;
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
7774 // of 'sourceData'.
7775 void* data = CData::GetData(sourceData);
7776 JSObject* result = CData::Create(cx, targetType, sourceData, data, false);
7777 if (!result) {
7778 return false;
7781 args.rval().setObject(*result);
7782 return true;
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());
7796 size_t targetSize;
7797 if (!CType::GetSafeSize(targetType, &targetSize) ||
7798 targetSize != sizeof(void*)) {
7799 JS_ReportErrorASCII(cx, "target CType has non-pointer size");
7800 return false;
7803 void* data = static_cast<void*>(cx->runtime());
7804 JSObject* result = CData::Create(cx, targetType, nullptr, &data, true);
7805 if (!result) {
7806 return false;
7809 args.rval().setObject(*result);
7810 return true;
7813 // Unwrap the `this` object to a CData object, or extract a CData object from a
7814 // CDataFinalizer.
7815 static bool GetThisDataObject(JSContext* cx, const CallArgs& args,
7816 const char* funName, MutableHandleObject obj) {
7817 obj.set(GetThisObject(cx, args, funName));
7818 if (!obj) {
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);
7827 if (!p) {
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());
7846 return true;
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)) {
7862 return false;
7865 // Make sure we are a pointer to, or an array of, an 8-bit or 16-bit
7866 // character or integer type.
7867 JSObject* baseType;
7868 JSObject* typeObj = CData::GetCType(obj);
7869 TypeCode typeCode = CType::GetTypeCode(typeObj);
7870 void* data;
7871 size_t maxLength = -1;
7872 switch (typeCode) {
7873 case TYPE_pointer:
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);
7879 break;
7880 case TYPE_array:
7881 baseType = ArrayType::GetBaseType(typeObj);
7882 data = CData::GetData(obj);
7883 maxLength = ArrayType::GetLength(typeObj);
7884 break;
7885 default:
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).
7891 JSString* result;
7892 switch (CType::GetTypeCode(baseType)) {
7893 case TYPE_int8_t:
7894 case TYPE_uint8_t:
7895 case TYPE_char:
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)
7904 .get());
7905 if (!dst) {
7906 return false;
7909 result = JS_NewUCString(cx, std::move(dst), length);
7910 if (!result) {
7911 return false;
7914 break;
7916 case TYPE_int16_t:
7917 case TYPE_uint16_t:
7918 case TYPE_short:
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);
7924 break;
7926 default:
7927 return NonStringBaseError(cx, args.thisv());
7930 if (!result) {
7931 return false;
7934 args.rval().setString(result);
7935 return true;
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,
7944 Value* vp) {
7945 return ReadStringCommon(cx, JS::UTF8CharsToNewTwoByteCharsZ, argc, vp,
7946 "CDataFinalizer.prototype.readString",
7947 js::StringBufferArena);
7950 bool CData::ReadStringReplaceMalformed(JSContext* cx, unsigned argc,
7951 Value* vp) {
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)) {
7963 case 4:
7964 return JS_NewFloat32Array;
7965 case 8:
7966 return JS_NewFloat64Array;
7967 default:
7968 return nullptr;
7972 constexpr bool isSigned = std::is_signed_v<Type>;
7973 switch (sizeof(Type)) {
7974 case 1:
7975 return isSigned ? JS_NewInt8Array : JS_NewUint8Array;
7976 case 2:
7977 return isSigned ? JS_NewInt16Array : JS_NewUint16Array;
7978 case 4:
7979 return isSigned ? JS_NewInt32Array : JS_NewUint32Array;
7980 default:
7981 return nullptr;
7985 static TypedArrayConstructor GetTypedArrayConstructor(TypeCode baseType) {
7986 switch (baseType) {
7987 #define MACRO(name, ctype, _) \
7988 case TYPE_##name: \
7989 return GetTypedArrayConstructorImpl<ctype>();
7990 CTYPES_FOR_EACH_TYPE(MACRO)
7991 #undef MACRO
7992 default:
7993 return nullptr;
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)) {
8006 return false;
8009 // Make sure we are a pointer to, or an array of, a type that is compatible
8010 // with a typed array base type.
8011 JSObject* baseType;
8012 JSObject* typeObj = CData::GetCType(obj);
8013 TypeCode typeCode = CType::GetTypeCode(typeObj);
8014 void* data;
8015 mozilla::Maybe<size_t> length;
8016 switch (typeCode) {
8017 case TYPE_pointer:
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);
8023 break;
8024 case TYPE_array:
8025 baseType = ArrayType::GetBaseType(typeObj);
8026 data = CData::GetData(obj);
8027 length.emplace(ArrayType::GetLength(typeObj));
8028 break;
8029 default:
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) {
8037 case TYPE_char:
8038 case TYPE_signed_char:
8039 case TYPE_unsigned_char:
8040 if (!length) {
8041 length.emplace(js_strnlen(static_cast<char*>(data), INT32_MAX));
8043 break;
8045 case TYPE_char16_t:
8046 if (!length) {
8047 length.emplace(js_strlen(static_cast<char16_t*>(data)));
8049 break;
8051 default:
8052 break;
8055 if (!length) {
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);
8071 if (!result) {
8072 return false;
8075 AutoCheckCannotGC nogc(cx);
8076 bool isShared;
8077 void* buffer = JS_GetArrayBufferViewData(&result->as<ArrayBufferViewObject>(),
8078 &isShared, nogc);
8079 MOZ_ASSERT(!isShared);
8080 memcpy(buffer, data, size.value());
8082 args.rval().setObject(*result);
8083 return true;
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,
8091 Value* vp) {
8092 return ReadTypedArrayCommon(cx, argc, vp,
8093 "CDataFinalizer.prototype.readTypedArray");
8096 JSString* CData::GetSourceString(JSContext* cx, HandleObject typeObj,
8097 void* data) {
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.)
8104 AutoString source;
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, ")");
8111 if (!source) {
8112 return nullptr;
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"));
8125 if (!obj) {
8126 return false;
8128 if (!CData::IsCDataMaybeUnwrap(&obj) && !CData::IsCDataProto(obj)) {
8129 return IncompatibleThisProto(cx, "CData.prototype.toSource",
8130 InformalValueTypeName(args.thisv()));
8133 JSString* result;
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);
8139 } else {
8140 result = JS_NewStringCopyZ(cx, "[CData proto object]");
8143 if (!result) {
8144 return false;
8147 args.rval().setString(result);
8148 return true;
8151 bool CData::ErrnoGetter(JSContext* cx, const JS::CallArgs& args) {
8152 args.rval().set(JS::GetReservedSlot(&args.thisv().toObject(), SLOT_ERRNO));
8153 return true;
8156 #if defined(XP_WIN)
8157 bool CData::LastErrorGetter(JSContext* cx, const JS::CallArgs& args) {
8158 args.rval().set(
8159 JS::GetReservedSlot(&args.thisv().toObject(), SLOT_LASTERROR));
8160 return true;
8162 #endif // defined(XP_WIN)
8164 bool CDataFinalizer::Methods::ToSource(JSContext* cx, unsigned argc,
8165 Value* vp) {
8166 CallArgs args = CallArgsFromVp(argc, vp);
8167 RootedObject objThis(
8168 cx, GetThisObject(cx, args, "CDataFinalizer.prototype.toSource"));
8169 if (!objThis) {
8170 return false;
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;
8180 if (!p) {
8181 strMessage = JS_NewStringCopyZ(cx, "ctypes.CDataFinalizer()");
8182 } else {
8183 RootedObject objType(cx, CDataFinalizer::GetCType(cx, objThis));
8184 if (!objType) {
8185 JS_ReportErrorASCII(cx, "CDataFinalizer has no type");
8186 return false;
8189 AutoString source;
8190 AppendString(cx, source, "ctypes.CDataFinalizer(");
8191 JSString* srcValue = CData::GetSourceString(cx, objType, p->cargs);
8192 if (!srcValue) {
8193 return false;
8195 AppendString(cx, source, srcValue);
8196 AppendString(cx, source, ", ");
8197 Value valCodePtrType =
8198 JS::GetReservedSlot(objThis, SLOT_DATAFINALIZER_CODETYPE);
8199 if (valCodePtrType.isPrimitive()) {
8200 return false;
8203 RootedObject typeObj(cx, valCodePtrType.toObjectOrNull());
8204 JSString* srcDispose = CData::GetSourceString(cx, typeObj, &(p->code));
8205 if (!srcDispose) {
8206 return false;
8209 AppendString(cx, source, srcDispose);
8210 AppendString(cx, source, ")");
8211 if (!source) {
8212 return false;
8214 strMessage = NewUCString(cx, source.finish());
8217 if (!strMessage) {
8218 // This is a memory issue, no error message
8219 return false;
8222 args.rval().setString(strMessage);
8223 return true;
8226 bool CDataFinalizer::Methods::ToString(JSContext* cx, unsigned argc,
8227 Value* vp) {
8228 CallArgs args = CallArgsFromVp(argc, vp);
8229 JSObject* objThis =
8230 GetThisObject(cx, args, "CDataFinalizer.prototype.toString");
8231 if (!objThis) {
8232 return false;
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]");
8245 if (!strMessage) {
8246 return false;
8248 } else if (!CDataFinalizer::GetValue(cx, objThis, &value)) {
8249 MOZ_CRASH("Could not convert an empty CDataFinalizer");
8250 } else {
8251 strMessage = ToString(cx, value);
8252 if (!strMessage) {
8253 return false;
8256 args.rval().setString(strMessage);
8257 return true;
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()) {
8269 return nullptr;
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);
8281 if (!p) {
8282 // We have called |dispose| or |forget| already.
8283 JS_ReportErrorASCII(
8284 cx, "Attempting to get the value of an empty CDataFinalizer");
8285 return false;
8288 RootedObject ctype(cx, GetCType(cx, obj));
8289 return ConvertToJS(cx, ctype, /*parent*/ nullptr, p->cargs, false, true,
8290 aResult);
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>
8298 * value, finalizer
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");
8312 return false;
8315 // Get arguments
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);
8321 return true;
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",
8331 valCodePtr);
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",
8341 valCodePtr);
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",
8350 valCodePtr);
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",
8359 valCodePtr);
8361 uintptr_t code = *reinterpret_cast<uintptr_t*>(CData::GetData(objCodePtr));
8362 if (!code) {
8363 return TypeError(cx, "a CData object of a _non-NULL_ function pointer type",
8364 valCodePtr);
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",
8374 valCodeType);
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|
8386 size_t sizeArg;
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",
8391 valCodeType);
8394 UniquePtr<void, JS::FreePolicy> cargs(malloc(sizeArg));
8396 if (!ImplicitConvert(cx, valData, objArgType, cargs.get(),
8397 ConversionType::Finalizer, &freePointer, objCodePtrType,
8398 0)) {
8399 return false;
8401 if (freePointer) {
8402 // Note: We could handle that case, if necessary.
8403 JS_ReportErrorASCII(
8405 "Internal Error during CDataFinalizer. Object cannot be represented");
8406 return false;
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);
8420 if (!objResult) {
8421 return false;
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);
8432 size_t sizeBestArg;
8433 if (!CType::GetSafeSize(objBestArgType, &sizeBestArg)) {
8434 MOZ_CRASH("object with unknown size");
8436 if (sizeBestArg != sizeArg) {
8437 return FinalizerSizeError(cx, objCodePtrType, valData);
8442 // Used by GetCType
8443 JS_SetReservedSlot(objResult, SLOT_DATAFINALIZER_VALTYPE,
8444 ObjectOrNullValue(objBestArgType));
8446 // Used by ToSource
8447 JS_SetReservedSlot(objResult, SLOT_DATAFINALIZER_CODETYPE,
8448 ObjectValue(*objCodePtrType));
8450 RootedValue abiType(cx, ObjectOrNullValue(funInfoFinalizer->mABI));
8451 ffi_abi abi;
8452 if (!GetABI(cx, abiType, &abi)) {
8453 JS_ReportErrorASCII(cx,
8454 "Internal Error: "
8455 "Invalid ABI specification in CDataFinalizer");
8456 return false;
8459 ffi_type* rtype = CType::GetFFIType(cx, funInfoFinalizer->mReturnType);
8460 if (!rtype) {
8461 JS_ReportErrorASCII(cx,
8462 "Internal Error: "
8463 "Could not access ffi type of CDataFinalizer");
8464 return false;
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;
8476 p->code = code;
8478 JS::SetReservedSlot(objResult, SLOT_DATAFINALIZER_PRIVATE,
8479 JS::PrivateValue(p.release()));
8480 args.rval().setObject(*objResult);
8481 return true;
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|
8489 * or |Finalize|.
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;
8501 errno = 0;
8502 #if defined(XP_WIN)
8503 int32_t savedLastError = GetLastError();
8504 SetLastError(0);
8505 #endif // defined(XP_WIN)
8507 void* args[1] = {p->cargs};
8508 ffi_call(&p->CIF, FFI_FN(p->code), p->rvalue, args);
8510 if (errnoStatus) {
8511 *errnoStatus = errno;
8513 errno = savedErrno;
8514 #if defined(XP_WIN)
8515 if (lastErrorStatus) {
8516 *lastErrorStatus = GetLastError();
8518 SetLastError(savedLastError);
8519 #endif // defined(XP_WIN)
8523 * Forget the value.
8525 * Preconditions: |this| must be a |CDataFinalizer|.
8526 * The function fails if |this| has gone through |Forget|/|Dispose|
8527 * or |Finalize|.
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",
8536 "s");
8539 RootedObject obj(cx,
8540 GetThisObject(cx, args, "CDataFinalizer.prototype.forget"));
8541 if (!obj) {
8542 return false;
8544 if (!CDataFinalizer::IsCDataFinalizer(obj)) {
8545 return IncompatibleThisProto(cx, "CDataFinalizer.prototype.forget",
8546 args.thisv());
8549 CDataFinalizer::Private* p = GetFinalizerPrivate(obj);
8551 if (!p) {
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");
8559 return false;
8562 CDataFinalizer::Cleanup(p, obj);
8564 args.rval().set(valJSData);
8565 return true;
8569 * Clean up the value.
8571 * Preconditions: |this| must be a |CDataFinalizer|.
8572 * The function fails if |this| has gone through |Forget|/|Dispose|
8573 * or |Finalize|.
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",
8582 "s");
8585 RootedObject obj(cx,
8586 GetThisObject(cx, args, "CDataFinalizer.prototype.dispose"));
8587 if (!obj) {
8588 return false;
8590 if (!CDataFinalizer::IsCDataFinalizer(obj)) {
8591 return IncompatibleThisProto(cx, "CDataFinalizer.prototype.dispose",
8592 args.thisv());
8595 CDataFinalizer::Private* p = GetFinalizerPrivate(obj);
8597 if (!p) {
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()));
8605 if (!objCTypes) {
8606 return false;
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);
8621 int errnoStatus;
8622 #if defined(XP_WIN)
8623 int32_t lastErrorStatus;
8624 CDataFinalizer::CallFinalizer(p, &errnoStatus, &lastErrorStatus);
8625 #else
8626 CDataFinalizer::CallFinalizer(p, &errnoStatus, nullptr);
8627 #endif // defined(XP_WIN)
8629 JS_SetReservedSlot(objCTypes, SLOT_ERRNO, Int32Value(errnoStatus));
8630 #if defined(XP_WIN)
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);
8637 return true;
8639 CDataFinalizer::Cleanup(p, obj);
8640 return false;
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);
8656 if (!p) {
8657 return;
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) {
8676 if (!p) {
8677 return; // We have already cleaned up
8680 free(p->cargs);
8681 free(p->rvalue);
8682 free(p);
8684 if (!obj) {
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,
8702 bool isUnsigned) {
8703 const JSClass* clasp = isUnsigned ? &sUInt64Class : &sInt64Class;
8704 RootedObject result(cx, JS_NewObjectWithGivenProto(cx, clasp, proto));
8705 if (!result) {
8706 return nullptr;
8709 // attach the Int64's data
8710 uint64_t* buffer = cx->new_<uint64_t>(data);
8711 if (!buffer) {
8712 return nullptr;
8715 JS_InitReservedSlot(result, SLOT_INT64, buffer, JS::MemoryUse::CTypesInt64);
8717 if (!JS_FreezeObject(cx, result)) {
8718 return nullptr;
8721 return result;
8724 void Int64Base::Finalize(JS::GCContext* gcx, JSObject* obj) {
8725 Value slot = JS::GetReservedSlot(obj, SLOT_INT64);
8726 if (slot.isUndefined()) {
8727 return;
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,
8742 bool isUnsigned) {
8743 if (args.length() > 1) {
8744 if (isUnsigned) {
8745 return ArgumentLengthError(cx, "UInt64.prototype.toString", "at most one",
8746 "");
8748 return ArgumentLengthError(cx, "Int64.prototype.toString", "at most one",
8749 "");
8752 int radix = 10;
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) {
8759 if (isUnsigned) {
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;
8771 if (isUnsigned) {
8772 IntegerToString(GetInt(obj), radix, intString);
8773 } else {
8774 IntegerToString(static_cast<int64_t>(GetInt(obj)), radix, intString);
8777 if (!intString) {
8778 return false;
8780 JSString* result = NewUCString(cx, intString.finish());
8781 if (!result) {
8782 return false;
8785 args.rval().setString(result);
8786 return true;
8789 bool Int64Base::ToSource(JSContext* cx, JSObject* obj, const CallArgs& args,
8790 bool isUnsigned) {
8791 if (args.length() != 0) {
8792 if (isUnsigned) {
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.
8799 AutoString source;
8800 if (isUnsigned) {
8801 AppendString(cx, source, "ctypes.UInt64(\"");
8802 IntegerToString(GetInt(obj), 10, source);
8803 } else {
8804 AppendString(cx, source, "ctypes.Int64(\"");
8805 IntegerToString(static_cast<int64_t>(GetInt(obj)), 10, source);
8807 AppendString(cx, source, "\")");
8808 if (!source) {
8809 return false;
8812 JSString* result = NewUCString(cx, source.finish());
8813 if (!result) {
8814 return false;
8817 args.rval().setString(result);
8818 return true;
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", "");
8829 int64_t i = 0;
8830 bool overflow = false;
8831 if (!jsvalToBigInteger(cx, args[0], true, &i, &overflow)) {
8832 if (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);
8846 if (!result) {
8847 return false;
8850 args.rval().setObject(*result);
8851 return true;
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"));
8859 if (!obj) {
8860 return false;
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",
8868 "non-Int64 CData");
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"));
8877 if (!obj) {
8878 return false;
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",
8886 "non-Int64 CData");
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);
8910 if (i1 == i2) {
8911 args.rval().setInt32(0);
8912 } else if (i1 < i2) {
8913 args.rval().setInt32(-1);
8914 } else {
8915 args.rval().setInt32(1);
8918 return true;
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);
8939 return true;
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);
8956 return true;
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");
8965 int32_t hi;
8966 uint32_t lo;
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);
8984 if (!result) {
8985 return false;
8988 args.rval().setObject(*result);
8989 return true;
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", "");
9000 uint64_t u = 0;
9001 bool overflow = false;
9002 if (!jsvalToBigInteger(cx, args[0], true, &u, &overflow)) {
9003 if (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);
9017 if (!result) {
9018 return false;
9021 args.rval().setObject(*result);
9022 return true;
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"));
9030 if (!obj) {
9031 return false;
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"));
9048 if (!obj) {
9049 return false;
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);
9081 if (u1 == u2) {
9082 args.rval().setInt32(0);
9083 } else if (u1 < u2) {
9084 args.rval().setInt32(-1);
9085 } else {
9086 args.rval().setInt32(1);
9089 return true;
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);
9106 return true;
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);
9123 return true;
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");
9132 uint32_t hi;
9133 uint32_t lo;
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);
9151 if (!result) {
9152 return false;
9155 args.rval().setObject(*result);
9156 return true;
9159 } // namespace ctypes
9160 } // namespace js