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/. */
6 
7 #include "ctypes/CTypes.h"
8 #include "js/experimental/CTypes.h"  // JS::CTypesActivity{Callback,Type}, JS::InitCTypesClass, JS::SetCTypesActivityCallback, JS::SetCTypesCallbacks
9 
10 #include "mozilla/CheckedInt.h"
11 #include "mozilla/FloatingPoint.h"
12 #include "mozilla/MemoryReporting.h"
13 #include "mozilla/Sprintf.h"
14 #include "mozilla/TextUtils.h"
15 #include "mozilla/Vector.h"
16 #include "mozilla/WrappingOperations.h"
17 
18 #if defined(XP_UNIX)
19 #  include <errno.h>
20 #endif
21 #if defined(XP_WIN)
22 #  include <float.h>
23 #endif
24 #if defined(SOLARIS)
25 #  include <ieeefp.h>
26 #endif
27 #include <limits>
28 #include <math.h>
29 #include <stdint.h>
30 #ifdef HAVE_SSIZE_T
31 #  include <sys/types.h>
32 #endif
33 #include <type_traits>
34 
35 #include "jsexn.h"
36 #include "jsnum.h"
37 
38 #include "ctypes/Library.h"
39 #include "gc/FreeOp.h"
40 #include "gc/Policy.h"
41 #include "jit/AtomicOperations.h"
42 #include "js/Array.h"  // JS::GetArrayLength, JS::IsArrayObject, JS::NewArrayObject
43 #include "js/ArrayBuffer.h"  // JS::{IsArrayBufferObject,GetArrayBufferData,GetArrayBuffer{ByteLength,Data}}
44 #include "js/CharacterEncoding.h"
45 #include "js/experimental/TypedData.h"  // JS_GetArrayBufferView{Type,Data}, JS_GetTypedArrayByteLength, JS_IsArrayBufferViewObject, JS_IsTypedArrayObject
46 #include "js/friend/ErrorMessages.h"    // js::GetErrorMessage, JSMSG_*
47 #include "js/Object.h"  // JS::GetPrivate, JS::GetReservedSlot, JS::SetPrivate
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/Windows.h"
57 #include "vm/JSContext.h"
58 #include "vm/JSFunction.h"
59 #include "vm/JSObject.h"
60 
61 #include "vm/JSObject-inl.h"
62 
63 using std::numeric_limits;
64 
65 using mozilla::CheckedInt;
66 using mozilla::IsAsciiAlpha;
67 using mozilla::IsAsciiDigit;
68 
69 using JS::AutoCheckCannotGC;
70 using JS::AutoCTypesActivityCallback;
71 using JS::AutoStableStringChars;
72 using JS::CTypesActivityType;
73 
74 namespace js::ctypes {
75 
HasUnpairedSurrogate(const char16_t * chars,size_t nchars,char16_t * unpaired)76 static bool HasUnpairedSurrogate(const char16_t* chars, size_t nchars,
77                                  char16_t* unpaired) {
78   for (const char16_t* end = chars + nchars; chars != end; chars++) {
79     char16_t c = *chars;
80     if (unicode::IsSurrogate(c)) {
81       chars++;
82       if (unicode::IsTrailSurrogate(c) || chars == end) {
83         *unpaired = c;
84         return true;
85       }
86       char16_t c2 = *chars;
87       if (!unicode::IsTrailSurrogate(c2)) {
88         *unpaired = c;
89         return true;
90       }
91     }
92   }
93   return false;
94 }
95 
ReportErrorIfUnpairedSurrogatePresent(JSContext * cx,JSLinearString * str)96 bool ReportErrorIfUnpairedSurrogatePresent(JSContext* cx, JSLinearString* str) {
97   if (str->hasLatin1Chars()) {
98     return true;
99   }
100 
101   char16_t unpaired;
102   {
103     JS::AutoCheckCannotGC nogc;
104     if (!HasUnpairedSurrogate(str->twoByteChars(nogc), str->length(),
105                               &unpaired)) {
106       return true;
107     }
108   }
109 
110   char buffer[10];
111   SprintfLiteral(buffer, "0x%x", unpaired);
112   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
113                             JSMSG_BAD_SURROGATE_CHAR, buffer);
114   return false;
115 }
116 
117 /*******************************************************************************
118 ** JSAPI function prototypes
119 *******************************************************************************/
120 
121 // We use an enclosing struct here out of paranoia about the ability of gcc 4.4
122 // (and maybe 4.5) to correctly compile this if it were a template function.
123 // See also the comments in dom/workers/Events.cpp (and other adjacent files) by
124 // the |struct Property| there.
125 template <JS::IsAcceptableThis Test, JS::NativeImpl Impl>
126 struct Property {
Funjs::ctypes::Property127   static bool Fun(JSContext* cx, unsigned argc, JS::Value* vp) {
128     JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
129     return JS::CallNonGenericMethod<Test, Impl>(cx, args);
130   }
131 };
132 
133 static bool ConstructAbstract(JSContext* cx, unsigned argc, Value* vp);
134 
135 namespace CType {
136 static bool ConstructData(JSContext* cx, unsigned argc, Value* vp);
137 static bool ConstructBasic(JSContext* cx, HandleObject obj,
138                            const CallArgs& args);
139 
140 static void Trace(JSTracer* trc, JSObject* obj);
141 static void Finalize(JSFreeOp* fop, JSObject* obj);
142 
143 bool IsCType(HandleValue v);
144 bool IsCTypeOrProto(HandleValue v);
145 
146 bool PrototypeGetter(JSContext* cx, const JS::CallArgs& args);
147 bool NameGetter(JSContext* cx, const JS::CallArgs& args);
148 bool SizeGetter(JSContext* cx, const JS::CallArgs& args);
149 bool PtrGetter(JSContext* cx, const JS::CallArgs& args);
150 
151 static bool CreateArray(JSContext* cx, unsigned argc, Value* vp);
152 static bool ToString(JSContext* cx, unsigned argc, Value* vp);
153 static bool ToSource(JSContext* cx, unsigned argc, Value* vp);
154 static bool HasInstance(JSContext* cx, HandleObject obj, MutableHandleValue v,
155                         bool* bp);
156 
157 /*
158  * Get the global "ctypes" object.
159  *
160  * |obj| must be a CType object.
161  *
162  * This function never returns nullptr.
163  */
164 static JSObject* GetGlobalCTypes(JSContext* cx, JSObject* obj);
165 
166 }  // namespace CType
167 
168 namespace ABI {
169 bool IsABI(JSObject* obj);
170 static bool ToSource(JSContext* cx, unsigned argc, Value* vp);
171 }  // namespace ABI
172 
173 namespace PointerType {
174 static bool Create(JSContext* cx, unsigned argc, Value* vp);
175 static bool ConstructData(JSContext* cx, HandleObject obj,
176                           const CallArgs& args);
177 
178 bool IsPointerType(HandleValue v);
179 bool IsPointer(HandleValue v);
180 
181 bool TargetTypeGetter(JSContext* cx, const JS::CallArgs& args);
182 bool ContentsGetter(JSContext* cx, const JS::CallArgs& args);
183 bool ContentsSetter(JSContext* cx, const JS::CallArgs& args);
184 
185 static bool IsNull(JSContext* cx, unsigned argc, Value* vp);
186 static bool Increment(JSContext* cx, unsigned argc, Value* vp);
187 static bool Decrement(JSContext* cx, unsigned argc, Value* vp);
188 // The following is not an instance function, since we don't want to expose
189 // arbitrary pointer arithmetic at this moment.
190 static bool OffsetBy(JSContext* cx, const CallArgs& args, int offset,
191                      const char* name);
192 }  // namespace PointerType
193 
194 namespace ArrayType {
195 bool IsArrayType(HandleValue v);
196 bool IsArrayOrArrayType(HandleValue v);
197 
198 static bool Create(JSContext* cx, unsigned argc, Value* vp);
199 static bool ConstructData(JSContext* cx, HandleObject obj,
200                           const CallArgs& args);
201 
202 bool ElementTypeGetter(JSContext* cx, const JS::CallArgs& args);
203 bool LengthGetter(JSContext* cx, const JS::CallArgs& args);
204 
205 static bool Getter(JSContext* cx, HandleObject obj, HandleId idval,
206                    MutableHandleValue vp, bool* handled);
207 static bool Setter(JSContext* cx, HandleObject obj, HandleId idval,
208                    HandleValue v, ObjectOpResult& result, bool* handled);
209 static bool AddressOfElement(JSContext* cx, unsigned argc, Value* vp);
210 }  // namespace ArrayType
211 
212 namespace StructType {
213 bool IsStruct(HandleValue v);
214 
215 static bool Create(JSContext* cx, unsigned argc, Value* vp);
216 static bool ConstructData(JSContext* cx, HandleObject obj,
217                           const CallArgs& args);
218 
219 bool FieldsArrayGetter(JSContext* cx, const JS::CallArgs& args);
220 
221 enum { SLOT_FIELDNAME };
222 
223 static bool FieldGetter(JSContext* cx, unsigned argc, Value* vp);
224 static bool FieldSetter(JSContext* cx, unsigned argc, Value* vp);
225 static bool AddressOfField(JSContext* cx, unsigned argc, Value* vp);
226 static bool Define(JSContext* cx, unsigned argc, Value* vp);
227 }  // namespace StructType
228 
229 namespace FunctionType {
230 static bool Create(JSContext* cx, unsigned argc, Value* vp);
231 static bool ConstructData(JSContext* cx, HandleObject typeObj,
232                           HandleObject dataObj, HandleObject fnObj,
233                           HandleObject thisObj, HandleValue errVal);
234 
235 static bool Call(JSContext* cx, unsigned argc, Value* vp);
236 
237 bool IsFunctionType(HandleValue v);
238 
239 bool ArgTypesGetter(JSContext* cx, const JS::CallArgs& args);
240 bool ReturnTypeGetter(JSContext* cx, const JS::CallArgs& args);
241 bool ABIGetter(JSContext* cx, const JS::CallArgs& args);
242 bool IsVariadicGetter(JSContext* cx, const JS::CallArgs& args);
243 }  // namespace FunctionType
244 
245 namespace CClosure {
246 static void Trace(JSTracer* trc, JSObject* obj);
247 static void Finalize(JSFreeOp* fop, JSObject* obj);
248 
249 // libffi callback
250 static void ClosureStub(ffi_cif* cif, void* result, void** args,
251                         void* userData);
252 
253 struct ArgClosure : public ScriptEnvironmentPreparer::Closure {
ArgClosurejs::ctypes::CClosure::ArgClosure254   ArgClosure(ffi_cif* cifArg, void* resultArg, void** argsArg,
255              ClosureInfo* cinfoArg)
256       : cif(cifArg), result(resultArg), args(argsArg), cinfo(cinfoArg) {}
257 
258   bool operator()(JSContext* cx) override;
259 
260   ffi_cif* cif;
261   void* result;
262   void** args;
263   ClosureInfo* cinfo;
264 };
265 }  // namespace CClosure
266 
267 namespace CData {
268 static void Finalize(JSFreeOp* fop, JSObject* obj);
269 
270 bool ValueGetter(JSContext* cx, const JS::CallArgs& args);
271 bool ValueSetter(JSContext* cx, const JS::CallArgs& args);
272 
273 static bool Address(JSContext* cx, unsigned argc, Value* vp);
274 static bool ReadString(JSContext* cx, unsigned argc, Value* vp);
275 static bool ReadStringReplaceMalformed(JSContext* cx, unsigned argc, Value* vp);
276 static bool ReadTypedArray(JSContext* cx, unsigned argc, Value* vp);
277 static bool ToSource(JSContext* cx, unsigned argc, Value* vp);
278 static JSString* GetSourceString(JSContext* cx, HandleObject typeObj,
279                                  void* data);
280 
281 bool ErrnoGetter(JSContext* cx, const JS::CallArgs& args);
282 
283 #if defined(XP_WIN)
284 bool LastErrorGetter(JSContext* cx, const JS::CallArgs& args);
285 #endif  // defined(XP_WIN)
286 }  // namespace CData
287 
288 namespace CDataFinalizer {
289 /*
290  * Attach a C function as a finalizer to a JS object.
291  *
292  * This function is available from JS as |ctypes.withFinalizer|.
293  *
294  * JavaScript signature:
295  * function(CData, CData):   CDataFinalizer
296  *          value  finalizer finalizable
297  *
298  * Where |finalizer| is a one-argument function taking a value
299  * with the same type as |value|.
300  */
301 static bool Construct(JSContext* cx, unsigned argc, Value* vp);
302 
303 /*
304  * Private data held by |CDataFinalizer|.
305  *
306  * See also |enum CDataFinalizerSlot| for the slots of
307  * |CDataFinalizer|.
308  *
309  * Note: the private data may be nullptr, if |dispose|, |forget| or the
310  * finalizer has already been called.
311  */
312 struct Private {
313   /*
314    * The C data to pass to the code.
315    * Finalization/|dispose|/|forget| release this memory.
316    */
317   void* cargs;
318 
319   /*
320    * The total size of the buffer pointed by |cargs|
321    */
322   size_t cargs_size;
323 
324   /*
325    * Low-level signature information.
326    * Finalization/|dispose|/|forget| release this memory.
327    */
328   ffi_cif CIF;
329 
330   /*
331    * The C function to invoke during finalization.
332    * Do not deallocate this.
333    */
334   uintptr_t code;
335 
336   /*
337    * A buffer for holding the return value.
338    * Finalization/|dispose|/|forget| release this memory.
339    */
340   void* rvalue;
341 };
342 
343 /*
344  * Methods of instances of |CDataFinalizer|
345  */
346 namespace Methods {
347 static bool Dispose(JSContext* cx, unsigned argc, Value* vp);
348 static bool Forget(JSContext* cx, unsigned argc, Value* vp);
349 static bool ReadString(JSContext* cx, unsigned argc, Value* vp);
350 static bool ReadTypedArray(JSContext* cx, unsigned argc, Value* vp);
351 static bool ToSource(JSContext* cx, unsigned argc, Value* vp);
352 static bool ToString(JSContext* cx, unsigned argc, Value* vp);
353 }  // namespace Methods
354 
355 /*
356  * Utility functions
357  *
358  * @return true if |obj| is a CDataFinalizer, false otherwise.
359  */
360 static bool IsCDataFinalizer(JSObject* obj);
361 
362 /*
363  * Clean up the finalization information of a CDataFinalizer.
364  *
365  * Used by |Finalize|, |Dispose| and |Forget|.
366  *
367  * @param p The private information of the CDataFinalizer. If nullptr,
368  * this function does nothing.
369  * @param obj Either nullptr, if the object should not be cleaned up (i.e.
370  * during finalization) or a CDataFinalizer JSObject. Always use nullptr
371  * if you are calling from a finalizer.
372  */
373 static void Cleanup(Private* p, JSObject* obj);
374 
375 /*
376  * Perform the actual call to the finalizer code.
377  */
378 static void CallFinalizer(CDataFinalizer::Private* p, int* errnoStatus,
379                           int32_t* lastErrorStatus);
380 
381 /*
382  * Return the CType of a CDataFinalizer object, or nullptr if the object
383  * has been cleaned-up already.
384  */
385 static JSObject* GetCType(JSContext* cx, JSObject* obj);
386 
387 /*
388  * Perform finalization of a |CDataFinalizer|
389  */
390 static void Finalize(JSFreeOp* fop, JSObject* obj);
391 
392 /*
393  * Return the Value contained by this finalizer.
394  *
395  * Note that the Value is actually not recorded, but converted back from C.
396  */
397 static bool GetValue(JSContext* cx, JSObject* obj, MutableHandleValue result);
398 
399 }  // namespace CDataFinalizer
400 
401 // Int64Base provides functions common to Int64 and UInt64.
402 namespace Int64Base {
403 JSObject* Construct(JSContext* cx, HandleObject proto, uint64_t data,
404                     bool isUnsigned);
405 
406 uint64_t GetInt(JSObject* obj);
407 
408 bool ToString(JSContext* cx, JSObject* obj, const CallArgs& args,
409               bool isUnsigned);
410 
411 bool ToSource(JSContext* cx, JSObject* obj, const CallArgs& args,
412               bool isUnsigned);
413 
414 static void Finalize(JSFreeOp* fop, JSObject* obj);
415 }  // namespace Int64Base
416 
417 namespace Int64 {
418 static bool Construct(JSContext* cx, unsigned argc, Value* vp);
419 
420 static bool ToString(JSContext* cx, unsigned argc, Value* vp);
421 static bool ToSource(JSContext* cx, unsigned argc, Value* vp);
422 
423 static bool Compare(JSContext* cx, unsigned argc, Value* vp);
424 static bool Lo(JSContext* cx, unsigned argc, Value* vp);
425 static bool Hi(JSContext* cx, unsigned argc, Value* vp);
426 static bool Join(JSContext* cx, unsigned argc, Value* vp);
427 }  // namespace Int64
428 
429 namespace UInt64 {
430 static bool Construct(JSContext* cx, unsigned argc, Value* vp);
431 
432 static bool ToString(JSContext* cx, unsigned argc, Value* vp);
433 static bool ToSource(JSContext* cx, unsigned argc, Value* vp);
434 
435 static bool Compare(JSContext* cx, unsigned argc, Value* vp);
436 static bool Lo(JSContext* cx, unsigned argc, Value* vp);
437 static bool Hi(JSContext* cx, unsigned argc, Value* vp);
438 static bool Join(JSContext* cx, unsigned argc, Value* vp);
439 }  // namespace UInt64
440 
441 /*******************************************************************************
442 ** JSClass definitions and initialization functions
443 *******************************************************************************/
444 
445 // Class representing the 'ctypes' object itself. This exists to contain the
446 // JS::CTypesCallbacks set of function pointers.
447 static const JSClass sCTypesGlobalClass = {
448     "ctypes", JSCLASS_HAS_RESERVED_SLOTS(CTYPESGLOBAL_SLOTS)};
449 
450 static const JSClass sCABIClass = {"CABI",
451                                    JSCLASS_HAS_RESERVED_SLOTS(CABI_SLOTS)};
452 
453 // Class representing ctypes.{C,Pointer,Array,Struct,Function}Type.prototype.
454 // This exists to give said prototypes a class of "CType", and to provide
455 // reserved slots for stashing various other prototype objects.
456 static const JSClassOps sCTypeProtoClassOps = {
457     nullptr,            // addProperty
458     nullptr,            // delProperty
459     nullptr,            // enumerate
460     nullptr,            // newEnumerate
461     nullptr,            // resolve
462     nullptr,            // mayResolve
463     nullptr,            // finalize
464     ConstructAbstract,  // call
465     nullptr,            // hasInstance
466     ConstructAbstract,  // construct
467     nullptr,            // trace
468 };
469 static const JSClass sCTypeProtoClass = {
470     "CType", JSCLASS_HAS_RESERVED_SLOTS(CTYPEPROTO_SLOTS),
471     &sCTypeProtoClassOps};
472 
473 // Class representing ctypes.CData.prototype and the 'prototype' properties
474 // of CTypes. This exists to give said prototypes a class of "CData".
475 static const JSClass sCDataProtoClass = {"CData", 0};
476 
477 static const JSClassOps sCTypeClassOps = {
478     nullptr,               // addProperty
479     nullptr,               // delProperty
480     nullptr,               // enumerate
481     nullptr,               // newEnumerate
482     nullptr,               // resolve
483     nullptr,               // mayResolve
484     CType::Finalize,       // finalize
485     CType::ConstructData,  // call
486     CType::HasInstance,    // hasInstance
487     CType::ConstructData,  // construct
488     CType::Trace,          // trace
489 };
490 static const JSClass sCTypeClass = {
491     "CType",
492     JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS) | JSCLASS_FOREGROUND_FINALIZE,
493     &sCTypeClassOps};
494 
495 static const JSClassOps sCDataClassOps = {
496     nullptr,             // addProperty
497     nullptr,             // delProperty
498     nullptr,             // enumerate
499     nullptr,             // newEnumerate
500     nullptr,             // resolve
501     nullptr,             // mayResolve
502     CData::Finalize,     // finalize
503     FunctionType::Call,  // call
504     nullptr,             // hasInstance
505     FunctionType::Call,  // construct
506     nullptr,             // trace
507 };
508 static const JSClass sCDataClass = {
509     "CData",
510     JSCLASS_HAS_RESERVED_SLOTS(CDATA_SLOTS) | JSCLASS_FOREGROUND_FINALIZE,
511     &sCDataClassOps};
512 
513 static const JSClassOps sCClosureClassOps = {
514     nullptr,             // addProperty
515     nullptr,             // delProperty
516     nullptr,             // enumerate
517     nullptr,             // newEnumerate
518     nullptr,             // resolve
519     nullptr,             // mayResolve
520     CClosure::Finalize,  // finalize
521     nullptr,             // call
522     nullptr,             // hasInstance
523     nullptr,             // construct
524     CClosure::Trace,     // trace
525 };
526 static const JSClass sCClosureClass = {
527     "CClosure",
528     JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS) | JSCLASS_FOREGROUND_FINALIZE,
529     &sCClosureClassOps};
530 
531 /*
532  * Class representing the prototype of CDataFinalizer.
533  */
534 static const JSClass sCDataFinalizerProtoClass = {"CDataFinalizer", 0};
535 
536 /*
537  * Class representing instances of CDataFinalizer.
538  *
539  * Instances of CDataFinalizer have both private data (with type
540  * |CDataFinalizer::Private|) and slots (see |CDataFinalizerSlots|).
541  */
542 static const JSClassOps sCDataFinalizerClassOps = {
543     nullptr,                   // addProperty
544     nullptr,                   // delProperty
545     nullptr,                   // enumerate
546     nullptr,                   // newEnumerate
547     nullptr,                   // resolve
548     nullptr,                   // mayResolve
549     CDataFinalizer::Finalize,  // finalize
550     nullptr,                   // call
551     nullptr,                   // hasInstance
552     nullptr,                   // construct
553     nullptr,                   // trace
554 };
555 static const JSClass sCDataFinalizerClass = {
556     "CDataFinalizer",
557     JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(CDATAFINALIZER_SLOTS) |
558         JSCLASS_FOREGROUND_FINALIZE,
559     &sCDataFinalizerClassOps};
560 
561 #define CTYPESFN_FLAGS (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
562 
563 #define CTYPESCTOR_FLAGS (CTYPESFN_FLAGS | JSFUN_CONSTRUCTOR)
564 
565 #define CTYPESACC_FLAGS (JSPROP_ENUMERATE | JSPROP_PERMANENT)
566 
567 #define CABIFN_FLAGS (JSPROP_READONLY | JSPROP_PERMANENT)
568 
569 #define CDATAFN_FLAGS (JSPROP_READONLY | JSPROP_PERMANENT)
570 
571 #define CDATAFINALIZERFN_FLAGS (JSPROP_READONLY | JSPROP_PERMANENT)
572 
573 static const JSPropertySpec sCTypeProps[] = {
574     JS_PSG("name", (Property<CType::IsCType, CType::NameGetter>::Fun),
575            CTYPESACC_FLAGS),
576     JS_PSG("size", (Property<CType::IsCType, CType::SizeGetter>::Fun),
577            CTYPESACC_FLAGS),
578     JS_PSG("ptr", (Property<CType::IsCType, CType::PtrGetter>::Fun),
579            CTYPESACC_FLAGS),
580     JS_PSG("prototype",
581            (Property<CType::IsCTypeOrProto, CType::PrototypeGetter>::Fun),
582            CTYPESACC_FLAGS),
583     JS_PS_END};
584 
585 static const JSFunctionSpec sCTypeFunctions[] = {
586     JS_FN("array", CType::CreateArray, 0, CTYPESFN_FLAGS),
587     JS_FN("toString", CType::ToString, 0, CTYPESFN_FLAGS),
588     JS_FN("toSource", CType::ToSource, 0, CTYPESFN_FLAGS), JS_FS_END};
589 
590 static const JSFunctionSpec sCABIFunctions[] = {
591     JS_FN("toSource", ABI::ToSource, 0, CABIFN_FLAGS),
592     JS_FN("toString", ABI::ToSource, 0, CABIFN_FLAGS), JS_FS_END};
593 
594 static const JSPropertySpec sCDataProps[] = {
595     JS_PSGS("value", (Property<CData::IsCData, CData::ValueGetter>::Fun),
596             (Property<CData::IsCData, CData::ValueSetter>::Fun),
597             JSPROP_PERMANENT),
598     JS_PS_END};
599 
600 static const JSFunctionSpec sCDataFunctions[] = {
601     JS_FN("address", CData::Address, 0, CDATAFN_FLAGS),
602     JS_FN("readString", CData::ReadString, 0, CDATAFN_FLAGS),
603     JS_FN("readStringReplaceMalformed", CData::ReadStringReplaceMalformed, 0,
604           CDATAFN_FLAGS),
605     JS_FN("readTypedArray", CData::ReadTypedArray, 0, CDATAFN_FLAGS),
606     JS_FN("toSource", CData::ToSource, 0, CDATAFN_FLAGS),
607     JS_FN("toString", CData::ToSource, 0, CDATAFN_FLAGS),
608     JS_FS_END};
609 
610 static const JSFunctionSpec sCDataFinalizerFunctions[] = {
611     JS_FN("dispose", CDataFinalizer::Methods::Dispose, 0,
612           CDATAFINALIZERFN_FLAGS),
613     JS_FN("forget", CDataFinalizer::Methods::Forget, 0, CDATAFINALIZERFN_FLAGS),
614     JS_FN("readString", CDataFinalizer::Methods::ReadString, 0,
615           CDATAFINALIZERFN_FLAGS),
616     JS_FN("readTypedArray", CDataFinalizer::Methods::ReadTypedArray, 0,
617           CDATAFINALIZERFN_FLAGS),
618     JS_FN("toString", CDataFinalizer::Methods::ToString, 0,
619           CDATAFINALIZERFN_FLAGS),
620     JS_FN("toSource", CDataFinalizer::Methods::ToSource, 0,
621           CDATAFINALIZERFN_FLAGS),
622     JS_FS_END};
623 
624 static const JSFunctionSpec sPointerFunction =
625     JS_FN("PointerType", PointerType::Create, 1, CTYPESCTOR_FLAGS);
626 
627 static const JSPropertySpec sPointerProps[] = {
628     JS_PSG("targetType",
629            (Property<PointerType::IsPointerType,
630                      PointerType::TargetTypeGetter>::Fun),
631            CTYPESACC_FLAGS),
632     JS_PS_END};
633 
634 static const JSFunctionSpec sPointerInstanceFunctions[] = {
635     JS_FN("isNull", PointerType::IsNull, 0, CTYPESFN_FLAGS),
636     JS_FN("increment", PointerType::Increment, 0, CTYPESFN_FLAGS),
637     JS_FN("decrement", PointerType::Decrement, 0, CTYPESFN_FLAGS), JS_FS_END};
638 
639 static const JSPropertySpec sPointerInstanceProps[] = {
640     JS_PSGS(
641         "contents",
642         (Property<PointerType::IsPointer, PointerType::ContentsGetter>::Fun),
643         (Property<PointerType::IsPointer, PointerType::ContentsSetter>::Fun),
644         JSPROP_PERMANENT),
645     JS_PS_END};
646 
647 static const JSFunctionSpec sArrayFunction =
648     JS_FN("ArrayType", ArrayType::Create, 1, CTYPESCTOR_FLAGS);
649 
650 static const JSPropertySpec sArrayProps[] = {
651     JS_PSG(
652         "elementType",
653         (Property<ArrayType::IsArrayType, ArrayType::ElementTypeGetter>::Fun),
654         CTYPESACC_FLAGS),
655     JS_PSG(
656         "length",
657         (Property<ArrayType::IsArrayOrArrayType, ArrayType::LengthGetter>::Fun),
658         CTYPESACC_FLAGS),
659     JS_PS_END};
660 
661 static const JSFunctionSpec sArrayInstanceFunctions[] = {
662     JS_FN("addressOfElement", ArrayType::AddressOfElement, 1, CDATAFN_FLAGS),
663     JS_FS_END};
664 
665 static const JSPropertySpec sArrayInstanceProps[] = {
666     JS_PSG(
667         "length",
668         (Property<ArrayType::IsArrayOrArrayType, ArrayType::LengthGetter>::Fun),
669         JSPROP_PERMANENT),
670     JS_PS_END};
671 
672 static const JSFunctionSpec sStructFunction =
673     JS_FN("StructType", StructType::Create, 2, CTYPESCTOR_FLAGS);
674 
675 static const JSPropertySpec sStructProps[] = {
676     JS_PSG("fields",
677            (Property<StructType::IsStruct, StructType::FieldsArrayGetter>::Fun),
678            CTYPESACC_FLAGS),
679     JS_PS_END};
680 
681 static const JSFunctionSpec sStructFunctions[] = {
682     JS_FN("define", StructType::Define, 1, CDATAFN_FLAGS), JS_FS_END};
683 
684 static const JSFunctionSpec sStructInstanceFunctions[] = {
685     JS_FN("addressOfField", StructType::AddressOfField, 1, CDATAFN_FLAGS),
686     JS_FS_END};
687 
688 static const JSFunctionSpec sFunctionFunction =
689     JS_FN("FunctionType", FunctionType::Create, 2, CTYPESCTOR_FLAGS);
690 
691 static const JSPropertySpec sFunctionProps[] = {
692     JS_PSG("argTypes",
693            (Property<FunctionType::IsFunctionType,
694                      FunctionType::ArgTypesGetter>::Fun),
695            CTYPESACC_FLAGS),
696     JS_PSG("returnType",
697            (Property<FunctionType::IsFunctionType,
698                      FunctionType::ReturnTypeGetter>::Fun),
699            CTYPESACC_FLAGS),
700     JS_PSG(
701         "abi",
702         (Property<FunctionType::IsFunctionType, FunctionType::ABIGetter>::Fun),
703         CTYPESACC_FLAGS),
704     JS_PSG("isVariadic",
705            (Property<FunctionType::IsFunctionType,
706                      FunctionType::IsVariadicGetter>::Fun),
707            CTYPESACC_FLAGS),
708     JS_PS_END};
709 
710 static const JSFunctionSpec sFunctionInstanceFunctions[] = {
711     JS_FN("call", js::fun_call, 1, CDATAFN_FLAGS),
712     JS_FN("apply", js::fun_apply, 2, CDATAFN_FLAGS), JS_FS_END};
713 
714 static const JSClass sInt64ProtoClass = {"Int64", 0};
715 
716 static const JSClass sUInt64ProtoClass = {"UInt64", 0};
717 
718 static const JSClassOps sInt64ClassOps = {
719     nullptr,              // addProperty
720     nullptr,              // delProperty
721     nullptr,              // enumerate
722     nullptr,              // newEnumerate
723     nullptr,              // resolve
724     nullptr,              // mayResolve
725     Int64Base::Finalize,  // finalize
726     nullptr,              // call
727     nullptr,              // hasInstance
728     nullptr,              // construct
729     nullptr,              // trace
730 };
731 
732 static const JSClass sInt64Class = {
733     "Int64",
734     JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS) | JSCLASS_FOREGROUND_FINALIZE,
735     &sInt64ClassOps};
736 
737 static const JSClass sUInt64Class = {
738     "UInt64",
739     JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS) | JSCLASS_FOREGROUND_FINALIZE,
740     &sInt64ClassOps};
741 
742 static const JSFunctionSpec sInt64StaticFunctions[] = {
743     JS_FN("compare", Int64::Compare, 2, CTYPESFN_FLAGS),
744     JS_FN("lo", Int64::Lo, 1, CTYPESFN_FLAGS),
745     JS_FN("hi", Int64::Hi, 1, CTYPESFN_FLAGS),
746     // "join" is defined specially; see InitInt64Class.
747     JS_FS_END};
748 
749 static const JSFunctionSpec sUInt64StaticFunctions[] = {
750     JS_FN("compare", UInt64::Compare, 2, CTYPESFN_FLAGS),
751     JS_FN("lo", UInt64::Lo, 1, CTYPESFN_FLAGS),
752     JS_FN("hi", UInt64::Hi, 1, CTYPESFN_FLAGS),
753     // "join" is defined specially; see InitInt64Class.
754     JS_FS_END};
755 
756 static const JSFunctionSpec sInt64Functions[] = {
757     JS_FN("toString", Int64::ToString, 0, CTYPESFN_FLAGS),
758     JS_FN("toSource", Int64::ToSource, 0, CTYPESFN_FLAGS), JS_FS_END};
759 
760 static const JSFunctionSpec sUInt64Functions[] = {
761     JS_FN("toString", UInt64::ToString, 0, CTYPESFN_FLAGS),
762     JS_FN("toSource", UInt64::ToSource, 0, CTYPESFN_FLAGS), JS_FS_END};
763 
764 static const JSPropertySpec sModuleProps[] = {
765     JS_PSG("errno", (Property<IsCTypesGlobal, CData::ErrnoGetter>::Fun),
766            JSPROP_PERMANENT),
767 #if defined(XP_WIN)
768     JS_PSG("winLastError",
769            (Property<IsCTypesGlobal, CData::LastErrorGetter>::Fun),
770            JSPROP_PERMANENT),
771 #endif  // defined(XP_WIN)
772     JS_PS_END};
773 
774 static const JSFunctionSpec sModuleFunctions[] = {
775     JS_FN("CDataFinalizer", CDataFinalizer::Construct, 2, CTYPESFN_FLAGS),
776     JS_FN("open", Library::Open, 1, CTYPESFN_FLAGS),
777     JS_FN("cast", CData::Cast, 2, CTYPESFN_FLAGS),
778     JS_FN("getRuntime", CData::GetRuntime, 1, CTYPESFN_FLAGS),
779     JS_FN("libraryName", Library::Name, 1, CTYPESFN_FLAGS),
780     JS_FS_END};
781 
782 // Wrapper for arrays, to intercept indexed gets/sets.
783 class CDataArrayProxyHandler : public ForwardingProxyHandler {
784  public:
785   static const CDataArrayProxyHandler singleton;
786   static const char family;
787 
CDataArrayProxyHandler()788   constexpr CDataArrayProxyHandler() : ForwardingProxyHandler(&family) {}
789 
790   bool get(JSContext* cx, HandleObject proxy, HandleValue receiver, HandleId id,
791            MutableHandleValue vp) const override;
792   bool set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v,
793            HandleValue receiver, ObjectOpResult& result) const override;
794 };
795 
796 const CDataArrayProxyHandler CDataArrayProxyHandler::singleton;
797 const char CDataArrayProxyHandler::family = 0;
798 
get(JSContext * cx,HandleObject proxy,HandleValue receiver,HandleId id,MutableHandleValue vp) const799 bool CDataArrayProxyHandler::get(JSContext* cx, HandleObject proxy,
800                                  HandleValue receiver, HandleId id,
801                                  MutableHandleValue vp) const {
802   RootedObject target(cx, proxy->as<ProxyObject>().target());
803   bool handled = false;
804   if (!ArrayType::Getter(cx, target, id, vp, &handled)) {
805     return false;
806   }
807   if (handled) {
808     return true;
809   }
810   return ForwardingProxyHandler::get(cx, proxy, receiver, id, vp);
811 }
812 
set(JSContext * cx,HandleObject proxy,HandleId id,HandleValue v,HandleValue receiver,ObjectOpResult & result) const813 bool CDataArrayProxyHandler::set(JSContext* cx, HandleObject proxy, HandleId id,
814                                  HandleValue v, HandleValue receiver,
815                                  ObjectOpResult& result) const {
816   RootedObject target(cx, proxy->as<ProxyObject>().target());
817   bool handled = false;
818   if (!ArrayType::Setter(cx, target, id, v, result, &handled)) {
819     return false;
820   }
821   if (handled) {
822     return true;
823   }
824   return ForwardingProxyHandler::set(cx, proxy, id, v, receiver, result);
825 }
826 
MaybeUnwrapArrayWrapper(JSObject * obj)827 static JSObject* MaybeUnwrapArrayWrapper(JSObject* obj) {
828   if (obj->is<ProxyObject>() &&
829       obj->as<ProxyObject>().handler() == &CDataArrayProxyHandler::singleton) {
830     return obj->as<ProxyObject>().target();
831   }
832   return obj;
833 }
834 
NewUCString(JSContext * cx,const AutoStringChars && from)835 static MOZ_ALWAYS_INLINE JSString* NewUCString(JSContext* cx,
836                                                const AutoStringChars&& from) {
837   return JS_NewUCStringCopyN(cx, from.begin(), from.length());
838 }
839 
840 /*
841  * Return a size rounded up to a multiple of a power of two.
842  *
843  * Note: |align| must be a power of 2.
844  */
Align(size_t val,size_t align)845 static MOZ_ALWAYS_INLINE size_t Align(size_t val, size_t align) {
846   // Ensure that align is a power of two.
847   MOZ_ASSERT(align != 0 && (align & (align - 1)) == 0);
848   return ((val - 1) | (align - 1)) + 1;
849 }
850 
GetABICode(JSObject * obj)851 static ABICode GetABICode(JSObject* obj) {
852   // make sure we have an object representing a CABI class,
853   // and extract the enumerated class type from the reserved slot.
854   if (!obj->hasClass(&sCABIClass)) {
855     return INVALID_ABI;
856   }
857 
858   Value result = JS::GetReservedSlot(obj, SLOT_ABICODE);
859   return ABICode(result.toInt32());
860 }
861 
862 static const JSErrorFormatString ErrorFormatString[CTYPESERR_LIMIT] = {
863 #define MSG_DEF(name, count, exception, format) \
864   {#name, format, count, exception},
865 #include "ctypes/ctypes.msg"
866 #undef MSG_DEF
867 };
868 
GetErrorMessage(void * userRef,const unsigned errorNumber)869 static const JSErrorFormatString* GetErrorMessage(void* userRef,
870                                                   const unsigned errorNumber) {
871   if (0 < errorNumber && errorNumber < CTYPESERR_LIMIT) {
872     return &ErrorFormatString[errorNumber];
873   }
874   return nullptr;
875 }
876 
EncodeUTF8(JSContext * cx,AutoString & str)877 static JS::UniqueChars EncodeUTF8(JSContext* cx, AutoString& str) {
878   RootedString string(cx, NewUCString(cx, str.finish()));
879   if (!string) {
880     return nullptr;
881   }
882   return JS_EncodeStringToUTF8(cx, string);
883 }
884 
CTypesToSourceForError(JSContext * cx,HandleValue val,JS::UniqueChars & bytes)885 static const char* CTypesToSourceForError(JSContext* cx, HandleValue val,
886                                           JS::UniqueChars& bytes) {
887   if (val.isObject()) {
888     RootedObject obj(cx, &val.toObject());
889     if (CType::IsCType(obj) || CData::IsCDataMaybeUnwrap(&obj)) {
890       RootedValue v(cx, ObjectValue(*obj));
891       RootedString str(cx, JS_ValueToSource(cx, v));
892       bytes = JS_EncodeStringToUTF8(cx, str);
893       return bytes.get();
894     }
895   }
896   return ValueToSourceForError(cx, val, bytes);
897 }
898 
899 static void BuildCStyleFunctionTypeSource(JSContext* cx, HandleObject typeObj,
900                                           HandleString nameStr,
901                                           unsigned ptrCount,
902                                           AutoString& source);
903 
BuildCStyleTypeSource(JSContext * cx,JSObject * typeObj_,AutoString & source)904 static void BuildCStyleTypeSource(JSContext* cx, JSObject* typeObj_,
905                                   AutoString& source) {
906   RootedObject typeObj(cx, typeObj_);
907 
908   MOZ_ASSERT(CType::IsCType(typeObj));
909 
910   switch (CType::GetTypeCode(typeObj)) {
911 #define BUILD_SOURCE(name, fromType, ffiType) \
912   case TYPE_##name:                           \
913     AppendString(cx, source, #name);          \
914     break;
915     CTYPES_FOR_EACH_TYPE(BUILD_SOURCE)
916 #undef BUILD_SOURCE
917     case TYPE_void_t:
918       AppendString(cx, source, "void");
919       break;
920     case TYPE_pointer: {
921       unsigned ptrCount = 0;
922       TypeCode type;
923       RootedObject baseTypeObj(cx, typeObj);
924       do {
925         baseTypeObj = PointerType::GetBaseType(baseTypeObj);
926         ptrCount++;
927         type = CType::GetTypeCode(baseTypeObj);
928       } while (type == TYPE_pointer || type == TYPE_array);
929       if (type == TYPE_function) {
930         BuildCStyleFunctionTypeSource(cx, baseTypeObj, nullptr, ptrCount,
931                                       source);
932         break;
933       }
934       BuildCStyleTypeSource(cx, baseTypeObj, source);
935       AppendChars(source, '*', ptrCount);
936       break;
937     }
938     case TYPE_struct: {
939       RootedString name(cx, CType::GetName(cx, typeObj));
940       AppendString(cx, source, "struct ");
941       AppendString(cx, source, name);
942       break;
943     }
944     case TYPE_function:
945       BuildCStyleFunctionTypeSource(cx, typeObj, nullptr, 0, source);
946       break;
947     case TYPE_array:
948       MOZ_CRASH("TYPE_array shouldn't appear in function type");
949   }
950 }
951 
BuildCStyleFunctionTypeSource(JSContext * cx,HandleObject typeObj,HandleString nameStr,unsigned ptrCount,AutoString & source)952 static void BuildCStyleFunctionTypeSource(JSContext* cx, HandleObject typeObj,
953                                           HandleString nameStr,
954                                           unsigned ptrCount,
955                                           AutoString& source) {
956   MOZ_ASSERT(CType::IsCType(typeObj));
957 
958   FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
959   BuildCStyleTypeSource(cx, fninfo->mReturnType, source);
960   AppendString(cx, source, " ");
961   if (nameStr) {
962     MOZ_ASSERT(ptrCount == 0);
963     AppendString(cx, source, nameStr);
964   } else if (ptrCount) {
965     AppendString(cx, source, "(");
966     AppendChars(source, '*', ptrCount);
967     AppendString(cx, source, ")");
968   }
969   AppendString(cx, source, "(");
970   if (fninfo->mArgTypes.length() > 0) {
971     for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
972       BuildCStyleTypeSource(cx, fninfo->mArgTypes[i], source);
973       if (i != fninfo->mArgTypes.length() - 1 || fninfo->mIsVariadic) {
974         AppendString(cx, source, ", ");
975       }
976     }
977     if (fninfo->mIsVariadic) {
978       AppendString(cx, source, "...");
979     }
980   }
981   AppendString(cx, source, ")");
982 }
983 
BuildFunctionTypeSource(JSContext * cx,HandleObject funObj,AutoString & source)984 static void BuildFunctionTypeSource(JSContext* cx, HandleObject funObj,
985                                     AutoString& source) {
986   MOZ_ASSERT(CData::IsCData(funObj) || CType::IsCType(funObj));
987 
988   if (CData::IsCData(funObj)) {
989     Value slot = JS::GetReservedSlot(funObj, SLOT_REFERENT);
990     if (!slot.isUndefined() && Library::IsLibrary(&slot.toObject())) {
991       slot = JS::GetReservedSlot(funObj, SLOT_FUNNAME);
992       MOZ_ASSERT(!slot.isUndefined());
993       RootedObject typeObj(cx, CData::GetCType(funObj));
994       RootedObject baseTypeObj(cx, PointerType::GetBaseType(typeObj));
995       RootedString nameStr(cx, slot.toString());
996       BuildCStyleFunctionTypeSource(cx, baseTypeObj, nameStr, 0, source);
997       return;
998     }
999   }
1000 
1001   RootedValue funVal(cx, ObjectValue(*funObj));
1002   RootedString funcStr(cx, JS_ValueToSource(cx, funVal));
1003   if (!funcStr) {
1004     JS_ClearPendingException(cx);
1005     AppendString(cx, source, "<<error converting function to string>>");
1006     return;
1007   }
1008   AppendString(cx, source, funcStr);
1009 }
1010 
1011 enum class ConversionType {
1012   Argument = 0,
1013   Construct,
1014   Finalizer,
1015   Return,
1016   Setter
1017 };
1018 
BuildConversionPosition(JSContext * cx,ConversionType convType,HandleObject funObj,unsigned argIndex,AutoString & source)1019 static void BuildConversionPosition(JSContext* cx, ConversionType convType,
1020                                     HandleObject funObj, unsigned argIndex,
1021                                     AutoString& source) {
1022   switch (convType) {
1023     case ConversionType::Argument: {
1024       MOZ_ASSERT(funObj);
1025 
1026       AppendString(cx, source, " at argument ");
1027       AppendUInt(source, argIndex + 1);
1028       AppendString(cx, source, " of ");
1029       BuildFunctionTypeSource(cx, funObj, source);
1030       break;
1031     }
1032     case ConversionType::Finalizer:
1033       MOZ_ASSERT(funObj);
1034 
1035       AppendString(cx, source, " at argument 1 of ");
1036       BuildFunctionTypeSource(cx, funObj, source);
1037       break;
1038     case ConversionType::Return:
1039       MOZ_ASSERT(funObj);
1040 
1041       AppendString(cx, source, " at the return value of ");
1042       BuildFunctionTypeSource(cx, funObj, source);
1043       break;
1044     default:
1045       MOZ_ASSERT(!funObj);
1046       break;
1047   }
1048 }
1049 
GetFieldName(HandleObject structObj,unsigned fieldIndex)1050 static JSLinearString* GetFieldName(HandleObject structObj,
1051                                     unsigned fieldIndex) {
1052   const FieldInfoHash* fields = StructType::GetFieldInfo(structObj);
1053   for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
1054     if (r.front().value().mIndex == fieldIndex) {
1055       return (&r.front())->key();
1056     }
1057   }
1058   return nullptr;
1059 }
1060 
1061 static void BuildTypeSource(JSContext* cx, JSObject* typeObj_, bool makeShort,
1062                             AutoString& result);
1063 
TypeSourceForError(JSContext * cx,JSObject * typeObj)1064 static JS::UniqueChars TypeSourceForError(JSContext* cx, JSObject* typeObj) {
1065   AutoString source;
1066   BuildTypeSource(cx, typeObj, true, source);
1067   if (!source) {
1068     return nullptr;
1069   }
1070   return EncodeUTF8(cx, source);
1071 }
1072 
FunctionTypeSourceForError(JSContext * cx,HandleObject funObj)1073 static JS::UniqueChars FunctionTypeSourceForError(JSContext* cx,
1074                                                   HandleObject funObj) {
1075   AutoString funSource;
1076   BuildFunctionTypeSource(cx, funObj, funSource);
1077   if (!funSource) {
1078     return nullptr;
1079   }
1080   return EncodeUTF8(cx, funSource);
1081 }
1082 
ConversionPositionForError(JSContext * cx,ConversionType convType,HandleObject funObj,unsigned argIndex)1083 static JS::UniqueChars ConversionPositionForError(JSContext* cx,
1084                                                   ConversionType convType,
1085                                                   HandleObject funObj,
1086                                                   unsigned argIndex) {
1087   AutoString posSource;
1088   BuildConversionPosition(cx, convType, funObj, argIndex, posSource);
1089   if (!posSource) {
1090     return nullptr;
1091   }
1092   return EncodeUTF8(cx, posSource);
1093 }
1094 
1095 class IndexCString final {
1096   char indexStr[21];  // space for UINT64_MAX plus terminating null
1097   static_assert(sizeof(size_t) <= 8, "index array too small");
1098 
1099  public:
IndexCString(size_t index)1100   explicit IndexCString(size_t index) {
1101     SprintfLiteral(indexStr, "%zu", index);
1102   }
1103 
get() const1104   const char* get() const { return indexStr; }
1105 };
1106 
ConvError(JSContext * cx,const char * expectedStr,HandleValue actual,ConversionType convType,HandleObject funObj=nullptr,unsigned argIndex=0,HandleObject arrObj=nullptr,unsigned arrIndex=0)1107 static bool ConvError(JSContext* cx, const char* expectedStr,
1108                       HandleValue actual, ConversionType convType,
1109                       HandleObject funObj = nullptr, unsigned argIndex = 0,
1110                       HandleObject arrObj = nullptr, unsigned arrIndex = 0) {
1111   JS::UniqueChars valBytes;
1112   const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
1113   if (!valStr) {
1114     return false;
1115   }
1116 
1117   if (arrObj) {
1118     MOZ_ASSERT(CType::IsCType(arrObj));
1119 
1120     switch (CType::GetTypeCode(arrObj)) {
1121       case TYPE_array: {
1122         MOZ_ASSERT(!funObj);
1123 
1124         IndexCString indexStr(arrIndex);
1125 
1126         JS::UniqueChars arrStr = TypeSourceForError(cx, arrObj);
1127         if (!arrStr) {
1128           return false;
1129         }
1130 
1131         JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1132                                  CTYPESMSG_CONV_ERROR_ARRAY, valStr,
1133                                  indexStr.get(), arrStr.get());
1134         break;
1135       }
1136       case TYPE_struct: {
1137         RootedString name(cx, GetFieldName(arrObj, arrIndex));
1138         MOZ_ASSERT(name);
1139         JS::UniqueChars nameStr = JS_EncodeStringToUTF8(cx, name);
1140         if (!nameStr) {
1141           return false;
1142         }
1143 
1144         JS::UniqueChars structStr = TypeSourceForError(cx, arrObj);
1145         if (!structStr) {
1146           return false;
1147         }
1148 
1149         JS::UniqueChars posStr;
1150         if (funObj) {
1151           posStr = ConversionPositionForError(cx, convType, funObj, argIndex);
1152           if (!posStr) {
1153             return false;
1154           }
1155         }
1156 
1157         JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1158                                  CTYPESMSG_CONV_ERROR_STRUCT, valStr,
1159                                  nameStr.get(), expectedStr, structStr.get(),
1160                                  (posStr ? posStr.get() : ""));
1161         break;
1162       }
1163       default:
1164         MOZ_CRASH("invalid arrObj value");
1165     }
1166     return false;
1167   }
1168 
1169   switch (convType) {
1170     case ConversionType::Argument: {
1171       MOZ_ASSERT(funObj);
1172 
1173       IndexCString indexStr(argIndex + 1);
1174 
1175       JS::UniqueChars funStr = FunctionTypeSourceForError(cx, funObj);
1176       if (!funStr) {
1177         return false;
1178       }
1179 
1180       JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1181                                CTYPESMSG_CONV_ERROR_ARG, valStr, indexStr.get(),
1182                                funStr.get());
1183       break;
1184     }
1185     case ConversionType::Finalizer: {
1186       MOZ_ASSERT(funObj);
1187 
1188       JS::UniqueChars funStr = FunctionTypeSourceForError(cx, funObj);
1189       if (!funStr) {
1190         return false;
1191       }
1192 
1193       JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1194                                CTYPESMSG_CONV_ERROR_FIN, valStr, funStr.get());
1195       break;
1196     }
1197     case ConversionType::Return: {
1198       MOZ_ASSERT(funObj);
1199 
1200       JS::UniqueChars funStr = FunctionTypeSourceForError(cx, funObj);
1201       if (!funStr) {
1202         return false;
1203       }
1204 
1205       JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1206                                CTYPESMSG_CONV_ERROR_RET, valStr, funStr.get());
1207       break;
1208     }
1209     case ConversionType::Setter:
1210     case ConversionType::Construct:
1211       MOZ_ASSERT(!funObj);
1212 
1213       JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1214                                CTYPESMSG_CONV_ERROR_SET, valStr, expectedStr);
1215       break;
1216   }
1217 
1218   return false;
1219 }
1220 
ConvError(JSContext * cx,HandleObject expectedType,HandleValue actual,ConversionType convType,HandleObject funObj=nullptr,unsigned argIndex=0,HandleObject arrObj=nullptr,unsigned arrIndex=0)1221 static bool ConvError(JSContext* cx, HandleObject expectedType,
1222                       HandleValue actual, ConversionType convType,
1223                       HandleObject funObj = nullptr, unsigned argIndex = 0,
1224                       HandleObject arrObj = nullptr, unsigned arrIndex = 0) {
1225   MOZ_ASSERT(CType::IsCType(expectedType));
1226 
1227   JS::UniqueChars expectedStr = TypeSourceForError(cx, expectedType);
1228   if (!expectedStr) {
1229     return false;
1230   }
1231 
1232   return ConvError(cx, expectedStr.get(), actual, convType, funObj, argIndex,
1233                    arrObj, arrIndex);
1234 }
1235 
ArgumentConvError(JSContext * cx,HandleValue actual,const char * funStr,unsigned argIndex)1236 static bool ArgumentConvError(JSContext* cx, HandleValue actual,
1237                               const char* funStr, unsigned argIndex) {
1238   MOZ_ASSERT(JS::StringIsASCII(funStr));
1239 
1240   JS::UniqueChars valBytes;
1241   const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
1242   if (!valStr) {
1243     return false;
1244   }
1245 
1246   IndexCString indexStr(argIndex + 1);
1247 
1248   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1249                            CTYPESMSG_CONV_ERROR_ARG, valStr, indexStr.get(),
1250                            funStr);
1251   return false;
1252 }
1253 
ArgumentLengthError(JSContext * cx,const char * fun,const char * count,const char * s)1254 static bool ArgumentLengthError(JSContext* cx, const char* fun,
1255                                 const char* count, const char* s) {
1256   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1257                            CTYPESMSG_WRONG_ARG_LENGTH, fun, count, s);
1258   return false;
1259 }
1260 
ArrayLengthMismatch(JSContext * cx,size_t expectedLength,HandleObject arrObj,size_t actualLength,HandleValue actual,ConversionType convType)1261 static bool ArrayLengthMismatch(JSContext* cx, size_t expectedLength,
1262                                 HandleObject arrObj, size_t actualLength,
1263                                 HandleValue actual, ConversionType convType) {
1264   MOZ_ASSERT(arrObj && CType::IsCType(arrObj));
1265 
1266   JS::UniqueChars valBytes;
1267   const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
1268   if (!valStr) {
1269     return false;
1270   }
1271 
1272   IndexCString expectedLengthStr(expectedLength);
1273   IndexCString actualLengthStr(actualLength);
1274 
1275   JS::UniqueChars arrStr = TypeSourceForError(cx, arrObj);
1276   if (!arrStr) {
1277     return false;
1278   }
1279 
1280   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1281                            CTYPESMSG_ARRAY_MISMATCH, valStr, arrStr.get(),
1282                            expectedLengthStr.get(), actualLengthStr.get());
1283   return false;
1284 }
1285 
ArrayLengthOverflow(JSContext * cx,unsigned expectedLength,HandleObject arrObj,unsigned actualLength,HandleValue actual,ConversionType convType)1286 static bool ArrayLengthOverflow(JSContext* cx, unsigned expectedLength,
1287                                 HandleObject arrObj, unsigned actualLength,
1288                                 HandleValue actual, ConversionType convType) {
1289   MOZ_ASSERT(arrObj && CType::IsCType(arrObj));
1290 
1291   JS::UniqueChars valBytes;
1292   const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
1293   if (!valStr) {
1294     return false;
1295   }
1296 
1297   IndexCString expectedLengthStr(expectedLength);
1298   IndexCString actualLengthStr(actualLength);
1299 
1300   JS::UniqueChars arrStr = TypeSourceForError(cx, arrObj);
1301   if (!arrStr) {
1302     return false;
1303   }
1304 
1305   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1306                            CTYPESMSG_ARRAY_OVERFLOW, valStr, arrStr.get(),
1307                            expectedLengthStr.get(), actualLengthStr.get());
1308   return false;
1309 }
1310 
ArgumentRangeMismatch(JSContext * cx,const char * func,const char * range)1311 static bool ArgumentRangeMismatch(JSContext* cx, const char* func,
1312                                   const char* range) {
1313   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1314                             CTYPESMSG_ARG_RANGE_MISMATCH, func, range);
1315   return false;
1316 }
1317 
ArgumentTypeMismatch(JSContext * cx,const char * arg,const char * func,const char * type)1318 static bool ArgumentTypeMismatch(JSContext* cx, const char* arg,
1319                                  const char* func, const char* type) {
1320   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1321                             CTYPESMSG_ARG_TYPE_MISMATCH, arg, func, type);
1322   return false;
1323 }
1324 
CannotConstructError(JSContext * cx,const char * type)1325 static bool CannotConstructError(JSContext* cx, const char* type) {
1326   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1327                             CTYPESMSG_CANNOT_CONSTRUCT, type);
1328   return false;
1329 }
1330 
DuplicateFieldError(JSContext * cx,Handle<JSLinearString * > name)1331 static bool DuplicateFieldError(JSContext* cx, Handle<JSLinearString*> name) {
1332   JS::UniqueChars nameStr = JS_EncodeStringToUTF8(cx, name);
1333   if (!nameStr) {
1334     return false;
1335   }
1336 
1337   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1338                            CTYPESMSG_DUPLICATE_FIELD, nameStr.get());
1339   return false;
1340 }
1341 
EmptyFinalizerCallError(JSContext * cx,const char * funName)1342 static bool EmptyFinalizerCallError(JSContext* cx, const char* funName) {
1343   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1344                             CTYPESMSG_EMPTY_FIN_CALL, funName);
1345   return false;
1346 }
1347 
EmptyFinalizerError(JSContext * cx,ConversionType convType,HandleObject funObj=nullptr,unsigned argIndex=0)1348 static bool EmptyFinalizerError(JSContext* cx, ConversionType convType,
1349                                 HandleObject funObj = nullptr,
1350                                 unsigned argIndex = 0) {
1351   JS::UniqueChars posStr;
1352   if (funObj) {
1353     posStr = ConversionPositionForError(cx, convType, funObj, argIndex);
1354     if (!posStr) {
1355       return false;
1356     }
1357   }
1358 
1359   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, CTYPESMSG_EMPTY_FIN,
1360                            (posStr ? posStr.get() : ""));
1361   return false;
1362 }
1363 
FieldCountMismatch(JSContext * cx,unsigned expectedCount,HandleObject structObj,unsigned actualCount,HandleValue actual,ConversionType convType,HandleObject funObj=nullptr,unsigned argIndex=0)1364 static bool FieldCountMismatch(JSContext* cx, unsigned expectedCount,
1365                                HandleObject structObj, unsigned actualCount,
1366                                HandleValue actual, ConversionType convType,
1367                                HandleObject funObj = nullptr,
1368                                unsigned argIndex = 0) {
1369   MOZ_ASSERT(structObj && CType::IsCType(structObj));
1370 
1371   JS::UniqueChars valBytes;
1372   const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
1373   if (!valStr) {
1374     return false;
1375   }
1376 
1377   JS::UniqueChars structStr = TypeSourceForError(cx, structObj);
1378   if (!structStr) {
1379     return false;
1380   }
1381 
1382   IndexCString expectedCountStr(expectedCount);
1383   IndexCString actualCountStr(actualCount);
1384 
1385   JS::UniqueChars posStr;
1386   if (funObj) {
1387     posStr = ConversionPositionForError(cx, convType, funObj, argIndex);
1388     if (!posStr) {
1389       return false;
1390     }
1391   }
1392 
1393   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1394                            CTYPESMSG_FIELD_MISMATCH, valStr, structStr.get(),
1395                            expectedCountStr.get(), actualCountStr.get(),
1396                            (posStr ? posStr.get() : ""));
1397   return false;
1398 }
1399 
FieldDescriptorCountError(JSContext * cx,HandleValue typeVal,size_t length)1400 static bool FieldDescriptorCountError(JSContext* cx, HandleValue typeVal,
1401                                       size_t length) {
1402   JS::UniqueChars valBytes;
1403   const char* valStr = CTypesToSourceForError(cx, typeVal, valBytes);
1404   if (!valStr) {
1405     return false;
1406   }
1407 
1408   IndexCString lengthStr(length);
1409 
1410   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1411                            CTYPESMSG_FIELD_DESC_COUNT, valStr, lengthStr.get());
1412   return false;
1413 }
1414 
FieldDescriptorNameError(JSContext * cx,HandleId id)1415 static bool FieldDescriptorNameError(JSContext* cx, HandleId id) {
1416   JS::UniqueChars idBytes;
1417   RootedValue idVal(cx, IdToValue(id));
1418   const char* propStr = CTypesToSourceForError(cx, idVal, idBytes);
1419   if (!propStr) {
1420     return false;
1421   }
1422 
1423   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1424                            CTYPESMSG_FIELD_DESC_NAME, propStr);
1425   return false;
1426 }
1427 
FieldDescriptorSizeError(JSContext * cx,HandleObject typeObj,HandleId id)1428 static bool FieldDescriptorSizeError(JSContext* cx, HandleObject typeObj,
1429                                      HandleId id) {
1430   RootedValue typeVal(cx, ObjectValue(*typeObj));
1431   JS::UniqueChars typeBytes;
1432   const char* typeStr = CTypesToSourceForError(cx, typeVal, typeBytes);
1433   if (!typeStr) {
1434     return false;
1435   }
1436 
1437   RootedString idStr(cx, IdToString(cx, id));
1438   JS::UniqueChars propStr = JS_EncodeStringToUTF8(cx, idStr);
1439   if (!propStr) {
1440     return false;
1441   }
1442 
1443   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1444                            CTYPESMSG_FIELD_DESC_SIZE, typeStr, propStr.get());
1445   return false;
1446 }
1447 
FieldDescriptorNameTypeError(JSContext * cx,HandleValue typeVal)1448 static bool FieldDescriptorNameTypeError(JSContext* cx, HandleValue typeVal) {
1449   JS::UniqueChars valBytes;
1450   const char* valStr = CTypesToSourceForError(cx, typeVal, valBytes);
1451   if (!valStr) {
1452     return false;
1453   }
1454 
1455   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1456                            CTYPESMSG_FIELD_DESC_NAMETYPE, valStr);
1457   return false;
1458 }
1459 
FieldDescriptorTypeError(JSContext * cx,HandleValue poroVal,HandleId id)1460 static bool FieldDescriptorTypeError(JSContext* cx, HandleValue poroVal,
1461                                      HandleId id) {
1462   JS::UniqueChars typeBytes;
1463   const char* typeStr = CTypesToSourceForError(cx, poroVal, typeBytes);
1464   if (!typeStr) {
1465     return false;
1466   }
1467 
1468   RootedString idStr(cx, IdToString(cx, id));
1469   JS::UniqueChars propStr = JS_EncodeStringToUTF8(cx, idStr);
1470   if (!propStr) {
1471     return false;
1472   }
1473 
1474   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1475                            CTYPESMSG_FIELD_DESC_TYPE, typeStr, propStr.get());
1476   return false;
1477 }
1478 
FieldMissingError(JSContext * cx,JSObject * typeObj,JSLinearString * name_)1479 static bool FieldMissingError(JSContext* cx, JSObject* typeObj,
1480                               JSLinearString* name_) {
1481   JS::UniqueChars typeBytes;
1482   RootedString name(cx, name_);
1483   RootedValue typeVal(cx, ObjectValue(*typeObj));
1484   const char* typeStr = CTypesToSourceForError(cx, typeVal, typeBytes);
1485   if (!typeStr) {
1486     return false;
1487   }
1488 
1489   JS::UniqueChars nameStr = JS_EncodeStringToUTF8(cx, name);
1490   if (!nameStr) {
1491     return false;
1492   }
1493 
1494   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1495                            CTYPESMSG_FIELD_MISSING, typeStr, nameStr.get());
1496   return false;
1497 }
1498 
FinalizerSizeError(JSContext * cx,HandleObject funObj,HandleValue actual)1499 static bool FinalizerSizeError(JSContext* cx, HandleObject funObj,
1500                                HandleValue actual) {
1501   MOZ_ASSERT(CType::IsCType(funObj));
1502 
1503   JS::UniqueChars valBytes;
1504   const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
1505   if (!valStr) {
1506     return false;
1507   }
1508 
1509   JS::UniqueChars funStr = FunctionTypeSourceForError(cx, funObj);
1510   if (!funStr) {
1511     return false;
1512   }
1513 
1514   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1515                            CTYPESMSG_FIN_SIZE_ERROR, funStr.get(), valStr);
1516   return false;
1517 }
1518 
FunctionArgumentLengthMismatch(JSContext * cx,unsigned expectedCount,unsigned actualCount,HandleObject funObj,HandleObject typeObj,bool isVariadic)1519 static bool FunctionArgumentLengthMismatch(
1520     JSContext* cx, unsigned expectedCount, unsigned actualCount,
1521     HandleObject funObj, HandleObject typeObj, bool isVariadic) {
1522   JS::UniqueChars funStr;
1523   Value slot = JS::GetReservedSlot(funObj, SLOT_REFERENT);
1524   if (!slot.isUndefined() && Library::IsLibrary(&slot.toObject())) {
1525     funStr = FunctionTypeSourceForError(cx, funObj);
1526   } else {
1527     funStr = FunctionTypeSourceForError(cx, typeObj);
1528   }
1529   if (!funStr) {
1530     return false;
1531   }
1532 
1533   IndexCString expectedCountStr(expectedCount);
1534   IndexCString actualCountStr(actualCount);
1535 
1536   const char* variadicStr = isVariadic ? " or more" : "";
1537 
1538   JS_ReportErrorNumberUTF8(
1539       cx, GetErrorMessage, nullptr, CTYPESMSG_ARG_COUNT_MISMATCH, funStr.get(),
1540       expectedCountStr.get(), variadicStr, actualCountStr.get());
1541   return false;
1542 }
1543 
FunctionArgumentTypeError(JSContext * cx,uint32_t index,HandleValue typeVal,const char * reason)1544 static bool FunctionArgumentTypeError(JSContext* cx, uint32_t index,
1545                                       HandleValue typeVal, const char* reason) {
1546   MOZ_ASSERT(JS::StringIsASCII(reason));
1547 
1548   JS::UniqueChars valBytes;
1549   const char* valStr = CTypesToSourceForError(cx, typeVal, valBytes);
1550   if (!valStr) {
1551     return false;
1552   }
1553 
1554   IndexCString indexStr(index + 1);
1555 
1556   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1557                            CTYPESMSG_ARG_TYPE_ERROR, indexStr.get(), reason,
1558                            valStr);
1559   return false;
1560 }
1561 
FunctionReturnTypeError(JSContext * cx,HandleValue type,const char * reason)1562 static bool FunctionReturnTypeError(JSContext* cx, HandleValue type,
1563                                     const char* reason) {
1564   MOZ_ASSERT(JS::StringIsASCII(reason));
1565 
1566   JS::UniqueChars valBytes;
1567   const char* valStr = CTypesToSourceForError(cx, type, valBytes);
1568   if (!valStr) {
1569     return false;
1570   }
1571 
1572   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1573                            CTYPESMSG_RET_TYPE_ERROR, reason, valStr);
1574   return false;
1575 }
1576 
IncompatibleCallee(JSContext * cx,const char * funName,HandleObject actualObj)1577 static bool IncompatibleCallee(JSContext* cx, const char* funName,
1578                                HandleObject actualObj) {
1579   MOZ_ASSERT(JS::StringIsASCII(funName));
1580 
1581   JS::UniqueChars valBytes;
1582   RootedValue val(cx, ObjectValue(*actualObj));
1583   const char* valStr = CTypesToSourceForError(cx, val, valBytes);
1584   if (!valStr) {
1585     return false;
1586   }
1587 
1588   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1589                            CTYPESMSG_INCOMPATIBLE_CALLEE, funName, valStr);
1590   return false;
1591 }
1592 
IncompatibleThisProto(JSContext * cx,const char * funName,const char * actualType)1593 static bool IncompatibleThisProto(JSContext* cx, const char* funName,
1594                                   const char* actualType) {
1595   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1596                             CTYPESMSG_INCOMPATIBLE_THIS, funName, actualType);
1597   return false;
1598 }
1599 
IncompatibleThisProto(JSContext * cx,const char * funName,HandleValue actualVal)1600 static bool IncompatibleThisProto(JSContext* cx, const char* funName,
1601                                   HandleValue actualVal) {
1602   MOZ_ASSERT(JS::StringIsASCII(funName));
1603 
1604   JS::UniqueChars valBytes;
1605   const char* valStr = CTypesToSourceForError(cx, actualVal, valBytes);
1606   if (!valStr) {
1607     return false;
1608   }
1609 
1610   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1611                            CTYPESMSG_INCOMPATIBLE_THIS_VAL, funName,
1612                            "incompatible object", valStr);
1613   return false;
1614 }
1615 
IncompatibleThisType(JSContext * cx,const char * funName,const char * actualType)1616 static bool IncompatibleThisType(JSContext* cx, const char* funName,
1617                                  const char* actualType) {
1618   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1619                             CTYPESMSG_INCOMPATIBLE_THIS_TYPE, funName,
1620                             actualType);
1621   return false;
1622 }
1623 
IncompatibleThisType(JSContext * cx,const char * funName,const char * actualType,HandleValue actualVal)1624 static bool IncompatibleThisType(JSContext* cx, const char* funName,
1625                                  const char* actualType,
1626                                  HandleValue actualVal) {
1627   MOZ_ASSERT(JS::StringIsASCII(funName));
1628   MOZ_ASSERT(JS::StringIsASCII(actualType));
1629 
1630   JS::UniqueChars valBytes;
1631   const char* valStr = CTypesToSourceForError(cx, actualVal, valBytes);
1632   if (!valStr) {
1633     return false;
1634   }
1635 
1636   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1637                            CTYPESMSG_INCOMPATIBLE_THIS_VAL, funName, actualType,
1638                            valStr);
1639   return false;
1640 }
1641 
InvalidIndexError(JSContext * cx,HandleValue val)1642 static bool InvalidIndexError(JSContext* cx, HandleValue val) {
1643   JS::UniqueChars idBytes;
1644   const char* indexStr = CTypesToSourceForError(cx, val, idBytes);
1645   if (!indexStr) {
1646     return false;
1647   }
1648 
1649   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1650                            CTYPESMSG_INVALID_INDEX, indexStr);
1651   return false;
1652 }
1653 
InvalidIndexError(JSContext * cx,HandleId id)1654 static bool InvalidIndexError(JSContext* cx, HandleId id) {
1655   RootedValue idVal(cx, IdToValue(id));
1656   return InvalidIndexError(cx, idVal);
1657 }
1658 
InvalidIndexRangeError(JSContext * cx,size_t index,size_t length)1659 static bool InvalidIndexRangeError(JSContext* cx, size_t index, size_t length) {
1660   IndexCString indexStr(index);
1661   IndexCString lengthStr(length);
1662 
1663   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1664                             CTYPESMSG_INVALID_RANGE, indexStr.get(),
1665                             lengthStr.get());
1666   return false;
1667 }
1668 
NonPrimitiveError(JSContext * cx,HandleObject typeObj)1669 static bool NonPrimitiveError(JSContext* cx, HandleObject typeObj) {
1670   MOZ_ASSERT(CType::IsCType(typeObj));
1671 
1672   JS::UniqueChars typeStr = TypeSourceForError(cx, typeObj);
1673   if (!typeStr) {
1674     return false;
1675   }
1676 
1677   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1678                            CTYPESMSG_NON_PRIMITIVE, typeStr.get());
1679   return false;
1680 }
1681 
NonStringBaseError(JSContext * cx,HandleValue thisVal)1682 static bool NonStringBaseError(JSContext* cx, HandleValue thisVal) {
1683   JS::UniqueChars valBytes;
1684   const char* valStr = CTypesToSourceForError(cx, thisVal, valBytes);
1685   if (!valStr) {
1686     return false;
1687   }
1688 
1689   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1690                            CTYPESMSG_NON_STRING_BASE, valStr);
1691   return false;
1692 }
1693 
NonTypedArrayBaseError(JSContext * cx,HandleValue thisVal)1694 static bool NonTypedArrayBaseError(JSContext* cx, HandleValue thisVal) {
1695   JS::UniqueChars valBytes;
1696   const char* valStr = CTypesToSourceForError(cx, thisVal, valBytes);
1697   if (!valStr) {
1698     return false;
1699   }
1700 
1701   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1702                            CTYPESMSG_NON_TYPEDARRAY_BASE, valStr);
1703   return false;
1704 }
1705 
NullPointerError(JSContext * cx,const char * action,HandleObject obj)1706 static bool NullPointerError(JSContext* cx, const char* action,
1707                              HandleObject obj) {
1708   MOZ_ASSERT(JS::StringIsASCII(action));
1709 
1710   JS::UniqueChars valBytes;
1711   RootedValue val(cx, ObjectValue(*obj));
1712   const char* valStr = CTypesToSourceForError(cx, val, valBytes);
1713   if (!valStr) {
1714     return false;
1715   }
1716 
1717   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, CTYPESMSG_NULL_POINTER,
1718                            action, valStr);
1719   return false;
1720 }
1721 
PropNameNonStringError(JSContext * cx,HandleId id,HandleValue actual,ConversionType convType,HandleObject funObj=nullptr,unsigned argIndex=0)1722 static bool PropNameNonStringError(JSContext* cx, HandleId id,
1723                                    HandleValue actual, ConversionType convType,
1724                                    HandleObject funObj = nullptr,
1725                                    unsigned argIndex = 0) {
1726   JS::UniqueChars valBytes;
1727   const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
1728   if (!valStr) {
1729     return false;
1730   }
1731 
1732   JS::UniqueChars idBytes;
1733   RootedValue idVal(cx, IdToValue(id));
1734   const char* propStr = CTypesToSourceForError(cx, idVal, idBytes);
1735   if (!propStr) {
1736     return false;
1737   }
1738 
1739   JS::UniqueChars posStr;
1740   if (funObj) {
1741     posStr = ConversionPositionForError(cx, convType, funObj, argIndex);
1742     if (!posStr) {
1743       return false;
1744     }
1745   }
1746 
1747   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1748                            CTYPESMSG_PROP_NONSTRING, propStr, valStr,
1749                            (posStr ? posStr.get() : ""));
1750   return false;
1751 }
1752 
SizeOverflow(JSContext * cx,const char * name,const char * limit)1753 static bool SizeOverflow(JSContext* cx, const char* name, const char* limit) {
1754   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1755                             CTYPESMSG_SIZE_OVERFLOW, name, limit);
1756   return false;
1757 }
1758 
TypeError(JSContext * cx,const char * expected,HandleValue actual)1759 static bool TypeError(JSContext* cx, const char* expected, HandleValue actual) {
1760   MOZ_ASSERT(JS::StringIsASCII(expected));
1761 
1762   JS::UniqueChars bytes;
1763   const char* src = CTypesToSourceForError(cx, actual, bytes);
1764   if (!src) {
1765     return false;
1766   }
1767 
1768   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, CTYPESMSG_TYPE_ERROR,
1769                            expected, src);
1770   return false;
1771 }
1772 
TypeOverflow(JSContext * cx,const char * expected,HandleValue actual)1773 static bool TypeOverflow(JSContext* cx, const char* expected,
1774                          HandleValue actual) {
1775   MOZ_ASSERT(JS::StringIsASCII(expected));
1776 
1777   JS::UniqueChars valBytes;
1778   const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
1779   if (!valStr) {
1780     return false;
1781   }
1782 
1783   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1784                            CTYPESMSG_TYPE_OVERFLOW, valStr, expected);
1785   return false;
1786 }
1787 
UndefinedSizeCastError(JSContext * cx,HandleObject targetTypeObj)1788 static bool UndefinedSizeCastError(JSContext* cx, HandleObject targetTypeObj) {
1789   JS::UniqueChars targetTypeStr = TypeSourceForError(cx, targetTypeObj);
1790   if (!targetTypeStr) {
1791     return false;
1792   }
1793 
1794   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1795                            CTYPESMSG_UNDEFINED_SIZE_CAST, targetTypeStr.get());
1796   return false;
1797 }
1798 
SizeMismatchCastError(JSContext * cx,HandleObject sourceTypeObj,HandleObject targetTypeObj,size_t sourceSize,size_t targetSize)1799 static bool SizeMismatchCastError(JSContext* cx, HandleObject sourceTypeObj,
1800                                   HandleObject targetTypeObj, size_t sourceSize,
1801                                   size_t targetSize) {
1802   JS::UniqueChars sourceTypeStr = TypeSourceForError(cx, sourceTypeObj);
1803   if (!sourceTypeStr) {
1804     return false;
1805   }
1806 
1807   JS::UniqueChars targetTypeStr = TypeSourceForError(cx, targetTypeObj);
1808   if (!targetTypeStr) {
1809     return false;
1810   }
1811 
1812   IndexCString sourceSizeStr(sourceSize);
1813   IndexCString targetSizeStr(targetSize);
1814 
1815   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1816                            CTYPESMSG_SIZE_MISMATCH_CAST, targetTypeStr.get(),
1817                            sourceTypeStr.get(), targetSizeStr.get(),
1818                            sourceSizeStr.get());
1819   return false;
1820 }
1821 
UndefinedSizePointerError(JSContext * cx,const char * action,HandleObject obj)1822 static bool UndefinedSizePointerError(JSContext* cx, const char* action,
1823                                       HandleObject obj) {
1824   MOZ_ASSERT(JS::StringIsASCII(action));
1825 
1826   JS::UniqueChars valBytes;
1827   RootedValue val(cx, ObjectValue(*obj));
1828   const char* valStr = CTypesToSourceForError(cx, val, valBytes);
1829   if (!valStr) {
1830     return false;
1831   }
1832 
1833   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1834                            CTYPESMSG_UNDEFINED_SIZE, action, valStr);
1835   return false;
1836 }
1837 
VariadicArgumentTypeError(JSContext * cx,uint32_t index,HandleValue actual)1838 static bool VariadicArgumentTypeError(JSContext* cx, uint32_t index,
1839                                       HandleValue actual) {
1840   JS::UniqueChars valBytes;
1841   const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
1842   if (!valStr) {
1843     return false;
1844   }
1845 
1846   IndexCString indexStr(index + 1);
1847 
1848   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1849                            CTYPESMSG_VARG_TYPE_ERROR, indexStr.get(), valStr);
1850   return false;
1851 }
1852 
GetThisObject(JSContext * cx,const CallArgs & args,const char * msg)1853 [[nodiscard]] JSObject* GetThisObject(JSContext* cx, const CallArgs& args,
1854                                       const char* msg) {
1855   if (!args.thisv().isObject()) {
1856     IncompatibleThisProto(cx, msg, args.thisv());
1857     return nullptr;
1858   }
1859 
1860   return &args.thisv().toObject();
1861 }
1862 
DefineToStringTag(JSContext * cx,HandleObject obj,const char * toStringTag)1863 static bool DefineToStringTag(JSContext* cx, HandleObject obj,
1864                               const char* toStringTag) {
1865   RootedString toStringTagStr(cx, JS_NewStringCopyZ(cx, toStringTag));
1866   if (!toStringTagStr) {
1867     return false;
1868   }
1869 
1870   RootedId toStringTagId(cx, SYMBOL_TO_JSID(JS::GetWellKnownSymbol(
1871                                  cx, JS::SymbolCode::toStringTag)));
1872   return JS_DefinePropertyById(cx, obj, toStringTagId, toStringTagStr,
1873                                JSPROP_READONLY);
1874 }
1875 
InitCTypeClass(JSContext * cx,HandleObject ctypesObj)1876 static JSObject* InitCTypeClass(JSContext* cx, HandleObject ctypesObj) {
1877   JSFunction* fun = JS_DefineFunction(cx, ctypesObj, "CType", ConstructAbstract,
1878                                       0, CTYPESCTOR_FLAGS);
1879   if (!fun) {
1880     return nullptr;
1881   }
1882 
1883   RootedObject ctor(cx, JS_GetFunctionObject(fun));
1884   RootedObject fnproto(cx);
1885   if (!JS_GetPrototype(cx, ctor, &fnproto)) {
1886     return nullptr;
1887   }
1888   MOZ_ASSERT(ctor);
1889   MOZ_ASSERT(fnproto);
1890 
1891   // Set up ctypes.CType.prototype.
1892   RootedObject prototype(
1893       cx, JS_NewObjectWithGivenProto(cx, &sCTypeProtoClass, fnproto));
1894   if (!prototype) {
1895     return nullptr;
1896   }
1897 
1898   if (!JS_DefineProperty(cx, ctor, "prototype", prototype,
1899                          JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
1900     return nullptr;
1901 
1902   if (!JS_DefineProperty(cx, prototype, "constructor", ctor,
1903                          JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
1904     return nullptr;
1905 
1906   // Define properties and functions common to all CTypes.
1907   if (!JS_DefineProperties(cx, prototype, sCTypeProps) ||
1908       !JS_DefineFunctions(cx, prototype, sCTypeFunctions))
1909     return nullptr;
1910 
1911   if (!DefineToStringTag(cx, prototype, "CType")) {
1912     return nullptr;
1913   }
1914 
1915   if (!JS_FreezeObject(cx, ctor) || !JS_FreezeObject(cx, prototype)) {
1916     return nullptr;
1917   }
1918 
1919   return prototype;
1920 }
1921 
InitABIClass(JSContext * cx)1922 static JSObject* InitABIClass(JSContext* cx) {
1923   RootedObject obj(cx, JS_NewPlainObject(cx));
1924 
1925   if (!obj) {
1926     return nullptr;
1927   }
1928 
1929   if (!JS_DefineFunctions(cx, obj, sCABIFunctions)) {
1930     return nullptr;
1931   }
1932 
1933   if (!DefineToStringTag(cx, obj, "CABI")) {
1934     return nullptr;
1935   }
1936 
1937   return obj;
1938 }
1939 
InitCDataClass(JSContext * cx,HandleObject parent,HandleObject CTypeProto)1940 static JSObject* InitCDataClass(JSContext* cx, HandleObject parent,
1941                                 HandleObject CTypeProto) {
1942   JSFunction* fun = JS_DefineFunction(cx, parent, "CData", ConstructAbstract, 0,
1943                                       CTYPESCTOR_FLAGS);
1944   if (!fun) {
1945     return nullptr;
1946   }
1947 
1948   RootedObject ctor(cx, JS_GetFunctionObject(fun));
1949   MOZ_ASSERT(ctor);
1950 
1951   // Set up ctypes.CData.__proto__ === ctypes.CType.prototype.
1952   // (Note that 'ctypes.CData instanceof Function' is still true, thanks to the
1953   // prototype chain.)
1954   if (!JS_SetPrototype(cx, ctor, CTypeProto)) {
1955     return nullptr;
1956   }
1957 
1958   // Set up ctypes.CData.prototype.
1959   RootedObject prototype(cx, JS_NewObject(cx, &sCDataProtoClass));
1960   if (!prototype) {
1961     return nullptr;
1962   }
1963 
1964   if (!JS_DefineProperty(cx, ctor, "prototype", prototype,
1965                          JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
1966     return nullptr;
1967 
1968   if (!JS_DefineProperty(cx, prototype, "constructor", ctor,
1969                          JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
1970     return nullptr;
1971 
1972   // Define properties and functions common to all CDatas.
1973   if (!JS_DefineProperties(cx, prototype, sCDataProps) ||
1974       !JS_DefineFunctions(cx, prototype, sCDataFunctions))
1975     return nullptr;
1976 
1977   if (!DefineToStringTag(cx, prototype, "CData")) {
1978     return nullptr;
1979   }
1980 
1981   if (  // !JS_FreezeObject(cx, prototype) || // XXX fixme - see bug 541212!
1982       !JS_FreezeObject(cx, ctor))
1983     return nullptr;
1984 
1985   return prototype;
1986 }
1987 
DefineABIConstant(JSContext * cx,HandleObject ctypesObj,const char * name,ABICode code,HandleObject prototype)1988 static bool DefineABIConstant(JSContext* cx, HandleObject ctypesObj,
1989                               const char* name, ABICode code,
1990                               HandleObject prototype) {
1991   RootedObject obj(cx, JS_NewObjectWithGivenProto(cx, &sCABIClass, prototype));
1992   if (!obj) {
1993     return false;
1994   }
1995   JS_SetReservedSlot(obj, SLOT_ABICODE, Int32Value(code));
1996 
1997   if (!JS_FreezeObject(cx, obj)) {
1998     return false;
1999   }
2000 
2001   return JS_DefineProperty(
2002       cx, ctypesObj, name, obj,
2003       JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
2004 }
2005 
2006 // Set up a single type constructor for
2007 // ctypes.{Pointer,Array,Struct,Function}Type.
InitTypeConstructor(JSContext * cx,HandleObject parent,HandleObject CTypeProto,HandleObject CDataProto,const JSFunctionSpec spec,const JSFunctionSpec * fns,const JSPropertySpec * props,const JSFunctionSpec * instanceFns,const JSPropertySpec * instanceProps,MutableHandleObject typeProto,MutableHandleObject dataProto)2008 static bool InitTypeConstructor(
2009     JSContext* cx, HandleObject parent, HandleObject CTypeProto,
2010     HandleObject CDataProto, const JSFunctionSpec spec,
2011     const JSFunctionSpec* fns, const JSPropertySpec* props,
2012     const JSFunctionSpec* instanceFns, const JSPropertySpec* instanceProps,
2013     MutableHandleObject typeProto, MutableHandleObject dataProto) {
2014   JSFunction* fun = js::DefineFunctionWithReserved(
2015       cx, parent, spec.name.string(), spec.call.op, spec.nargs, spec.flags);
2016   if (!fun) {
2017     return false;
2018   }
2019 
2020   RootedObject obj(cx, JS_GetFunctionObject(fun));
2021   if (!obj) {
2022     return false;
2023   }
2024 
2025   // Set up the .prototype and .prototype.constructor properties.
2026   typeProto.set(JS_NewObjectWithGivenProto(cx, &sCTypeProtoClass, CTypeProto));
2027   if (!typeProto) {
2028     return false;
2029   }
2030 
2031   // Define property before proceeding, for GC safety.
2032   if (!JS_DefineProperty(cx, obj, "prototype", typeProto,
2033                          JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
2034     return false;
2035 
2036   if (fns && !JS_DefineFunctions(cx, typeProto, fns)) {
2037     return false;
2038   }
2039 
2040   if (!JS_DefineProperties(cx, typeProto, props)) {
2041     return false;
2042   }
2043 
2044   if (!JS_DefineProperty(cx, typeProto, "constructor", obj,
2045                          JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
2046     return false;
2047 
2048   // Stash ctypes.{Pointer,Array,Struct}Type.prototype on a reserved slot of
2049   // the type constructor, for faster lookup.
2050   js::SetFunctionNativeReserved(obj, SLOT_FN_CTORPROTO,
2051                                 ObjectValue(*typeProto));
2052 
2053   // Create an object to serve as the common ancestor for all CData objects
2054   // created from the given type constructor. This has ctypes.CData.prototype
2055   // as its prototype, such that it inherits the properties and functions
2056   // common to all CDatas.
2057   dataProto.set(JS_NewObjectWithGivenProto(cx, &sCDataProtoClass, CDataProto));
2058   if (!dataProto) {
2059     return false;
2060   }
2061 
2062   // Define functions and properties on the 'dataProto' object that are common
2063   // to all CData objects created from this type constructor. (These will
2064   // become functions and properties on CData objects created from this type.)
2065   if (instanceFns && !JS_DefineFunctions(cx, dataProto, instanceFns)) {
2066     return false;
2067   }
2068 
2069   if (instanceProps && !JS_DefineProperties(cx, dataProto, instanceProps)) {
2070     return false;
2071   }
2072 
2073   // Link the type prototype to the data prototype.
2074   JS_SetReservedSlot(typeProto, SLOT_OURDATAPROTO, ObjectValue(*dataProto));
2075 
2076   if (!JS_FreezeObject(cx, obj) ||
2077       // !JS_FreezeObject(cx, dataProto) || // XXX fixme - see bug 541212!
2078       !JS_FreezeObject(cx, typeProto))
2079     return false;
2080 
2081   return true;
2082 }
2083 
InitInt64Class(JSContext * cx,HandleObject parent,const JSClass * clasp,JSNative construct,const JSFunctionSpec * fs,const JSFunctionSpec * static_fs)2084 static JSObject* InitInt64Class(JSContext* cx, HandleObject parent,
2085                                 const JSClass* clasp, JSNative construct,
2086                                 const JSFunctionSpec* fs,
2087                                 const JSFunctionSpec* static_fs) {
2088   // Init type class and constructor
2089   RootedObject prototype(cx, JS_InitClass(cx, parent, nullptr, clasp, construct,
2090                                           0, nullptr, fs, nullptr, static_fs));
2091   if (!prototype) {
2092     return nullptr;
2093   }
2094 
2095   if (clasp == &sInt64ProtoClass) {
2096     if (!DefineToStringTag(cx, prototype, "Int64")) {
2097       return nullptr;
2098     }
2099   } else {
2100     MOZ_ASSERT(clasp == &sUInt64ProtoClass);
2101     if (!DefineToStringTag(cx, prototype, "UInt64")) {
2102       return nullptr;
2103     }
2104   }
2105 
2106   RootedObject ctor(cx, JS_GetConstructor(cx, prototype));
2107   if (!ctor) {
2108     return nullptr;
2109   }
2110 
2111   // Define the 'join' function as an extended native and stash
2112   // ctypes.{Int64,UInt64}.prototype in a reserved slot of the new function.
2113   JSNative native = (clasp == &sInt64ProtoClass) ? Int64::Join : UInt64::Join;
2114   JSFunction* fun = js::DefineFunctionWithReserved(cx, ctor, "join", native, 2,
2115                                                    CTYPESFN_FLAGS);
2116   if (!fun) {
2117     return nullptr;
2118   }
2119 
2120   js::SetFunctionNativeReserved(fun, SLOT_FN_INT64PROTO,
2121                                 ObjectValue(*prototype));
2122 
2123   if (!JS_FreezeObject(cx, ctor)) {
2124     return nullptr;
2125   }
2126   if (!JS_FreezeObject(cx, prototype)) {
2127     return nullptr;
2128   }
2129 
2130   return prototype;
2131 }
2132 
AttachProtos(JSObject * proto,HandleObjectVector protos)2133 static void AttachProtos(JSObject* proto, HandleObjectVector protos) {
2134   // For a given 'proto' of [[Class]] "CTypeProto", attach each of the 'protos'
2135   // to the appropriate CTypeProtoSlot. (SLOT_CTYPES is the last slot
2136   // of [[Class]] "CTypeProto" that we fill in this automated manner.)
2137   for (uint32_t i = 0; i <= SLOT_CTYPES; ++i) {
2138     JS_SetReservedSlot(proto, i, ObjectOrNullValue(protos[i]));
2139   }
2140 }
2141 
InitTypeClasses(JSContext * cx,HandleObject ctypesObj)2142 static bool InitTypeClasses(JSContext* cx, HandleObject ctypesObj) {
2143   // Initialize the ctypes.CType class. This acts as an abstract base class for
2144   // the various types, and provides the common API functions. It has:
2145   //   * [[Class]] "Function"
2146   //   * __proto__ === Function.prototype
2147   //   * A constructor that throws a TypeError. (You can't construct an
2148   //     abstract type!)
2149   //   * 'prototype' property:
2150   //     * [[Class]] "CTypeProto"
2151   //     * __proto__ === Function.prototype
2152   //     * A constructor that throws a TypeError. (You can't construct an
2153   //       abstract type instance!)
2154   //     * 'constructor' property === ctypes.CType
2155   //     * Provides properties and functions common to all CTypes.
2156   RootedObject CTypeProto(cx, InitCTypeClass(cx, ctypesObj));
2157   if (!CTypeProto) {
2158     return false;
2159   }
2160 
2161   // Initialize the ctypes.CData class. This acts as an abstract base class for
2162   // instances of the various types, and provides the common API functions.
2163   // It has:
2164   //   * [[Class]] "Function"
2165   //   * __proto__ === Function.prototype
2166   //   * A constructor that throws a TypeError. (You can't construct an
2167   //     abstract type instance!)
2168   //   * 'prototype' property:
2169   //     * [[Class]] "CDataProto"
2170   //     * 'constructor' property === ctypes.CData
2171   //     * Provides properties and functions common to all CDatas.
2172   RootedObject CDataProto(cx, InitCDataClass(cx, ctypesObj, CTypeProto));
2173   if (!CDataProto) {
2174     return false;
2175   }
2176 
2177   // Link CTypeProto to CDataProto.
2178   JS_SetReservedSlot(CTypeProto, SLOT_OURDATAPROTO, ObjectValue(*CDataProto));
2179 
2180   // Create and attach the special class constructors: ctypes.PointerType,
2181   // ctypes.ArrayType, ctypes.StructType, and ctypes.FunctionType.
2182   // Each of these constructors 'c' has, respectively:
2183   //   * [[Class]] "Function"
2184   //   * __proto__ === Function.prototype
2185   //   * A constructor that creates a user-defined type.
2186   //   * 'prototype' property:
2187   //     * [[Class]] "CTypeProto"
2188   //     * __proto__ === ctypes.CType.prototype
2189   //     * 'constructor' property === 'c'
2190   // We also construct an object 'p' to serve, given a type object 't'
2191   // constructed from one of these type constructors, as
2192   // 't.prototype.__proto__'. This object has:
2193   //   * [[Class]] "CDataProto"
2194   //   * __proto__ === ctypes.CData.prototype
2195   //   * Properties and functions common to all CDatas.
2196   // Therefore an instance 't' of ctypes.{Pointer,Array,Struct,Function}Type
2197   // will have, resp.:
2198   //   * [[Class]] "CType"
2199   //   * __proto__ === ctypes.{Pointer,Array,Struct,Function}Type.prototype
2200   //   * A constructor which creates and returns a CData object, containing
2201   //     binary data of the given type.
2202   //   * 'prototype' property:
2203   //     * [[Class]] "CDataProto"
2204   //     * __proto__ === 'p', the prototype object from above
2205   //     * 'constructor' property === 't'
2206   RootedObjectVector protos(cx);
2207   if (!protos.resize(CTYPEPROTO_SLOTS)) {
2208     return false;
2209   }
2210   if (!InitTypeConstructor(
2211           cx, ctypesObj, CTypeProto, CDataProto, sPointerFunction, nullptr,
2212           sPointerProps, sPointerInstanceFunctions, sPointerInstanceProps,
2213           protos[SLOT_POINTERPROTO], protos[SLOT_POINTERDATAPROTO]))
2214     return false;
2215 
2216   if (!InitTypeConstructor(
2217           cx, ctypesObj, CTypeProto, CDataProto, sArrayFunction, nullptr,
2218           sArrayProps, sArrayInstanceFunctions, sArrayInstanceProps,
2219           protos[SLOT_ARRAYPROTO], protos[SLOT_ARRAYDATAPROTO]))
2220     return false;
2221 
2222   if (!InitTypeConstructor(
2223           cx, ctypesObj, CTypeProto, CDataProto, sStructFunction,
2224           sStructFunctions, sStructProps, sStructInstanceFunctions, nullptr,
2225           protos[SLOT_STRUCTPROTO], protos[SLOT_STRUCTDATAPROTO]))
2226     return false;
2227 
2228   if (!InitTypeConstructor(cx, ctypesObj, CTypeProto,
2229                            protos[SLOT_POINTERDATAPROTO], sFunctionFunction,
2230                            nullptr, sFunctionProps, sFunctionInstanceFunctions,
2231                            nullptr, protos[SLOT_FUNCTIONPROTO],
2232                            protos[SLOT_FUNCTIONDATAPROTO]))
2233     return false;
2234 
2235   protos[SLOT_CDATAPROTO].set(CDataProto);
2236 
2237   // Create and attach the ctypes.{Int64,UInt64} constructors.
2238   // Each of these has, respectively:
2239   //   * [[Class]] "Function"
2240   //   * __proto__ === Function.prototype
2241   //   * A constructor that creates a ctypes.{Int64,UInt64} object,
2242   //     respectively.
2243   //   * 'prototype' property:
2244   //     * [[Class]] {"Int64Proto","UInt64Proto"}
2245   //     * 'constructor' property === ctypes.{Int64,UInt64}
2246   protos[SLOT_INT64PROTO].set(InitInt64Class(cx, ctypesObj, &sInt64ProtoClass,
2247                                              Int64::Construct, sInt64Functions,
2248                                              sInt64StaticFunctions));
2249   if (!protos[SLOT_INT64PROTO]) {
2250     return false;
2251   }
2252   protos[SLOT_UINT64PROTO].set(
2253       InitInt64Class(cx, ctypesObj, &sUInt64ProtoClass, UInt64::Construct,
2254                      sUInt64Functions, sUInt64StaticFunctions));
2255   if (!protos[SLOT_UINT64PROTO]) {
2256     return false;
2257   }
2258 
2259   // Finally, store a pointer to the global ctypes object.
2260   // Note that there is no other reliable manner of locating this object.
2261   protos[SLOT_CTYPES].set(ctypesObj);
2262 
2263   // Attach the prototypes just created to each of ctypes.CType.prototype,
2264   // and the special type constructors, so we can access them when constructing
2265   // instances of those types.
2266   AttachProtos(CTypeProto, protos);
2267   AttachProtos(protos[SLOT_POINTERPROTO], protos);
2268   AttachProtos(protos[SLOT_ARRAYPROTO], protos);
2269   AttachProtos(protos[SLOT_STRUCTPROTO], protos);
2270   AttachProtos(protos[SLOT_FUNCTIONPROTO], protos);
2271 
2272   RootedObject ABIProto(cx, InitABIClass(cx));
2273   if (!ABIProto) {
2274     return false;
2275   }
2276 
2277   // Attach objects representing ABI constants.
2278   if (!DefineABIConstant(cx, ctypesObj, "default_abi", ABI_DEFAULT, ABIProto) ||
2279       !DefineABIConstant(cx, ctypesObj, "stdcall_abi", ABI_STDCALL, ABIProto) ||
2280       !DefineABIConstant(cx, ctypesObj, "thiscall_abi", ABI_THISCALL,
2281                          ABIProto) ||
2282       !DefineABIConstant(cx, ctypesObj, "winapi_abi", ABI_WINAPI, ABIProto))
2283     return false;
2284 
2285     // Create objects representing the builtin types, and attach them to the
2286     // ctypes object. Each type object 't' has:
2287     //   * [[Class]] "CType"
2288     //   * __proto__ === ctypes.CType.prototype
2289     //   * A constructor which creates and returns a CData object, containing
2290     //     binary data of the given type.
2291     //   * 'prototype' property:
2292     //     * [[Class]] "CDataProto"
2293     //     * __proto__ === ctypes.CData.prototype
2294     //     * 'constructor' property === 't'
2295 #define DEFINE_TYPE(name, type, ffiType)                                       \
2296   RootedObject typeObj_##name(cx);                                             \
2297   {                                                                            \
2298     RootedValue typeVal(cx, Int32Value(sizeof(type)));                         \
2299     RootedValue alignVal(cx, Int32Value(ffiType.alignment));                   \
2300     typeObj_##name =                                                           \
2301         CType::DefineBuiltin(cx, ctypesObj, #name, CTypeProto, CDataProto,     \
2302                              #name, TYPE_##name, typeVal, alignVal, &ffiType); \
2303     if (!typeObj_##name) return false;                                         \
2304   }
2305   CTYPES_FOR_EACH_TYPE(DEFINE_TYPE)
2306 #undef DEFINE_TYPE
2307 
2308   // Alias 'ctypes.unsigned' as 'ctypes.unsigned_int', since they represent
2309   // the same type in C.
2310   if (!JS_DefineProperty(cx, ctypesObj, "unsigned", typeObj_unsigned_int,
2311                          JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
2312     return false;
2313 
2314   // Alias 'ctypes.jschar' as 'ctypes.char16_t' to prevent breaking addons
2315   // that are still using jschar (bug 1064935).
2316   if (!JS_DefineProperty(cx, ctypesObj, "jschar", typeObj_char16_t,
2317                          JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
2318     return false;
2319 
2320   // Create objects representing the special types void_t and voidptr_t.
2321   RootedObject typeObj(
2322       cx, CType::DefineBuiltin(cx, ctypesObj, "void_t", CTypeProto, CDataProto,
2323                                "void", TYPE_void_t, JS::UndefinedHandleValue,
2324                                JS::UndefinedHandleValue, &ffi_type_void));
2325   if (!typeObj) {
2326     return false;
2327   }
2328 
2329   typeObj = PointerType::CreateInternal(cx, typeObj);
2330   if (!typeObj) {
2331     return false;
2332   }
2333   if (!JS_DefineProperty(cx, ctypesObj, "voidptr_t", typeObj,
2334                          JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
2335     return false;
2336 
2337   return true;
2338 }
2339 
IsCTypesGlobal(JSObject * obj)2340 bool IsCTypesGlobal(JSObject* obj) {
2341   return obj->hasClass(&sCTypesGlobalClass);
2342 }
2343 
IsCTypesGlobal(HandleValue v)2344 bool IsCTypesGlobal(HandleValue v) {
2345   return v.isObject() && IsCTypesGlobal(&v.toObject());
2346 }
2347 
2348 // Get the JS::CTypesCallbacks struct from the 'ctypes' object 'obj'.
GetCallbacks(JSObject * obj)2349 const JS::CTypesCallbacks* GetCallbacks(JSObject* obj) {
2350   MOZ_ASSERT(IsCTypesGlobal(obj));
2351 
2352   Value result = JS::GetReservedSlot(obj, SLOT_CALLBACKS);
2353   if (result.isUndefined()) {
2354     return nullptr;
2355   }
2356 
2357   return static_cast<const JS::CTypesCallbacks*>(result.toPrivate());
2358 }
2359 
2360 // Utility function to access a property of an object as an object
2361 // returns false and sets the error if the property does not exist
2362 // or is not an object
GetObjectProperty(JSContext * cx,HandleObject obj,const char * property,MutableHandleObject result)2363 static bool GetObjectProperty(JSContext* cx, HandleObject obj,
2364                               const char* property,
2365                               MutableHandleObject result) {
2366   RootedValue val(cx);
2367   if (!JS_GetProperty(cx, obj, property, &val)) {
2368     return false;
2369   }
2370 
2371   if (val.isPrimitive()) {
2372     JS_ReportErrorASCII(cx, "missing or non-object field");
2373     return false;
2374   }
2375 
2376   result.set(val.toObjectOrNull());
2377   return true;
2378 }
2379 
2380 }  // namespace js::ctypes
2381 
2382 using namespace js;
2383 using namespace js::ctypes;
2384 
InitCTypesClass(JSContext * cx,Handle<JSObject * > global)2385 JS_PUBLIC_API bool JS::InitCTypesClass(JSContext* cx,
2386                                        Handle<JSObject*> global) {
2387   // attach ctypes property to global object
2388   RootedObject ctypes(cx, JS_NewObject(cx, &sCTypesGlobalClass));
2389   if (!ctypes) {
2390     return false;
2391   }
2392 
2393   if (!JS_DefineProperty(cx, global, "ctypes", ctypes,
2394                          JSPROP_READONLY | JSPROP_PERMANENT)) {
2395     return false;
2396   }
2397 
2398   if (!InitTypeClasses(cx, ctypes)) {
2399     return false;
2400   }
2401 
2402   // attach API functions and properties
2403   if (!JS_DefineFunctions(cx, ctypes, sModuleFunctions) ||
2404       !JS_DefineProperties(cx, ctypes, sModuleProps))
2405     return false;
2406 
2407   if (!DefineToStringTag(cx, ctypes, "ctypes")) {
2408     return false;
2409   }
2410 
2411   // Set up ctypes.CDataFinalizer.prototype.
2412   RootedObject ctor(cx);
2413   if (!GetObjectProperty(cx, ctypes, "CDataFinalizer", &ctor)) {
2414     return false;
2415   }
2416 
2417   RootedObject prototype(cx, JS_NewObject(cx, &sCDataFinalizerProtoClass));
2418   if (!prototype) {
2419     return false;
2420   }
2421 
2422   if (!JS_DefineFunctions(cx, prototype, sCDataFinalizerFunctions)) {
2423     return false;
2424   }
2425 
2426   if (!DefineToStringTag(cx, prototype, "CDataFinalizer")) {
2427     return false;
2428   }
2429 
2430   if (!JS_DefineProperty(cx, ctor, "prototype", prototype,
2431                          JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
2432     return false;
2433 
2434   if (!JS_DefineProperty(cx, prototype, "constructor", ctor,
2435                          JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
2436     return false;
2437 
2438   // Seal the ctypes object, to prevent modification.
2439   return JS_FreezeObject(cx, ctypes);
2440 }
2441 
SetCTypesCallbacks(JSObject * ctypesObj,const CTypesCallbacks * callbacks)2442 JS_PUBLIC_API void JS::SetCTypesCallbacks(JSObject* ctypesObj,
2443                                           const CTypesCallbacks* callbacks) {
2444   MOZ_ASSERT(callbacks);
2445   MOZ_ASSERT(IsCTypesGlobal(ctypesObj));
2446 
2447   // Set the callbacks on a reserved slot.
2448   JS_SetReservedSlot(ctypesObj, SLOT_CALLBACKS,
2449                      PrivateValue(const_cast<CTypesCallbacks*>(callbacks)));
2450 }
2451 
2452 namespace js {
2453 
2454 namespace ctypes {
2455 
SizeOfDataIfCDataObject(mozilla::MallocSizeOf mallocSizeOf,JSObject * obj)2456 size_t SizeOfDataIfCDataObject(mozilla::MallocSizeOf mallocSizeOf,
2457                                JSObject* obj) {
2458   if (!CData::IsCData(obj)) {
2459     return 0;
2460   }
2461 
2462   size_t n = 0;
2463   Value slot = JS::GetReservedSlot(obj, ctypes::SLOT_OWNS);
2464   if (!slot.isUndefined()) {
2465     bool owns = slot.toBoolean();
2466     slot = JS::GetReservedSlot(obj, ctypes::SLOT_DATA);
2467     if (!slot.isUndefined()) {
2468       char** buffer = static_cast<char**>(slot.toPrivate());
2469       n += mallocSizeOf(buffer);
2470       if (owns) {
2471         n += mallocSizeOf(*buffer);
2472       }
2473     }
2474   }
2475   return n;
2476 }
2477 
2478 /*******************************************************************************
2479 ** Type conversion functions
2480 *******************************************************************************/
2481 
2482 // Enforce some sanity checks on type widths and properties.
2483 // Where the architecture is 64-bit, make sure it's LP64 or LLP64. (ctypes.int
2484 // autoconverts to a primitive JS number; to support ILP64 architectures, it
2485 // would need to autoconvert to an Int64 object instead. Therefore we enforce
2486 // this invariant here.)
2487 static_assert(sizeof(bool) == 1 || sizeof(bool) == 4);
2488 static_assert(sizeof(char) == 1);
2489 static_assert(sizeof(short) == 2);
2490 static_assert(sizeof(int) == 4);
2491 static_assert(sizeof(unsigned) == 4);
2492 static_assert(sizeof(long) == 4 || sizeof(long) == 8);
2493 static_assert(sizeof(long long) == 8);
2494 static_assert(sizeof(size_t) == sizeof(uintptr_t));
2495 static_assert(sizeof(float) == 4);
2496 static_assert(sizeof(PRFuncPtr) == sizeof(void*));
2497 static_assert(numeric_limits<double>::is_signed);
2498 
2499 template <typename TargetType, typename FromType,
2500           bool FromIsIntegral = std::is_integral_v<FromType>>
2501 struct ConvertImpl;
2502 
2503 template <typename TargetType, typename FromType>
2504 struct ConvertImpl<TargetType, FromType, false> {
Convertjs::ctypes::ConvertImpl2505   static MOZ_ALWAYS_INLINE TargetType Convert(FromType input) {
2506     return JS::ToSignedOrUnsignedInteger<TargetType>(input);
2507   }
2508 };
2509 
2510 template <typename TargetType>
2511 struct ConvertUnsignedTargetTo {
convertjs::ctypes::ConvertUnsignedTargetTo2512   static TargetType convert(std::make_unsigned_t<TargetType> input) {
2513     return std::is_signed_v<TargetType> ? mozilla::WrapToSigned(input) : input;
2514   }
2515 };
2516 
2517 template <>
2518 struct ConvertUnsignedTargetTo<char16_t> {
convertjs::ctypes::ConvertUnsignedTargetTo2519   static char16_t convert(char16_t input) {
2520     // mozilla::WrapToSigned can't be used on char16_t.
2521     return input;
2522   }
2523 };
2524 
2525 template <typename TargetType, typename FromType>
2526 struct ConvertImpl<TargetType, FromType, true> {
Convertjs::ctypes::ConvertImpl2527   static MOZ_ALWAYS_INLINE TargetType Convert(FromType input) {
2528     using UnsignedTargetType = std::make_unsigned_t<TargetType>;
2529     auto resultUnsigned = static_cast<UnsignedTargetType>(input);
2530 
2531     return ConvertUnsignedTargetTo<TargetType>::convert(resultUnsigned);
2532   }
2533 };
2534 
2535 template <class TargetType, class FromType>
Convert(FromType d)2536 static MOZ_ALWAYS_INLINE TargetType Convert(FromType d) {
2537   static_assert(
2538       std::is_integral_v<FromType> != std::is_floating_point_v<FromType>,
2539       "should only be converting from floating/integral type");
2540 
2541   return ConvertImpl<TargetType, FromType>::Convert(d);
2542 }
2543 
2544 template <class TargetType, class FromType>
IsAlwaysExact()2545 static MOZ_ALWAYS_INLINE bool IsAlwaysExact() {
2546   // Return 'true' if TargetType can always exactly represent FromType.
2547   // This means that:
2548   // 1) TargetType must be the same or more bits wide as FromType. For integers
2549   //    represented in 'n' bits, unsigned variants will have 'n' digits while
2550   //    signed will have 'n - 1'. For floating point types, 'digits' is the
2551   //    mantissa width.
2552   // 2) If FromType is signed, TargetType must also be signed. (Floating point
2553   //    types are always signed.)
2554   // 3) If TargetType is an exact integral type, FromType must be also.
2555   if (numeric_limits<TargetType>::digits < numeric_limits<FromType>::digits) {
2556     return false;
2557   }
2558 
2559   if (numeric_limits<FromType>::is_signed &&
2560       !numeric_limits<TargetType>::is_signed)
2561     return false;
2562 
2563   if (!numeric_limits<FromType>::is_exact &&
2564       numeric_limits<TargetType>::is_exact)
2565     return false;
2566 
2567   return true;
2568 }
2569 
2570 // Templated helper to determine if FromType 'i' converts losslessly to
2571 // TargetType 'j'. Default case where both types are the same signedness.
2572 template <class TargetType, class FromType, bool TargetSigned, bool FromSigned>
2573 struct IsExactImpl {
Testjs::ctypes::IsExactImpl2574   static MOZ_ALWAYS_INLINE bool Test(FromType i, TargetType j) {
2575     static_assert(numeric_limits<TargetType>::is_exact);
2576     return FromType(j) == i;
2577   }
2578 };
2579 
2580 // Specialization where TargetType is unsigned, FromType is signed.
2581 template <class TargetType, class FromType>
2582 struct IsExactImpl<TargetType, FromType, false, true> {
Testjs::ctypes::IsExactImpl2583   static MOZ_ALWAYS_INLINE bool Test(FromType i, TargetType j) {
2584     static_assert(numeric_limits<TargetType>::is_exact);
2585     return i >= 0 && FromType(j) == i;
2586   }
2587 };
2588 
2589 // Specialization where TargetType is signed, FromType is unsigned.
2590 template <class TargetType, class FromType>
2591 struct IsExactImpl<TargetType, FromType, true, false> {
Testjs::ctypes::IsExactImpl2592   static MOZ_ALWAYS_INLINE bool Test(FromType i, TargetType j) {
2593     static_assert(numeric_limits<TargetType>::is_exact);
2594     return TargetType(i) >= 0 && FromType(j) == i;
2595   }
2596 };
2597 
2598 // Convert FromType 'i' to TargetType 'result', returning true iff 'result'
2599 // is an exact representation of 'i'.
2600 template <class TargetType, class FromType>
ConvertExact(FromType i,TargetType * result)2601 static MOZ_ALWAYS_INLINE bool ConvertExact(FromType i, TargetType* result) {
2602   static_assert(std::numeric_limits<TargetType>::is_exact,
2603                 "TargetType must be exact to simplify conversion");
2604 
2605   *result = Convert<TargetType>(i);
2606 
2607   // See if we can avoid a dynamic check.
2608   if (IsAlwaysExact<TargetType, FromType>()) {
2609     return true;
2610   }
2611 
2612   // Return 'true' if 'i' is exactly representable in 'TargetType'.
2613   return IsExactImpl<TargetType, FromType,
2614                      numeric_limits<TargetType>::is_signed,
2615                      numeric_limits<FromType>::is_signed>::Test(i, *result);
2616 }
2617 
2618 // Templated helper to determine if Type 'i' is negative. Default case
2619 // where IntegerType is unsigned.
2620 template <class Type, bool IsSigned>
2621 struct IsNegativeImpl {
Testjs::ctypes::IsNegativeImpl2622   static MOZ_ALWAYS_INLINE bool Test(Type i) { return false; }
2623 };
2624 
2625 // Specialization where Type is signed.
2626 template <class Type>
2627 struct IsNegativeImpl<Type, true> {
Testjs::ctypes::IsNegativeImpl2628   static MOZ_ALWAYS_INLINE bool Test(Type i) { return i < 0; }
2629 };
2630 
2631 // Determine whether Type 'i' is negative.
2632 template <class Type>
IsNegative(Type i)2633 static MOZ_ALWAYS_INLINE bool IsNegative(Type i) {
2634   return IsNegativeImpl<Type, numeric_limits<Type>::is_signed>::Test(i);
2635 }
2636 
2637 // Implicitly convert val to bool, allowing bool, int, and double
2638 // arguments numerically equal to 0 or 1.
jsvalToBool(JSContext * cx,HandleValue val,bool * result)2639 static bool jsvalToBool(JSContext* cx, HandleValue val, bool* result) {
2640   if (val.isBoolean()) {
2641     *result = val.toBoolean();
2642     return true;
2643   }
2644   if (val.isInt32()) {
2645     int32_t i = val.toInt32();
2646     *result = i != 0;
2647     return i == 0 || i == 1;
2648   }
2649   if (val.isDouble()) {
2650     double d = val.toDouble();
2651     *result = d != 0;
2652     // Allow -0.
2653     return d == 1 || d == 0;
2654   }
2655   // Don't silently convert null to bool. It's probably a mistake.
2656   return false;
2657 }
2658 
2659 // Implicitly convert val to IntegerType, allowing bool, int, double,
2660 // Int64, UInt64, and CData integer types 't' where all values of 't' are
2661 // representable by IntegerType.
2662 template <class IntegerType>
jsvalToInteger(JSContext * cx,HandleValue val,IntegerType * result)2663 static bool jsvalToInteger(JSContext* cx, HandleValue val,
2664                            IntegerType* result) {
2665   static_assert(numeric_limits<IntegerType>::is_exact);
2666 
2667   if (val.isInt32()) {
2668     // Make sure the integer fits in the alotted precision, and has the right
2669     // sign.
2670     int32_t i = val.toInt32();
2671     return ConvertExact(i, result);
2672   }
2673   if (val.isDouble()) {
2674     // Don't silently lose bits here -- check that val really is an
2675     // integer value, and has the right sign.
2676     double d = val.toDouble();
2677     return ConvertExact(d, result);
2678   }
2679   if (val.isObject()) {
2680     RootedObject obj(cx, &val.toObject());
2681     if (CData::IsCDataMaybeUnwrap(&obj)) {
2682       JSObject* typeObj = CData::GetCType(obj);
2683       void* data = CData::GetData(obj);
2684 
2685       // Check whether the source type is always representable, with exact
2686       // precision, by the target type. If it is, convert the value.
2687       switch (CType::GetTypeCode(typeObj)) {
2688 #define INTEGER_CASE(name, fromType, ffiType)                  \
2689   case TYPE_##name:                                            \
2690     if (!IsAlwaysExact<IntegerType, fromType>()) return false; \
2691     *result = IntegerType(*static_cast<fromType*>(data));      \
2692     return true;
2693         CTYPES_FOR_EACH_INT_TYPE(INTEGER_CASE)
2694         CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGER_CASE)
2695 #undef INTEGER_CASE
2696         case TYPE_void_t:
2697         case TYPE_bool:
2698         case TYPE_float:
2699         case TYPE_double:
2700         case TYPE_float32_t:
2701         case TYPE_float64_t:
2702         case TYPE_char:
2703         case TYPE_signed_char:
2704         case TYPE_unsigned_char:
2705         case TYPE_char16_t:
2706         case TYPE_pointer:
2707         case TYPE_function:
2708         case TYPE_array:
2709         case TYPE_struct:
2710           // Not a compatible number type.
2711           return false;
2712       }
2713     }
2714 
2715     if (Int64::IsInt64(obj)) {
2716       // Make sure the integer fits in IntegerType.
2717       int64_t i = Int64Base::GetInt(obj);
2718       return ConvertExact(i, result);
2719     }
2720 
2721     if (UInt64::IsUInt64(obj)) {
2722       // Make sure the integer fits in IntegerType.
2723       uint64_t i = Int64Base::GetInt(obj);
2724       return ConvertExact(i, result);
2725     }
2726 
2727     if (CDataFinalizer::IsCDataFinalizer(obj)) {
2728       RootedValue innerData(cx);
2729       if (!CDataFinalizer::GetValue(cx, obj, &innerData)) {
2730         return false;  // Nothing to convert
2731       }
2732       return jsvalToInteger(cx, innerData, result);
2733     }
2734 
2735     return false;
2736   }
2737   if (val.isBoolean()) {
2738     // Implicitly promote boolean values to 0 or 1, like C.
2739     *result = val.toBoolean();
2740     MOZ_ASSERT(*result == 0 || *result == 1);
2741     return true;
2742   }
2743   // Don't silently convert null to an integer. It's probably a mistake.
2744   return false;
2745 }
2746 
2747 // Implicitly convert val to FloatType, allowing int, double,
2748 // Int64, UInt64, and CData numeric types 't' where all values of 't' are
2749 // representable by FloatType.
2750 template <class FloatType>
jsvalToFloat(JSContext * cx,HandleValue val,FloatType * result)2751 static bool jsvalToFloat(JSContext* cx, HandleValue val, FloatType* result) {
2752   static_assert(!numeric_limits<FloatType>::is_exact);
2753 
2754   // The following casts may silently throw away some bits, but there's
2755   // no good way around it. Sternly requiring that the 64-bit double
2756   // argument be exactly representable as a 32-bit float is
2757   // unrealistic: it would allow 1/2 to pass but not 1/3.
2758   if (val.isInt32()) {
2759     *result = FloatType(val.toInt32());
2760     return true;
2761   }
2762   if (val.isDouble()) {
2763     *result = FloatType(val.toDouble());
2764     return true;
2765   }
2766   if (val.isObject()) {
2767     RootedObject obj(cx, &val.toObject());
2768     if (CData::IsCDataMaybeUnwrap(&obj)) {
2769       JSObject* typeObj = CData::GetCType(obj);
2770       void* data = CData::GetData(obj);
2771 
2772       // Check whether the source type is always representable, with exact
2773       // precision, by the target type. If it is, convert the value.
2774       switch (CType::GetTypeCode(typeObj)) {
2775 #define NUMERIC_CASE(name, fromType, ffiType)                \
2776   case TYPE_##name:                                          \
2777     if (!IsAlwaysExact<FloatType, fromType>()) return false; \
2778     *result = FloatType(*static_cast<fromType*>(data));      \
2779     return true;
2780         CTYPES_FOR_EACH_FLOAT_TYPE(NUMERIC_CASE)
2781         CTYPES_FOR_EACH_INT_TYPE(NUMERIC_CASE)
2782         CTYPES_FOR_EACH_WRAPPED_INT_TYPE(NUMERIC_CASE)
2783 #undef NUMERIC_CASE
2784         case TYPE_void_t:
2785         case TYPE_bool:
2786         case TYPE_char:
2787         case TYPE_signed_char:
2788         case TYPE_unsigned_char:
2789         case TYPE_char16_t:
2790         case TYPE_pointer:
2791         case TYPE_function:
2792         case TYPE_array:
2793         case TYPE_struct:
2794           // Not a compatible number type.
2795           return false;
2796       }
2797     }
2798   }
2799   // Don't silently convert true to 1.0 or false to 0.0, even though C/C++
2800   // does it. It's likely to be a mistake.
2801   return false;
2802 }
2803 
2804 template <class IntegerType, class CharT>
StringToInteger(JSContext * cx,CharT * cp,size_t length,IntegerType * result,bool * overflow)2805 static bool StringToInteger(JSContext* cx, CharT* cp, size_t length,
2806                             IntegerType* result, bool* overflow) {
2807   static_assert(numeric_limits<IntegerType>::is_exact);
2808 
2809   const CharT* end = cp + length;
2810   if (cp == end) {
2811     return false;
2812   }
2813 
2814   IntegerType sign = 1;
2815   if (cp[0] == '-') {
2816     if (!numeric_limits<IntegerType>::is_signed) {
2817       return false;
2818     }
2819 
2820     sign = -1;
2821     ++cp;
2822   }
2823 
2824   // Assume base-10, unless the string begins with '0x' or '0X'.
2825   IntegerType base = 10;
2826   if (end - cp > 2 && cp[0] == '0' && (cp[1] == 'x' || cp[1] == 'X')) {
2827     cp += 2;
2828     base = 16;
2829   }
2830 
2831   // Scan the string left to right and build the number,
2832   // checking for valid characters 0 - 9, a - f, A - F and overflow.
2833   IntegerType i = 0;
2834   while (cp != end) {
2835     char16_t c = *cp++;
2836     uint8_t digit;
2837     if (IsAsciiDigit(c)) {
2838       digit = c - '0';
2839     } else if (base == 16 && c >= 'a' && c <= 'f') {
2840       digit = c - 'a' + 10;
2841     } else if (base == 16 && c >= 'A' && c <= 'F') {
2842       digit = c - 'A' + 10;
2843     } else {
2844       return false;
2845     }
2846 
2847     IntegerType ii = i;
2848     i = ii * base + sign * digit;
2849     if (i / base != ii) {
2850       *overflow = true;
2851       return false;
2852     }
2853   }
2854 
2855   *result = i;
2856   return true;
2857 }
2858 
2859 template <class IntegerType>
StringToInteger(JSContext * cx,JSString * string,IntegerType * result,bool * overflow)2860 static bool StringToInteger(JSContext* cx, JSString* string,
2861                             IntegerType* result, bool* overflow) {
2862   JSLinearString* linear = string->ensureLinear(cx);
2863   if (!linear) {
2864     return false;
2865   }
2866 
2867   AutoCheckCannotGC nogc;
2868   size_t length = linear->length();
2869   return string->hasLatin1Chars()
2870              ? StringToInteger<IntegerType>(cx, linear->latin1Chars(nogc),
2871                                             length, result, overflow)
2872              : StringToInteger<IntegerType>(cx, linear->twoByteChars(nogc),
2873                                             length, result, overflow);
2874 }
2875 
2876 // Implicitly convert val to IntegerType, allowing int, double,
2877 // Int64, UInt64, and optionally a decimal or hexadecimal string argument.
2878 // (This is common code shared by jsvalToSize and the Int64/UInt64
2879 // constructors.)
2880 template <class IntegerType>
jsvalToBigInteger(JSContext * cx,HandleValue val,bool allowString,IntegerType * result,bool * overflow)2881 static bool jsvalToBigInteger(JSContext* cx, HandleValue val, bool allowString,
2882                               IntegerType* result, bool* overflow) {
2883   static_assert(numeric_limits<IntegerType>::is_exact);
2884 
2885   if (val.isInt32()) {
2886     // Make sure the integer fits in the alotted precision, and has the right
2887     // sign.
2888     int32_t i = val.toInt32();
2889     return ConvertExact(i, result);
2890   }
2891   if (val.isDouble()) {
2892     // Don't silently lose bits here -- check that val really is an
2893     // integer value, and has the right sign.
2894     double d = val.toDouble();
2895     return ConvertExact(d, result);
2896   }
2897   if (allowString && val.isString()) {
2898     // Allow conversion from base-10 or base-16 strings, provided the result
2899     // fits in IntegerType. (This allows an Int64 or UInt64 object to be passed
2900     // to the JS array element operator, which will automatically call
2901     // toString() on the object for us.)
2902     return StringToInteger(cx, val.toString(), result, overflow);
2903   }
2904   if (val.isObject()) {
2905     // Allow conversion from an Int64 or UInt64 object directly.
2906     JSObject* obj = &val.toObject();
2907 
2908     if (UInt64::IsUInt64(obj)) {
2909       // Make sure the integer fits in IntegerType.
2910       uint64_t i = Int64Base::GetInt(obj);
2911       return ConvertExact(i, result);
2912     }
2913 
2914     if (Int64::IsInt64(obj)) {
2915       // Make sure the integer fits in IntegerType.
2916       int64_t i = Int64Base::GetInt(obj);
2917       return ConvertExact(i, result);
2918     }
2919 
2920     if (CDataFinalizer::IsCDataFinalizer(obj)) {
2921       RootedValue innerData(cx);
2922       if (!CDataFinalizer::GetValue(cx, obj, &innerData)) {
2923         return false;  // Nothing to convert
2924       }
2925       return jsvalToBigInteger(cx, innerData, allowString, result, overflow);
2926     }
2927   }
2928   return false;
2929 }
2930 
2931 // Implicitly convert val to a size value, where the size value is represented
2932 // by size_t but must also fit in a double.
jsvalToSize(JSContext * cx,HandleValue val,bool allowString,size_t * result)2933 static bool jsvalToSize(JSContext* cx, HandleValue val, bool allowString,
2934                         size_t* result) {
2935   bool dummy;
2936   if (!jsvalToBigInteger(cx, val, allowString, result, &dummy)) {
2937     return false;
2938   }
2939 
2940   // Also check that the result fits in a double.
2941   return Convert<size_t>(double(*result)) == *result;
2942 }
2943 
2944 // Implicitly convert val to IntegerType, allowing int, double,
2945 // Int64, UInt64, and optionally a decimal or hexadecimal string argument.
2946 // (This is common code shared by jsvalToSize and the Int64/UInt64
2947 // constructors.)
2948 template <class IntegerType>
jsidToBigInteger(JSContext * cx,jsid val,bool allowString,IntegerType * result)2949 static bool jsidToBigInteger(JSContext* cx, jsid val, bool allowString,
2950                              IntegerType* result) {
2951   static_assert(numeric_limits<IntegerType>::is_exact);
2952 
2953   if (JSID_IS_INT(val)) {
2954     // Make sure the integer fits in the alotted precision, and has the right
2955     // sign.
2956     int32_t i = JSID_TO_INT(val);
2957     return ConvertExact(i, result);
2958   }
2959   if (allowString && JSID_IS_STRING(val)) {
2960     // Allow conversion from base-10 or base-16 strings, provided the result
2961     // fits in IntegerType. (This allows an Int64 or UInt64 object to be passed
2962     // to the JS array element operator, which will automatically call
2963     // toString() on the object for us.)
2964     bool dummy;
2965     return StringToInteger(cx, JSID_TO_STRING(val), result, &dummy);
2966   }
2967   return false;
2968 }
2969 
2970 // Implicitly convert val to a size value, where the size value is represented
2971 // by size_t but must also fit in a double.
jsidToSize(JSContext * cx,jsid val,bool allowString,size_t * result)2972 static bool jsidToSize(JSContext* cx, jsid val, bool allowString,
2973                        size_t* result) {
2974   if (!jsidToBigInteger(cx, val, allowString, result)) {
2975     return false;
2976   }
2977 
2978   // Also check that the result fits in a double.
2979   return Convert<size_t>(double(*result)) == *result;
2980 }
2981 
2982 // Implicitly convert a size value to a Value, ensuring that the size_t value
2983 // fits in a double.
SizeTojsval(JSContext * cx,size_t size,MutableHandleValue result)2984 static bool SizeTojsval(JSContext* cx, size_t size, MutableHandleValue result) {
2985   if (Convert<size_t>(double(size)) != size) {
2986     return false;
2987   }
2988 
2989   result.setNumber(double(size));
2990   return true;
2991 }
2992 
2993 // Forcefully convert val to IntegerType when explicitly requested.
2994 template <class IntegerType>
jsvalToIntegerExplicit(HandleValue val,IntegerType * result)2995 static bool jsvalToIntegerExplicit(HandleValue val, IntegerType* result) {
2996   static_assert(numeric_limits<IntegerType>::is_exact);
2997 
2998   if (val.isDouble()) {
2999     // Convert using ToInt32-style semantics: non-finite numbers become 0, and
3000     // everything else rounds toward zero then maps into |IntegerType| with
3001     // wraparound semantics.
3002     double d = val.toDouble();
3003     *result = JS::ToSignedOrUnsignedInteger<IntegerType>(d);
3004     return true;
3005   }
3006   if (val.isObject()) {
3007     // Convert Int64 and UInt64 values by C-style cast.
3008     JSObject* obj = &val.toObject();
3009     if (Int64::IsInt64(obj)) {
3010       int64_t i = Int64Base::GetInt(obj);
3011       *result = IntegerType(i);
3012       return true;
3013     }
3014     if (UInt64::IsUInt64(obj)) {
3015       uint64_t i = Int64Base::GetInt(obj);
3016       *result = IntegerType(i);
3017       return true;
3018     }
3019   }
3020   return false;
3021 }
3022 
3023 // Forcefully convert val to a pointer value when explicitly requested.
jsvalToPtrExplicit(JSContext * cx,HandleValue val,uintptr_t * result)3024 static bool jsvalToPtrExplicit(JSContext* cx, HandleValue val,
3025                                uintptr_t* result) {
3026   if (val.isInt32()) {
3027     // int32_t always fits in intptr_t. If the integer is negative, cast through
3028     // an intptr_t intermediate to sign-extend.
3029     int32_t i = val.toInt32();
3030     *result = i < 0 ? uintptr_t(intptr_t(i)) : uintptr_t(i);
3031     return true;
3032   }
3033   if (val.isDouble()) {
3034     double d = val.toDouble();
3035     if (d < 0) {
3036       // Cast through an intptr_t intermediate to sign-extend.
3037       intptr_t i = Convert<intptr_t>(d);
3038       if (double(i) != d) {
3039         return false;
3040       }
3041 
3042       *result = uintptr_t(i);
3043       return true;
3044     }
3045 
3046     // Don't silently lose bits here -- check that val really is an
3047     // integer value, and has the right sign.
3048     *result = Convert<uintptr_t>(d);
3049     return double(*result) == d;
3050   }
3051   if (val.isObject()) {
3052     JSObject* obj = &val.toObject();
3053     if (Int64::IsInt64(obj)) {
3054       int64_t i = Int64Base::GetInt(obj);
3055       intptr_t p = intptr_t(i);
3056 
3057       // Make sure the integer fits in the alotted precision.
3058       if (int64_t(p) != i) {
3059         return false;
3060       }
3061       *result = uintptr_t(p);
3062       return true;
3063     }
3064 
3065     if (UInt64::IsUInt64(obj)) {
3066       uint64_t i = Int64Base::GetInt(obj);
3067 
3068       // Make sure the integer fits in the alotted precision.
3069       *result = uintptr_t(i);
3070       return uint64_t(*result) == i;
3071     }
3072   }
3073   return false;
3074 }
3075 
3076 template <class IntegerType, class CharType, size_t N>
IntegerToString(IntegerType i,int radix,StringBuilder<CharType,N> & result)3077 void IntegerToString(IntegerType i, int radix,
3078                      StringBuilder<CharType, N>& result) {
3079   static_assert(numeric_limits<IntegerType>::is_exact);
3080 
3081   // The buffer must be big enough for all the bits of IntegerType to fit,
3082   // in base-2, including '-'.
3083   CharType buffer[sizeof(IntegerType) * 8 + 1];
3084   CharType* end = buffer + sizeof(buffer) / sizeof(CharType);
3085   CharType* cp = end;
3086 
3087   // Build the string in reverse. We use multiplication and subtraction
3088   // instead of modulus because that's much faster.
3089   const bool isNegative = IsNegative(i);
3090   size_t sign = isNegative ? -1 : 1;
3091   do {
3092     IntegerType ii = i / IntegerType(radix);
3093     size_t index = sign * size_t(i - ii * IntegerType(radix));
3094     *--cp = "0123456789abcdefghijklmnopqrstuvwxyz"[index];
3095     i = ii;
3096   } while (i != 0);
3097 
3098   if (isNegative) {
3099     *--cp = '-';
3100   }
3101 
3102   MOZ_ASSERT(cp >= buffer);
3103   if (!result.append(cp, end)) {
3104     return;
3105   }
3106 }
3107 
3108 // Convert C binary value 'data' of CType 'typeObj' to a JS primitive, where
3109 // possible; otherwise, construct and return a CData object. The following
3110 // semantics apply when constructing a CData object for return:
3111 // * If 'wantPrimitive' is true, the caller indicates that 'result' must be
3112 //   a JS primitive, and ConvertToJS will fail if 'result' would be a CData
3113 //   object. Otherwise:
3114 // * If a CData object 'parentObj' is supplied, the new CData object is
3115 //   dependent on the given parent and its buffer refers to a slice of the
3116 //   parent's buffer.
3117 // * If 'parentObj' is null, the new CData object may or may not own its
3118 //   resulting buffer depending on the 'ownResult' argument.
ConvertToJS(JSContext * cx,HandleObject typeObj,HandleObject parentObj,void * data,bool wantPrimitive,bool ownResult,MutableHandleValue result)3119 static bool ConvertToJS(JSContext* cx, HandleObject typeObj,
3120                         HandleObject parentObj, void* data, bool wantPrimitive,
3121                         bool ownResult, MutableHandleValue result) {
3122   MOZ_ASSERT(!parentObj || CData::IsCData(parentObj));
3123   MOZ_ASSERT(!parentObj || !ownResult);
3124   MOZ_ASSERT(!wantPrimitive || !ownResult);
3125 
3126   TypeCode typeCode = CType::GetTypeCode(typeObj);
3127 
3128   switch (typeCode) {
3129     case TYPE_void_t:
3130       result.setUndefined();
3131       break;
3132     case TYPE_bool:
3133       result.setBoolean(*static_cast<bool*>(data));
3134       break;
3135 #define INT_CASE(name, type, ffiType)       \
3136   case TYPE_##name: {                       \
3137     type value = *static_cast<type*>(data); \
3138     if (sizeof(type) < 4)                   \
3139       result.setInt32(int32_t(value));      \
3140     else                                    \
3141       result.setDouble(double(value));      \
3142     break;                                  \
3143   }
3144       CTYPES_FOR_EACH_INT_TYPE(INT_CASE)
3145 #undef INT_CASE
3146 #define WRAPPED_INT_CASE(name, type, ffiType)                               \
3147   case TYPE_##name: {                                                       \
3148     /* Return an Int64 or UInt64 object - do not convert to a JS number. */ \
3149     uint64_t value;                                                         \
3150     RootedObject proto(cx);                                                 \
3151     if (!numeric_limits<type>::is_signed) {                                 \
3152       value = *static_cast<type*>(data);                                    \
3153       /* Get ctypes.UInt64.prototype from ctypes.CType.prototype. */        \
3154       proto = CType::GetProtoFromType(cx, typeObj, SLOT_UINT64PROTO);       \
3155       if (!proto) return false;                                             \
3156     } else {                                                                \
3157       value = int64_t(*static_cast<type*>(data));                           \
3158       /* Get ctypes.Int64.prototype from ctypes.CType.prototype. */         \
3159       proto = CType::GetProtoFromType(cx, typeObj, SLOT_INT64PROTO);        \
3160       if (!proto) return false;                                             \
3161     }                                                                       \
3162                                                                             \
3163     JSObject* obj = Int64Base::Construct(cx, proto, value,                  \
3164                                          !numeric_limits<type>::is_signed); \
3165     if (!obj) return false;                                                 \
3166     result.setObject(*obj);                                                 \
3167     break;                                                                  \
3168   }
3169       CTYPES_FOR_EACH_WRAPPED_INT_TYPE(WRAPPED_INT_CASE)
3170 #undef WRAPPED_INT_CASE
3171 #define FLOAT_CASE(name, type, ffiType)     \
3172   case TYPE_##name: {                       \
3173     type value = *static_cast<type*>(data); \
3174     result.setDouble(double(value));        \
3175     break;                                  \
3176   }
3177       CTYPES_FOR_EACH_FLOAT_TYPE(FLOAT_CASE)
3178 #undef FLOAT_CASE
3179 #define CHAR_CASE(name, type, ffiType)                                      \
3180   case TYPE_##name:                                                         \
3181     /* Convert to an integer. We have no idea what character encoding to */ \
3182     /* use, if any. */                                                      \
3183     result.setInt32(*static_cast<type*>(data));                             \
3184     break;
3185       CTYPES_FOR_EACH_CHAR_TYPE(CHAR_CASE)
3186 #undef CHAR_CASE
3187     case TYPE_char16_t: {
3188       // Convert the char16_t to a 1-character string.
3189       JSString* str = JS_NewUCStringCopyN(cx, static_cast<char16_t*>(data), 1);
3190       if (!str) {
3191         return false;
3192       }
3193 
3194       result.setString(str);
3195       break;
3196     }
3197     case TYPE_pointer:
3198     case TYPE_array:
3199     case TYPE_struct: {
3200       // We're about to create a new CData object to return. If the caller
3201       // doesn't want this, return early.
3202       if (wantPrimitive) {
3203         return NonPrimitiveError(cx, typeObj);
3204       }
3205 
3206       JSObject* obj = CData::Create(cx, typeObj, parentObj, data, ownResult);
3207       if (!obj) {
3208         return false;
3209       }
3210 
3211       result.setObject(*obj);
3212       break;
3213     }
3214     case TYPE_function:
3215       MOZ_CRASH("cannot return a FunctionType");
3216   }
3217 
3218   return true;
3219 }
3220 
3221 // Determine if the contents of a typed array can be converted without
3222 // ambiguity to a C type. Elements of a Int8Array are converted to
3223 // ctypes.int8_t, UInt8Array to ctypes.uint8_t, etc.
CanConvertTypedArrayItemTo(JSObject * baseType,JSObject * valObj,JSContext * cx)3224 bool CanConvertTypedArrayItemTo(JSObject* baseType, JSObject* valObj,
3225                                 JSContext* cx) {
3226   TypeCode baseTypeCode = CType::GetTypeCode(baseType);
3227   if (baseTypeCode == TYPE_void_t || baseTypeCode == TYPE_char) {
3228     return true;
3229   }
3230   TypeCode elementTypeCode;
3231   switch (JS_GetArrayBufferViewType(valObj)) {
3232     case Scalar::Int8:
3233       elementTypeCode = TYPE_int8_t;
3234       break;
3235     case Scalar::Uint8:
3236     case Scalar::Uint8Clamped:
3237       elementTypeCode = TYPE_uint8_t;
3238       break;
3239     case Scalar::Int16:
3240       elementTypeCode = TYPE_int16_t;
3241       break;
3242     case Scalar::Uint16:
3243       elementTypeCode = TYPE_uint16_t;
3244       break;
3245     case Scalar::Int32:
3246       elementTypeCode = TYPE_int32_t;
3247       break;
3248     case Scalar::Uint32:
3249       elementTypeCode = TYPE_uint32_t;
3250       break;
3251     case Scalar::Float32:
3252       elementTypeCode = TYPE_float32_t;
3253       break;
3254     case Scalar::Float64:
3255       elementTypeCode = TYPE_float64_t;
3256       break;
3257     default:
3258       return false;
3259   }
3260 
3261   return elementTypeCode == baseTypeCode;
3262 }
3263 
3264 // Implicitly convert Value 'val' to a C binary representation of CType
3265 // 'targetType', storing the result in 'buffer'. Adequate space must be
3266 // provided in 'buffer' by the caller. This function generally does minimal
3267 // coercion between types. There are two cases in which this function is used:
3268 // 1) The target buffer is internal to a CData object; we simply write data
3269 //    into it.
3270 // 2) We are converting an argument for an ffi call, in which case 'convType'
3271 //    will be 'ConversionType::Argument'. This allows us to handle a special
3272 //    case: if necessary, we can autoconvert a JS string primitive to a
3273 //    pointer-to-character type. In this case, ownership of the allocated string
3274 //    is handed off to the caller; 'freePointer' will be set to indicate this.
ImplicitConvert(JSContext * cx,HandleValue val,JSObject * targetType_,void * buffer,ConversionType convType,bool * freePointer,HandleObject funObj=nullptr,unsigned argIndex=0,HandleObject arrObj=nullptr,unsigned arrIndex=0)3275 static bool ImplicitConvert(JSContext* cx, HandleValue val,
3276                             JSObject* targetType_, void* buffer,
3277                             ConversionType convType, bool* freePointer,
3278                             HandleObject funObj = nullptr,
3279                             unsigned argIndex = 0,
3280                             HandleObject arrObj = nullptr,
3281                             unsigned arrIndex = 0) {
3282   RootedObject targetType(cx, targetType_);
3283   MOZ_ASSERT(CType::IsSizeDefined(targetType));
3284 
3285   // First, check if val is either a CData object or a CDataFinalizer
3286   // of type targetType.
3287   JSObject* sourceData = nullptr;
3288   JSObject* sourceType = nullptr;
3289   RootedObject valObj(cx, nullptr);
3290   if (val.isObject()) {
3291     valObj = &val.toObject();
3292     if (CData::IsCDataMaybeUnwrap(&valObj)) {
3293       sourceData = valObj;
3294       sourceType = CData::GetCType(sourceData);
3295 
3296       // If the types are equal, copy the buffer contained within the CData.
3297       // (Note that the buffers may overlap partially or completely.)
3298       if (CType::TypesEqual(sourceType, targetType)) {
3299         size_t size = CType::GetSize(sourceType);
3300         memmove(buffer, CData::GetData(sourceData), size);
3301         return true;
3302       }
3303     } else if (CDataFinalizer::IsCDataFinalizer(valObj)) {
3304       sourceData = valObj;
3305       sourceType = CDataFinalizer::GetCType(cx, sourceData);
3306 
3307       auto* p = (CDataFinalizer::Private*)JS::GetPrivate(sourceData);
3308 
3309       if (!p) {
3310         // We have called |dispose| or |forget| already.
3311         return EmptyFinalizerError(cx, convType, funObj, argIndex);
3312       }
3313 
3314       // If the types are equal, copy the buffer contained within the CData.
3315       if (CType::TypesEqual(sourceType, targetType)) {
3316         memmove(buffer, p->cargs, p->cargs_size);
3317         return true;
3318       }
3319     }
3320   }
3321 
3322   TypeCode targetCode = CType::GetTypeCode(targetType);
3323 
3324   switch (targetCode) {
3325     case TYPE_bool: {
3326       // Do not implicitly lose bits, but allow the values 0, 1, and -0.
3327       // Programs can convert explicitly, if needed, using `Boolean(v)` or
3328       // `!!v`.
3329       bool result;
3330       if (!jsvalToBool(cx, val, &result)) {
3331         return ConvError(cx, "boolean", val, convType, funObj, argIndex, arrObj,
3332                          arrIndex);
3333       }
3334       *static_cast<bool*>(buffer) = result;
3335       break;
3336     }
3337 #define CHAR16_CASE(name, type, ffiType)                                     \
3338   case TYPE_##name: {                                                        \
3339     /* Convert from a 1-character string, regardless of encoding, */         \
3340     /* or from an integer, provided the result fits in 'type'. */            \
3341     type result = 0;                                                         \
3342     if (val.isString()) {                                                    \
3343       JSString* str = val.toString();                                        \
3344       if (str->length() != 1)                                                \
3345         return ConvError(cx, #name, val, convType, funObj, argIndex, arrObj, \
3346                          arrIndex);                                          \
3347       JSLinearString* linear = str->ensureLinear(cx);                        \
3348       if (!linear) return false;                                             \
3349       result = linear->latin1OrTwoByteChar(0);                               \
3350     } else if (!jsvalToInteger(cx, val, &result)) {                          \
3351       return ConvError(cx, #name, val, convType, funObj, argIndex, arrObj,   \
3352                        arrIndex);                                            \
3353     }                                                                        \
3354     *static_cast<type*>(buffer) = result;                                    \
3355     break;                                                                   \
3356   }
3357       CTYPES_FOR_EACH_CHAR16_TYPE(CHAR16_CASE)
3358 #undef CHAR16_CASE
3359 #define INTEGRAL_CASE(name, type, ffiType)                                 \
3360   case TYPE_##name: {                                                      \
3361     /* Do not implicitly lose bits. */                                     \
3362     type result;                                                           \
3363     if (!jsvalToInteger(cx, val, &result))                                 \
3364       return ConvError(cx, #name, val, convType, funObj, argIndex, arrObj, \
3365                        arrIndex);                                          \
3366     *static_cast<type*>(buffer) = result;                                  \
3367     break;                                                                 \
3368   }
3369       CTYPES_FOR_EACH_INT_TYPE(INTEGRAL_CASE)
3370       CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGRAL_CASE)
3371       // It's hard to believe ctypes.char16_t("f") should work yet
3372       // ctypes.char("f") should not.  Ditto for ctypes.{un,}signed_char.  But
3373       // this is how ctypes has always worked, so preserve these semantics, and
3374       // don't switch to an algorithm similar to that in DEFINE_CHAR16_TYPE
3375       // above, just yet.
3376       CTYPES_FOR_EACH_CHAR_TYPE(INTEGRAL_CASE)
3377 #undef INTEGRAL_CASE
3378 #define FLOAT_CASE(name, type, ffiType)                                    \
3379   case TYPE_##name: {                                                      \
3380     type result;                                                           \
3381     if (!jsvalToFloat(cx, val, &result))                                   \
3382       return ConvError(cx, #name, val, convType, funObj, argIndex, arrObj, \
3383                        arrIndex);                                          \
3384     *static_cast<type*>(buffer) = result;                                  \
3385     break;                                                                 \
3386   }
3387       CTYPES_FOR_EACH_FLOAT_TYPE(FLOAT_CASE)
3388 #undef FLOAT_CASE
3389     case TYPE_pointer: {
3390       if (val.isNull()) {
3391         // Convert to a null pointer.
3392         *static_cast<void**>(buffer) = nullptr;
3393         break;
3394       }
3395 
3396       JS::Rooted<JSObject*> baseType(cx, PointerType::GetBaseType(targetType));
3397       if (sourceData) {
3398         // First, determine if the targetType is ctypes.void_t.ptr.
3399         TypeCode sourceCode = CType::GetTypeCode(sourceType);
3400         void* sourceBuffer = CData::GetData(sourceData);
3401         bool voidptrTarget = CType::GetTypeCode(baseType) == TYPE_void_t;
3402 
3403         if (sourceCode == TYPE_pointer && voidptrTarget) {
3404           // Autoconvert if targetType is ctypes.voidptr_t.
3405           *static_cast<void**>(buffer) = *static_cast<void**>(sourceBuffer);
3406           break;
3407         }
3408         if (sourceCode == TYPE_array) {
3409           // Autoconvert an array to a ctypes.void_t.ptr or to
3410           // sourceType.elementType.ptr, just like C.
3411           JSObject* elementType = ArrayType::GetBaseType(sourceType);
3412           if (voidptrTarget || CType::TypesEqual(baseType, elementType)) {
3413             *static_cast<void**>(buffer) = sourceBuffer;
3414             break;
3415           }
3416         }
3417 
3418       } else if (convType == ConversionType::Argument && val.isString()) {
3419         // Convert the string for the ffi call. This requires allocating space
3420         // which the caller assumes ownership of.
3421         // TODO: Extend this so we can safely convert strings at other times
3422         // also.
3423         JSString* sourceString = val.toString();
3424         size_t sourceLength = sourceString->length();
3425         Rooted<JSLinearString*> sourceLinear(cx,
3426                                              sourceString->ensureLinear(cx));
3427         if (!sourceLinear) {
3428           return false;
3429         }
3430 
3431         switch (CType::GetTypeCode(baseType)) {
3432           case TYPE_char:
3433           case TYPE_signed_char:
3434           case TYPE_unsigned_char: {
3435             // Reject if unpaired surrogate characters are present.
3436             if (!ReportErrorIfUnpairedSurrogatePresent(cx, sourceLinear)) {
3437               return false;
3438             }
3439 
3440             // Convert from UTF-16 to UTF-8.
3441             size_t nbytes = JS::GetDeflatedUTF8StringLength(sourceLinear);
3442 
3443             char** charBuffer = static_cast<char**>(buffer);
3444             *charBuffer = cx->pod_malloc<char>(nbytes + 1);
3445             if (!*charBuffer) {
3446               return false;
3447             }
3448 
3449             nbytes = JS::DeflateStringToUTF8Buffer(
3450                 sourceLinear, mozilla::Span(*charBuffer, nbytes));
3451             (*charBuffer)[nbytes] = '\0';
3452             *freePointer = true;
3453             break;
3454           }
3455           case TYPE_char16_t: {
3456             // Copy the char16_t string data. (We could provide direct access to
3457             // the JSString's buffer, but this approach is safer if the caller
3458             // happens to modify the string.)
3459             char16_t** char16Buffer = static_cast<char16_t**>(buffer);
3460             *char16Buffer = cx->pod_malloc<char16_t>(sourceLength + 1);
3461             if (!*char16Buffer) {
3462               return false;
3463             }
3464 
3465             *freePointer = true;
3466 
3467             CopyChars(*char16Buffer, *sourceLinear);
3468             (*char16Buffer)[sourceLength] = '\0';
3469             break;
3470           }
3471           default:
3472             return ConvError(cx, targetType, val, convType, funObj, argIndex,
3473                              arrObj, arrIndex);
3474         }
3475         break;
3476       } else if (val.isObject() && JS::IsArrayBufferObject(valObj)) {
3477         // Convert ArrayBuffer to pointer without any copy. This is only valid
3478         // when converting an argument to a function call, as it is possible for
3479         // the pointer to be invalidated by anything that runs JS code. (It is
3480         // invalid to invoke JS code from a ctypes function call.)
3481         if (convType != ConversionType::Argument) {
3482           return ConvError(cx, targetType, val, convType, funObj, argIndex,
3483                            arrObj, arrIndex);
3484         }
3485         void* ptr;
3486         {
3487           JS::AutoCheckCannotGC nogc;
3488           bool isShared;
3489           ptr = JS::GetArrayBufferData(valObj, &isShared, nogc);
3490           MOZ_ASSERT(!isShared);  // Because ArrayBuffer
3491         }
3492         if (!ptr) {
3493           return ConvError(cx, targetType, val, convType, funObj, argIndex,
3494                            arrObj, arrIndex);
3495         }
3496         *static_cast<void**>(buffer) = ptr;
3497         break;
3498       } else if (val.isObject() && JS::IsSharedArrayBufferObject(valObj)) {
3499         // CTypes has not yet opted in to allowing shared memory pointers
3500         // to escape.  Exporting a pointer to the shared buffer without
3501         // indicating sharedness would expose client code to races.
3502         return ConvError(cx, targetType, val, convType, funObj, argIndex,
3503                          arrObj, arrIndex);
3504       } else if (val.isObject() && JS_IsArrayBufferViewObject(valObj)) {
3505         // Same as ArrayBuffer, above, though note that this will take the
3506         // offset of the view into account.
3507         if (!CanConvertTypedArrayItemTo(baseType, valObj, cx)) {
3508           return ConvError(cx, targetType, val, convType, funObj, argIndex,
3509                            arrObj, arrIndex);
3510         }
3511         if (convType != ConversionType::Argument) {
3512           return ConvError(cx, targetType, val, convType, funObj, argIndex,
3513                            arrObj, arrIndex);
3514         }
3515         void* ptr;
3516         {
3517           JS::AutoCheckCannotGC nogc;
3518           bool isShared;
3519           ptr = JS_GetArrayBufferViewData(valObj, &isShared, nogc);
3520           if (isShared) {
3521             // Opt out of shared memory, for now.  Exporting a
3522             // pointer to the shared buffer without indicating
3523             // sharedness would expose client code to races.
3524             ptr = nullptr;
3525           }
3526         }
3527         if (!ptr) {
3528           return ConvError(cx, targetType, val, convType, funObj, argIndex,
3529                            arrObj, arrIndex);
3530         }
3531         *static_cast<void**>(buffer) = ptr;
3532         break;
3533       }
3534       return ConvError(cx, targetType, val, convType, funObj, argIndex, arrObj,
3535                        arrIndex);
3536     }
3537     case TYPE_array: {
3538       MOZ_ASSERT(!funObj);
3539 
3540       RootedObject baseType(cx, ArrayType::GetBaseType(targetType));
3541       size_t targetLength = ArrayType::GetLength(targetType);
3542 
3543       if (val.isString()) {
3544         JSString* sourceString = val.toString();
3545         size_t sourceLength = sourceString->length();
3546         Rooted<JSLinearString*> sourceLinear(cx,
3547                                              sourceString->ensureLinear(cx));
3548         if (!sourceLinear) {
3549           return false;
3550         }
3551 
3552         switch (CType::GetTypeCode(baseType)) {
3553           case TYPE_char:
3554           case TYPE_signed_char:
3555           case TYPE_unsigned_char: {
3556             // Reject if unpaired surrogate characters are present.
3557             if (!ReportErrorIfUnpairedSurrogatePresent(cx, sourceLinear)) {
3558               return false;
3559             }
3560 
3561             // Convert from UTF-16 or Latin1 to UTF-8.
3562             size_t nbytes = JS::GetDeflatedUTF8StringLength(sourceLinear);
3563 
3564             if (targetLength < nbytes) {
3565               MOZ_ASSERT(!funObj);
3566               return ArrayLengthOverflow(cx, targetLength, targetType, nbytes,
3567                                          val, convType);
3568             }
3569 
3570             char* charBuffer = static_cast<char*>(buffer);
3571             nbytes = JS::DeflateStringToUTF8Buffer(
3572                 sourceLinear, mozilla::Span(charBuffer, nbytes));
3573 
3574             if (targetLength > nbytes) {
3575               charBuffer[nbytes] = '\0';
3576             }
3577 
3578             break;
3579           }
3580           case TYPE_char16_t: {
3581             // Copy the string data, char16_t for char16_t, including the
3582             // terminator if there's space.
3583             if (targetLength < sourceLength) {
3584               MOZ_ASSERT(!funObj);
3585               return ArrayLengthOverflow(cx, targetLength, targetType,
3586                                          sourceLength, val, convType);
3587             }
3588 
3589             char16_t* dest = static_cast<char16_t*>(buffer);
3590             CopyChars(dest, *sourceLinear);
3591 
3592             if (targetLength > sourceLength) {
3593               dest[sourceLength] = '\0';
3594             }
3595 
3596             break;
3597           }
3598           default:
3599             return ConvError(cx, targetType, val, convType, funObj, argIndex,
3600                              arrObj, arrIndex);
3601         }
3602       } else {
3603         ESClass cls;
3604         if (!GetClassOfValue(cx, val, &cls)) {
3605           return false;
3606         }
3607 
3608         if (cls == ESClass::Array) {
3609           // Convert each element of the array by calling ImplicitConvert.
3610           uint32_t sourceLength;
3611           if (!JS::GetArrayLength(cx, valObj, &sourceLength) ||
3612               targetLength != size_t(sourceLength)) {
3613             MOZ_ASSERT(!funObj);
3614             return ArrayLengthMismatch(cx, targetLength, targetType,
3615                                        size_t(sourceLength), val, convType);
3616           }
3617 
3618           // Convert into an intermediate, in case of failure.
3619           size_t elementSize = CType::GetSize(baseType);
3620           size_t arraySize = elementSize * targetLength;
3621           auto intermediate = cx->make_pod_array<char>(arraySize);
3622           if (!intermediate) {
3623             return false;
3624           }
3625 
3626           RootedValue item(cx);
3627           for (uint32_t i = 0; i < sourceLength; ++i) {
3628             if (!JS_GetElement(cx, valObj, i, &item)) {
3629               return false;
3630             }
3631 
3632             char* data = intermediate.get() + elementSize * i;
3633             if (!ImplicitConvert(cx, item, baseType, data, convType, nullptr,
3634                                  funObj, argIndex, targetType, i))
3635               return false;
3636           }
3637 
3638           memcpy(buffer, intermediate.get(), arraySize);
3639         } else if (cls == ESClass::ArrayBuffer ||
3640                    cls == ESClass::SharedArrayBuffer) {
3641           // Check that array is consistent with type, then
3642           // copy the array.
3643           const bool bufferShared = cls == ESClass::SharedArrayBuffer;
3644           size_t sourceLength = bufferShared
3645                                     ? JS::GetSharedArrayBufferByteLength(valObj)
3646                                     : JS::GetArrayBufferByteLength(valObj);
3647           size_t elementSize = CType::GetSize(baseType);
3648           size_t arraySize = elementSize * targetLength;
3649           if (arraySize != sourceLength) {
3650             MOZ_ASSERT(!funObj);
3651             return ArrayLengthMismatch(cx, arraySize, targetType, sourceLength,
3652                                        val, convType);
3653           }
3654           SharedMem<void*> target = SharedMem<void*>::unshared(buffer);
3655           JS::AutoCheckCannotGC nogc;
3656           bool isShared;
3657           SharedMem<void*> src =
3658               (bufferShared
3659                    ? SharedMem<void*>::shared(
3660                          JS::GetSharedArrayBufferData(valObj, &isShared, nogc))
3661                    : SharedMem<void*>::unshared(
3662                          JS::GetArrayBufferData(valObj, &isShared, nogc)));
3663           MOZ_ASSERT(isShared == bufferShared);
3664           jit::AtomicOperations::memcpySafeWhenRacy(target, src, sourceLength);
3665           break;
3666         } else if (JS_IsTypedArrayObject(valObj)) {
3667           // Check that array is consistent with type, then
3668           // copy the array.  It is OK to copy from shared to unshared
3669           // or vice versa.
3670           if (!CanConvertTypedArrayItemTo(baseType, valObj, cx)) {
3671             return ConvError(cx, targetType, val, convType, funObj, argIndex,
3672                              arrObj, arrIndex);
3673           }
3674 
3675           size_t sourceLength = JS_GetTypedArrayByteLength(valObj);
3676           size_t elementSize = CType::GetSize(baseType);
3677           size_t arraySize = elementSize * targetLength;
3678           if (arraySize != sourceLength) {
3679             MOZ_ASSERT(!funObj);
3680             return ArrayLengthMismatch(cx, arraySize, targetType, sourceLength,
3681                                        val, convType);
3682           }
3683           SharedMem<void*> target = SharedMem<void*>::unshared(buffer);
3684           JS::AutoCheckCannotGC nogc;
3685           bool isShared;
3686           SharedMem<void*> src = SharedMem<void*>::shared(
3687               JS_GetArrayBufferViewData(valObj, &isShared, nogc));
3688           jit::AtomicOperations::memcpySafeWhenRacy(target, src, sourceLength);
3689           break;
3690         } else {
3691           // Don't implicitly convert to string. Users can implicitly convert
3692           // with `String(x)` or `""+x`.
3693           return ConvError(cx, targetType, val, convType, funObj, argIndex,
3694                            arrObj, arrIndex);
3695         }
3696       }
3697       break;
3698     }
3699     case TYPE_struct: {
3700       if (val.isObject() && !sourceData) {
3701         // Enumerate the properties of the object; if they match the struct
3702         // specification, convert the fields.
3703         Rooted<IdVector> props(cx, IdVector(cx));
3704         if (!JS_Enumerate(cx, valObj, &props)) {
3705           return false;
3706         }
3707 
3708         // Convert into an intermediate, in case of failure.
3709         size_t structSize = CType::GetSize(targetType);
3710         auto intermediate = cx->make_pod_array<char>(structSize);
3711         if (!intermediate) {
3712           return false;
3713         }
3714 
3715         const FieldInfoHash* fields = StructType::GetFieldInfo(targetType);
3716         if (props.length() != fields->count()) {
3717           return FieldCountMismatch(cx, fields->count(), targetType,
3718                                     props.length(), val, convType, funObj,
3719                                     argIndex);
3720         }
3721 
3722         RootedId id(cx);
3723         for (size_t i = 0; i < props.length(); ++i) {
3724           id = props[i];
3725 
3726           if (!JSID_IS_STRING(id)) {
3727             return PropNameNonStringError(cx, id, val, convType, funObj,
3728                                           argIndex);
3729           }
3730 
3731           JSLinearString* name = JSID_TO_LINEAR_STRING(id);
3732           const FieldInfo* field =
3733               StructType::LookupField(cx, targetType, name);
3734           if (!field) {
3735             return false;
3736           }
3737 
3738           RootedValue prop(cx);
3739           if (!JS_GetPropertyById(cx, valObj, id, &prop)) {
3740             return false;
3741           }
3742 
3743           // Convert the field via ImplicitConvert().
3744           char* fieldData = intermediate.get() + field->mOffset;
3745           if (!ImplicitConvert(cx, prop, field->mType, fieldData, convType,
3746                                nullptr, funObj, argIndex, targetType, i))
3747             return false;
3748         }
3749 
3750         memcpy(buffer, intermediate.get(), structSize);
3751         break;
3752       }
3753 
3754       return ConvError(cx, targetType, val, convType, funObj, argIndex, arrObj,
3755                        arrIndex);
3756     }
3757     case TYPE_void_t:
3758     case TYPE_function:
3759       MOZ_CRASH("invalid type");
3760   }
3761 
3762   return true;
3763 }
3764 
3765 // Convert Value 'val' to a C binary representation of CType 'targetType',
3766 // storing the result in 'buffer'. This function is more forceful than
3767 // ImplicitConvert.
ExplicitConvert(JSContext * cx,HandleValue val,HandleObject targetType,void * buffer,ConversionType convType)3768 static bool ExplicitConvert(JSContext* cx, HandleValue val,
3769                             HandleObject targetType, void* buffer,
3770                             ConversionType convType) {
3771   // If ImplicitConvert succeeds, use that result.
3772   if (ImplicitConvert(cx, val, targetType, buffer, convType, nullptr)) {
3773     return true;
3774   }
3775 
3776   // If ImplicitConvert failed, and there is no pending exception, then assume
3777   // hard failure (out of memory, or some other similarly serious condition).
3778   // We store any pending exception in case we need to re-throw it.
3779   RootedValue ex(cx);
3780   if (!JS_GetPendingException(cx, &ex)) {
3781     return false;
3782   }
3783 
3784   // Otherwise, assume soft failure. Clear the pending exception so that we
3785   // can throw a different one as required.
3786   JS_ClearPendingException(cx);
3787 
3788   TypeCode type = CType::GetTypeCode(targetType);
3789 
3790   switch (type) {
3791     case TYPE_bool: {
3792       *static_cast<bool*>(buffer) = ToBoolean(val);
3793       break;
3794     }
3795 #define INTEGRAL_CASE(name, type, ffiType)                            \
3796   case TYPE_##name: {                                                 \
3797     /* Convert numeric values with a C-style cast, and */             \
3798     /* allow conversion from a base-10 or base-16 string. */          \
3799     type result;                                                      \
3800     bool overflow = false;                                            \
3801     if (!jsvalToIntegerExplicit(val, &result) &&                      \
3802         (!val.isString() ||                                           \
3803          !StringToInteger(cx, val.toString(), &result, &overflow))) { \
3804       if (overflow) {                                                 \
3805         return TypeOverflow(cx, #name, val);                          \
3806       }                                                               \
3807       return ConvError(cx, #name, val, convType);                     \
3808     }                                                                 \
3809     *static_cast<type*>(buffer) = result;                             \
3810     break;                                                            \
3811   }
3812       CTYPES_FOR_EACH_INT_TYPE(INTEGRAL_CASE)
3813       CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGRAL_CASE)
3814       CTYPES_FOR_EACH_CHAR_TYPE(INTEGRAL_CASE)
3815       CTYPES_FOR_EACH_CHAR16_TYPE(INTEGRAL_CASE)
3816 #undef INTEGRAL_CASE
3817     case TYPE_pointer: {
3818       // Convert a number, Int64 object, or UInt64 object to a pointer.
3819       uintptr_t result;
3820       if (!jsvalToPtrExplicit(cx, val, &result)) {
3821         return ConvError(cx, targetType, val, convType);
3822       }
3823       *static_cast<uintptr_t*>(buffer) = result;
3824       break;
3825     }
3826     case TYPE_float32_t:
3827     case TYPE_float64_t:
3828     case TYPE_float:
3829     case TYPE_double:
3830     case TYPE_array:
3831     case TYPE_struct:
3832       // ImplicitConvert is sufficient. Re-throw the exception it generated.
3833       JS_SetPendingException(cx, ex);
3834       return false;
3835     case TYPE_void_t:
3836     case TYPE_function:
3837       MOZ_CRASH("invalid type");
3838   }
3839   return true;
3840 }
3841 
3842 // Given a CType 'typeObj', generate a string describing the C type declaration
3843 // corresponding to 'typeObj'. For instance, the CType constructed from
3844 // 'ctypes.int32_t.ptr.array(4).ptr.ptr' will result in the type string
3845 // 'int32_t*(**)[4]'.
BuildTypeName(JSContext * cx,JSObject * typeObj_)3846 static JSString* BuildTypeName(JSContext* cx, JSObject* typeObj_) {
3847   AutoString result;
3848   RootedObject typeObj(cx, typeObj_);
3849 
3850   // Walk the hierarchy of types, outermost to innermost, building up the type
3851   // string. This consists of the base type, which goes on the left.
3852   // Derived type modifiers (* and []) build from the inside outward, with
3853   // pointers on the left and arrays on the right. An excellent description
3854   // of the rules for building C type declarations can be found at:
3855   // http://unixwiz.net/techtips/reading-cdecl.html
3856   TypeCode prevGrouping = CType::GetTypeCode(typeObj), currentGrouping;
3857   while (true) {
3858     currentGrouping = CType::GetTypeCode(typeObj);
3859     switch (currentGrouping) {
3860       case TYPE_pointer: {
3861         // Pointer types go on the left.
3862         PrependString(cx, result, "*");
3863 
3864         typeObj = PointerType::GetBaseType(typeObj);
3865         prevGrouping = currentGrouping;
3866         continue;
3867       }
3868       case TYPE_array: {
3869         if (prevGrouping == TYPE_pointer) {
3870           // Outer type is pointer, inner type is array. Grouping is required.
3871           PrependString(cx, result, "(");
3872           AppendString(cx, result, ")");
3873         }
3874 
3875         // Array types go on the right.
3876         AppendString(cx, result, "[");
3877         size_t length;
3878         if (ArrayType::GetSafeLength(typeObj, &length)) {
3879           IntegerToString(length, 10, result);
3880         }
3881 
3882         AppendString(cx, result, "]");
3883 
3884         typeObj = ArrayType::GetBaseType(typeObj);
3885         prevGrouping = currentGrouping;
3886         continue;
3887       }
3888       case TYPE_function: {
3889         FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
3890 
3891         // Add in the calling convention, if it's not cdecl.
3892         // There's no trailing or leading space needed here, as none of the
3893         // modifiers can produce a string beginning with an identifier ---
3894         // except for TYPE_function itself, which is fine because functions
3895         // can't return functions.
3896         ABICode abi = GetABICode(fninfo->mABI);
3897         if (abi == ABI_STDCALL) {
3898           PrependString(cx, result, "__stdcall");
3899         } else if (abi == ABI_THISCALL) {
3900           PrependString(cx, result, "__thiscall");
3901         } else if (abi == ABI_WINAPI) {
3902           PrependString(cx, result, "WINAPI");
3903         }
3904 
3905         // Function application binds more tightly than dereferencing, so
3906         // wrap pointer types in parens. Functions can't return functions
3907         // (only pointers to them), and arrays can't hold functions
3908         // (similarly), so we don't need to address those cases.
3909         if (prevGrouping == TYPE_pointer) {
3910           PrependString(cx, result, "(");
3911           AppendString(cx, result, ")");
3912         }
3913 
3914         // Argument list goes on the right.
3915         AppendString(cx, result, "(");
3916         for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
3917           RootedObject argType(cx, fninfo->mArgTypes[i]);
3918           JSString* argName = CType::GetName(cx, argType);
3919           AppendString(cx, result, argName);
3920           if (i != fninfo->mArgTypes.length() - 1 || fninfo->mIsVariadic)
3921             AppendString(cx, result, ", ");
3922         }
3923         if (fninfo->mIsVariadic) {
3924           AppendString(cx, result, "...");
3925         }
3926         AppendString(cx, result, ")");
3927 
3928         // Set 'typeObj' to the return type, and let the loop process it.
3929         // 'prevGrouping' doesn't matter here, because functions cannot return
3930         // arrays -- thus the parenthetical rules don't get tickled.
3931         typeObj = fninfo->mReturnType;
3932         continue;
3933       }
3934       default:
3935         // Either a basic or struct type. Use the type's name as the base type.
3936         break;
3937     }
3938     break;
3939   }
3940 
3941   // If prepending the base type name directly would splice two
3942   // identifiers, insert a space.
3943   if (IsAsciiAlpha(result[0]) || result[0] == '_') {
3944     PrependString(cx, result, " ");
3945   }
3946 
3947   // Stick the base type and derived type parts together.
3948   JSString* baseName = CType::GetName(cx, typeObj);
3949   PrependString(cx, result, baseName);
3950   if (!result) {
3951     return nullptr;
3952   }
3953   return NewUCString(cx, result.finish());
3954 }
3955 
3956 // Given a CType 'typeObj', generate a string 'result' such that 'eval(result)'
3957 // would construct the same CType. If 'makeShort' is true, assume that any
3958 // StructType 't' is bound to an in-scope variable of name 't.name', and use
3959 // that variable in place of generating a string to construct the type 't'.
3960 // (This means the type comparison function CType::TypesEqual will return true
3961 // when comparing the input and output of AppendTypeSource, since struct
3962 // equality is determined by strict JSObject pointer equality.)
BuildTypeSource(JSContext * cx,JSObject * typeObj_,bool makeShort,AutoString & result)3963 static void BuildTypeSource(JSContext* cx, JSObject* typeObj_, bool makeShort,
3964                             AutoString& result) {
3965   RootedObject typeObj(cx, typeObj_);
3966 
3967   // Walk the types, building up the toSource() string.
3968   switch (CType::GetTypeCode(typeObj)) {
3969     case TYPE_void_t:
3970 #define CASE_FOR_TYPE(name, type, ffiType) case TYPE_##name:
3971       CTYPES_FOR_EACH_TYPE(CASE_FOR_TYPE)
3972 #undef CASE_FOR_TYPE
3973       {
3974         AppendString(cx, result, "ctypes.");
3975         JSString* nameStr = CType::GetName(cx, typeObj);
3976         AppendString(cx, result, nameStr);
3977         break;
3978       }
3979     case TYPE_pointer: {
3980       RootedObject baseType(cx, PointerType::GetBaseType(typeObj));
3981 
3982       // Specialcase ctypes.voidptr_t.
3983       if (CType::GetTypeCode(baseType) == TYPE_void_t) {
3984         AppendString(cx, result, "ctypes.voidptr_t");
3985         break;
3986       }
3987 
3988       // Recursively build the source string, and append '.ptr'.
3989       BuildTypeSource(cx, baseType, makeShort, result);
3990       AppendString(cx, result, ".ptr");
3991       break;
3992     }
3993     case TYPE_function: {
3994       FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
3995 
3996       AppendString(cx, result, "ctypes.FunctionType(");
3997 
3998       switch (GetABICode(fninfo->mABI)) {
3999         case ABI_DEFAULT:
4000           AppendString(cx, result, "ctypes.default_abi, ");
4001           break;
4002         case ABI_STDCALL:
4003           AppendString(cx, result, "ctypes.stdcall_abi, ");
4004           break;
4005         case ABI_THISCALL:
4006           AppendString(cx, result, "ctypes.thiscall_abi, ");
4007           break;
4008         case ABI_WINAPI:
4009           AppendString(cx, result, "ctypes.winapi_abi, ");
4010           break;
4011         case INVALID_ABI:
4012           MOZ_CRASH("invalid abi");
4013       }
4014 
4015       // Recursively build the source string describing the function return and
4016       // argument types.
4017       BuildTypeSource(cx, fninfo->mReturnType, true, result);
4018 
4019       if (fninfo->mArgTypes.length() > 0) {
4020         AppendString(cx, result, ", [");
4021         for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
4022           BuildTypeSource(cx, fninfo->mArgTypes[i], true, result);
4023           if (i != fninfo->mArgTypes.length() - 1 || fninfo->mIsVariadic)
4024             AppendString(cx, result, ", ");
4025         }
4026         if (fninfo->mIsVariadic) {
4027           AppendString(cx, result, "\"...\"");
4028         }
4029         AppendString(cx, result, "]");
4030       }
4031 
4032       AppendString(cx, result, ")");
4033       break;
4034     }
4035     case TYPE_array: {
4036       // Recursively build the source string, and append '.array(n)',
4037       // where n is the array length, or the empty string if the array length
4038       // is undefined.
4039       JSObject* baseType = ArrayType::GetBaseType(typeObj);
4040       BuildTypeSource(cx, baseType, makeShort, result);
4041       AppendString(cx, result, ".array(");
4042 
4043       size_t length;
4044       if (ArrayType::GetSafeLength(typeObj, &length)) {
4045         IntegerToString(length, 10, result);
4046       }
4047 
4048       AppendString(cx, result, ")");
4049       break;
4050     }
4051     case TYPE_struct: {
4052       JSString* name = CType::GetName(cx, typeObj);
4053 
4054       if (makeShort) {
4055         // Shorten the type declaration by assuming that StructType 't' is bound
4056         // to an in-scope variable of name 't.name'.
4057         AppendString(cx, result, name);
4058         break;
4059       }
4060 
4061       // Write the full struct declaration.
4062       AppendString(cx, result, "ctypes.StructType(\"");
4063       AppendString(cx, result, name);
4064       AppendString(cx, result, "\"");
4065 
4066       // If it's an opaque struct, we're done.
4067       if (!CType::IsSizeDefined(typeObj)) {
4068         AppendString(cx, result, ")");
4069         break;
4070       }
4071 
4072       AppendString(cx, result, ", [");
4073 
4074       const FieldInfoHash* fields = StructType::GetFieldInfo(typeObj);
4075       size_t length = fields->count();
4076       Vector<const FieldInfoHash::Entry*, 64, SystemAllocPolicy> fieldsArray;
4077       if (!fieldsArray.resize(length)) {
4078         break;
4079       }
4080 
4081       for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
4082         fieldsArray[r.front().value().mIndex] = &r.front();
4083       }
4084 
4085       for (size_t i = 0; i < length; ++i) {
4086         const FieldInfoHash::Entry* entry = fieldsArray[i];
4087         AppendString(cx, result, "{ \"");
4088         AppendString(cx, result, entry->key());
4089         AppendString(cx, result, "\": ");
4090         BuildTypeSource(cx, entry->value().mType, true, result);
4091         AppendString(cx, result, " }");
4092         if (i != length - 1) {
4093           AppendString(cx, result, ", ");
4094         }
4095       }
4096 
4097       AppendString(cx, result, "])");
4098       break;
4099     }
4100   }
4101 }
4102 
4103 // Given a CData object of CType 'typeObj' with binary value 'data', generate a
4104 // string 'result' such that 'eval(result)' would construct a CData object with
4105 // the same CType and containing the same binary value. This assumes that any
4106 // StructType 't' is bound to an in-scope variable of name 't.name'. (This means
4107 // the type comparison function CType::TypesEqual will return true when
4108 // comparing the types, since struct equality is determined by strict JSObject
4109 // pointer equality.) Further, if 'isImplicit' is true, ensure that the
4110 // resulting string can ImplicitConvert successfully if passed to another data
4111 // constructor. (This is important when called recursively, since fields of
4112 // structs and arrays are converted with ImplicitConvert.)
BuildDataSource(JSContext * cx,HandleObject typeObj,void * data,bool isImplicit,AutoString & result)4113 [[nodiscard]] static bool BuildDataSource(JSContext* cx, HandleObject typeObj,
4114                                           void* data, bool isImplicit,
4115                                           AutoString& result) {
4116   TypeCode type = CType::GetTypeCode(typeObj);
4117   switch (type) {
4118     case TYPE_bool:
4119       if (*static_cast<bool*>(data)) {
4120         AppendString(cx, result, "true");
4121       } else {
4122         AppendString(cx, result, "false");
4123       }
4124       break;
4125 #define INTEGRAL_CASE(name, type, ffiType)                  \
4126   case TYPE_##name:                                         \
4127     /* Serialize as a primitive decimal integer. */         \
4128     IntegerToString(*static_cast<type*>(data), 10, result); \
4129     break;
4130       CTYPES_FOR_EACH_INT_TYPE(INTEGRAL_CASE)
4131 #undef INTEGRAL_CASE
4132 #define WRAPPED_INT_CASE(name, type, ffiType)               \
4133   case TYPE_##name:                                         \
4134     /* Serialize as a wrapped decimal integer. */           \
4135     if (!numeric_limits<type>::is_signed)                   \
4136       AppendString(cx, result, "ctypes.UInt64(\"");         \
4137     else                                                    \
4138       AppendString(cx, result, "ctypes.Int64(\"");          \
4139                                                             \
4140     IntegerToString(*static_cast<type*>(data), 10, result); \
4141     AppendString(cx, result, "\")");                        \
4142     break;
4143       CTYPES_FOR_EACH_WRAPPED_INT_TYPE(WRAPPED_INT_CASE)
4144 #undef WRAPPED_INT_CASE
4145 #define FLOAT_CASE(name, type, ffiType)             \
4146   case TYPE_##name: {                               \
4147     /* Serialize as a primitive double. */          \
4148     double fp = *static_cast<type*>(data);          \
4149     ToCStringBuf cbuf;                              \
4150     char* str = NumberToCString(cx, &cbuf, fp);     \
4151     if (!str || !result.append(str, strlen(str))) { \
4152       JS_ReportOutOfMemory(cx);                     \
4153       return false;                                 \
4154     }                                               \
4155     break;                                          \
4156   }
4157       CTYPES_FOR_EACH_FLOAT_TYPE(FLOAT_CASE)
4158 #undef FLOAT_CASE
4159 #define CHAR_CASE(name, type, ffiType)                      \
4160   case TYPE_##name:                                         \
4161     /* Serialize as an integer. */                          \
4162     IntegerToString(*static_cast<type*>(data), 10, result); \
4163     break;
4164       CTYPES_FOR_EACH_CHAR_TYPE(CHAR_CASE)
4165 #undef CHAR_CASE
4166     case TYPE_char16_t: {
4167       // Serialize as a 1-character JS string.
4168       JSString* str = JS_NewUCStringCopyN(cx, static_cast<char16_t*>(data), 1);
4169       if (!str) {
4170         return false;
4171       }
4172 
4173       // Escape characters, and quote as necessary.
4174       RootedValue valStr(cx, StringValue(str));
4175       JSString* src = JS_ValueToSource(cx, valStr);
4176       if (!src) {
4177         return false;
4178       }
4179 
4180       AppendString(cx, result, src);
4181       break;
4182     }
4183     case TYPE_pointer:
4184     case TYPE_function: {
4185       if (isImplicit) {
4186         // The result must be able to ImplicitConvert successfully.
4187         // Wrap in a type constructor, then serialize for ExplicitConvert.
4188         BuildTypeSource(cx, typeObj, true, result);
4189         AppendString(cx, result, "(");
4190       }
4191 
4192       // Serialize the pointer value as a wrapped hexadecimal integer.
4193       uintptr_t ptr = *static_cast<uintptr_t*>(data);
4194       AppendString(cx, result, "ctypes.UInt64(\"0x");
4195       IntegerToString(ptr, 16, result);
4196       AppendString(cx, result, "\")");
4197 
4198       if (isImplicit) {
4199         AppendString(cx, result, ")");
4200       }
4201 
4202       break;
4203     }
4204     case TYPE_array: {
4205       // Serialize each element of the array recursively. Each element must
4206       // be able to ImplicitConvert successfully.
4207       RootedObject baseType(cx, ArrayType::GetBaseType(typeObj));
4208       AppendString(cx, result, "[");
4209 
4210       size_t length = ArrayType::GetLength(typeObj);
4211       size_t elementSize = CType::GetSize(baseType);
4212       for (size_t i = 0; i < length; ++i) {
4213         char* element = static_cast<char*>(data) + elementSize * i;
4214         if (!BuildDataSource(cx, baseType, element, true, result)) {
4215           return false;
4216         }
4217 
4218         if (i + 1 < length) {
4219           AppendString(cx, result, ", ");
4220         }
4221       }
4222       AppendString(cx, result, "]");
4223       break;
4224     }
4225     case TYPE_struct: {
4226       if (isImplicit) {
4227         // The result must be able to ImplicitConvert successfully.
4228         // Serialize the data as an object with properties, rather than
4229         // a sequence of arguments to the StructType constructor.
4230         AppendString(cx, result, "{");
4231       }
4232 
4233       // Serialize each field of the struct recursively. Each field must
4234       // be able to ImplicitConvert successfully.
4235       const FieldInfoHash* fields = StructType::GetFieldInfo(typeObj);
4236       size_t length = fields->count();
4237       Vector<const FieldInfoHash::Entry*, 64, SystemAllocPolicy> fieldsArray;
4238       if (!fieldsArray.resize(length)) {
4239         return false;
4240       }
4241 
4242       for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
4243         fieldsArray[r.front().value().mIndex] = &r.front();
4244       }
4245 
4246       for (size_t i = 0; i < length; ++i) {
4247         const FieldInfoHash::Entry* entry = fieldsArray[i];
4248 
4249         if (isImplicit) {
4250           AppendString(cx, result, "\"");
4251           AppendString(cx, result, entry->key());
4252           AppendString(cx, result, "\": ");
4253         }
4254 
4255         char* fieldData = static_cast<char*>(data) + entry->value().mOffset;
4256         RootedObject entryType(cx, entry->value().mType);
4257         if (!BuildDataSource(cx, entryType, fieldData, true, result)) {
4258           return false;
4259         }
4260 
4261         if (i + 1 != length) {
4262           AppendString(cx, result, ", ");
4263         }
4264       }
4265 
4266       if (isImplicit) {
4267         AppendString(cx, result, "}");
4268       }
4269 
4270       break;
4271     }
4272     case TYPE_void_t:
4273       MOZ_CRASH("invalid type");
4274   }
4275 
4276   return true;
4277 }
4278 
4279 /*******************************************************************************
4280 ** JSAPI callback function implementations
4281 *******************************************************************************/
4282 
ConstructAbstract(JSContext * cx,unsigned argc,Value * vp)4283 bool ConstructAbstract(JSContext* cx, unsigned argc, Value* vp) {
4284   // Calling an abstract base class constructor is disallowed.
4285   return CannotConstructError(cx, "abstract type");
4286 }
4287 
4288 /*******************************************************************************
4289 ** CType implementation
4290 *******************************************************************************/
4291 
ConstructData(JSContext * cx,unsigned argc,Value * vp)4292 bool CType::ConstructData(JSContext* cx, unsigned argc, Value* vp) {
4293   CallArgs args = CallArgsFromVp(argc, vp);
4294   // get the callee object...
4295   RootedObject obj(cx, &args.callee());
4296   if (!CType::IsCType(obj)) {
4297     return IncompatibleCallee(cx, "CType constructor", obj);
4298   }
4299 
4300   // How we construct the CData object depends on what type we represent.
4301   // An instance 'd' of a CData object of type 't' has:
4302   //   * [[Class]] "CData"
4303   //   * __proto__ === t.prototype
4304   switch (GetTypeCode(obj)) {
4305     case TYPE_void_t:
4306       return CannotConstructError(cx, "void_t");
4307     case TYPE_function:
4308       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
4309                                 CTYPESMSG_FUNCTION_CONSTRUCT);
4310       return false;
4311     case TYPE_pointer:
4312       return PointerType::ConstructData(cx, obj, args);
4313     case TYPE_array:
4314       return ArrayType::ConstructData(cx, obj, args);
4315     case TYPE_struct:
4316       return StructType::ConstructData(cx, obj, args);
4317     default:
4318       return ConstructBasic(cx, obj, args);
4319   }
4320 }
4321 
ConstructBasic(JSContext * cx,HandleObject obj,const CallArgs & args)4322 bool CType::ConstructBasic(JSContext* cx, HandleObject obj,
4323                            const CallArgs& args) {
4324   if (args.length() > 1) {
4325     return ArgumentLengthError(cx, "CType constructor", "at most one", "");
4326   }
4327 
4328   // construct a CData object
4329   RootedObject result(cx, CData::Create(cx, obj, nullptr, nullptr, true));
4330   if (!result) {
4331     return false;
4332   }
4333 
4334   if (args.length() == 1) {
4335     if (!ExplicitConvert(cx, args[0], obj, CData::GetData(result),
4336                          ConversionType::Construct))
4337       return false;
4338   }
4339 
4340   args.rval().setObject(*result);
4341   return true;
4342 }
4343 
Create(JSContext * cx,HandleObject typeProto,HandleObject dataProto,TypeCode type,JSString * name_,HandleValue size,HandleValue align,ffi_type * ffiType)4344 JSObject* CType::Create(JSContext* cx, HandleObject typeProto,
4345                         HandleObject dataProto, TypeCode type, JSString* name_,
4346                         HandleValue size, HandleValue align,
4347                         ffi_type* ffiType) {
4348   RootedString name(cx, name_);
4349 
4350   // Create a CType object with the properties and slots common to all CTypes.
4351   // Each type object 't' has:
4352   //   * [[Class]] "CType"
4353   //   * __proto__ === 'typeProto'; one of ctypes.{CType,PointerType,ArrayType,
4354   //     StructType}.prototype
4355   //   * A constructor which creates and returns a CData object, containing
4356   //     binary data of the given type.
4357   //   * 'prototype' property:
4358   //     * [[Class]] "CDataProto"
4359   //     * __proto__ === 'dataProto'; an object containing properties and
4360   //       functions common to all CData objects of types derived from
4361   //       'typeProto'. (For instance, this could be ctypes.CData.prototype
4362   //       for simple types, or something representing structs for StructTypes.)
4363   //     * 'constructor' property === 't'
4364   //     * Additional properties specified by 'ps', as appropriate for the
4365   //       specific type instance 't'.
4366   RootedObject typeObj(cx,
4367                        JS_NewObjectWithGivenProto(cx, &sCTypeClass, typeProto));
4368   if (!typeObj) {
4369     return nullptr;
4370   }
4371 
4372   // Set up the reserved slots.
4373   JS_SetReservedSlot(typeObj, SLOT_TYPECODE, Int32Value(type));
4374   if (ffiType) {
4375     JS_SetReservedSlot(typeObj, SLOT_FFITYPE, PrivateValue(ffiType));
4376     if (type == TYPE_struct || type == TYPE_array) {
4377       AddCellMemory(typeObj, sizeof(ffi_type), MemoryUse::CTypeFFIType);
4378     }
4379   }
4380   if (name) {
4381     JS_SetReservedSlot(typeObj, SLOT_NAME, StringValue(name));
4382   }
4383   JS_SetReservedSlot(typeObj, SLOT_SIZE, size);
4384   JS_SetReservedSlot(typeObj, SLOT_ALIGN, align);
4385 
4386   if (dataProto) {
4387     // Set up the 'prototype' and 'prototype.constructor' properties.
4388     RootedObject prototype(
4389         cx, JS_NewObjectWithGivenProto(cx, &sCDataProtoClass, dataProto));
4390     if (!prototype) {
4391       return nullptr;
4392     }
4393 
4394     if (!JS_DefineProperty(cx, prototype, "constructor", typeObj,
4395                            JSPROP_READONLY | JSPROP_PERMANENT))
4396       return nullptr;
4397 
4398     // Set the 'prototype' object.
4399     // if (!JS_FreezeObject(cx, prototype)) // XXX fixme - see bug 541212!
4400     //  return nullptr;
4401     JS_SetReservedSlot(typeObj, SLOT_PROTO, ObjectValue(*prototype));
4402   }
4403 
4404   if (!JS_FreezeObject(cx, typeObj)) {
4405     return nullptr;
4406   }
4407 
4408   // Assert a sanity check on size and alignment: size % alignment should always
4409   // be zero.
4410   MOZ_ASSERT_IF(IsSizeDefined(typeObj),
4411                 GetSize(typeObj) % GetAlignment(typeObj) == 0);
4412 
4413   return typeObj;
4414 }
4415 
DefineBuiltin(JSContext * cx,HandleObject ctypesObj,const char * propName,JSObject * typeProto_,JSObject * dataProto_,const char * name,TypeCode type,HandleValue size,HandleValue align,ffi_type * ffiType)4416 JSObject* CType::DefineBuiltin(JSContext* cx, HandleObject ctypesObj,
4417                                const char* propName, JSObject* typeProto_,
4418                                JSObject* dataProto_, const char* name,
4419                                TypeCode type, HandleValue size,
4420                                HandleValue align, ffi_type* ffiType) {
4421   RootedObject typeProto(cx, typeProto_);
4422   RootedObject dataProto(cx, dataProto_);
4423 
4424   RootedString nameStr(cx, JS_NewStringCopyZ(cx, name));
4425   if (!nameStr) {
4426     return nullptr;
4427   }
4428 
4429   // Create a new CType object with the common properties and slots.
4430   RootedObject typeObj(cx, Create(cx, typeProto, dataProto, type, nameStr, size,
4431                                   align, ffiType));
4432   if (!typeObj) {
4433     return nullptr;
4434   }
4435 
4436   // Define the CType as a 'propName' property on 'ctypesObj'.
4437   if (!JS_DefineProperty(cx, ctypesObj, propName, typeObj,
4438                          JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
4439     return nullptr;
4440 
4441   return typeObj;
4442 }
4443 
FinalizeFFIType(JSFreeOp * fop,JSObject * obj,const Value & slot,size_t elementCount)4444 static void FinalizeFFIType(JSFreeOp* fop, JSObject* obj, const Value& slot,
4445                             size_t elementCount) {
4446   ffi_type* ffiType = static_cast<ffi_type*>(slot.toPrivate());
4447   size_t size = elementCount * sizeof(ffi_type*);
4448   fop->free_(obj, ffiType->elements, size, MemoryUse::CTypeFFITypeElements);
4449   fop->delete_(obj, ffiType, MemoryUse::CTypeFFIType);
4450 }
4451 
Finalize(JSFreeOp * fop,JSObject * obj)4452 void CType::Finalize(JSFreeOp* fop, JSObject* obj) {
4453   // Make sure our TypeCode slot is legit. If it's not, bail.
4454   Value slot = JS::GetReservedSlot(obj, SLOT_TYPECODE);
4455   if (slot.isUndefined()) {
4456     return;
4457   }
4458 
4459   // The contents of our slots depends on what kind of type we are.
4460   switch (TypeCode(slot.toInt32())) {
4461     case TYPE_function: {
4462       // Free the FunctionInfo.
4463       slot = JS::GetReservedSlot(obj, SLOT_FNINFO);
4464       if (!slot.isUndefined()) {
4465         auto fninfo = static_cast<FunctionInfo*>(slot.toPrivate());
4466         fop->delete_(obj, fninfo, MemoryUse::CTypeFunctionInfo);
4467       }
4468       break;
4469     }
4470 
4471     case TYPE_struct: {
4472       size_t fieldCount = 0;
4473 
4474       // Free the FieldInfoHash table.
4475       slot = JS::GetReservedSlot(obj, SLOT_FIELDINFO);
4476       if (!slot.isUndefined()) {
4477         auto info = static_cast<FieldInfoHash*>(slot.toPrivate());
4478         fieldCount = info->count();
4479         fop->delete_(obj, info, MemoryUse::CTypeFieldInfo);
4480       }
4481 
4482       // Free the ffi_type info.
4483       Value slot = JS::GetReservedSlot(obj, SLOT_FFITYPE);
4484       if (!slot.isUndefined()) {
4485         size_t elementCount = fieldCount != 0 ? fieldCount + 1 : 2;
4486         FinalizeFFIType(fop, obj, slot, elementCount);
4487       }
4488 
4489       // Free the ffi_type info.
4490       break;
4491     }
4492 
4493     case TYPE_array: {
4494       // Free the ffi_type info.
4495       Value slot = JS::GetReservedSlot(obj, SLOT_FFITYPE);
4496       if (!slot.isUndefined()) {
4497         size_t elementCount = ArrayType::GetLength(obj);
4498         FinalizeFFIType(fop, obj, slot, elementCount);
4499       }
4500       break;
4501     }
4502 
4503     default:
4504       // Nothing to do here.
4505       break;
4506   }
4507 }
4508 
Trace(JSTracer * trc,JSObject * obj)4509 void CType::Trace(JSTracer* trc, JSObject* obj) {
4510   // Make sure our TypeCode slot is legit. If it's not, bail.
4511   Value slot = obj->as<NativeObject>().getSlot(SLOT_TYPECODE);
4512   if (slot.isUndefined()) {
4513     return;
4514   }
4515 
4516   // The contents of our slots depends on what kind of type we are.
4517   switch (TypeCode(slot.toInt32())) {
4518     case TYPE_struct: {
4519       slot = obj->as<NativeObject>().getReservedSlot(SLOT_FIELDINFO);
4520       if (slot.isUndefined()) {
4521         return;
4522       }
4523 
4524       FieldInfoHash* fields = static_cast<FieldInfoHash*>(slot.toPrivate());
4525       fields->trace(trc);
4526       break;
4527     }
4528     case TYPE_function: {
4529       // Check if we have a FunctionInfo.
4530       slot = obj->as<NativeObject>().getReservedSlot(SLOT_FNINFO);
4531       if (slot.isUndefined()) {
4532         return;
4533       }
4534 
4535       FunctionInfo* fninfo = static_cast<FunctionInfo*>(slot.toPrivate());
4536       MOZ_ASSERT(fninfo);
4537 
4538       // Identify our objects to the tracer.
4539       TraceEdge(trc, &fninfo->mABI, "abi");
4540       TraceEdge(trc, &fninfo->mReturnType, "returnType");
4541       fninfo->mArgTypes.trace(trc);
4542 
4543       break;
4544     }
4545     default:
4546       // Nothing to do here.
4547       break;
4548   }
4549 }
4550 
IsCType(JSObject * obj)4551 bool CType::IsCType(JSObject* obj) { return obj->hasClass(&sCTypeClass); }
4552 
IsCTypeProto(JSObject * obj)4553 bool CType::IsCTypeProto(JSObject* obj) {
4554   return obj->hasClass(&sCTypeProtoClass);
4555 }
4556 
GetTypeCode(JSObject * typeObj)4557 TypeCode CType::GetTypeCode(JSObject* typeObj) {
4558   MOZ_ASSERT(IsCType(typeObj));
4559 
4560   Value result = JS::GetReservedSlot(typeObj, SLOT_TYPECODE);
4561   return TypeCode(result.toInt32());
4562 }
4563 
TypesEqual(JSObject * t1,JSObject * t2)4564 bool CType::TypesEqual(JSObject* t1, JSObject* t2) {
4565   MOZ_ASSERT(IsCType(t1) && IsCType(t2));
4566 
4567   // Fast path: check for object equality.
4568   if (t1 == t2) {
4569     return true;
4570   }
4571 
4572   // First, perform shallow comparison.
4573   TypeCode c1 = GetTypeCode(t1);
4574   TypeCode c2 = GetTypeCode(t2);
4575   if (c1 != c2) {
4576     return false;
4577   }
4578 
4579   // Determine whether the types require shallow or deep comparison.
4580   switch (c1) {
4581     case TYPE_pointer: {
4582       // Compare base types.
4583       JSObject* b1 = PointerType::GetBaseType(t1);
4584       JSObject* b2 = PointerType::GetBaseType(t2);
4585       return TypesEqual(b1, b2);
4586     }
4587     case TYPE_function: {
4588       FunctionInfo* f1 = FunctionType::GetFunctionInfo(t1);
4589       FunctionInfo* f2 = FunctionType::GetFunctionInfo(t2);
4590 
4591       // Compare abi, return type, and argument types.
4592       if (f1->mABI != f2->mABI) {
4593         return false;
4594       }
4595 
4596       if (!TypesEqual(f1->mReturnType, f2->mReturnType)) {
4597         return false;
4598       }
4599 
4600       if (f1->mArgTypes.length() != f2->mArgTypes.length()) {
4601         return false;
4602       }
4603 
4604       if (f1->mIsVariadic != f2->mIsVariadic) {
4605         return false;
4606       }
4607 
4608       for (size_t i = 0; i < f1->mArgTypes.length(); ++i) {
4609         if (!TypesEqual(f1->mArgTypes[i], f2->mArgTypes[i])) {
4610           return false;
4611         }
4612       }
4613 
4614       return true;
4615     }
4616     case TYPE_array: {
4617       // Compare length, then base types.
4618       // An undefined length array matches other undefined length arrays.
4619       size_t s1 = 0, s2 = 0;
4620       bool d1 = ArrayType::GetSafeLength(t1, &s1);
4621       bool d2 = ArrayType::GetSafeLength(t2, &s2);
4622       if (d1 != d2 || (d1 && s1 != s2)) {
4623         return false;
4624       }
4625 
4626       JSObject* b1 = ArrayType::GetBaseType(t1);
4627       JSObject* b2 = ArrayType::GetBaseType(t2);
4628       return TypesEqual(b1, b2);
4629     }
4630     case TYPE_struct:
4631       // Require exact type object equality.
4632       return false;
4633     default:
4634       // Shallow comparison is sufficient.
4635       return true;
4636   }
4637 }
4638 
GetSafeSize(JSObject * obj,size_t * result)4639 bool CType::GetSafeSize(JSObject* obj, size_t* result) {
4640   MOZ_ASSERT(CType::IsCType(obj));
4641 
4642   Value size = JS::GetReservedSlot(obj, SLOT_SIZE);
4643 
4644   // The "size" property can be an int, a double, or JS::UndefinedValue()
4645   // (for arrays of undefined length), and must always fit in a size_t.
4646   if (size.isInt32()) {
4647     *result = size.toInt32();
4648     return true;
4649   }
4650   if (size.isDouble()) {
4651     *result = Convert<size_t>(size.toDouble());
4652     return true;
4653   }
4654 
4655   MOZ_ASSERT(size.isUndefined());
4656   return false;
4657 }
4658 
GetSize(JSObject * obj)4659 size_t CType::GetSize(JSObject* obj) {
4660   MOZ_ASSERT(CType::IsCType(obj));
4661 
4662   Value size = JS::GetReservedSlot(obj, SLOT_SIZE);
4663 
4664   MOZ_ASSERT(!size.isUndefined());
4665 
4666   // The "size" property can be an int, a double, or JS::UndefinedValue()
4667   // (for arrays of undefined length), and must always fit in a size_t.
4668   // For callers who know it can never be JS::UndefinedValue(), return a size_t
4669   // directly.
4670   if (size.isInt32()) {
4671     return size.toInt32();
4672   }
4673   return Convert<size_t>(size.toDouble());
4674 }
4675 
IsSizeDefined(JSObject * obj)4676 bool CType::IsSizeDefined(JSObject* obj) {
4677   MOZ_ASSERT(CType::IsCType(obj));
4678 
4679   Value size = JS::GetReservedSlot(obj, SLOT_SIZE);
4680 
4681   // The "size" property can be an int, a double, or JS::UndefinedValue()
4682   // (for arrays of undefined length), and must always fit in a size_t.
4683   MOZ_ASSERT(size.isInt32() || size.isDouble() || size.isUndefined());
4684   return !size.isUndefined();
4685 }
4686 
GetAlignment(JSObject * obj)4687 size_t CType::GetAlignment(JSObject* obj) {
4688   MOZ_ASSERT(CType::IsCType(obj));
4689 
4690   Value slot = JS::GetReservedSlot(obj, SLOT_ALIGN);
4691   return static_cast<size_t>(slot.toInt32());
4692 }
4693 
GetFFIType(JSContext * cx,JSObject * obj)4694 ffi_type* CType::GetFFIType(JSContext* cx, JSObject* obj) {
4695   MOZ_ASSERT(CType::IsCType(obj));
4696 
4697   Value slot = JS::GetReservedSlot(obj, SLOT_FFITYPE);
4698 
4699   if (!slot.isUndefined()) {
4700     return static_cast<ffi_type*>(slot.toPrivate());
4701   }
4702 
4703   UniquePtrFFIType result;
4704   switch (CType::GetTypeCode(obj)) {
4705     case TYPE_array:
4706       result = ArrayType::BuildFFIType(cx, obj);
4707       break;
4708 
4709     case TYPE_struct:
4710       result = StructType::BuildFFIType(cx, obj);
4711       break;
4712 
4713     default:
4714       MOZ_CRASH("simple types must have an ffi_type");
4715   }
4716 
4717   if (!result) {
4718     return nullptr;
4719   }
4720   JS_InitReservedSlot(obj, SLOT_FFITYPE, result.get(),
4721                       JS::MemoryUse::CTypeFFIType);
4722   return result.release();
4723 }
4724 
GetName(JSContext * cx,HandleObject obj)4725 JSString* CType::GetName(JSContext* cx, HandleObject obj) {
4726   MOZ_ASSERT(CType::IsCType(obj));
4727 
4728   Value string = JS::GetReservedSlot(obj, SLOT_NAME);
4729   if (!string.isUndefined()) {
4730     return string.toString();
4731   }
4732 
4733   // Build the type name lazily.
4734   JSString* name = BuildTypeName(cx, obj);
4735   if (!name) {
4736     return nullptr;
4737   }
4738   JS_SetReservedSlot(obj, SLOT_NAME, StringValue(name));
4739   return name;
4740 }
4741 
GetProtoFromCtor(JSObject * obj,CTypeProtoSlot slot)4742 JSObject* CType::GetProtoFromCtor(JSObject* obj, CTypeProtoSlot slot) {
4743   // Get ctypes.{Pointer,Array,Struct}Type.prototype from a reserved slot
4744   // on the type constructor.
4745   Value protoslot = js::GetFunctionNativeReserved(obj, SLOT_FN_CTORPROTO);
4746   JSObject* proto = &protoslot.toObject();
4747   MOZ_ASSERT(proto);
4748   MOZ_ASSERT(CType::IsCTypeProto(proto));
4749 
4750   // Get the desired prototype.
4751   Value result = JS::GetReservedSlot(proto, slot);
4752   return &result.toObject();
4753 }
4754 
GetProtoFromType(JSContext * cx,JSObject * objArg,CTypeProtoSlot slot)4755 JSObject* CType::GetProtoFromType(JSContext* cx, JSObject* objArg,
4756                                   CTypeProtoSlot slot) {
4757   MOZ_ASSERT(IsCType(objArg));
4758   RootedObject obj(cx, objArg);
4759 
4760   // Get the prototype of the type object.
4761   RootedObject proto(cx);
4762   if (!JS_GetPrototype(cx, obj, &proto)) {
4763     return nullptr;
4764   }
4765   MOZ_ASSERT(proto);
4766   MOZ_ASSERT(CType::IsCTypeProto(proto));
4767 
4768   // Get the requested ctypes.{Pointer,Array,Struct,Function}Type.prototype.
4769   Value result = JS::GetReservedSlot(proto, slot);
4770   MOZ_ASSERT(result.isObject());
4771   return &result.toObject();
4772 }
4773 
IsCTypeOrProto(HandleValue v)4774 bool CType::IsCTypeOrProto(HandleValue v) {
4775   if (!v.isObject()) {
4776     return false;
4777   }
4778   JSObject* obj = &v.toObject();
4779   return CType::IsCType(obj) || CType::IsCTypeProto(obj);
4780 }
4781 
PrototypeGetter(JSContext * cx,const JS::CallArgs & args)4782 bool CType::PrototypeGetter(JSContext* cx, const JS::CallArgs& args) {
4783   RootedObject obj(cx, &args.thisv().toObject());
4784   unsigned slot = CType::IsCTypeProto(obj) ? (unsigned)SLOT_OURDATAPROTO
4785                                            : (unsigned)SLOT_PROTO;
4786   args.rval().set(JS::GetReservedSlot(obj, slot));
4787   MOZ_ASSERT(args.rval().isObject() || args.rval().isUndefined());
4788   return true;
4789 }
4790 
IsCType(HandleValue v)4791 bool CType::IsCType(HandleValue v) {
4792   return v.isObject() && CType::IsCType(&v.toObject());
4793 }
4794 
NameGetter(JSContext * cx,const JS::CallArgs & args)4795 bool CType::NameGetter(JSContext* cx, const JS::CallArgs& args) {
4796   RootedObject obj(cx, &args.thisv().toObject());
4797   JSString* name = CType::GetName(cx, obj);
4798   if (!name) {
4799     return false;
4800   }
4801 
4802   args.rval().setString(name);
4803   return true;
4804 }
4805 
SizeGetter(JSContext * cx,const JS::CallArgs & args)4806 bool CType::SizeGetter(JSContext* cx, const JS::CallArgs& args) {
4807   RootedObject obj(cx, &args.thisv().toObject());
4808   args.rval().set(JS::GetReservedSlot(obj, SLOT_SIZE));
4809   MOZ_ASSERT(args.rval().isNumber() || args.rval().isUndefined());
4810   return true;
4811 }
4812 
PtrGetter(JSContext * cx,const JS::CallArgs & args)4813 bool CType::PtrGetter(JSContext* cx, const JS::CallArgs& args) {
4814   RootedObject obj(cx, &args.thisv().toObject());
4815   JSObject* pointerType = PointerType::CreateInternal(cx, obj);
4816   if (!pointerType) {
4817     return false;
4818   }
4819 
4820   args.rval().setObject(*pointerType);
4821   return true;
4822 }
4823 
CreateArray(JSContext * cx,unsigned argc,Value * vp)4824 bool CType::CreateArray(JSContext* cx, unsigned argc, Value* vp) {
4825   CallArgs args = CallArgsFromVp(argc, vp);
4826   RootedObject baseType(cx, GetThisObject(cx, args, "CType.prototype.array"));
4827   if (!baseType) {
4828     return false;
4829   }
4830   if (!CType::IsCType(baseType)) {
4831     return IncompatibleThisProto(cx, "CType.prototype.array", args.thisv());
4832   }
4833 
4834   // Construct and return a new ArrayType object.
4835   if (args.length() > 1) {
4836     return ArgumentLengthError(cx, "CType.prototype.array", "at most one", "");
4837   }
4838 
4839   // Convert the length argument to a size_t.
4840   size_t length = 0;
4841   if (args.length() == 1 && !jsvalToSize(cx, args[0], false, &length)) {
4842     return ArgumentTypeMismatch(cx, "", "CType.prototype.array",
4843                                 "a nonnegative integer");
4844   }
4845 
4846   JSObject* result =
4847       ArrayType::CreateInternal(cx, baseType, length, args.length() == 1);
4848   if (!result) {
4849     return false;
4850   }
4851 
4852   args.rval().setObject(*result);
4853   return true;
4854 }
4855 
ToString(JSContext * cx,unsigned argc,Value * vp)4856 bool CType::ToString(JSContext* cx, unsigned argc, Value* vp) {
4857   CallArgs args = CallArgsFromVp(argc, vp);
4858   RootedObject obj(cx, GetThisObject(cx, args, "CType.prototype.toString"));
4859   if (!obj) {
4860     return false;
4861   }
4862   if (!CType::IsCType(obj) && !CType::IsCTypeProto(obj)) {
4863     return IncompatibleThisProto(cx, "CType.prototype.toString",
4864                                  InformalValueTypeName(args.thisv()));
4865   }
4866 
4867   // Create the appropriate string depending on whether we're sCTypeClass or
4868   // sCTypeProtoClass.
4869   JSString* result;
4870   if (CType::IsCType(obj)) {
4871     AutoString type;
4872     AppendString(cx, type, "type ");
4873     AppendString(cx, type, GetName(cx, obj));
4874     if (!type) {
4875       return false;
4876     }
4877     result = NewUCString(cx, type.finish());
4878   } else {
4879     result = JS_NewStringCopyZ(cx, "[CType proto object]");
4880   }
4881   if (!result) {
4882     return false;
4883   }
4884 
4885   args.rval().setString(result);
4886   return true;
4887 }
4888 
ToSource(JSContext * cx,unsigned argc,Value * vp)4889 bool CType::ToSource(JSContext* cx, unsigned argc, Value* vp) {
4890   CallArgs args = CallArgsFromVp(argc, vp);
4891   JSObject* obj = GetThisObject(cx, args, "CType.prototype.toSource");
4892   if (!obj) {
4893     return false;
4894   }
4895   if (!CType::IsCType(obj) && !CType::IsCTypeProto(obj)) {
4896     return IncompatibleThisProto(cx, "CType.prototype.toSource",
4897                                  InformalValueTypeName(args.thisv()));
4898   }
4899 
4900   // Create the appropriate string depending on whether we're sCTypeClass or
4901   // sCTypeProtoClass.
4902   JSString* result;
4903   if (CType::IsCType(obj)) {
4904     AutoString source;
4905     BuildTypeSource(cx, obj, false, source);
4906     if (!source) {
4907       return false;
4908     }
4909     result = NewUCString(cx, source.finish());
4910   } else {
4911     result = JS_NewStringCopyZ(cx, "[CType proto object]");
4912   }
4913   if (!result) {
4914     return false;
4915   }
4916 
4917   args.rval().setString(result);
4918   return true;
4919 }
4920 
HasInstance(JSContext * cx,HandleObject obj,MutableHandleValue v,bool * bp)4921 bool CType::HasInstance(JSContext* cx, HandleObject obj, MutableHandleValue v,
4922                         bool* bp) {
4923   MOZ_ASSERT(CType::IsCType(obj));
4924 
4925   Value slot = JS::GetReservedSlot(obj, SLOT_PROTO);
4926   JS::Rooted<JSObject*> prototype(cx, &slot.toObject());
4927   MOZ_ASSERT(prototype);
4928   MOZ_ASSERT(CData::IsCDataProto(prototype));
4929 
4930   *bp = false;
4931   if (v.isPrimitive()) {
4932     return true;
4933   }
4934 
4935   RootedObject proto(cx, &v.toObject());
4936   for (;;) {
4937     if (!JS_GetPrototype(cx, proto, &proto)) {
4938       return false;
4939     }
4940     if (!proto) {
4941       break;
4942     }
4943     if (proto == prototype) {
4944       *bp = true;
4945       break;
4946     }
4947   }
4948   return true;
4949 }
4950 
GetGlobalCTypes(JSContext * cx,JSObject * objArg)4951 static JSObject* CType::GetGlobalCTypes(JSContext* cx, JSObject* objArg) {
4952   MOZ_ASSERT(CType::IsCType(objArg));
4953 
4954   RootedObject obj(cx, objArg);
4955   RootedObject objTypeProto(cx);
4956   if (!JS_GetPrototype(cx, obj, &objTypeProto)) {
4957     return nullptr;
4958   }
4959   MOZ_ASSERT(objTypeProto);
4960   MOZ_ASSERT(CType::IsCTypeProto(objTypeProto));
4961 
4962   Value valCTypes = JS::GetReservedSlot(objTypeProto, SLOT_CTYPES);
4963   MOZ_ASSERT(valCTypes.isObject());
4964   return &valCTypes.toObject();
4965 }
4966 
4967 /*******************************************************************************
4968 ** ABI implementation
4969 *******************************************************************************/
4970 
IsABI(JSObject * obj)4971 bool ABI::IsABI(JSObject* obj) { return obj->hasClass(&sCABIClass); }
4972 
ToSource(JSContext * cx,unsigned argc,Value * vp)4973 bool ABI::ToSource(JSContext* cx, unsigned argc, Value* vp) {
4974   CallArgs args = CallArgsFromVp(argc, vp);
4975   if (args.length() != 0) {
4976     return ArgumentLengthError(cx, "ABI.prototype.toSource", "no", "s");
4977   }
4978 
4979   JSObject* obj = GetThisObject(cx, args, "ABI.prototype.toSource");
4980   if (!obj) {
4981     return false;
4982   }
4983   if (!ABI::IsABI(obj)) {
4984     return IncompatibleThisProto(cx, "ABI.prototype.toSource",
4985                                  InformalValueTypeName(args.thisv()));
4986   }
4987 
4988   JSString* result;
4989   switch (GetABICode(obj)) {
4990     case ABI_DEFAULT:
4991       result = JS_NewStringCopyZ(cx, "ctypes.default_abi");
4992       break;
4993     case ABI_STDCALL:
4994       result = JS_NewStringCopyZ(cx, "ctypes.stdcall_abi");
4995       break;
4996     case ABI_THISCALL:
4997       result = JS_NewStringCopyZ(cx, "ctypes.thiscall_abi");
4998       break;
4999     case ABI_WINAPI:
5000       result = JS_NewStringCopyZ(cx, "ctypes.winapi_abi");
5001       break;
5002     default:
5003       JS_ReportErrorASCII(cx, "not a valid ABICode");
5004       return false;
5005   }
5006   if (!result) {
5007     return false;
5008   }
5009 
5010   args.rval().setString(result);
5011   return true;
5012 }
5013 
5014 /*******************************************************************************
5015 ** PointerType implementation
5016 *******************************************************************************/
5017 
Create(JSContext * cx,unsigned argc,Value * vp)5018 bool PointerType::Create(JSContext* cx, unsigned argc, Value* vp) {
5019   CallArgs args = CallArgsFromVp(argc, vp);
5020   // Construct and return a new PointerType object.
5021   if (args.length() != 1) {
5022     return ArgumentLengthError(cx, "PointerType", "one", "");
5023   }
5024 
5025   Value arg = args[0];
5026   RootedObject obj(cx);
5027   if (arg.isPrimitive() || !CType::IsCType(obj = &arg.toObject())) {
5028     return ArgumentTypeMismatch(cx, "", "PointerType", "a CType");
5029   }
5030 
5031   JSObject* result = CreateInternal(cx, obj);
5032   if (!result) {
5033     return false;
5034   }
5035 
5036   args.rval().setObject(*result);
5037   return true;
5038 }
5039 
CreateInternal(JSContext * cx,HandleObject baseType)5040 JSObject* PointerType::CreateInternal(JSContext* cx, HandleObject baseType) {
5041   // check if we have a cached PointerType on our base CType.
5042   Value slot = JS::GetReservedSlot(baseType, SLOT_PTR);
5043   if (!slot.isUndefined()) {
5044     return &slot.toObject();
5045   }
5046 
5047   // Get ctypes.PointerType.prototype and the common prototype for CData objects
5048   // of this type, or ctypes.FunctionType.prototype for function pointers.
5049   CTypeProtoSlot slotId = CType::GetTypeCode(baseType) == TYPE_function
5050                               ? SLOT_FUNCTIONDATAPROTO
5051                               : SLOT_POINTERDATAPROTO;
5052   RootedObject dataProto(cx, CType::GetProtoFromType(cx, baseType, slotId));
5053   if (!dataProto) {
5054     return nullptr;
5055   }
5056   RootedObject typeProto(
5057       cx, CType::GetProtoFromType(cx, baseType, SLOT_POINTERPROTO));
5058   if (!typeProto) {
5059     return nullptr;
5060   }
5061 
5062   // Create a new CType object with the common properties and slots.
5063   RootedValue sizeVal(cx, Int32Value(sizeof(void*)));
5064   RootedValue alignVal(cx, Int32Value(ffi_type_pointer.alignment));
5065   JSObject* typeObj =
5066       CType::Create(cx, typeProto, dataProto, TYPE_pointer, nullptr, sizeVal,
5067                     alignVal, &ffi_type_pointer);
5068   if (!typeObj) {
5069     return nullptr;
5070   }
5071 
5072   // Set the target type. (This will be 'null' for an opaque pointer type.)
5073   JS_SetReservedSlot(typeObj, SLOT_TARGET_T, ObjectValue(*baseType));
5074 
5075   // Finally, cache our newly-created PointerType on our pointed-to CType.
5076   JS_SetReservedSlot(baseType, SLOT_PTR, ObjectValue(*typeObj));
5077 
5078   return typeObj;
5079 }
5080 
ConstructData(JSContext * cx,HandleObject obj,const CallArgs & args)5081 bool PointerType::ConstructData(JSContext* cx, HandleObject obj,
5082                                 const CallArgs& args) {
5083   if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_pointer) {
5084     return IncompatibleCallee(cx, "PointerType constructor", obj);
5085   }
5086 
5087   if (args.length() > 3) {
5088     return ArgumentLengthError(cx, "PointerType constructor", "0, 1, 2, or 3",
5089                                "s");
5090   }
5091 
5092   RootedObject result(cx, CData::Create(cx, obj, nullptr, nullptr, true));
5093   if (!result) {
5094     return false;
5095   }
5096 
5097   // Set return value early, must not observe *vp after
5098   args.rval().setObject(*result);
5099 
5100   // There are 3 things that we might be creating here:
5101   // 1 - A null pointer (no arguments)
5102   // 2 - An initialized pointer (1 argument)
5103   // 3 - A closure (1-3 arguments)
5104   //
5105   // The API doesn't give us a perfect way to distinguish 2 and 3, but the
5106   // heuristics we use should be fine.
5107 
5108   //
5109   // Case 1 - Null pointer
5110   //
5111   if (args.length() == 0) {
5112     return true;
5113   }
5114 
5115   // Analyze the arguments a bit to decide what to do next.
5116   RootedObject baseObj(cx, PointerType::GetBaseType(obj));
5117   bool looksLikeClosure = CType::GetTypeCode(baseObj) == TYPE_function &&
5118                           args[0].isObject() &&
5119                           JS::IsCallable(&args[0].toObject());
5120 
5121   //
5122   // Case 2 - Initialized pointer
5123   //
5124   if (!looksLikeClosure) {
5125     if (args.length() != 1) {
5126       return ArgumentLengthError(cx, "FunctionType constructor", "one", "");
5127     }
5128     return ExplicitConvert(cx, args[0], obj, CData::GetData(result),
5129                            ConversionType::Construct);
5130   }
5131 
5132   //
5133   // Case 3 - Closure
5134   //
5135 
5136   // The second argument is an optional 'this' parameter with which to invoke
5137   // the given js function. Callers may leave this blank, or pass null if they
5138   // wish to pass the third argument.
5139   RootedObject thisObj(cx, nullptr);
5140   if (args.length() >= 2) {
5141     if (args[1].isNull()) {
5142       thisObj = nullptr;
5143     } else if (args[1].isObject()) {
5144       thisObj = &args[1].toObject();
5145     } else if (!JS_ValueToObject(cx, args[1], &thisObj)) {
5146       return false;
5147     }
5148   }
5149 
5150   // The third argument is an optional error sentinel that js-ctypes will return
5151   // if an exception is raised while executing the closure. The type must match
5152   // the return type of the callback.
5153   RootedValue errVal(cx);
5154   if (args.length() == 3) {
5155     errVal = args[2];
5156   }
5157 
5158   RootedObject fnObj(cx, &args[0].toObject());
5159   return FunctionType::ConstructData(cx, baseObj, result, fnObj, thisObj,
5160                                      errVal);
5161 }
5162 
GetBaseType(JSObject * obj)5163 JSObject* PointerType::GetBaseType(JSObject* obj) {
5164   MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_pointer);
5165 
5166   Value type = JS::GetReservedSlot(obj, SLOT_TARGET_T);
5167   MOZ_ASSERT(!type.isNull());
5168   return &type.toObject();
5169 }
5170 
IsPointerType(HandleValue v)5171 bool PointerType::IsPointerType(HandleValue v) {
5172   if (!v.isObject()) {
5173     return false;
5174   }
5175   JSObject* obj = &v.toObject();
5176   return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_pointer;
5177 }
5178 
IsPointer(HandleValue v)5179 bool PointerType::IsPointer(HandleValue v) {
5180   if (!v.isObject()) {
5181     return false;
5182   }
5183   JSObject* obj = MaybeUnwrapArrayWrapper(&v.toObject());
5184   return CData::IsCData(obj) &&
5185          CType::GetTypeCode(CData::GetCType(obj)) == TYPE_pointer;
5186 }
5187 
TargetTypeGetter(JSContext * cx,const JS::CallArgs & args)5188 bool PointerType::TargetTypeGetter(JSContext* cx, const JS::CallArgs& args) {
5189   RootedObject obj(cx, &args.thisv().toObject());
5190   args.rval().set(JS::GetReservedSlot(obj, SLOT_TARGET_T));
5191   MOZ_ASSERT(args.rval().isObject());
5192   return true;
5193 }
5194 
IsNull(JSContext * cx,unsigned argc,Value * vp)5195 bool PointerType::IsNull(JSContext* cx, unsigned argc, Value* vp) {
5196   CallArgs args = CallArgsFromVp(argc, vp);
5197   RootedObject obj(cx, GetThisObject(cx, args, "PointerType.prototype.isNull"));
5198   if (!obj) {
5199     return false;
5200   }
5201   if (!CData::IsCDataMaybeUnwrap(&obj)) {
5202     return IncompatibleThisProto(cx, "PointerType.prototype.isNull",
5203                                  args.thisv());
5204   }
5205 
5206   // Get pointer type and base type.
5207   JSObject* typeObj = CData::GetCType(obj);
5208   if (CType::GetTypeCode(typeObj) != TYPE_pointer) {
5209     return IncompatibleThisType(cx, "PointerType.prototype.isNull",
5210                                 "non-PointerType CData", args.thisv());
5211   }
5212 
5213   void* data = *static_cast<void**>(CData::GetData(obj));
5214   args.rval().setBoolean(data == nullptr);
5215   return true;
5216 }
5217 
OffsetBy(JSContext * cx,const CallArgs & args,int offset,const char * name)5218 bool PointerType::OffsetBy(JSContext* cx, const CallArgs& args, int offset,
5219                            const char* name) {
5220   RootedObject obj(cx, GetThisObject(cx, args, name));
5221   if (!obj) {
5222     return false;
5223   }
5224   if (!CData::IsCDataMaybeUnwrap(&obj)) {
5225     return IncompatibleThisProto(cx, name, args.thisv());
5226   }
5227 
5228   RootedObject typeObj(cx, CData::GetCType(obj));
5229   if (CType::GetTypeCode(typeObj) != TYPE_pointer) {
5230     return IncompatibleThisType(cx, name, "non-PointerType CData",
5231                                 args.thisv());
5232   }
5233 
5234   RootedObject baseType(cx, PointerType::GetBaseType(typeObj));
5235   if (!CType::IsSizeDefined(baseType)) {
5236     return UndefinedSizePointerError(cx, "modify", obj);
5237   }
5238 
5239   size_t elementSize = CType::GetSize(baseType);
5240   char* data = static_cast<char*>(*static_cast<void**>(CData::GetData(obj)));
5241   void* address = data + offset * ptrdiff_t(elementSize);
5242 
5243   // Create a PointerType CData object containing the new address.
5244   JSObject* result = CData::Create(cx, typeObj, nullptr, &address, true);
5245   if (!result) {
5246     return false;
5247   }
5248 
5249   args.rval().setObject(*result);
5250   return true;
5251 }
5252 
Increment(JSContext * cx,unsigned argc,Value * vp)5253 bool PointerType::Increment(JSContext* cx, unsigned argc, Value* vp) {
5254   CallArgs args = CallArgsFromVp(argc, vp);
5255   return OffsetBy(cx, args, 1, "PointerType.prototype.increment");
5256 }
5257 
Decrement(JSContext * cx,unsigned argc,Value * vp)5258 bool PointerType::Decrement(JSContext* cx, unsigned argc, Value* vp) {
5259   CallArgs args = CallArgsFromVp(argc, vp);
5260   return OffsetBy(cx, args, -1, "PointerType.prototype.decrement");
5261 }
5262 
ContentsGetter(JSContext * cx,const JS::CallArgs & args)5263 bool PointerType::ContentsGetter(JSContext* cx, const JS::CallArgs& args) {
5264   RootedObject obj(cx, &args.thisv().toObject());
5265   RootedObject baseType(cx, GetBaseType(CData::GetCType(obj)));
5266   if (!CType::IsSizeDefined(baseType)) {
5267     return UndefinedSizePointerError(cx, "get contents of", obj);
5268   }
5269 
5270   void* data = *static_cast<void**>(CData::GetData(obj));
5271   if (data == nullptr) {
5272     return NullPointerError(cx, "read contents of", obj);
5273   }
5274 
5275   RootedValue result(cx);
5276   if (!ConvertToJS(cx, baseType, nullptr, data, false, false, &result)) {
5277     return false;
5278   }
5279 
5280   args.rval().set(result);
5281   return true;
5282 }
5283 
ContentsSetter(JSContext * cx,const JS::CallArgs & args)5284 bool PointerType::ContentsSetter(JSContext* cx, const JS::CallArgs& args) {
5285   RootedObject obj(cx, &args.thisv().toObject());
5286   RootedObject baseType(cx, GetBaseType(CData::GetCType(obj)));
5287   if (!CType::IsSizeDefined(baseType)) {
5288     return UndefinedSizePointerError(cx, "set contents of", obj);
5289   }
5290 
5291   void* data = *static_cast<void**>(CData::GetData(obj));
5292   if (data == nullptr) {
5293     return NullPointerError(cx, "write contents to", obj);
5294   }
5295 
5296   args.rval().setUndefined();
5297   return ImplicitConvert(cx, args.get(0), baseType, data,
5298                          ConversionType::Setter, nullptr);
5299 }
5300 
5301 /*******************************************************************************
5302 ** ArrayType implementation
5303 *******************************************************************************/
5304 
Create(JSContext * cx,unsigned argc,Value * vp)5305 bool ArrayType::Create(JSContext* cx, unsigned argc, Value* vp) {
5306   CallArgs args = CallArgsFromVp(argc, vp);
5307   // Construct and return a new ArrayType object.
5308   if (args.length() < 1 || args.length() > 2) {
5309     return ArgumentLengthError(cx, "ArrayType", "one or two", "s");
5310   }
5311 
5312   if (args[0].isPrimitive() || !CType::IsCType(&args[0].toObject())) {
5313     return ArgumentTypeMismatch(cx, "first ", "ArrayType", "a CType");
5314   }
5315 
5316   // Convert the length argument to a size_t.
5317   size_t length = 0;
5318   if (args.length() == 2 && !jsvalToSize(cx, args[1], false, &length)) {
5319     return ArgumentTypeMismatch(cx, "second ", "ArrayType",
5320                                 "a nonnegative integer");
5321   }
5322 
5323   RootedObject baseType(cx, &args[0].toObject());
5324   JSObject* result = CreateInternal(cx, baseType, length, args.length() == 2);
5325   if (!result) {
5326     return false;
5327   }
5328 
5329   args.rval().setObject(*result);
5330   return true;
5331 }
5332 
CreateInternal(JSContext * cx,HandleObject baseType,size_t length,bool lengthDefined)5333 JSObject* ArrayType::CreateInternal(JSContext* cx, HandleObject baseType,
5334                                     size_t length, bool lengthDefined) {
5335   // Get ctypes.ArrayType.prototype and the common prototype for CData objects
5336   // of this type, from ctypes.CType.prototype.
5337   RootedObject typeProto(
5338       cx, CType::GetProtoFromType(cx, baseType, SLOT_ARRAYPROTO));
5339   if (!typeProto) {
5340     return nullptr;
5341   }
5342   RootedObject dataProto(
5343       cx, CType::GetProtoFromType(cx, baseType, SLOT_ARRAYDATAPROTO));
5344   if (!dataProto) {
5345     return nullptr;
5346   }
5347 
5348   // Determine the size of the array from the base type, if possible.
5349   // The size of the base type must be defined.
5350   // If our length is undefined, both our size and length will be undefined.
5351   size_t baseSize;
5352   if (!CType::GetSafeSize(baseType, &baseSize)) {
5353     JS_ReportErrorASCII(cx, "base size must be defined");
5354     return nullptr;
5355   }
5356 
5357   RootedValue sizeVal(cx);
5358   RootedValue lengthVal(cx);
5359   if (lengthDefined) {
5360     // Check for overflow, and convert to an int or double as required.
5361     size_t size = length * baseSize;
5362     if (length > 0 && size / length != baseSize) {
5363       SizeOverflow(cx, "array size", "size_t");
5364       return nullptr;
5365     }
5366     if (!SizeTojsval(cx, size, &sizeVal)) {
5367       SizeOverflow(cx, "array size", "JavaScript number");
5368       return nullptr;
5369     }
5370     if (!SizeTojsval(cx, length, &lengthVal)) {
5371       SizeOverflow(cx, "array length", "JavaScript number");
5372       return nullptr;
5373     }
5374   }
5375 
5376   RootedValue alignVal(cx, Int32Value(CType::GetAlignment(baseType)));
5377 
5378   // Create a new CType object with the common properties and slots.
5379   JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_array,
5380                                     nullptr, sizeVal, alignVal, nullptr);
5381   if (!typeObj) {
5382     return nullptr;
5383   }
5384 
5385   // Set the element type.
5386   JS_SetReservedSlot(typeObj, SLOT_ELEMENT_T, ObjectValue(*baseType));
5387 
5388   // Set the length.
5389   JS_SetReservedSlot(typeObj, SLOT_LENGTH, lengthVal);
5390 
5391   return typeObj;
5392 }
5393 
ConstructData(JSContext * cx,HandleObject obj_,const CallArgs & args)5394 bool ArrayType::ConstructData(JSContext* cx, HandleObject obj_,
5395                               const CallArgs& args) {
5396   RootedObject obj(cx, obj_);  // Make a mutable version
5397 
5398   if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_array) {
5399     return IncompatibleCallee(cx, "ArrayType constructor", obj);
5400   }
5401 
5402   // Decide whether we have an object to initialize from. We'll override this
5403   // if we get a length argument instead.
5404   bool convertObject = args.length() == 1;
5405 
5406   // Check if we're an array of undefined length. If we are, allow construction
5407   // with a length argument, or with an actual JS array.
5408   if (CType::IsSizeDefined(obj)) {
5409     if (args.length() > 1) {
5410       return ArgumentLengthError(cx, "size defined ArrayType constructor",
5411                                  "at most one", "");
5412     }
5413 
5414   } else {
5415     if (args.length() != 1) {
5416       return ArgumentLengthError(cx, "size undefined ArrayType constructor",
5417                                  "one", "");
5418     }
5419 
5420     RootedObject baseType(cx, GetBaseType(obj));
5421 
5422     size_t length;
5423     if (jsvalToSize(cx, args[0], false, &length)) {
5424       // Have a length, rather than an object to initialize from.
5425       convertObject = false;
5426 
5427     } else if (args[0].isObject()) {
5428       // We were given an object with a .length property.
5429       // This could be a JS array, or a CData array.
5430       RootedObject arg(cx, &args[0].toObject());
5431       RootedValue lengthVal(cx);
5432       if (!JS_GetProperty(cx, arg, "length", &lengthVal) ||
5433           !jsvalToSize(cx, lengthVal, false, &length)) {
5434         return ArgumentTypeMismatch(cx, "",
5435                                     "size undefined ArrayType constructor",
5436                                     "an array object or integer");
5437       }
5438 
5439     } else if (args[0].isString()) {
5440       // We were given a string. Size the array to the appropriate length,
5441       // including space for the terminator.
5442       JSString* sourceString = args[0].toString();
5443       size_t sourceLength = sourceString->length();
5444       Rooted<JSLinearString*> sourceLinear(cx, sourceString->ensureLinear(cx));
5445       if (!sourceLinear) {
5446         return false;
5447       }
5448 
5449       switch (CType::GetTypeCode(baseType)) {
5450         case TYPE_char:
5451         case TYPE_signed_char:
5452         case TYPE_unsigned_char: {
5453           // Reject if unpaired surrogate characters are present.
5454           if (!ReportErrorIfUnpairedSurrogatePresent(cx, sourceLinear)) {
5455             return false;
5456           }
5457 
5458           // Determine the UTF-8 length.
5459           length = JS::GetDeflatedUTF8StringLength(sourceLinear);
5460 
5461           ++length;
5462           break;
5463         }
5464         case TYPE_char16_t:
5465           length = sourceLength + 1;
5466           break;
5467         default:
5468           return ConvError(cx, obj, args[0], ConversionType::Construct);
5469       }
5470 
5471     } else {
5472       return ArgumentTypeMismatch(cx, "",
5473                                   "size undefined ArrayType constructor",
5474                                   "an array object or integer");
5475     }
5476 
5477     // Construct a new ArrayType of defined length, for the new CData object.
5478     obj = CreateInternal(cx, baseType, length, true);
5479     if (!obj) {
5480       return false;
5481     }
5482   }
5483 
5484   JSObject* result = CData::Create(cx, obj, nullptr, nullptr, true);
5485   if (!result) {
5486     return false;
5487   }
5488 
5489   args.rval().setObject(*result);
5490 
5491   if (convertObject) {
5492     if (!ExplicitConvert(cx, args[0], obj, CData::GetData(result),
5493                          ConversionType::Construct))
5494       return false;
5495   }
5496 
5497   return true;
5498 }
5499 
GetBaseType(JSObject * obj)5500 JSObject* ArrayType::GetBaseType(JSObject* obj) {
5501   MOZ_ASSERT(CType::IsCType(obj));
5502   MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_array);
5503 
5504   Value type = JS::GetReservedSlot(obj, SLOT_ELEMENT_T);
5505   MOZ_ASSERT(!type.isNull());
5506   return &type.toObject();
5507 }
5508 
GetSafeLength(JSObject * obj,size_t * result)5509 bool ArrayType::GetSafeLength(JSObject* obj, size_t* result) {
5510   MOZ_ASSERT(CType::IsCType(obj));
5511   MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_array);
5512 
5513   Value length = JS::GetReservedSlot(obj, SLOT_LENGTH);
5514 
5515   // The "length" property can be an int, a double, or JS::UndefinedValue()
5516   // (for arrays of undefined length), and must always fit in a size_t.
5517   if (length.isInt32()) {
5518     *result = length.toInt32();
5519     return true;
5520   }
5521   if (length.isDouble()) {
5522     *result = Convert<size_t>(length.toDouble());
5523     return true;
5524   }
5525 
5526   MOZ_ASSERT(length.isUndefined());
5527   return false;
5528 }
5529 
GetLength(JSObject * obj)5530 size_t ArrayType::GetLength(JSObject* obj) {
5531   MOZ_ASSERT(CType::IsCType(obj));
5532   MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_array);
5533 
5534   Value length = JS::GetReservedSlot(obj, SLOT_LENGTH);
5535 
5536   MOZ_ASSERT(!length.isUndefined());
5537 
5538   // The "length" property can be an int, a double, or JS::UndefinedValue()
5539   // (for arrays of undefined length), and must always fit in a size_t.
5540   // For callers who know it can never be JS::UndefinedValue(), return a size_t
5541   // directly.
5542   if (length.isInt32()) {
5543     return length.toInt32();
5544   }
5545   return Convert<size_t>(length.toDouble());
5546 }
5547 
BuildFFIType(JSContext * cx,JSObject * obj)5548 UniquePtrFFIType ArrayType::BuildFFIType(JSContext* cx, JSObject* obj) {
5549   MOZ_ASSERT(CType::IsCType(obj));
5550   MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_array);
5551   MOZ_ASSERT(CType::IsSizeDefined(obj));
5552 
5553   JSObject* baseType = ArrayType::GetBaseType(obj);
5554   ffi_type* ffiBaseType = CType::GetFFIType(cx, baseType);
5555   if (!ffiBaseType) {
5556     return nullptr;
5557   }
5558 
5559   size_t length = ArrayType::GetLength(obj);
5560 
5561   // Create an ffi_type to represent the array. This is necessary for the case
5562   // where the array is part of a struct. Since libffi has no intrinsic
5563   // support for array types, we approximate it by creating a struct type
5564   // with elements of type 'baseType' and with appropriate size and alignment
5565   // values. It would be nice to not do all the work of setting up 'elements',
5566   // but some libffi platforms currently require that it be meaningful. I'm
5567   // looking at you, x86_64.
5568   auto ffiType = cx->make_unique<ffi_type>();
5569   if (!ffiType) {
5570     return nullptr;
5571   }
5572 
5573   ffiType->type = FFI_TYPE_STRUCT;
5574   ffiType->size = CType::GetSize(obj);
5575   ffiType->alignment = CType::GetAlignment(obj);
5576   ffiType->elements = cx->pod_malloc<ffi_type*>(length + 1);
5577   if (!ffiType->elements) {
5578     return nullptr;
5579   }
5580 
5581   for (size_t i = 0; i < length; ++i) {
5582     ffiType->elements[i] = ffiBaseType;
5583   }
5584   ffiType->elements[length] = nullptr;
5585 
5586   return ffiType;
5587 }
5588 
IsArrayType(HandleValue v)5589 bool ArrayType::IsArrayType(HandleValue v) {
5590   if (!v.isObject()) {
5591     return false;
5592   }
5593   JSObject* obj = &v.toObject();
5594   return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_array;
5595 }
5596 
IsArrayOrArrayType(HandleValue v)5597 bool ArrayType::IsArrayOrArrayType(HandleValue v) {
5598   if (!v.isObject()) {
5599     return false;
5600   }
5601   JSObject* obj = MaybeUnwrapArrayWrapper(&v.toObject());
5602 
5603   // Allow both CTypes and CDatas of the ArrayType persuasion by extracting the
5604   // CType if we're dealing with a CData.
5605   if (CData::IsCData(obj)) {
5606     obj = CData::GetCType(obj);
5607   }
5608   return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_array;
5609 }
5610 
ElementTypeGetter(JSContext * cx,const JS::CallArgs & args)5611 bool ArrayType::ElementTypeGetter(JSContext* cx, const JS::CallArgs& args) {
5612   RootedObject obj(cx, &args.thisv().toObject());
5613   args.rval().set(JS::GetReservedSlot(obj, SLOT_ELEMENT_T));
5614   MOZ_ASSERT(args.rval().isObject());
5615   return true;
5616 }
5617 
LengthGetter(JSContext * cx,const JS::CallArgs & args)5618 bool ArrayType::LengthGetter(JSContext* cx, const JS::CallArgs& args) {
5619   RootedObject obj(cx, &args.thisv().toObject());
5620 
5621   // This getter exists for both CTypes and CDatas of the ArrayType persuasion.
5622   // If we're dealing with a CData, get the CType from it.
5623   if (CData::IsCDataMaybeUnwrap(&obj)) {
5624     obj = CData::GetCType(obj);
5625   }
5626 
5627   args.rval().set(JS::GetReservedSlot(obj, SLOT_LENGTH));
5628   MOZ_ASSERT(args.rval().isNumber() || args.rval().isUndefined());
5629   return true;
5630 }
5631 
Getter(JSContext * cx,HandleObject obj,HandleId idval,MutableHandleValue vp,bool * handled)5632 bool ArrayType::Getter(JSContext* cx, HandleObject obj, HandleId idval,
5633                        MutableHandleValue vp, bool* handled) {
5634   *handled = false;
5635 
5636   // This should never happen, but we'll check to be safe.
5637   if (!CData::IsCData(obj)) {
5638     RootedValue objVal(cx, ObjectValue(*obj));
5639     return IncompatibleThisProto(cx, "ArrayType property getter", objVal);
5640   }
5641 
5642   // Bail early if we're not an ArrayType. (This setter is present for all
5643   // CData, regardless of CType.)
5644   JSObject* typeObj = CData::GetCType(obj);
5645   if (CType::GetTypeCode(typeObj) != TYPE_array) {
5646     return true;
5647   }
5648 
5649   // Convert the index to a size_t and bounds-check it.
5650   size_t index;
5651   size_t length = GetLength(typeObj);
5652   bool ok = jsidToSize(cx, idval, true, &index);
5653   int32_t dummy;
5654   if (!ok && idval.isSymbol()) {
5655     return true;
5656   }
5657   bool dummy2;
5658   if (!ok && JSID_IS_STRING(idval) &&
5659       !StringToInteger(cx, JSID_TO_STRING(idval), &dummy, &dummy2)) {
5660     // String either isn't a number, or doesn't fit in size_t.
5661     // Chances are it's a regular property lookup, so return.
5662     return true;
5663   }
5664   if (!ok) {
5665     return InvalidIndexError(cx, idval);
5666   }
5667   if (index >= length) {
5668     return InvalidIndexRangeError(cx, index, length);
5669   }
5670 
5671   *handled = true;
5672 
5673   RootedObject baseType(cx, GetBaseType(typeObj));
5674   size_t elementSize = CType::GetSize(baseType);
5675   char* data = static_cast<char*>(CData::GetData(obj)) + elementSize * index;
5676   return ConvertToJS(cx, baseType, obj, data, false, false, vp);
5677 }
5678 
Setter(JSContext * cx,HandleObject obj,HandleId idval,HandleValue vp,ObjectOpResult & result,bool * handled)5679 bool ArrayType::Setter(JSContext* cx, HandleObject obj, HandleId idval,
5680                        HandleValue vp, ObjectOpResult& result, bool* handled) {
5681   *handled = false;
5682 
5683   // This should never happen, but we'll check to be safe.
5684   if (!CData::IsCData(obj)) {
5685     RootedValue objVal(cx, ObjectValue(*obj));
5686     return IncompatibleThisProto(cx, "ArrayType property setter", objVal);
5687   }
5688 
5689   // Bail early if we're not an ArrayType. (This setter is present for all
5690   // CData, regardless of CType.)
5691   RootedObject typeObj(cx, CData::GetCType(obj));
5692   if (CType::GetTypeCode(typeObj) != TYPE_array) {
5693     return result.succeed();
5694   }
5695 
5696   // Convert the index to a size_t and bounds-check it.
5697   size_t index;
5698   size_t length = GetLength(typeObj);
5699   bool ok = jsidToSize(cx, idval, true, &index);
5700   int32_t dummy;
5701   if (!ok && idval.isSymbol()) {
5702     return true;
5703   }
5704   bool dummy2;
5705   if (!ok && JSID_IS_STRING(idval) &&
5706       !StringToInteger(cx, JSID_TO_STRING(idval), &dummy, &dummy2)) {
5707     // String either isn't a number, or doesn't fit in size_t.
5708     // Chances are it's a regular property lookup, so return.
5709     return result.succeed();
5710   }
5711   if (!ok) {
5712     return InvalidIndexError(cx, idval);
5713   }
5714   if (index >= length) {
5715     return InvalidIndexRangeError(cx, index, length);
5716   }
5717 
5718   *handled = true;
5719 
5720   RootedObject baseType(cx, GetBaseType(typeObj));
5721   size_t elementSize = CType::GetSize(baseType);
5722   char* data = static_cast<char*>(CData::GetData(obj)) + elementSize * index;
5723   if (!ImplicitConvert(cx, vp, baseType, data, ConversionType::Setter, nullptr,
5724                        nullptr, 0, typeObj, index))
5725     return false;
5726   return result.succeed();
5727 }
5728 
AddressOfElement(JSContext * cx,unsigned argc,Value * vp)5729 bool ArrayType::AddressOfElement(JSContext* cx, unsigned argc, Value* vp) {
5730   CallArgs args = CallArgsFromVp(argc, vp);
5731   RootedObject obj(
5732       cx, GetThisObject(cx, args, "ArrayType.prototype.addressOfElement"));
5733   if (!obj) {
5734     return false;
5735   }
5736   if (!CData::IsCDataMaybeUnwrap(&obj)) {
5737     return IncompatibleThisProto(cx, "ArrayType.prototype.addressOfElement",
5738                                  args.thisv());
5739   }
5740 
5741   RootedObject typeObj(cx, CData::GetCType(obj));
5742   if (CType::GetTypeCode(typeObj) != TYPE_array) {
5743     return IncompatibleThisType(cx, "ArrayType.prototype.addressOfElement",
5744                                 "non-ArrayType CData", args.thisv());
5745   }
5746 
5747   if (args.length() != 1) {
5748     return ArgumentLengthError(cx, "ArrayType.prototype.addressOfElement",
5749                                "one", "");
5750   }
5751 
5752   RootedObject baseType(cx, GetBaseType(typeObj));
5753   RootedObject pointerType(cx, PointerType::CreateInternal(cx, baseType));
5754   if (!pointerType) {
5755     return false;
5756   }
5757 
5758   // Create a PointerType CData object containing null.
5759   RootedObject result(cx,
5760                       CData::Create(cx, pointerType, nullptr, nullptr, true));
5761   if (!result) {
5762     return false;
5763   }
5764 
5765   args.rval().setObject(*result);
5766 
5767   // Convert the index to a size_t and bounds-check it.
5768   size_t index;
5769   size_t length = GetLength(typeObj);
5770   if (!jsvalToSize(cx, args[0], false, &index)) {
5771     return InvalidIndexError(cx, args[0]);
5772   }
5773   if (index >= length) {
5774     return InvalidIndexRangeError(cx, index, length);
5775   }
5776 
5777   // Manually set the pointer inside the object, so we skip the conversion step.
5778   void** data = static_cast<void**>(CData::GetData(result));
5779   size_t elementSize = CType::GetSize(baseType);
5780   *data = static_cast<char*>(CData::GetData(obj)) + elementSize * index;
5781   return true;
5782 }
5783 
5784 /*******************************************************************************
5785 ** StructType implementation
5786 *******************************************************************************/
5787 
5788 // For a struct field descriptor 'val' of the form { name : type }, extract
5789 // 'name' and 'type'.
ExtractStructField(JSContext * cx,HandleValue val,MutableHandleObject typeObj)5790 static JSLinearString* ExtractStructField(JSContext* cx, HandleValue val,
5791                                           MutableHandleObject typeObj) {
5792   if (val.isPrimitive()) {
5793     FieldDescriptorNameTypeError(cx, val);
5794     return nullptr;
5795   }
5796 
5797   RootedObject obj(cx, &val.toObject());
5798   Rooted<IdVector> props(cx, IdVector(cx));
5799   if (!JS_Enumerate(cx, obj, &props)) {
5800     return nullptr;
5801   }
5802 
5803   // make sure we have one, and only one, property
5804   if (props.length() != 1) {
5805     FieldDescriptorCountError(cx, val, props.length());
5806     return nullptr;
5807   }
5808 
5809   RootedId nameid(cx, props[0]);
5810   if (!JSID_IS_STRING(nameid)) {
5811     FieldDescriptorNameError(cx, nameid);
5812     return nullptr;
5813   }
5814 
5815   RootedValue propVal(cx);
5816   if (!JS_GetPropertyById(cx, obj, nameid, &propVal)) {
5817     return nullptr;
5818   }
5819 
5820   if (propVal.isPrimitive() || !CType::IsCType(&propVal.toObject())) {
5821     FieldDescriptorTypeError(cx, propVal, nameid);
5822     return nullptr;
5823   }
5824 
5825   // Undefined size or zero size struct members are illegal.
5826   // (Zero-size arrays are legal as struct members in C++, but libffi will
5827   // choke on a zero-size struct, so we disallow them.)
5828   typeObj.set(&propVal.toObject());
5829   size_t size;
5830   if (!CType::GetSafeSize(typeObj, &size) || size == 0) {
5831     FieldDescriptorSizeError(cx, typeObj, nameid);
5832     return nullptr;
5833   }
5834 
5835   return JSID_TO_LINEAR_STRING(nameid);
5836 }
5837 
5838 // For a struct field with 'name' and 'type', add an element of the form
5839 // { name : type }.
AddFieldToArray(JSContext * cx,MutableHandleValue element,JSLinearString * name_,JSObject * typeObj_)5840 static bool AddFieldToArray(JSContext* cx, MutableHandleValue element,
5841                             JSLinearString* name_, JSObject* typeObj_) {
5842   RootedObject typeObj(cx, typeObj_);
5843   Rooted<JSLinearString*> name(cx, name_);
5844   RootedObject fieldObj(cx, JS_NewPlainObject(cx));
5845   if (!fieldObj) {
5846     return false;
5847   }
5848 
5849   element.setObject(*fieldObj);
5850 
5851   AutoStableStringChars nameChars(cx);
5852   if (!nameChars.initTwoByte(cx, name)) {
5853     return false;
5854   }
5855 
5856   if (!JS_DefineUCProperty(
5857           cx, fieldObj, nameChars.twoByteChars(), name->length(), typeObj,
5858           JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
5859     return false;
5860 
5861   return JS_FreezeObject(cx, fieldObj);
5862 }
5863 
Create(JSContext * cx,unsigned argc,Value * vp)5864 bool StructType::Create(JSContext* cx, unsigned argc, Value* vp) {
5865   CallArgs args = CallArgsFromVp(argc, vp);
5866 
5867   // Construct and return a new StructType object.
5868   if (args.length() < 1 || args.length() > 2) {
5869     return ArgumentLengthError(cx, "StructType", "one or two", "s");
5870   }
5871 
5872   Value name = args[0];
5873   if (!name.isString()) {
5874     return ArgumentTypeMismatch(cx, "first ", "StructType", "a string");
5875   }
5876 
5877   // Get ctypes.StructType.prototype from the ctypes.StructType constructor.
5878   RootedObject typeProto(
5879       cx, CType::GetProtoFromCtor(&args.callee(), SLOT_STRUCTPROTO));
5880 
5881   // Create a simple StructType with no defined fields. The result will be
5882   // non-instantiable as CData, will have no 'prototype' property, and will
5883   // have undefined size and alignment and no ffi_type.
5884   RootedObject result(
5885       cx, CType::Create(cx, typeProto, nullptr, TYPE_struct, name.toString(),
5886                         JS::UndefinedHandleValue, JS::UndefinedHandleValue,
5887                         nullptr));
5888   if (!result) {
5889     return false;
5890   }
5891 
5892   if (args.length() == 2) {
5893     RootedObject arr(cx, args[1].isObject() ? &args[1].toObject() : nullptr);
5894     bool isArray;
5895     if (!arr) {
5896       isArray = false;
5897     } else {
5898       if (!JS::IsArrayObject(cx, arr, &isArray)) {
5899         return false;
5900       }
5901     }
5902     if (!isArray) {
5903       return ArgumentTypeMismatch(cx, "second ", "StructType", "an array");
5904     }
5905 
5906     // Define the struct fields.
5907     if (!DefineInternal(cx, result, arr)) {
5908       return false;
5909     }
5910   }
5911 
5912   args.rval().setObject(*result);
5913   return true;
5914 }
5915 
DefineInternal(JSContext * cx,JSObject * typeObj_,JSObject * fieldsObj_)5916 bool StructType::DefineInternal(JSContext* cx, JSObject* typeObj_,
5917                                 JSObject* fieldsObj_) {
5918   RootedObject typeObj(cx, typeObj_);
5919   RootedObject fieldsObj(cx, fieldsObj_);
5920 
5921   uint32_t len;
5922   MOZ_ALWAYS_TRUE(JS::GetArrayLength(cx, fieldsObj, &len));
5923 
5924   // Get the common prototype for CData objects of this type from
5925   // ctypes.CType.prototype.
5926   RootedObject dataProto(
5927       cx, CType::GetProtoFromType(cx, typeObj, SLOT_STRUCTDATAPROTO));
5928   if (!dataProto) {
5929     return false;
5930   }
5931 
5932   // Set up the 'prototype' and 'prototype.constructor' properties.
5933   // The prototype will reflect the struct fields as properties on CData objects
5934   // created from this type.
5935   RootedObject prototype(
5936       cx, JS_NewObjectWithGivenProto(cx, &sCDataProtoClass, dataProto));
5937   if (!prototype) {
5938     return false;
5939   }
5940 
5941   if (!JS_DefineProperty(cx, prototype, "constructor", typeObj,
5942                          JSPROP_READONLY | JSPROP_PERMANENT))
5943     return false;
5944 
5945   // Create a FieldInfoHash to stash on the type object.
5946   Rooted<FieldInfoHash> fields(cx, FieldInfoHash(cx->zone(), len));
5947 
5948   // Process the field types.
5949   size_t structSize, structAlign;
5950   if (len != 0) {
5951     structSize = 0;
5952     structAlign = 0;
5953 
5954     for (uint32_t i = 0; i < len; ++i) {
5955       RootedValue item(cx);
5956       if (!JS_GetElement(cx, fieldsObj, i, &item)) {
5957         return false;
5958       }
5959 
5960       RootedObject fieldType(cx, nullptr);
5961       Rooted<JSLinearString*> name(cx,
5962                                    ExtractStructField(cx, item, &fieldType));
5963       if (!name) {
5964         return false;
5965       }
5966 
5967       // Make sure each field name is unique
5968       FieldInfoHash::AddPtr entryPtr = fields.lookupForAdd(name);
5969       if (entryPtr) {
5970         return DuplicateFieldError(cx, name);
5971       }
5972 
5973       // Add the field to the StructType's 'prototype' property.
5974       AutoStableStringChars nameChars(cx);
5975       if (!nameChars.initTwoByte(cx, name)) {
5976         return false;
5977       }
5978 
5979       RootedFunction getter(
5980           cx,
5981           NewFunctionWithReserved(cx, StructType::FieldGetter, 0, 0, nullptr));
5982       if (!getter) {
5983         return false;
5984       }
5985       SetFunctionNativeReserved(getter, StructType::SLOT_FIELDNAME,
5986                                 StringValue(JS_FORGET_STRING_LINEARNESS(name)));
5987       RootedObject getterObj(cx, JS_GetFunctionObject(getter));
5988 
5989       RootedFunction setter(
5990           cx,
5991           NewFunctionWithReserved(cx, StructType::FieldSetter, 1, 0, nullptr));
5992       if (!setter) {
5993         return false;
5994       }
5995       SetFunctionNativeReserved(setter, StructType::SLOT_FIELDNAME,
5996                                 StringValue(JS_FORGET_STRING_LINEARNESS(name)));
5997       RootedObject setterObj(cx, JS_GetFunctionObject(setter));
5998 
5999       if (!JS_DefineUCProperty(cx, prototype, nameChars.twoByteChars(),
6000                                name->length(), getterObj, setterObj,
6001                                JSPROP_ENUMERATE | JSPROP_PERMANENT)) {
6002         return false;
6003       }
6004 
6005       size_t fieldSize = CType::GetSize(fieldType);
6006       size_t fieldAlign = CType::GetAlignment(fieldType);
6007       size_t fieldOffset = Align(structSize, fieldAlign);
6008       // Check for overflow. Since we hold invariant that fieldSize % fieldAlign
6009       // be zero, we can safely check fieldOffset + fieldSize without first
6010       // checking fieldOffset for overflow.
6011       if (fieldOffset + fieldSize < structSize) {
6012         SizeOverflow(cx, "struct size", "size_t");
6013         return false;
6014       }
6015 
6016       // Add field name to the hash
6017       FieldInfo info;
6018       info.mType = fieldType;
6019       info.mIndex = i;
6020       info.mOffset = fieldOffset;
6021       if (!fields.add(entryPtr, name, info)) {
6022         JS_ReportOutOfMemory(cx);
6023         return false;
6024       }
6025 
6026       structSize = fieldOffset + fieldSize;
6027 
6028       if (fieldAlign > structAlign) {
6029         structAlign = fieldAlign;
6030       }
6031     }
6032 
6033     // Pad the struct tail according to struct alignment.
6034     size_t structTail = Align(structSize, structAlign);
6035     if (structTail < structSize) {
6036       SizeOverflow(cx, "struct size", "size_t");
6037       return false;
6038     }
6039     structSize = structTail;
6040 
6041   } else {
6042     // Empty structs are illegal in C, but are legal and have a size of
6043     // 1 byte in C++. We're going to allow them, and trick libffi into
6044     // believing this by adding a char member. The resulting struct will have
6045     // no getters or setters, and will be initialized to zero.
6046     structSize = 1;
6047     structAlign = 1;
6048   }
6049 
6050   RootedValue sizeVal(cx);
6051   if (!SizeTojsval(cx, structSize, &sizeVal)) {
6052     SizeOverflow(cx, "struct size", "double");
6053     return false;
6054   }
6055 
6056   // Move the field hash to the heap and store it in the typeObj.
6057   FieldInfoHash* heapHash = cx->new_<FieldInfoHash>(std::move(fields.get()));
6058   if (!heapHash) {
6059     JS_ReportOutOfMemory(cx);
6060     return false;
6061   }
6062   JS_InitReservedSlot(typeObj, SLOT_FIELDINFO, heapHash,
6063                       JS::MemoryUse::CTypeFieldInfo);
6064   JS_SetReservedSlot(typeObj, SLOT_SIZE, sizeVal);
6065   JS_SetReservedSlot(typeObj, SLOT_ALIGN, Int32Value(structAlign));
6066   // if (!JS_FreezeObject(cx, prototype)0 // XXX fixme - see bug 541212!
6067   //  return false;
6068   JS_SetReservedSlot(typeObj, SLOT_PROTO, ObjectValue(*prototype));
6069   return true;
6070 }
6071 
BuildFFIType(JSContext * cx,JSObject * obj)6072 UniquePtrFFIType StructType::BuildFFIType(JSContext* cx, JSObject* obj) {
6073   MOZ_ASSERT(CType::IsCType(obj));
6074   MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_struct);
6075   MOZ_ASSERT(CType::IsSizeDefined(obj));
6076 
6077   const FieldInfoHash* fields = GetFieldInfo(obj);
6078   size_t len = fields->count();
6079 
6080   size_t structSize = CType::GetSize(obj);
6081   size_t structAlign = CType::GetAlignment(obj);
6082 
6083   auto ffiType = cx->make_unique<ffi_type>();
6084   if (!ffiType) {
6085     return nullptr;
6086   }
6087   ffiType->type = FFI_TYPE_STRUCT;
6088 
6089   size_t count = len != 0 ? len + 1 : 2;
6090   auto elements = cx->make_pod_array<ffi_type*>(count);
6091   if (!elements) {
6092     return nullptr;
6093   }
6094 
6095   if (len != 0) {
6096     elements[len] = nullptr;
6097 
6098     for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
6099       const FieldInfoHash::Entry& entry = r.front();
6100       ffi_type* fieldType = CType::GetFFIType(cx, entry.value().mType);
6101       if (!fieldType) {
6102         return nullptr;
6103       }
6104       elements[entry.value().mIndex] = fieldType;
6105     }
6106   } else {
6107     // Represent an empty struct as having a size of 1 byte, just like C++.
6108     MOZ_ASSERT(structSize == 1);
6109     MOZ_ASSERT(structAlign == 1);
6110     elements[0] = &ffi_type_uint8;
6111     elements[1] = nullptr;
6112   }
6113 
6114   ffiType->elements = elements.release();
6115   AddCellMemory(obj, count * sizeof(ffi_type*),
6116                 MemoryUse::CTypeFFITypeElements);
6117 
6118 #ifdef DEBUG
6119   // Perform a sanity check: the result of our struct size and alignment
6120   // calculations should match libffi's. We force it to do this calculation
6121   // by calling ffi_prep_cif.
6122   ffi_cif cif;
6123   ffiType->size = 0;
6124   ffiType->alignment = 0;
6125   ffi_status status =
6126       ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 0, ffiType.get(), nullptr);
6127   MOZ_ASSERT(status == FFI_OK);
6128   MOZ_ASSERT(structSize == ffiType->size);
6129   MOZ_ASSERT(structAlign == ffiType->alignment);
6130 #else
6131   // Fill in the ffi_type's size and align fields. This makes libffi treat the
6132   // type as initialized; it will not recompute the values. (We assume
6133   // everything agrees; if it doesn't, we really want to know about it, which
6134   // is the purpose of the above debug-only check.)
6135   ffiType->size = structSize;
6136   ffiType->alignment = structAlign;
6137 #endif
6138 
6139   return ffiType;
6140 }
6141 
Define(JSContext * cx,unsigned argc,Value * vp)6142 bool StructType::Define(JSContext* cx, unsigned argc, Value* vp) {
6143   CallArgs args = CallArgsFromVp(argc, vp);
6144   RootedObject obj(cx, GetThisObject(cx, args, "StructType.prototype.define"));
6145   if (!obj) {
6146     return false;
6147   }
6148   if (!CType::IsCType(obj)) {
6149     return IncompatibleThisProto(cx, "StructType.prototype.define",
6150                                  args.thisv());
6151   }
6152   if (CType::GetTypeCode(obj) != TYPE_struct) {
6153     return IncompatibleThisType(cx, "StructType.prototype.define",
6154                                 "non-StructType", args.thisv());
6155   }
6156 
6157   if (CType::IsSizeDefined(obj)) {
6158     JS_ReportErrorASCII(cx, "StructType has already been defined");
6159     return false;
6160   }
6161 
6162   if (args.length() != 1) {
6163     return ArgumentLengthError(cx, "StructType.prototype.define", "one", "");
6164   }
6165 
6166   HandleValue arg = args[0];
6167   if (arg.isPrimitive()) {
6168     return ArgumentTypeMismatch(cx, "", "StructType.prototype.define",
6169                                 "an array");
6170   }
6171 
6172   bool isArray;
6173   if (!arg.isObject()) {
6174     isArray = false;
6175   } else {
6176     if (!JS::IsArrayObject(cx, arg, &isArray)) {
6177       return false;
6178     }
6179   }
6180 
6181   if (!isArray) {
6182     return ArgumentTypeMismatch(cx, "", "StructType.prototype.define",
6183                                 "an array");
6184   }
6185 
6186   RootedObject arr(cx, &arg.toObject());
6187   return DefineInternal(cx, obj, arr);
6188 }
6189 
ConstructData(JSContext * cx,HandleObject obj,const CallArgs & args)6190 bool StructType::ConstructData(JSContext* cx, HandleObject obj,
6191                                const CallArgs& args) {
6192   if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_struct) {
6193     return IncompatibleCallee(cx, "StructType constructor", obj);
6194   }
6195 
6196   if (!CType::IsSizeDefined(obj)) {
6197     JS_ReportErrorASCII(cx, "cannot construct an opaque StructType");
6198     return false;
6199   }
6200 
6201   JSObject* result = CData::Create(cx, obj, nullptr, nullptr, true);
6202   if (!result) {
6203     return false;
6204   }
6205 
6206   args.rval().setObject(*result);
6207 
6208   if (args.length() == 0) {
6209     return true;
6210   }
6211 
6212   char* buffer = static_cast<char*>(CData::GetData(result));
6213   const FieldInfoHash* fields = GetFieldInfo(obj);
6214 
6215   if (args.length() == 1) {
6216     // There are two possible interpretations of the argument:
6217     // 1) It may be an object '{ ... }' with properties representing the
6218     //    struct fields intended to ExplicitConvert wholesale to our StructType.
6219     // 2) If the struct contains one field, the arg may be intended to
6220     //    ImplicitConvert directly to that arg's CType.
6221     // Thankfully, the conditions for these two possibilities to succeed
6222     // are mutually exclusive, so we can pick the right one.
6223 
6224     // Try option 1) first.
6225     if (ExplicitConvert(cx, args[0], obj, buffer, ConversionType::Construct)) {
6226       return true;
6227     }
6228 
6229     if (fields->count() != 1) {
6230       return false;
6231     }
6232 
6233     // If ExplicitConvert failed, and there is no pending exception, then assume
6234     // hard failure (out of memory, or some other similarly serious condition).
6235     if (!JS_IsExceptionPending(cx)) {
6236       return false;
6237     }
6238 
6239     // Otherwise, assume soft failure, and clear the pending exception so that
6240     // we can throw a different one as required.
6241     JS_ClearPendingException(cx);
6242 
6243     // Fall through to try option 2).
6244   }
6245 
6246   // We have a type constructor of the form 'ctypes.StructType(a, b, c, ...)'.
6247   // ImplicitConvert each field.
6248   if (args.length() == fields->count()) {
6249     for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
6250       const FieldInfo& field = r.front().value();
6251       MOZ_ASSERT(field.mIndex < fields->count()); /* Quantified invariant */
6252       if (!ImplicitConvert(cx, args[field.mIndex], field.mType,
6253                            buffer + field.mOffset, ConversionType::Construct,
6254                            nullptr, nullptr, 0, obj, field.mIndex))
6255         return false;
6256     }
6257 
6258     return true;
6259   }
6260 
6261   size_t count = fields->count();
6262   if (count >= 2) {
6263     char fieldLengthStr[32];
6264     SprintfLiteral(fieldLengthStr, "0, 1, or %zu", count);
6265     return ArgumentLengthError(cx, "StructType constructor", fieldLengthStr,
6266                                "s");
6267   }
6268   return ArgumentLengthError(cx, "StructType constructor", "at most one", "");
6269 }
6270 
GetFieldInfo(JSObject * obj)6271 const FieldInfoHash* StructType::GetFieldInfo(JSObject* obj) {
6272   MOZ_ASSERT(CType::IsCType(obj));
6273   MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_struct);
6274 
6275   Value slot = JS::GetReservedSlot(obj, SLOT_FIELDINFO);
6276   MOZ_ASSERT(!slot.isUndefined() && slot.toPrivate());
6277 
6278   return static_cast<const FieldInfoHash*>(slot.toPrivate());
6279 }
6280 
LookupField(JSContext * cx,JSObject * obj,JSLinearString * name)6281 const FieldInfo* StructType::LookupField(JSContext* cx, JSObject* obj,
6282                                          JSLinearString* name) {
6283   MOZ_ASSERT(CType::IsCType(obj));
6284   MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_struct);
6285 
6286   FieldInfoHash::Ptr ptr = GetFieldInfo(obj)->lookup(name);
6287   if (ptr) {
6288     return &ptr->value();
6289   }
6290 
6291   FieldMissingError(cx, obj, name);
6292   return nullptr;
6293 }
6294 
BuildFieldsArray(JSContext * cx,JSObject * obj)6295 JSObject* StructType::BuildFieldsArray(JSContext* cx, JSObject* obj) {
6296   MOZ_ASSERT(CType::IsCType(obj));
6297   MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_struct);
6298   MOZ_ASSERT(CType::IsSizeDefined(obj));
6299 
6300   const FieldInfoHash* fields = GetFieldInfo(obj);
6301   size_t len = fields->count();
6302 
6303   // Prepare a new array for the 'fields' property of the StructType.
6304   JS::RootedValueVector fieldsVec(cx);
6305   if (!fieldsVec.resize(len)) {
6306     return nullptr;
6307   }
6308 
6309   for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
6310     const FieldInfoHash::Entry& entry = r.front();
6311     // Add the field descriptor to the array.
6312     if (!AddFieldToArray(cx, fieldsVec[entry.value().mIndex], entry.key(),
6313                          entry.value().mType))
6314       return nullptr;
6315   }
6316 
6317   RootedObject fieldsProp(cx, JS::NewArrayObject(cx, fieldsVec));
6318   if (!fieldsProp) {
6319     return nullptr;
6320   }
6321 
6322   // Seal the fields array.
6323   if (!JS_FreezeObject(cx, fieldsProp)) {
6324     return nullptr;
6325   }
6326 
6327   return fieldsProp;
6328 }
6329 
6330 /* static */
IsStruct(HandleValue v)6331 bool StructType::IsStruct(HandleValue v) {
6332   if (!v.isObject()) {
6333     return false;
6334   }
6335   JSObject* obj = &v.toObject();
6336   return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_struct;
6337 }
6338 
FieldsArrayGetter(JSContext * cx,const JS::CallArgs & args)6339 bool StructType::FieldsArrayGetter(JSContext* cx, const JS::CallArgs& args) {
6340   RootedObject obj(cx, &args.thisv().toObject());
6341 
6342   args.rval().set(JS::GetReservedSlot(obj, SLOT_FIELDS));
6343 
6344   if (!CType::IsSizeDefined(obj)) {
6345     MOZ_ASSERT(args.rval().isUndefined());
6346     return true;
6347   }
6348 
6349   if (args.rval().isUndefined()) {
6350     // Build the 'fields' array lazily.
6351     JSObject* fields = BuildFieldsArray(cx, obj);
6352     if (!fields) {
6353       return false;
6354     }
6355     JS_SetReservedSlot(obj, SLOT_FIELDS, ObjectValue(*fields));
6356 
6357     args.rval().setObject(*fields);
6358   }
6359 
6360   MOZ_ASSERT(args.rval().isObject());
6361   return true;
6362 }
6363 
FieldGetter(JSContext * cx,unsigned argc,Value * vp)6364 bool StructType::FieldGetter(JSContext* cx, unsigned argc, Value* vp) {
6365   CallArgs args = CallArgsFromVp(argc, vp);
6366 
6367   if (!args.thisv().isObject()) {
6368     return IncompatibleThisProto(cx, "StructType property getter",
6369                                  args.thisv());
6370   }
6371 
6372   RootedObject obj(cx, &args.thisv().toObject());
6373   if (!CData::IsCDataMaybeUnwrap(&obj)) {
6374     return IncompatibleThisProto(cx, "StructType property getter",
6375                                  args.thisv());
6376   }
6377 
6378   JSObject* typeObj = CData::GetCType(obj);
6379   if (CType::GetTypeCode(typeObj) != TYPE_struct) {
6380     return IncompatibleThisType(cx, "StructType property getter",
6381                                 "non-StructType CData", args.thisv());
6382   }
6383 
6384   RootedValue nameVal(
6385       cx, GetFunctionNativeReserved(&args.callee(), SLOT_FIELDNAME));
6386   Rooted<JSLinearString*> name(cx,
6387                                JS_EnsureLinearString(cx, nameVal.toString()));
6388   if (!name) {
6389     return false;
6390   }
6391 
6392   const FieldInfo* field = LookupField(cx, typeObj, name);
6393   if (!field) {
6394     return false;
6395   }
6396 
6397   char* data = static_cast<char*>(CData::GetData(obj)) + field->mOffset;
6398   RootedObject fieldType(cx, field->mType);
6399   return ConvertToJS(cx, fieldType, obj, data, false, false, args.rval());
6400 }
6401 
FieldSetter(JSContext * cx,unsigned argc,Value * vp)6402 bool StructType::FieldSetter(JSContext* cx, unsigned argc, Value* vp) {
6403   CallArgs args = CallArgsFromVp(argc, vp);
6404 
6405   if (!args.thisv().isObject()) {
6406     return IncompatibleThisProto(cx, "StructType property setter",
6407                                  args.thisv());
6408   }
6409 
6410   RootedObject obj(cx, &args.thisv().toObject());
6411   if (!CData::IsCDataMaybeUnwrap(&obj)) {
6412     return IncompatibleThisProto(cx, "StructType property setter",
6413                                  args.thisv());
6414   }
6415 
6416   RootedObject typeObj(cx, CData::GetCType(obj));
6417   if (CType::GetTypeCode(typeObj) != TYPE_struct) {
6418     return IncompatibleThisType(cx, "StructType property setter",
6419                                 "non-StructType CData", args.thisv());
6420   }
6421 
6422   RootedValue nameVal(
6423       cx, GetFunctionNativeReserved(&args.callee(), SLOT_FIELDNAME));
6424   Rooted<JSLinearString*> name(cx,
6425                                JS_EnsureLinearString(cx, nameVal.toString()));
6426   if (!name) {
6427     return false;
6428   }
6429 
6430   const FieldInfo* field = LookupField(cx, typeObj, name);
6431   if (!field) {
6432     return false;
6433   }
6434 
6435   args.rval().setUndefined();
6436 
6437   char* data = static_cast<char*>(CData::GetData(obj)) + field->mOffset;
6438   return ImplicitConvert(cx, args.get(0), field->mType, data,
6439                          ConversionType::Setter, nullptr, nullptr, 0, typeObj,
6440                          field->mIndex);
6441 }
6442 
AddressOfField(JSContext * cx,unsigned argc,Value * vp)6443 bool StructType::AddressOfField(JSContext* cx, unsigned argc, Value* vp) {
6444   CallArgs args = CallArgsFromVp(argc, vp);
6445   RootedObject obj(
6446       cx, GetThisObject(cx, args, "StructType.prototype.addressOfField"));
6447   if (!obj) {
6448     return false;
6449   }
6450 
6451   if (!CData::IsCDataMaybeUnwrap(&obj)) {
6452     return IncompatibleThisProto(cx, "StructType.prototype.addressOfField",
6453                                  args.thisv());
6454   }
6455 
6456   JSObject* typeObj = CData::GetCType(obj);
6457   if (CType::GetTypeCode(typeObj) != TYPE_struct) {
6458     return IncompatibleThisType(cx, "StructType.prototype.addressOfField",
6459                                 "non-StructType CData", args.thisv());
6460   }
6461 
6462   if (args.length() != 1) {
6463     return ArgumentLengthError(cx, "StructType.prototype.addressOfField", "one",
6464                                "");
6465   }
6466 
6467   if (!args[0].isString()) {
6468     return ArgumentTypeMismatch(cx, "", "StructType.prototype.addressOfField",
6469                                 "a string");
6470   }
6471 
6472   JSLinearString* str = JS_EnsureLinearString(cx, args[0].toString());
6473   if (!str) {
6474     return false;
6475   }
6476 
6477   const FieldInfo* field = LookupField(cx, typeObj, str);
6478   if (!field) {
6479     return false;
6480   }
6481 
6482   RootedObject baseType(cx, field->mType);
6483   RootedObject pointerType(cx, PointerType::CreateInternal(cx, baseType));
6484   if (!pointerType) {
6485     return false;
6486   }
6487 
6488   // Create a PointerType CData object containing null.
6489   JSObject* result = CData::Create(cx, pointerType, nullptr, nullptr, true);
6490   if (!result) {
6491     return false;
6492   }
6493 
6494   args.rval().setObject(*result);
6495 
6496   // Manually set the pointer inside the object, so we skip the conversion step.
6497   void** data = static_cast<void**>(CData::GetData(result));
6498   *data = static_cast<char*>(CData::GetData(obj)) + field->mOffset;
6499   return true;
6500 }
6501 
6502 /*******************************************************************************
6503 ** FunctionType implementation
6504 *******************************************************************************/
6505 
6506 // Helper class for handling allocation of function arguments.
6507 struct AutoValue {
AutoValuejs::ctypes::AutoValue6508   AutoValue() : mData(nullptr) {}
6509 
~AutoValuejs::ctypes::AutoValue6510   ~AutoValue() { js_free(mData); }
6511 
SizeToTypejs::ctypes::AutoValue6512   bool SizeToType(JSContext* cx, JSObject* type) {
6513     // Allocate a minimum of sizeof(ffi_arg) to handle small integers.
6514     size_t size = Align(CType::GetSize(type), sizeof(ffi_arg));
6515     mData = js_malloc(size);
6516     if (mData) {
6517       memset(mData, 0, size);
6518     }
6519     return mData != nullptr;
6520   }
6521 
6522   void* mData;
6523 };
6524 
GetABI(JSContext * cx,HandleValue abiType,ffi_abi * result)6525 static bool GetABI(JSContext* cx, HandleValue abiType, ffi_abi* result) {
6526   if (abiType.isPrimitive()) {
6527     return false;
6528   }
6529 
6530   ABICode abi = GetABICode(abiType.toObjectOrNull());
6531 
6532   // determine the ABI from the subset of those available on the
6533   // given platform. ABI_DEFAULT specifies the default
6534   // C calling convention (cdecl) on each platform.
6535   switch (abi) {
6536     case ABI_DEFAULT:
6537       *result = FFI_DEFAULT_ABI;
6538       return true;
6539     case ABI_THISCALL:
6540 #if defined(_WIN64)
6541 #  if defined(_M_X64)
6542       *result = FFI_WIN64;
6543 #  elif defined(_M_ARM64)
6544       *result = FFI_SYSV;
6545 #  else
6546 #    error unknown 64-bit Windows platform
6547 #  endif
6548       return true;
6549 #elif defined(_WIN32)
6550       *result = FFI_THISCALL;
6551       return true;
6552 #else
6553       break;
6554 #endif
6555     case ABI_STDCALL:
6556     case ABI_WINAPI:
6557 #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
6558       *result = FFI_STDCALL;
6559       return true;
6560 #elif (defined(_WIN64))
6561       // We'd like the same code to work across Win32 and Win64, so stdcall_api
6562       // and winapi_abi become aliases to the lone Win64 ABI.
6563 #  if defined(_M_X64)
6564       *result = FFI_WIN64;
6565 #  elif defined(_M_ARM64)
6566       *result = FFI_SYSV;
6567 #  else
6568 #    error unknown 64-bit Windows platform
6569 #  endif
6570       return true;
6571 #endif
6572     case INVALID_ABI:
6573       break;
6574   }
6575   return false;
6576 }
6577 
PrepareType(JSContext * cx,uint32_t index,HandleValue type)6578 static JSObject* PrepareType(JSContext* cx, uint32_t index, HandleValue type) {
6579   if (type.isPrimitive() || !CType::IsCType(type.toObjectOrNull())) {
6580     FunctionArgumentTypeError(cx, index, type, "is not a ctypes type");
6581     return nullptr;
6582   }
6583 
6584   JSObject* result = type.toObjectOrNull();
6585   TypeCode typeCode = CType::GetTypeCode(result);
6586 
6587   if (typeCode == TYPE_array) {
6588     // convert array argument types to pointers, just like C.
6589     // ImplicitConvert will do the same, when passing an array as data.
6590     RootedObject baseType(cx, ArrayType::GetBaseType(result));
6591     result = PointerType::CreateInternal(cx, baseType);
6592     if (!result) {
6593       return nullptr;
6594     }
6595 
6596   } else if (typeCode == TYPE_void_t || typeCode == TYPE_function) {
6597     // disallow void or function argument types
6598     FunctionArgumentTypeError(cx, index, type, "cannot be void or function");
6599     return nullptr;
6600   }
6601 
6602   if (!CType::IsSizeDefined(result)) {
6603     FunctionArgumentTypeError(cx, index, type, "must have defined size");
6604     return nullptr;
6605   }
6606 
6607   // libffi cannot pass types of zero size by value.
6608   MOZ_ASSERT(CType::GetSize(result) != 0);
6609 
6610   return result;
6611 }
6612 
PrepareReturnType(JSContext * cx,HandleValue type)6613 static JSObject* PrepareReturnType(JSContext* cx, HandleValue type) {
6614   if (type.isPrimitive() || !CType::IsCType(type.toObjectOrNull())) {
6615     FunctionReturnTypeError(cx, type, "is not a ctypes type");
6616     return nullptr;
6617   }
6618 
6619   JSObject* result = type.toObjectOrNull();
6620   TypeCode typeCode = CType::GetTypeCode(result);
6621 
6622   // Arrays and functions can never be return types.
6623   if (typeCode == TYPE_array || typeCode == TYPE_function) {
6624     FunctionReturnTypeError(cx, type, "cannot be an array or function");
6625     return nullptr;
6626   }
6627 
6628   if (typeCode != TYPE_void_t && !CType::IsSizeDefined(result)) {
6629     FunctionReturnTypeError(cx, type, "must have defined size");
6630     return nullptr;
6631   }
6632 
6633   // libffi cannot pass types of zero size by value.
6634   MOZ_ASSERT(typeCode == TYPE_void_t || CType::GetSize(result) != 0);
6635 
6636   return result;
6637 }
6638 
IsEllipsis(JSContext * cx,HandleValue v,bool * isEllipsis)6639 static MOZ_ALWAYS_INLINE bool IsEllipsis(JSContext* cx, HandleValue v,
6640                                          bool* isEllipsis) {
6641   *isEllipsis = false;
6642   if (!v.isString()) {
6643     return true;
6644   }
6645   JSString* str = v.toString();
6646   if (str->length() != 3) {
6647     return true;
6648   }
6649   JSLinearString* linear = str->ensureLinear(cx);
6650   if (!linear) {
6651     return false;
6652   }
6653   char16_t dot = '.';
6654   *isEllipsis = (linear->latin1OrTwoByteChar(0) == dot &&
6655                  linear->latin1OrTwoByteChar(1) == dot &&
6656                  linear->latin1OrTwoByteChar(2) == dot);
6657   return true;
6658 }
6659 
PrepareCIF(JSContext * cx,FunctionInfo * fninfo)6660 static bool PrepareCIF(JSContext* cx, FunctionInfo* fninfo) {
6661   ffi_abi abi;
6662   RootedValue abiType(cx, ObjectOrNullValue(fninfo->mABI));
6663   if (!GetABI(cx, abiType, &abi)) {
6664     JS_ReportErrorASCII(cx, "Invalid ABI specification");
6665     return false;
6666   }
6667 
6668   ffi_type* rtype = CType::GetFFIType(cx, fninfo->mReturnType);
6669   if (!rtype) {
6670     return false;
6671   }
6672 
6673   ffi_status status;
6674   if (fninfo->mIsVariadic) {
6675     status = ffi_prep_cif_var(&fninfo->mCIF, abi, fninfo->mArgTypes.length(),
6676                               fninfo->mFFITypes.length(), rtype,
6677                               fninfo->mFFITypes.begin());
6678   } else {
6679     status = ffi_prep_cif(&fninfo->mCIF, abi, fninfo->mFFITypes.length(), rtype,
6680                           fninfo->mFFITypes.begin());
6681   }
6682 
6683   switch (status) {
6684     case FFI_OK:
6685       return true;
6686     case FFI_BAD_ABI:
6687       JS_ReportErrorASCII(cx, "Invalid ABI specification");
6688       return false;
6689     case FFI_BAD_TYPEDEF:
6690       JS_ReportErrorASCII(cx, "Invalid type specification");
6691       return false;
6692     default:
6693       JS_ReportErrorASCII(cx, "Unknown libffi error");
6694       return false;
6695   }
6696 }
6697 
BuildSymbolName(JSContext * cx,JSString * name,JSObject * typeObj,AutoCString & result)6698 void FunctionType::BuildSymbolName(JSContext* cx, JSString* name,
6699                                    JSObject* typeObj, AutoCString& result) {
6700   FunctionInfo* fninfo = GetFunctionInfo(typeObj);
6701 
6702   switch (GetABICode(fninfo->mABI)) {
6703     case ABI_DEFAULT:
6704     case ABI_THISCALL:
6705     case ABI_WINAPI:
6706       // For cdecl or WINAPI functions, no mangling is necessary.
6707       AppendString(cx, result, name);
6708       break;
6709 
6710     case ABI_STDCALL: {
6711 #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
6712       // On WIN32, stdcall functions look like:
6713       //   _foo@40
6714       // where 'foo' is the function name, and '40' is the aligned size of the
6715       // arguments.
6716       AppendString(cx, result, "_");
6717       AppendString(cx, result, name);
6718       AppendString(cx, result, "@");
6719 
6720       // Compute the suffix by aligning each argument to sizeof(ffi_arg).
6721       size_t size = 0;
6722       for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
6723         JSObject* argType = fninfo->mArgTypes[i];
6724         size += Align(CType::GetSize(argType), sizeof(ffi_arg));
6725       }
6726 
6727       IntegerToString(size, 10, result);
6728 #elif defined(_WIN64)
6729       // On Win64, stdcall is an alias to the default ABI for compatibility, so
6730       // no mangling is done.
6731       AppendString(cx, result, name);
6732 #endif
6733       break;
6734     }
6735 
6736     case INVALID_ABI:
6737       MOZ_CRASH("invalid abi");
6738   }
6739 }
6740 
CreateFunctionInfo(JSContext * cx,HandleObject typeObj,HandleValue abiType,HandleObject returnType,const HandleValueArray & args)6741 static bool CreateFunctionInfo(JSContext* cx, HandleObject typeObj,
6742                                HandleValue abiType, HandleObject returnType,
6743                                const HandleValueArray& args) {
6744   FunctionInfo* fninfo(cx->new_<FunctionInfo>(cx->zone()));
6745   if (!fninfo) {
6746     return false;
6747   }
6748 
6749   // Stash the FunctionInfo in a reserved slot.
6750   JS_InitReservedSlot(typeObj, SLOT_FNINFO, fninfo,
6751                       JS::MemoryUse::CTypeFunctionInfo);
6752 
6753   ffi_abi abi;
6754   if (!GetABI(cx, abiType, &abi)) {
6755     JS_ReportErrorASCII(cx, "Invalid ABI specification");
6756     return false;
6757   }
6758   fninfo->mABI = abiType.toObjectOrNull();
6759 
6760   fninfo->mReturnType = returnType;
6761 
6762   // prepare the argument types
6763   if (!fninfo->mArgTypes.reserve(args.length()) ||
6764       !fninfo->mFFITypes.reserve(args.length())) {
6765     JS_ReportOutOfMemory(cx);
6766     return false;
6767   }
6768 
6769   fninfo->mIsVariadic = false;
6770 
6771   for (uint32_t i = 0; i < args.length(); ++i) {
6772     bool isEllipsis;
6773     if (!IsEllipsis(cx, args[i], &isEllipsis)) {
6774       return false;
6775     }
6776     if (isEllipsis) {
6777       fninfo->mIsVariadic = true;
6778       if (i < 1) {
6779         JS_ReportErrorASCII(cx,
6780                             "\"...\" may not be the first and only parameter "
6781                             "type of a variadic function declaration");
6782         return false;
6783       }
6784       if (i < args.length() - 1) {
6785         JS_ReportErrorASCII(cx,
6786                             "\"...\" must be the last parameter type of a "
6787                             "variadic function declaration");
6788         return false;
6789       }
6790       if (GetABICode(fninfo->mABI) != ABI_DEFAULT) {
6791         JS_ReportErrorASCII(cx,
6792                             "Variadic functions must use the __cdecl calling "
6793                             "convention");
6794         return false;
6795       }
6796       break;
6797     }
6798 
6799     JSObject* argType = PrepareType(cx, i, args[i]);
6800     if (!argType) {
6801       return false;
6802     }
6803 
6804     ffi_type* ffiType = CType::GetFFIType(cx, argType);
6805     if (!ffiType) {
6806       return false;
6807     }
6808 
6809     fninfo->mArgTypes.infallibleAppend(argType);
6810     fninfo->mFFITypes.infallibleAppend(ffiType);
6811   }
6812 
6813   if (fninfo->mIsVariadic) {
6814     // wait to PrepareCIF until function is called
6815     return true;
6816   }
6817 
6818   if (!PrepareCIF(cx, fninfo)) {
6819     return false;
6820   }
6821 
6822   return true;
6823 }
6824 
Create(JSContext * cx,unsigned argc,Value * vp)6825 bool FunctionType::Create(JSContext* cx, unsigned argc, Value* vp) {
6826   // Construct and return a new FunctionType object.
6827   CallArgs args = CallArgsFromVp(argc, vp);
6828   if (args.length() < 2 || args.length() > 3) {
6829     return ArgumentLengthError(cx, "FunctionType", "two or three", "s");
6830   }
6831 
6832   JS::RootedValueVector argTypes(cx);
6833   RootedObject arrayObj(cx, nullptr);
6834 
6835   if (args.length() == 3) {
6836     // Prepare an array of Values for the arguments.
6837     bool isArray;
6838     if (!args[2].isObject()) {
6839       isArray = false;
6840     } else {
6841       if (!JS::IsArrayObject(cx, args[2], &isArray)) {
6842         return false;
6843       }
6844     }
6845 
6846     if (!isArray) {
6847       return ArgumentTypeMismatch(cx, "third ", "FunctionType", "an array");
6848     }
6849 
6850     arrayObj = &args[2].toObject();
6851 
6852     uint32_t len;
6853     MOZ_ALWAYS_TRUE(JS::GetArrayLength(cx, arrayObj, &len));
6854 
6855     if (!argTypes.resize(len)) {
6856       JS_ReportOutOfMemory(cx);
6857       return false;
6858     }
6859   }
6860 
6861   // Pull out the argument types from the array, if any.
6862   MOZ_ASSERT_IF(argTypes.length(), arrayObj);
6863   for (uint32_t i = 0; i < argTypes.length(); ++i) {
6864     if (!JS_GetElement(cx, arrayObj, i, argTypes[i])) {
6865       return false;
6866     }
6867   }
6868 
6869   JSObject* result = CreateInternal(cx, args[0], args[1], argTypes);
6870   if (!result) {
6871     return false;
6872   }
6873 
6874   args.rval().setObject(*result);
6875   return true;
6876 }
6877 
CreateInternal(JSContext * cx,HandleValue abi,HandleValue rtype,const HandleValueArray & args)6878 JSObject* FunctionType::CreateInternal(JSContext* cx, HandleValue abi,
6879                                        HandleValue rtype,
6880                                        const HandleValueArray& args) {
6881   // Prepare the result type
6882   RootedObject returnType(cx, PrepareReturnType(cx, rtype));
6883   if (!returnType) {
6884     return nullptr;
6885   }
6886 
6887   // Get ctypes.FunctionType.prototype and the common prototype for CData
6888   // objects of this type, from ctypes.CType.prototype.
6889   RootedObject typeProto(
6890       cx, CType::GetProtoFromType(cx, returnType, SLOT_FUNCTIONPROTO));
6891   if (!typeProto) {
6892     return nullptr;
6893   }
6894   RootedObject dataProto(
6895       cx, CType::GetProtoFromType(cx, returnType, SLOT_FUNCTIONDATAPROTO));
6896   if (!dataProto) {
6897     return nullptr;
6898   }
6899 
6900   // Create a new CType object with the common properties and slots.
6901   RootedObject typeObj(
6902       cx, CType::Create(cx, typeProto, dataProto, TYPE_function, nullptr,
6903                         JS::UndefinedHandleValue, JS::UndefinedHandleValue,
6904                         nullptr));
6905   if (!typeObj) {
6906     return nullptr;
6907   }
6908 
6909   // Determine and check the types, and prepare the function CIF.
6910   if (!CreateFunctionInfo(cx, typeObj, abi, returnType, args)) {
6911     return nullptr;
6912   }
6913 
6914   return typeObj;
6915 }
6916 
6917 // Construct a function pointer to a JS function (see CClosure::Create()).
6918 // Regular function pointers are constructed directly in
6919 // PointerType::ConstructData().
ConstructData(JSContext * cx,HandleObject typeObj,HandleObject dataObj,HandleObject fnObj,HandleObject thisObj,HandleValue errVal)6920 bool FunctionType::ConstructData(JSContext* cx, HandleObject typeObj,
6921                                  HandleObject dataObj, HandleObject fnObj,
6922                                  HandleObject thisObj, HandleValue errVal) {
6923   MOZ_ASSERT(CType::GetTypeCode(typeObj) == TYPE_function);
6924 
6925   PRFuncPtr* data = static_cast<PRFuncPtr*>(CData::GetData(dataObj));
6926 
6927   FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
6928   if (fninfo->mIsVariadic) {
6929     JS_ReportErrorASCII(cx, "Can't declare a variadic callback function");
6930     return false;
6931   }
6932   if (GetABICode(fninfo->mABI) == ABI_WINAPI) {
6933     JS_ReportErrorASCII(cx,
6934                         "Can't declare a ctypes.winapi_abi callback function, "
6935                         "use ctypes.stdcall_abi instead");
6936     return false;
6937   }
6938 
6939   RootedObject closureObj(
6940       cx, CClosure::Create(cx, typeObj, fnObj, thisObj, errVal, data));
6941   if (!closureObj) {
6942     return false;
6943   }
6944 
6945   // Set the closure object as the referent of the new CData object.
6946   JS_SetReservedSlot(dataObj, SLOT_REFERENT, ObjectValue(*closureObj));
6947 
6948   // Seal the CData object, to prevent modification of the function pointer.
6949   // This permanently associates this object with the closure, and avoids
6950   // having to do things like reset SLOT_REFERENT when someone tries to
6951   // change the pointer value.
6952   // XXX This will need to change when bug 541212 is fixed -- CData::ValueSetter
6953   // could be called on a frozen object.
6954   return JS_FreezeObject(cx, dataObj);
6955 }
6956 
6957 typedef Vector<AutoValue, 16, SystemAllocPolicy> AutoValueAutoArray;
6958 
ConvertArgument(JSContext * cx,HandleObject funObj,unsigned argIndex,HandleValue arg,JSObject * type,AutoValue * value,AutoValueAutoArray * strings)6959 static bool ConvertArgument(JSContext* cx, HandleObject funObj,
6960                             unsigned argIndex, HandleValue arg, JSObject* type,
6961                             AutoValue* value, AutoValueAutoArray* strings) {
6962   if (!value->SizeToType(cx, type)) {
6963     JS_ReportAllocationOverflow(cx);
6964     return false;
6965   }
6966 
6967   bool freePointer = false;
6968   if (!ImplicitConvert(cx, arg, type, value->mData, ConversionType::Argument,
6969                        &freePointer, funObj, argIndex))
6970     return false;
6971 
6972   if (freePointer) {
6973     // ImplicitConvert converted a string for us, which we have to free.
6974     // Keep track of it.
6975     if (!strings->growBy(1)) {
6976       JS_ReportOutOfMemory(cx);
6977       return false;
6978     }
6979     strings->back().mData = *static_cast<char**>(value->mData);
6980   }
6981 
6982   return true;
6983 }
6984 
Call(JSContext * cx,unsigned argc,Value * vp)6985 bool FunctionType::Call(JSContext* cx, unsigned argc, Value* vp) {
6986   CallArgs args = CallArgsFromVp(argc, vp);
6987   // get the callee object...
6988   RootedObject obj(cx, &args.callee());
6989   if (!CData::IsCDataMaybeUnwrap(&obj)) {
6990     return IncompatibleThisProto(cx, "FunctionType.prototype.call",
6991                                  args.calleev());
6992   }
6993 
6994   RootedObject typeObj(cx, CData::GetCType(obj));
6995   if (CType::GetTypeCode(typeObj) != TYPE_pointer) {
6996     return IncompatibleThisType(cx, "FunctionType.prototype.call",
6997                                 "non-PointerType CData", args.calleev());
6998   }
6999 
7000   typeObj = PointerType::GetBaseType(typeObj);
7001   if (CType::GetTypeCode(typeObj) != TYPE_function) {
7002     return IncompatibleThisType(cx, "FunctionType.prototype.call",
7003                                 "non-FunctionType pointer", args.calleev());
7004   }
7005 
7006   FunctionInfo* fninfo = GetFunctionInfo(typeObj);
7007   uint32_t argcFixed = fninfo->mArgTypes.length();
7008 
7009   if ((!fninfo->mIsVariadic && args.length() != argcFixed) ||
7010       (fninfo->mIsVariadic && args.length() < argcFixed)) {
7011     return FunctionArgumentLengthMismatch(cx, argcFixed, args.length(), obj,
7012                                           typeObj, fninfo->mIsVariadic);
7013   }
7014 
7015   // Check if we have a Library object. If we do, make sure it's open.
7016   Value slot = JS::GetReservedSlot(obj, SLOT_REFERENT);
7017   if (!slot.isUndefined() && Library::IsLibrary(&slot.toObject())) {
7018     PRLibrary* library = Library::GetLibrary(&slot.toObject());
7019     if (!library) {
7020       JS_ReportErrorASCII(cx, "library is not open");
7021       return false;
7022     }
7023   }
7024 
7025   // prepare the values for each argument
7026   AutoValueAutoArray values;
7027   AutoValueAutoArray strings;
7028   if (!values.resize(args.length())) {
7029     JS_ReportOutOfMemory(cx);
7030     return false;
7031   }
7032 
7033   for (unsigned i = 0; i < argcFixed; ++i) {
7034     if (!ConvertArgument(cx, obj, i, args[i], fninfo->mArgTypes[i], &values[i],
7035                          &strings)) {
7036       return false;
7037     }
7038   }
7039 
7040   if (fninfo->mIsVariadic) {
7041     if (!fninfo->mFFITypes.resize(args.length())) {
7042       JS_ReportOutOfMemory(cx);
7043       return false;
7044     }
7045 
7046     RootedObject obj(cx);   // Could reuse obj instead of declaring a second
7047     RootedObject type(cx);  // RootedObject, but readability would suffer.
7048     RootedValue arg(cx);
7049 
7050     for (uint32_t i = argcFixed; i < args.length(); ++i) {
7051       obj = args[i].isObject() ? &args[i].toObject() : nullptr;
7052       if (!obj || !CData::IsCDataMaybeUnwrap(&obj)) {
7053         // Since we know nothing about the CTypes of the ... arguments,
7054         // they absolutely must be CData objects already.
7055         return VariadicArgumentTypeError(cx, i, args[i]);
7056       }
7057       type = CData::GetCType(obj);
7058       if (!type) {
7059         // These functions report their own errors.
7060         return false;
7061       }
7062       RootedValue typeVal(cx, ObjectValue(*type));
7063       type = PrepareType(cx, i, typeVal);
7064       if (!type) {
7065         return false;
7066       }
7067       // Relying on ImplicitConvert only for the limited purpose of
7068       // converting one CType to another (e.g., T[] to T*).
7069       arg = ObjectValue(*obj);
7070       if (!ConvertArgument(cx, obj, i, arg, type, &values[i], &strings)) {
7071         return false;
7072       }
7073       fninfo->mFFITypes[i] = CType::GetFFIType(cx, type);
7074       if (!fninfo->mFFITypes[i]) {
7075         return false;
7076       }
7077     }
7078     if (!PrepareCIF(cx, fninfo)) {
7079       return false;
7080     }
7081   }
7082 
7083   // initialize a pointer to an appropriate location, for storing the result
7084   AutoValue returnValue;
7085   TypeCode typeCode = CType::GetTypeCode(fninfo->mReturnType);
7086   if (typeCode != TYPE_void_t &&
7087       !returnValue.SizeToType(cx, fninfo->mReturnType)) {
7088     JS_ReportAllocationOverflow(cx);
7089     return false;
7090   }
7091 
7092   // Let the runtime callback know that we are about to call into C.
7093   AutoCTypesActivityCallback autoCallback(cx, CTypesActivityType::BeginCall,
7094                                           CTypesActivityType::EndCall);
7095 
7096   uintptr_t fn = *reinterpret_cast<uintptr_t*>(CData::GetData(obj));
7097 
7098 #if defined(XP_WIN)
7099   int32_t lastErrorStatus;  // The status as defined by |GetLastError|
7100   int32_t savedLastError = GetLastError();
7101   SetLastError(0);
7102 #endif              // defined(XP_WIN)
7103   int errnoStatus;  // The status as defined by |errno|
7104   int savedErrno = errno;
7105   errno = 0;
7106 
7107   ffi_call(&fninfo->mCIF, FFI_FN(fn), returnValue.mData,
7108            reinterpret_cast<void**>(values.begin()));
7109 
7110   // Save error value.
7111   // We need to save it before leaving the scope of |suspend| as destructing
7112   // |suspend| has the side-effect of clearing |GetLastError|
7113   // (see bug 684017).
7114 
7115   errnoStatus = errno;
7116 #if defined(XP_WIN)
7117   lastErrorStatus = GetLastError();
7118   SetLastError(savedLastError);
7119 #endif  // defined(XP_WIN)
7120 
7121   errno = savedErrno;
7122 
7123   // We're no longer calling into C.
7124   autoCallback.DoEndCallback();
7125 
7126   // Store the error value for later consultation with |ctypes.getStatus|
7127   JSObject* objCTypes = CType::GetGlobalCTypes(cx, typeObj);
7128   if (!objCTypes) {
7129     return false;
7130   }
7131 
7132   JS_SetReservedSlot(objCTypes, SLOT_ERRNO, Int32Value(errnoStatus));
7133 #if defined(XP_WIN)
7134   JS_SetReservedSlot(objCTypes, SLOT_LASTERROR, Int32Value(lastErrorStatus));
7135 #endif  // defined(XP_WIN)
7136 
7137   // Small integer types get returned as a word-sized ffi_arg. Coerce it back
7138   // into the correct size for ConvertToJS.
7139   switch (typeCode) {
7140 #define INTEGRAL_CASE(name, type, ffiType)                              \
7141   case TYPE_##name:                                                     \
7142     if (sizeof(type) < sizeof(ffi_arg)) {                               \
7143       ffi_arg data = *static_cast<ffi_arg*>(returnValue.mData);         \
7144       *static_cast<type*>(returnValue.mData) = static_cast<type>(data); \
7145     }                                                                   \
7146     break;
7147     CTYPES_FOR_EACH_INT_TYPE(INTEGRAL_CASE)
7148     CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGRAL_CASE)
7149     CTYPES_FOR_EACH_BOOL_TYPE(INTEGRAL_CASE)
7150     CTYPES_FOR_EACH_CHAR_TYPE(INTEGRAL_CASE)
7151     CTYPES_FOR_EACH_CHAR16_TYPE(INTEGRAL_CASE)
7152 #undef INTEGRAL_CASE
7153     default:
7154       break;
7155   }
7156 
7157   // prepare a JS object from the result
7158   RootedObject returnType(cx, fninfo->mReturnType);
7159   return ConvertToJS(cx, returnType, nullptr, returnValue.mData, false, true,
7160                      args.rval());
7161 }
7162 
GetFunctionInfo(JSObject * obj)7163 FunctionInfo* FunctionType::GetFunctionInfo(JSObject* obj) {
7164   MOZ_ASSERT(CType::IsCType(obj));
7165   MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_function);
7166 
7167   Value slot = JS::GetReservedSlot(obj, SLOT_FNINFO);
7168   MOZ_ASSERT(!slot.isUndefined() && slot.toPrivate());
7169 
7170   return static_cast<FunctionInfo*>(slot.toPrivate());
7171 }
7172 
IsFunctionType(HandleValue v)7173 bool FunctionType::IsFunctionType(HandleValue v) {
7174   if (!v.isObject()) {
7175     return false;
7176   }
7177   JSObject* obj = &v.toObject();
7178   return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_function;
7179 }
7180 
ArgTypesGetter(JSContext * cx,const JS::CallArgs & args)7181 bool FunctionType::ArgTypesGetter(JSContext* cx, const JS::CallArgs& args) {
7182   JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
7183 
7184   args.rval().set(JS::GetReservedSlot(obj, SLOT_ARGS_T));
7185   if (!args.rval().isUndefined()) {
7186     return true;
7187   }
7188 
7189   FunctionInfo* fninfo = GetFunctionInfo(obj);
7190   size_t len = fninfo->mArgTypes.length();
7191 
7192   // Prepare a new array.
7193   JS::Rooted<JSObject*> argTypes(cx);
7194   {
7195     JS::RootedValueVector vec(cx);
7196     if (!vec.resize(len)) {
7197       return false;
7198     }
7199 
7200     for (size_t i = 0; i < len; ++i) {
7201       vec[i].setObject(*fninfo->mArgTypes[i]);
7202     }
7203 
7204     argTypes = JS::NewArrayObject(cx, vec);
7205     if (!argTypes) {
7206       return false;
7207     }
7208   }
7209 
7210   // Seal and cache it.
7211   if (!JS_FreezeObject(cx, argTypes)) {
7212     return false;
7213   }
7214   JS_SetReservedSlot(obj, SLOT_ARGS_T, JS::ObjectValue(*argTypes));
7215 
7216   args.rval().setObject(*argTypes);
7217   return true;
7218 }
7219 
ReturnTypeGetter(JSContext * cx,const JS::CallArgs & args)7220 bool FunctionType::ReturnTypeGetter(JSContext* cx, const JS::CallArgs& args) {
7221   // Get the returnType object from the FunctionInfo.
7222   args.rval().setObject(
7223       *GetFunctionInfo(&args.thisv().toObject())->mReturnType);
7224   return true;
7225 }
7226 
ABIGetter(JSContext * cx,const JS::CallArgs & args)7227 bool FunctionType::ABIGetter(JSContext* cx, const JS::CallArgs& args) {
7228   // Get the abi object from the FunctionInfo.
7229   args.rval().setObject(*GetFunctionInfo(&args.thisv().toObject())->mABI);
7230   return true;
7231 }
7232 
IsVariadicGetter(JSContext * cx,const JS::CallArgs & args)7233 bool FunctionType::IsVariadicGetter(JSContext* cx, const JS::CallArgs& args) {
7234   args.rval().setBoolean(
7235       GetFunctionInfo(&args.thisv().toObject())->mIsVariadic);
7236   return true;
7237 }
7238 
7239 /*******************************************************************************
7240 ** CClosure implementation
7241 *******************************************************************************/
7242 
Create(JSContext * cx,HandleObject typeObj,HandleObject fnObj,HandleObject thisObj,HandleValue errVal,PRFuncPtr * fnptr)7243 JSObject* CClosure::Create(JSContext* cx, HandleObject typeObj,
7244                            HandleObject fnObj, HandleObject thisObj,
7245                            HandleValue errVal, PRFuncPtr* fnptr) {
7246   MOZ_ASSERT(fnObj);
7247 
7248   RootedObject result(cx, JS_NewObject(cx, &sCClosureClass));
7249   if (!result) {
7250     return nullptr;
7251   }
7252 
7253   // Get the FunctionInfo from the FunctionType.
7254   FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
7255   MOZ_ASSERT(!fninfo->mIsVariadic);
7256   MOZ_ASSERT(GetABICode(fninfo->mABI) != ABI_WINAPI);
7257 
7258   // Get the prototype of the FunctionType object, of class CTypeProto,
7259   // which stores our JSContext for use with the closure.
7260   RootedObject proto(cx);
7261   if (!JS_GetPrototype(cx, typeObj, &proto)) {
7262     return nullptr;
7263   }
7264   MOZ_ASSERT(proto);
7265   MOZ_ASSERT(CType::IsCTypeProto(proto));
7266 
7267   // Prepare the error sentinel value. It's important to do this now, because
7268   // we might be unable to convert the value to the proper type. If so, we want
7269   // the caller to know about it _now_, rather than some uncertain time in the
7270   // future when the error sentinel is actually needed.
7271   UniquePtr<uint8_t[], JS::FreePolicy> errResult;
7272   if (!errVal.isUndefined()) {
7273     // Make sure the callback returns something.
7274     if (CType::GetTypeCode(fninfo->mReturnType) == TYPE_void_t) {
7275       JS_ReportErrorASCII(cx, "A void callback can't pass an error sentinel");
7276       return nullptr;
7277     }
7278 
7279     // With the exception of void, the FunctionType constructor ensures that
7280     // the return type has a defined size.
7281     MOZ_ASSERT(CType::IsSizeDefined(fninfo->mReturnType));
7282 
7283     // Allocate a buffer for the return value.
7284     size_t rvSize = CType::GetSize(fninfo->mReturnType);
7285     errResult = cx->make_pod_array<uint8_t>(rvSize);
7286     if (!errResult) {
7287       return nullptr;
7288     }
7289 
7290     // Do the value conversion. This might fail, in which case we throw.
7291     if (!ImplicitConvert(cx, errVal, fninfo->mReturnType, errResult.get(),
7292                          ConversionType::Return, nullptr, typeObj))
7293       return nullptr;
7294   }
7295 
7296   ClosureInfo* cinfo = cx->new_<ClosureInfo>(cx);
7297   if (!cinfo) {
7298     JS_ReportOutOfMemory(cx);
7299     return nullptr;
7300   }
7301 
7302   // Copy the important bits of context into cinfo.
7303   cinfo->errResult = errResult.release();
7304   cinfo->closureObj = result;
7305   cinfo->typeObj = typeObj;
7306   cinfo->thisObj = thisObj;
7307   cinfo->jsfnObj = fnObj;
7308 
7309   // Stash the ClosureInfo struct on our new object.
7310   JS_InitReservedSlot(result, SLOT_CLOSUREINFO, cinfo,
7311                       JS::MemoryUse::CClosureInfo);
7312 
7313   // Create an ffi_closure object and initialize it.
7314   void* code;
7315   cinfo->closure =
7316       static_cast<ffi_closure*>(ffi_closure_alloc(sizeof(ffi_closure), &code));
7317   if (!cinfo->closure || !code) {
7318     JS_ReportErrorASCII(cx, "couldn't create closure - libffi error");
7319     return nullptr;
7320   }
7321 
7322   ffi_status status = ffi_prep_closure_loc(cinfo->closure, &fninfo->mCIF,
7323                                            CClosure::ClosureStub, cinfo, code);
7324   if (status != FFI_OK) {
7325     JS_ReportErrorASCII(cx, "couldn't create closure - libffi error");
7326     return nullptr;
7327   }
7328 
7329   // Casting between void* and a function pointer is forbidden in C and C++.
7330   // Do it via an integral type.
7331   *fnptr = reinterpret_cast<PRFuncPtr>(reinterpret_cast<uintptr_t>(code));
7332   return result;
7333 }
7334 
Trace(JSTracer * trc,JSObject * obj)7335 void CClosure::Trace(JSTracer* trc, JSObject* obj) {
7336   // Make sure our ClosureInfo slot is legit. If it's not, bail.
7337   Value slot = JS::GetReservedSlot(obj, SLOT_CLOSUREINFO);
7338   if (slot.isUndefined()) {
7339     return;
7340   }
7341 
7342   ClosureInfo* cinfo = static_cast<ClosureInfo*>(slot.toPrivate());
7343 
7344   TraceEdge(trc, &cinfo->closureObj, "closureObj");
7345   TraceEdge(trc, &cinfo->typeObj, "typeObj");
7346   TraceEdge(trc, &cinfo->jsfnObj, "jsfnObj");
7347   TraceNullableEdge(trc, &cinfo->thisObj, "thisObj");
7348 }
7349 
Finalize(JSFreeOp * fop,JSObject * obj)7350 void CClosure::Finalize(JSFreeOp* fop, JSObject* obj) {
7351   // Make sure our ClosureInfo slot is legit. If it's not, bail.
7352   Value slot = JS::GetReservedSlot(obj, SLOT_CLOSUREINFO);
7353   if (slot.isUndefined()) {
7354     return;
7355   }
7356 
7357   ClosureInfo* cinfo = static_cast<ClosureInfo*>(slot.toPrivate());
7358   fop->delete_(obj, cinfo, MemoryUse::CClosureInfo);
7359 }
7360 
ClosureStub(ffi_cif * cif,void * result,void ** args,void * userData)7361 void CClosure::ClosureStub(ffi_cif* cif, void* result, void** args,
7362                            void* userData) {
7363   MOZ_ASSERT(cif);
7364   MOZ_ASSERT(result);
7365   MOZ_ASSERT(args);
7366   MOZ_ASSERT(userData);
7367 
7368   // Retrieve the essentials from our closure object.
7369   ArgClosure argClosure(cif, result, args, static_cast<ClosureInfo*>(userData));
7370   JSContext* cx = argClosure.cinfo->cx;
7371 
7372   js::AssertSameCompartment(cx, argClosure.cinfo->jsfnObj);
7373 
7374   RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
7375   MOZ_ASSERT(global);
7376 
7377   js::PrepareScriptEnvironmentAndInvoke(cx, global, argClosure);
7378 }
7379 
operator ()(JSContext * cx)7380 bool CClosure::ArgClosure::operator()(JSContext* cx) {
7381   // Let the runtime callback know that we are about to call into JS again. The
7382   // end callback will fire automatically when we exit this function.
7383   AutoCTypesActivityCallback autoCallback(cx, CTypesActivityType::BeginCallback,
7384                                           CTypesActivityType::EndCallback);
7385 
7386   RootedObject typeObj(cx, cinfo->typeObj);
7387   RootedObject thisObj(cx, cinfo->thisObj);
7388   RootedValue jsfnVal(cx, ObjectValue(*cinfo->jsfnObj));
7389   AssertSameCompartment(cx, cinfo->jsfnObj);
7390 
7391   JS_AbortIfWrongThread(cx);
7392 
7393   // Assert that our CIFs agree.
7394   FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
7395   MOZ_ASSERT(cif == &fninfo->mCIF);
7396 
7397   TypeCode typeCode = CType::GetTypeCode(fninfo->mReturnType);
7398 
7399   // Initialize the result to zero, in case something fails. Small integer types
7400   // are promoted to a word-sized ffi_arg, so we must be careful to zero the
7401   // whole word.
7402   size_t rvSize = 0;
7403   if (cif->rtype != &ffi_type_void) {
7404     rvSize = cif->rtype->size;
7405     switch (typeCode) {
7406 #define INTEGRAL_CASE(name, type, ffiType) case TYPE_##name:
7407       CTYPES_FOR_EACH_INT_TYPE(INTEGRAL_CASE)
7408       CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGRAL_CASE)
7409       CTYPES_FOR_EACH_BOOL_TYPE(INTEGRAL_CASE)
7410       CTYPES_FOR_EACH_CHAR_TYPE(INTEGRAL_CASE)
7411       CTYPES_FOR_EACH_CHAR16_TYPE(INTEGRAL_CASE)
7412 #undef INTEGRAL_CASE
7413       rvSize = Align(rvSize, sizeof(ffi_arg));
7414       break;
7415       default:
7416         break;
7417     }
7418     memset(result, 0, rvSize);
7419   }
7420 
7421   // Set up an array for converted arguments.
7422   JS::RootedValueVector argv(cx);
7423   if (!argv.resize(cif->nargs)) {
7424     JS_ReportOutOfMemory(cx);
7425     return false;
7426   }
7427 
7428   for (uint32_t i = 0; i < cif->nargs; ++i) {
7429     // Convert each argument, and have any CData objects created depend on
7430     // the existing buffers.
7431     RootedObject argType(cx, fninfo->mArgTypes[i]);
7432     if (!ConvertToJS(cx, argType, nullptr, args[i], false, false, argv[i])) {
7433       return false;
7434     }
7435   }
7436 
7437   // Call the JS function. 'thisObj' may be nullptr, in which case the JS
7438   // engine will find an appropriate object to use.
7439   RootedValue rval(cx);
7440   bool success = JS_CallFunctionValue(cx, thisObj, jsfnVal, argv, &rval);
7441 
7442   // Convert the result. Note that we pass 'ConversionType::Return', such that
7443   // ImplicitConvert will *not* autoconvert a JS string into a pointer-to-char
7444   // type, which would require an allocation that we can't track. The JS
7445   // function must perform this conversion itself and return a PointerType
7446   // CData; thusly, the burden of freeing the data is left to the user.
7447   if (success && cif->rtype != &ffi_type_void) {
7448     success = ImplicitConvert(cx, rval, fninfo->mReturnType, result,
7449                               ConversionType::Return, nullptr, typeObj);
7450   }
7451 
7452   if (!success) {
7453     // Something failed. The callee may have thrown, or it may not have
7454     // returned a value that ImplicitConvert() was happy with. Depending on how
7455     // prudent the consumer has been, we may or may not have a recovery plan.
7456     //
7457     // Note that PrepareScriptEnvironmentAndInvoke should take care of reporting
7458     // the exception.
7459 
7460     if (cinfo->errResult) {
7461       // Good case: we have a sentinel that we can return. Copy it in place of
7462       // the actual return value, and then proceed.
7463 
7464       // The buffer we're returning might be larger than the size of the return
7465       // type, due to libffi alignment issues (see above). But it should never
7466       // be smaller.
7467       size_t copySize = CType::GetSize(fninfo->mReturnType);
7468       MOZ_ASSERT(copySize <= rvSize);
7469       memcpy(result, cinfo->errResult, copySize);
7470 
7471       // We still want to return false here, so that
7472       // PrepareScriptEnvironmentAndInvoke will report the exception.
7473     } else {
7474       // Bad case: not much we can do here. The rv is already zeroed out, so we
7475       // just return and hope for the best.
7476     }
7477     return false;
7478   }
7479 
7480   // Small integer types must be returned as a word-sized ffi_arg. Coerce it
7481   // back into the size libffi expects.
7482   switch (typeCode) {
7483 #define INTEGRAL_CASE(name, type, ffiType)        \
7484   case TYPE_##name:                               \
7485     if (sizeof(type) < sizeof(ffi_arg)) {         \
7486       ffi_arg data = *static_cast<type*>(result); \
7487       *static_cast<ffi_arg*>(result) = data;      \
7488     }                                             \
7489     break;
7490     CTYPES_FOR_EACH_INT_TYPE(INTEGRAL_CASE)
7491     CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGRAL_CASE)
7492     CTYPES_FOR_EACH_BOOL_TYPE(INTEGRAL_CASE)
7493     CTYPES_FOR_EACH_CHAR_TYPE(INTEGRAL_CASE)
7494     CTYPES_FOR_EACH_CHAR16_TYPE(INTEGRAL_CASE)
7495 #undef INTEGRAL_CASE
7496     default:
7497       break;
7498   }
7499 
7500   return true;
7501 }
7502 
7503 /*******************************************************************************
7504 ** CData implementation
7505 *******************************************************************************/
7506 
7507 // Create a new CData object of type 'typeObj' containing binary data supplied
7508 // in 'source', optionally with a referent object 'refObj'.
7509 //
7510 // * 'typeObj' must be a CType of defined (but possibly zero) size.
7511 //
7512 // * If an object 'refObj' is supplied, the new CData object stores the
7513 //   referent object in a reserved slot for GC safety, such that 'refObj' will
7514 //   be held alive by the resulting CData object. 'refObj' may or may not be
7515 //   a CData object; merely an object we want to keep alive.
7516 //   * If 'refObj' is a CData object, 'ownResult' must be false.
7517 //   * Otherwise, 'refObj' is a Library or CClosure object, and 'ownResult'
7518 //     may be true or false.
7519 // * Otherwise 'refObj' is nullptr. In this case, 'ownResult' may be true or
7520 //   false.
7521 //
7522 // * If 'ownResult' is true, the CData object will allocate an appropriately
7523 //   sized buffer, and free it upon finalization. If 'source' data is
7524 //   supplied, the data will be copied from 'source' into the buffer;
7525 //   otherwise, the entirety of the new buffer will be initialized to zero.
7526 // * If 'ownResult' is false, the new CData's buffer refers to a slice of
7527 //   another buffer kept alive by 'refObj'. 'source' data must be provided,
7528 //   and the new CData's buffer will refer to 'source'.
Create(JSContext * cx,HandleObject typeObj,HandleObject refObj,void * source,bool ownResult)7529 JSObject* CData::Create(JSContext* cx, HandleObject typeObj,
7530                         HandleObject refObj, void* source, bool ownResult) {
7531   MOZ_ASSERT(typeObj);
7532   MOZ_ASSERT(CType::IsCType(typeObj));
7533   MOZ_ASSERT(CType::IsSizeDefined(typeObj));
7534   MOZ_ASSERT(ownResult || source);
7535   MOZ_ASSERT_IF(refObj && CData::IsCData(refObj), !ownResult);
7536 
7537   // Get the 'prototype' property from the type.
7538   Value slot = JS::GetReservedSlot(typeObj, SLOT_PROTO);
7539   MOZ_ASSERT(slot.isObject());
7540 
7541   RootedObject proto(cx, &slot.toObject());
7542 
7543   RootedObject dataObj(cx, JS_NewObjectWithGivenProto(cx, &sCDataClass, proto));
7544   if (!dataObj) {
7545     return nullptr;
7546   }
7547 
7548   // set the CData's associated type
7549   JS_SetReservedSlot(dataObj, SLOT_CTYPE, ObjectValue(*typeObj));
7550 
7551   // Stash the referent object, if any, for GC safety.
7552   if (refObj) {
7553     JS_SetReservedSlot(dataObj, SLOT_REFERENT, ObjectValue(*refObj));
7554   }
7555 
7556   // Set our ownership flag.
7557   JS_SetReservedSlot(dataObj, SLOT_OWNS, BooleanValue(ownResult));
7558 
7559   // attach the buffer. since it might not be 2-byte aligned, we need to
7560   // allocate an aligned space for it and store it there. :(
7561   UniquePtr<char*, JS::FreePolicy> buffer(cx->new_<char*>());
7562   if (!buffer) {
7563     return nullptr;
7564   }
7565 
7566   char* data;
7567   if (!ownResult) {
7568     data = static_cast<char*>(source);
7569   } else {
7570     // Initialize our own buffer.
7571     size_t size = CType::GetSize(typeObj);
7572     data = cx->pod_malloc<char>(size);
7573     if (!data) {
7574       return nullptr;
7575     }
7576 
7577     if (!source) {
7578       memset(data, 0, size);
7579     } else {
7580       memcpy(data, source, size);
7581     }
7582 
7583     AddCellMemory(dataObj, size, MemoryUse::CDataBuffer);
7584   }
7585 
7586   *buffer.get() = data;
7587   JS_InitReservedSlot(dataObj, SLOT_DATA, buffer.release(),
7588                       JS::MemoryUse::CDataBufferPtr);
7589 
7590   // If this is an array, wrap it in a proxy so we can intercept element
7591   // gets/sets.
7592 
7593   if (CType::GetTypeCode(typeObj) != TYPE_array) {
7594     return dataObj;
7595   }
7596 
7597   RootedValue priv(cx, ObjectValue(*dataObj));
7598   ProxyOptions options;
7599   options.setLazyProto(true);
7600   return NewProxyObject(cx, &CDataArrayProxyHandler::singleton, priv, nullptr,
7601                         options);
7602 }
7603 
Finalize(JSFreeOp * fop,JSObject * obj)7604 void CData::Finalize(JSFreeOp* fop, JSObject* obj) {
7605   // Delete our buffer, and the data it contains if we own it.
7606   Value slot = JS::GetReservedSlot(obj, SLOT_OWNS);
7607   if (slot.isUndefined()) {
7608     return;
7609   }
7610 
7611   bool owns = slot.toBoolean();
7612 
7613   slot = JS::GetReservedSlot(obj, SLOT_DATA);
7614   if (slot.isUndefined()) {
7615     return;
7616   }
7617   char** buffer = static_cast<char**>(slot.toPrivate());
7618 
7619   if (owns) {
7620     JSObject* typeObj = &JS::GetReservedSlot(obj, SLOT_CTYPE).toObject();
7621     size_t size = CType::GetSize(typeObj);
7622     fop->free_(obj, *buffer, size, MemoryUse::CDataBuffer);
7623   }
7624   fop->delete_(obj, buffer, MemoryUse::CDataBufferPtr);
7625 }
7626 
GetCType(JSObject * dataObj)7627 JSObject* CData::GetCType(JSObject* dataObj) {
7628   dataObj = MaybeUnwrapArrayWrapper(dataObj);
7629   MOZ_ASSERT(CData::IsCData(dataObj));
7630 
7631   Value slot = JS::GetReservedSlot(dataObj, SLOT_CTYPE);
7632   JSObject* typeObj = slot.toObjectOrNull();
7633   MOZ_ASSERT(CType::IsCType(typeObj));
7634   return typeObj;
7635 }
7636 
GetData(JSObject * dataObj)7637 void* CData::GetData(JSObject* dataObj) {
7638   dataObj = MaybeUnwrapArrayWrapper(dataObj);
7639   MOZ_ASSERT(CData::IsCData(dataObj));
7640 
7641   Value slot = JS::GetReservedSlot(dataObj, SLOT_DATA);
7642 
7643   void** buffer = static_cast<void**>(slot.toPrivate());
7644   MOZ_ASSERT(buffer);
7645   MOZ_ASSERT(*buffer);
7646   return *buffer;
7647 }
7648 
IsCData(JSObject * obj)7649 bool CData::IsCData(JSObject* obj) {
7650   // Assert we don't have an array wrapper.
7651   MOZ_ASSERT(MaybeUnwrapArrayWrapper(obj) == obj);
7652 
7653   return obj->hasClass(&sCDataClass);
7654 }
7655 
IsCDataMaybeUnwrap(MutableHandleObject obj)7656 bool CData::IsCDataMaybeUnwrap(MutableHandleObject obj) {
7657   obj.set(MaybeUnwrapArrayWrapper(obj));
7658   return IsCData(obj);
7659 }
7660 
IsCData(HandleValue v)7661 bool CData::IsCData(HandleValue v) {
7662   return v.isObject() && CData::IsCData(MaybeUnwrapArrayWrapper(&v.toObject()));
7663 }
7664 
IsCDataProto(JSObject * obj)7665 bool CData::IsCDataProto(JSObject* obj) {
7666   return obj->hasClass(&sCDataProtoClass);
7667 }
7668 
ValueGetter(JSContext * cx,const JS::CallArgs & args)7669 bool CData::ValueGetter(JSContext* cx, const JS::CallArgs& args) {
7670   RootedObject obj(cx, &args.thisv().toObject());
7671 
7672   // Convert the value to a primitive; do not create a new CData object.
7673   RootedObject ctype(cx, GetCType(obj));
7674   return ConvertToJS(cx, ctype, nullptr, GetData(obj), true, false,
7675                      args.rval());
7676 }
7677 
ValueSetter(JSContext * cx,const JS::CallArgs & args)7678 bool CData::ValueSetter(JSContext* cx, const JS::CallArgs& args) {
7679   RootedObject obj(cx, &args.thisv().toObject());
7680   args.rval().setUndefined();
7681   return ImplicitConvert(cx, args.get(0), GetCType(obj), GetData(obj),
7682                          ConversionType::Setter, nullptr);
7683 }
7684 
Address(JSContext * cx,unsigned argc,Value * vp)7685 bool CData::Address(JSContext* cx, unsigned argc, Value* vp) {
7686   CallArgs args = CallArgsFromVp(argc, vp);
7687   if (args.length() != 0) {
7688     return ArgumentLengthError(cx, "CData.prototype.address", "no", "s");
7689   }
7690 
7691   RootedObject obj(cx, GetThisObject(cx, args, "CData.prototype.address"));
7692   if (!obj) {
7693     return false;
7694   }
7695   if (!IsCDataMaybeUnwrap(&obj)) {
7696     return IncompatibleThisProto(cx, "CData.prototype.address", args.thisv());
7697   }
7698 
7699   RootedObject typeObj(cx, CData::GetCType(obj));
7700   RootedObject pointerType(cx, PointerType::CreateInternal(cx, typeObj));
7701   if (!pointerType) {
7702     return false;
7703   }
7704 
7705   // Create a PointerType CData object containing null.
7706   JSObject* result = CData::Create(cx, pointerType, nullptr, nullptr, true);
7707   if (!result) {
7708     return false;
7709   }
7710 
7711   args.rval().setObject(*result);
7712 
7713   // Manually set the pointer inside the object, so we skip the conversion step.
7714   void** data = static_cast<void**>(GetData(result));
7715   *data = GetData(obj);
7716   return true;
7717 }
7718 
Cast(JSContext * cx,unsigned argc,Value * vp)7719 bool CData::Cast(JSContext* cx, unsigned argc, Value* vp) {
7720   CallArgs args = CallArgsFromVp(argc, vp);
7721   if (args.length() != 2) {
7722     return ArgumentLengthError(cx, "ctypes.cast", "two", "s");
7723   }
7724 
7725   RootedObject sourceData(cx);
7726   if (args[0].isObject()) {
7727     sourceData = &args[0].toObject();
7728   }
7729 
7730   if (!sourceData || !CData::IsCDataMaybeUnwrap(&sourceData)) {
7731     return ArgumentTypeMismatch(cx, "first ", "ctypes.cast", "a CData");
7732   }
7733   RootedObject sourceType(cx, CData::GetCType(sourceData));
7734 
7735   if (args[1].isPrimitive() || !CType::IsCType(&args[1].toObject())) {
7736     return ArgumentTypeMismatch(cx, "second ", "ctypes.cast", "a CType");
7737   }
7738 
7739   RootedObject targetType(cx, &args[1].toObject());
7740   size_t targetSize;
7741   if (!CType::GetSafeSize(targetType, &targetSize)) {
7742     return UndefinedSizeCastError(cx, targetType);
7743   }
7744   if (targetSize > CType::GetSize(sourceType)) {
7745     return SizeMismatchCastError(cx, sourceType, targetType,
7746                                  CType::GetSize(sourceType), targetSize);
7747   }
7748 
7749   // Construct a new CData object with a type of 'targetType' and a referent
7750   // of 'sourceData'.
7751   void* data = CData::GetData(sourceData);
7752   JSObject* result = CData::Create(cx, targetType, sourceData, data, false);
7753   if (!result) {
7754     return false;
7755   }
7756 
7757   args.rval().setObject(*result);
7758   return true;
7759 }
7760 
GetRuntime(JSContext * cx,unsigned argc,Value * vp)7761 bool CData::GetRuntime(JSContext* cx, unsigned argc, Value* vp) {
7762   CallArgs args = CallArgsFromVp(argc, vp);
7763   if (args.length() != 1) {
7764     return ArgumentLengthError(cx, "ctypes.getRuntime", "one", "");
7765   }
7766 
7767   if (args[0].isPrimitive() || !CType::IsCType(&args[0].toObject())) {
7768     return ArgumentTypeMismatch(cx, "", "ctypes.getRuntime", "a CType");
7769   }
7770 
7771   RootedObject targetType(cx, &args[0].toObject());
7772   size_t targetSize;
7773   if (!CType::GetSafeSize(targetType, &targetSize) ||
7774       targetSize != sizeof(void*)) {
7775     JS_ReportErrorASCII(cx, "target CType has non-pointer size");
7776     return false;
7777   }
7778 
7779   void* data = static_cast<void*>(cx->runtime());
7780   JSObject* result = CData::Create(cx, targetType, nullptr, &data, true);
7781   if (!result) {
7782     return false;
7783   }
7784 
7785   args.rval().setObject(*result);
7786   return true;
7787 }
7788 
7789 // Unwrap the `this` object to a CData object, or extract a CData object from a
7790 // CDataFinalizer.
GetThisDataObject(JSContext * cx,const CallArgs & args,const char * funName,MutableHandleObject obj)7791 static bool GetThisDataObject(JSContext* cx, const CallArgs& args,
7792                               const char* funName, MutableHandleObject obj) {
7793   obj.set(GetThisObject(cx, args, funName));
7794   if (!obj) {
7795     return IncompatibleThisProto(cx, funName, args.thisv());
7796   }
7797   if (!CData::IsCDataMaybeUnwrap(obj)) {
7798     if (!CDataFinalizer::IsCDataFinalizer(obj)) {
7799       return IncompatibleThisProto(cx, funName, args.thisv());
7800     }
7801 
7802     auto* p = (CDataFinalizer::Private*)JS::GetPrivate(obj);
7803     if (!p) {
7804       return EmptyFinalizerCallError(cx, funName);
7805     }
7806 
7807     RootedValue dataVal(cx);
7808     if (!CDataFinalizer::GetValue(cx, obj, &dataVal)) {
7809       return IncompatibleThisProto(cx, funName, args.thisv());
7810     }
7811 
7812     if (dataVal.isPrimitive()) {
7813       return IncompatibleThisProto(cx, funName, args.thisv());
7814     }
7815 
7816     obj.set(dataVal.toObjectOrNull());
7817     if (!obj || !CData::IsCDataMaybeUnwrap(obj)) {
7818       return IncompatibleThisProto(cx, funName, args.thisv());
7819     }
7820   }
7821 
7822   return true;
7823 }
7824 
7825 typedef JS::TwoByteCharsZ (*InflateUTF8Method)(JSContext*, const JS::UTF8Chars,
7826                                                size_t*, arena_id_t);
7827 
ReadStringCommon(JSContext * cx,InflateUTF8Method inflateUTF8,unsigned argc,Value * vp,const char * funName,arena_id_t destArenaId)7828 static bool ReadStringCommon(JSContext* cx, InflateUTF8Method inflateUTF8,
7829                              unsigned argc, Value* vp, const char* funName,
7830                              arena_id_t destArenaId) {
7831   CallArgs args = CallArgsFromVp(argc, vp);
7832   if (args.length() != 0) {
7833     return ArgumentLengthError(cx, funName, "no", "s");
7834   }
7835 
7836   RootedObject obj(cx);
7837   if (!GetThisDataObject(cx, args, funName, &obj)) {
7838     return false;
7839   }
7840 
7841   // Make sure we are a pointer to, or an array of, an 8-bit or 16-bit
7842   // character or integer type.
7843   JSObject* baseType;
7844   JSObject* typeObj = CData::GetCType(obj);
7845   TypeCode typeCode = CType::GetTypeCode(typeObj);
7846   void* data;
7847   size_t maxLength = -1;
7848   switch (typeCode) {
7849     case TYPE_pointer:
7850       baseType = PointerType::GetBaseType(typeObj);
7851       data = *static_cast<void**>(CData::GetData(obj));
7852       if (data == nullptr) {
7853         return NullPointerError(cx, "read contents of", obj);
7854       }
7855       break;
7856     case TYPE_array:
7857       baseType = ArrayType::GetBaseType(typeObj);
7858       data = CData::GetData(obj);
7859       maxLength = ArrayType::GetLength(typeObj);
7860       break;
7861     default:
7862       return TypeError(cx, "PointerType or ArrayType", args.thisv());
7863   }
7864 
7865   // Convert the string buffer, taking care to determine the correct string
7866   // length in the case of arrays (which may contain embedded nulls).
7867   JSString* result;
7868   switch (CType::GetTypeCode(baseType)) {
7869     case TYPE_int8_t:
7870     case TYPE_uint8_t:
7871     case TYPE_char:
7872     case TYPE_signed_char:
7873     case TYPE_unsigned_char: {
7874       char* bytes = static_cast<char*>(data);
7875       size_t length = js_strnlen(bytes, maxLength);
7876 
7877       // Determine the length.
7878       UniqueTwoByteChars dst(
7879           inflateUTF8(cx, JS::UTF8Chars(bytes, length), &length, destArenaId)
7880               .get());
7881       if (!dst) {
7882         return false;
7883       }
7884 
7885       result = JS_NewUCString(cx, std::move(dst), length);
7886       if (!result) {
7887         return false;
7888       }
7889 
7890       break;
7891     }
7892     case TYPE_int16_t:
7893     case TYPE_uint16_t:
7894     case TYPE_short:
7895     case TYPE_unsigned_short:
7896     case TYPE_char16_t: {
7897       char16_t* chars = static_cast<char16_t*>(data);
7898       size_t length = js_strnlen(chars, maxLength);
7899       result = JS_NewUCStringCopyN(cx, chars, length);
7900       break;
7901     }
7902     default:
7903       return NonStringBaseError(cx, args.thisv());
7904   }
7905 
7906   if (!result) {
7907     return false;
7908   }
7909 
7910   args.rval().setString(result);
7911   return true;
7912 }
7913 
ReadString(JSContext * cx,unsigned argc,Value * vp)7914 bool CData::ReadString(JSContext* cx, unsigned argc, Value* vp) {
7915   return ReadStringCommon(cx, JS::UTF8CharsToNewTwoByteCharsZ, argc, vp,
7916                           "CData.prototype.readString", js::StringBufferArena);
7917 }
7918 
ReadString(JSContext * cx,unsigned argc,Value * vp)7919 bool CDataFinalizer::Methods::ReadString(JSContext* cx, unsigned argc,
7920                                          Value* vp) {
7921   return ReadStringCommon(cx, JS::UTF8CharsToNewTwoByteCharsZ, argc, vp,
7922                           "CDataFinalizer.prototype.readString",
7923                           js::StringBufferArena);
7924 }
7925 
ReadStringReplaceMalformed(JSContext * cx,unsigned argc,Value * vp)7926 bool CData::ReadStringReplaceMalformed(JSContext* cx, unsigned argc,
7927                                        Value* vp) {
7928   return ReadStringCommon(cx, JS::LossyUTF8CharsToNewTwoByteCharsZ, argc, vp,
7929                           "CData.prototype.readStringReplaceMalformed",
7930                           js::StringBufferArena);
7931 }
7932 
7933 using TypedArrayConstructor = JSObject* (*)(JSContext*, size_t);
7934 
7935 template <typename Type>
GetTypedArrayConstructorImpl()7936 TypedArrayConstructor GetTypedArrayConstructorImpl() {
7937   if (std::is_floating_point_v<Type>) {
7938     switch (sizeof(Type)) {
7939       case 4:
7940         return JS_NewFloat32Array;
7941       case 8:
7942         return JS_NewFloat64Array;
7943       default:
7944         return nullptr;
7945     }
7946   }
7947 
7948   constexpr bool isSigned = std::is_signed_v<Type>;
7949   switch (sizeof(Type)) {
7950     case 1:
7951       return isSigned ? JS_NewInt8Array : JS_NewUint8Array;
7952     case 2:
7953       return isSigned ? JS_NewInt16Array : JS_NewUint16Array;
7954     case 4:
7955       return isSigned ? JS_NewInt32Array : JS_NewUint32Array;
7956     default:
7957       return nullptr;
7958   }
7959 }
7960 
GetTypedArrayConstructor(TypeCode baseType)7961 static TypedArrayConstructor GetTypedArrayConstructor(TypeCode baseType) {
7962   switch (baseType) {
7963 #define MACRO(name, ctype, _) \
7964   case TYPE_##name:           \
7965     return GetTypedArrayConstructorImpl<ctype>();
7966     CTYPES_FOR_EACH_TYPE(MACRO)
7967 #undef MACRO
7968     default:
7969       return nullptr;
7970   }
7971 }
7972 
ReadTypedArrayCommon(JSContext * cx,unsigned argc,Value * vp,const char * funName)7973 static bool ReadTypedArrayCommon(JSContext* cx, unsigned argc, Value* vp,
7974                                  const char* funName) {
7975   CallArgs args = CallArgsFromVp(argc, vp);
7976   if (args.length() != 0) {
7977     return ArgumentLengthError(cx, funName, "no", "s");
7978   }
7979 
7980   RootedObject obj(cx);
7981   if (!GetThisDataObject(cx, args, funName, &obj)) {
7982     return false;
7983   }
7984 
7985   // Make sure we are a pointer to, or an array of, a type that is compatible
7986   // with a typed array base type.
7987   JSObject* baseType;
7988   JSObject* typeObj = CData::GetCType(obj);
7989   TypeCode typeCode = CType::GetTypeCode(typeObj);
7990   void* data;
7991   mozilla::Maybe<size_t> length;
7992   switch (typeCode) {
7993     case TYPE_pointer:
7994       baseType = PointerType::GetBaseType(typeObj);
7995       data = *static_cast<void**>(CData::GetData(obj));
7996       if (data == nullptr) {
7997         return NullPointerError(cx, "read contents of", obj);
7998       }
7999       break;
8000     case TYPE_array:
8001       baseType = ArrayType::GetBaseType(typeObj);
8002       data = CData::GetData(obj);
8003       length.emplace(ArrayType::GetLength(typeObj));
8004       break;
8005     default:
8006       return TypeError(cx, "PointerType or ArrayType", args.thisv());
8007   }
8008 
8009   TypeCode baseTypeCode = CType::GetTypeCode(baseType);
8010 
8011   // For string inputs only, use strlen to determine the length.
8012   switch (baseTypeCode) {
8013     case TYPE_char:
8014     case TYPE_signed_char:
8015     case TYPE_unsigned_char:
8016       if (!length) {
8017         length.emplace(js_strnlen(static_cast<char*>(data), INT32_MAX));
8018       }
8019       break;
8020 
8021     case TYPE_char16_t:
8022       if (!length) {
8023         length.emplace(js_strlen(static_cast<char16_t*>(data)));
8024       }
8025       break;
8026 
8027     default:
8028       break;
8029   }
8030 
8031   if (!length) {
8032     return NonStringBaseError(cx, args.thisv());
8033   }
8034 
8035   auto makeTypedArray = GetTypedArrayConstructor(baseTypeCode);
8036   if (!makeTypedArray) {
8037     return NonTypedArrayBaseError(cx, args.thisv());
8038   }
8039 
8040   CheckedInt<size_t> size = *length;
8041   size *= CType::GetSize(baseType);
8042   if (!size.isValid() ||
8043       size.value() > ArrayBufferObject::maxBufferByteLength()) {
8044     return SizeOverflow(cx, "data", "typed array");
8045   }
8046 
8047   JSObject* result = makeTypedArray(cx, *length);
8048   if (!result) {
8049     return false;
8050   }
8051 
8052   AutoCheckCannotGC nogc(cx);
8053   bool isShared;
8054   void* buffer = JS_GetArrayBufferViewData(&result->as<ArrayBufferViewObject>(),
8055                                            &isShared, nogc);
8056   MOZ_ASSERT(!isShared);
8057   memcpy(buffer, data, size.value());
8058 
8059   args.rval().setObject(*result);
8060   return true;
8061 }
8062 
ReadTypedArray(JSContext * cx,unsigned argc,Value * vp)8063 bool CData::ReadTypedArray(JSContext* cx, unsigned argc, Value* vp) {
8064   return ReadTypedArrayCommon(cx, argc, vp, "CData.prototype.readTypedArray");
8065 }
8066 
ReadTypedArray(JSContext * cx,unsigned argc,Value * vp)8067 bool CDataFinalizer::Methods::ReadTypedArray(JSContext* cx, unsigned argc,
8068                                              Value* vp) {
8069   return ReadTypedArrayCommon(cx, argc, vp,
8070                               "CDataFinalizer.prototype.readTypedArray");
8071 }
8072 
GetSourceString(JSContext * cx,HandleObject typeObj,void * data)8073 JSString* CData::GetSourceString(JSContext* cx, HandleObject typeObj,
8074                                  void* data) {
8075   // Walk the types, building up the toSource() string.
8076   // First, we build up the type expression:
8077   // 't.ptr' for pointers;
8078   // 't.array([n])' for arrays;
8079   // 'n' for structs, where n = t.name, the struct's name. (We assume this is
8080   // bound to a variable in the current scope.)
8081   AutoString source;
8082   BuildTypeSource(cx, typeObj, true, source);
8083   AppendString(cx, source, "(");
8084   if (!BuildDataSource(cx, typeObj, data, false, source)) {
8085     source.handle(false);
8086   }
8087   AppendString(cx, source, ")");
8088   if (!source) {
8089     return nullptr;
8090   }
8091 
8092   return NewUCString(cx, source.finish());
8093 }
8094 
ToSource(JSContext * cx,unsigned argc,Value * vp)8095 bool CData::ToSource(JSContext* cx, unsigned argc, Value* vp) {
8096   CallArgs args = CallArgsFromVp(argc, vp);
8097   if (args.length() != 0) {
8098     return ArgumentLengthError(cx, "CData.prototype.toSource", "no", "s");
8099   }
8100 
8101   RootedObject obj(cx, GetThisObject(cx, args, "CData.prototype.toSource"));
8102   if (!obj) {
8103     return false;
8104   }
8105   if (!CData::IsCDataMaybeUnwrap(&obj) && !CData::IsCDataProto(obj)) {
8106     return IncompatibleThisProto(cx, "CData.prototype.toSource",
8107                                  InformalValueTypeName(args.thisv()));
8108   }
8109 
8110   JSString* result;
8111   if (CData::IsCData(obj)) {
8112     RootedObject typeObj(cx, CData::GetCType(obj));
8113     void* data = CData::GetData(obj);
8114 
8115     result = CData::GetSourceString(cx, typeObj, data);
8116   } else {
8117     result = JS_NewStringCopyZ(cx, "[CData proto object]");
8118   }
8119 
8120   if (!result) {
8121     return false;
8122   }
8123 
8124   args.rval().setString(result);
8125   return true;
8126 }
8127 
ErrnoGetter(JSContext * cx,const JS::CallArgs & args)8128 bool CData::ErrnoGetter(JSContext* cx, const JS::CallArgs& args) {
8129   args.rval().set(JS::GetReservedSlot(&args.thisv().toObject(), SLOT_ERRNO));
8130   return true;
8131 }
8132 
8133 #if defined(XP_WIN)
LastErrorGetter(JSContext * cx,const JS::CallArgs & args)8134 bool CData::LastErrorGetter(JSContext* cx, const JS::CallArgs& args) {
8135   args.rval().set(
8136       JS::GetReservedSlot(&args.thisv().toObject(), SLOT_LASTERROR));
8137   return true;
8138 }
8139 #endif  // defined(XP_WIN)
8140 
ToSource(JSContext * cx,unsigned argc,Value * vp)8141 bool CDataFinalizer::Methods::ToSource(JSContext* cx, unsigned argc,
8142                                        Value* vp) {
8143   CallArgs args = CallArgsFromVp(argc, vp);
8144   RootedObject objThis(
8145       cx, GetThisObject(cx, args, "CDataFinalizer.prototype.toSource"));
8146   if (!objThis) {
8147     return false;
8148   }
8149   if (!CDataFinalizer::IsCDataFinalizer(objThis)) {
8150     return IncompatibleThisProto(cx, "CDataFinalizer.prototype.toSource",
8151                                  InformalValueTypeName(args.thisv()));
8152   }
8153 
8154   auto* p = (CDataFinalizer::Private*)JS::GetPrivate(objThis);
8155 
8156   JSString* strMessage;
8157   if (!p) {
8158     strMessage = JS_NewStringCopyZ(cx, "ctypes.CDataFinalizer()");
8159   } else {
8160     RootedObject objType(cx, CDataFinalizer::GetCType(cx, objThis));
8161     if (!objType) {
8162       JS_ReportErrorASCII(cx, "CDataFinalizer has no type");
8163       return false;
8164     }
8165 
8166     AutoString source;
8167     AppendString(cx, source, "ctypes.CDataFinalizer(");
8168     JSString* srcValue = CData::GetSourceString(cx, objType, p->cargs);
8169     if (!srcValue) {
8170       return false;
8171     }
8172     AppendString(cx, source, srcValue);
8173     AppendString(cx, source, ", ");
8174     Value valCodePtrType =
8175         JS::GetReservedSlot(objThis, SLOT_DATAFINALIZER_CODETYPE);
8176     if (valCodePtrType.isPrimitive()) {
8177       return false;
8178     }
8179 
8180     RootedObject typeObj(cx, valCodePtrType.toObjectOrNull());
8181     JSString* srcDispose = CData::GetSourceString(cx, typeObj, &(p->code));
8182     if (!srcDispose) {
8183       return false;
8184     }
8185 
8186     AppendString(cx, source, srcDispose);
8187     AppendString(cx, source, ")");
8188     if (!source) {
8189       return false;
8190     }
8191     strMessage = NewUCString(cx, source.finish());
8192   }
8193 
8194   if (!strMessage) {
8195     // This is a memory issue, no error message
8196     return false;
8197   }
8198 
8199   args.rval().setString(strMessage);
8200   return true;
8201 }
8202 
ToString(JSContext * cx,unsigned argc,Value * vp)8203 bool CDataFinalizer::Methods::ToString(JSContext* cx, unsigned argc,
8204                                        Value* vp) {
8205   CallArgs args = CallArgsFromVp(argc, vp);
8206   JSObject* objThis =
8207       GetThisObject(cx, args, "CDataFinalizer.prototype.toString");
8208   if (!objThis) {
8209     return false;
8210   }
8211   if (!CDataFinalizer::IsCDataFinalizer(objThis)) {
8212     return IncompatibleThisProto(cx, "CDataFinalizer.prototype.toString",
8213                                  InformalValueTypeName(args.thisv()));
8214   }
8215 
8216   JSString* strMessage;
8217   RootedValue value(cx);
8218   if (!JS::GetPrivate(objThis)) {
8219     // Pre-check whether CDataFinalizer::GetValue can fail
8220     // to avoid reporting an error when not appropriate.
8221     strMessage = JS_NewStringCopyZ(cx, "[CDataFinalizer - empty]");
8222     if (!strMessage) {
8223       return false;
8224     }
8225   } else if (!CDataFinalizer::GetValue(cx, objThis, &value)) {
8226     MOZ_CRASH("Could not convert an empty CDataFinalizer");
8227   } else {
8228     strMessage = ToString(cx, value);
8229     if (!strMessage) {
8230       return false;
8231     }
8232   }
8233   args.rval().setString(strMessage);
8234   return true;
8235 }
8236 
IsCDataFinalizer(JSObject * obj)8237 bool CDataFinalizer::IsCDataFinalizer(JSObject* obj) {
8238   return obj->hasClass(&sCDataFinalizerClass);
8239 }
8240 
GetCType(JSContext * cx,JSObject * obj)8241 JSObject* CDataFinalizer::GetCType(JSContext* cx, JSObject* obj) {
8242   MOZ_ASSERT(IsCDataFinalizer(obj));
8243 
8244   Value valData = JS::GetReservedSlot(obj, SLOT_DATAFINALIZER_VALTYPE);
8245   if (valData.isUndefined()) {
8246     return nullptr;
8247   }
8248 
8249   return valData.toObjectOrNull();
8250 }
8251 
GetValue(JSContext * cx,JSObject * obj,MutableHandleValue aResult)8252 bool CDataFinalizer::GetValue(JSContext* cx, JSObject* obj,
8253                               MutableHandleValue aResult) {
8254   MOZ_ASSERT(IsCDataFinalizer(obj));
8255 
8256   auto* p = (CDataFinalizer::Private*)JS::GetPrivate(obj);
8257 
8258   if (!p) {
8259     // We have called |dispose| or |forget| already.
8260     JS_ReportErrorASCII(
8261         cx, "Attempting to get the value of an empty CDataFinalizer");
8262     return false;
8263   }
8264 
8265   RootedObject ctype(cx, GetCType(cx, obj));
8266   return ConvertToJS(cx, ctype, /*parent*/ nullptr, p->cargs, false, true,
8267                      aResult);
8268 }
8269 
8270 /*
8271  * Attach a C function as a finalizer to a JS object.
8272  *
8273  * Pseudo-JS signature:
8274  * function(CData<T>, CData<T -> U>): CDataFinalizer<T>
8275  *          value,    finalizer
8276  *
8277  * This function attaches strong references to the following values:
8278  * - the CType of |value|
8279  *
8280  * Note: This function takes advantage of the fact that non-variadic
8281  * CData functions are initialized during creation.
8282  */
Construct(JSContext * cx,unsigned argc,Value * vp)8283 bool CDataFinalizer::Construct(JSContext* cx, unsigned argc, Value* vp) {
8284   CallArgs args = CallArgsFromVp(argc, vp);
8285   RootedObject objSelf(cx, &args.callee());
8286   RootedObject objProto(cx);
8287   if (!GetObjectProperty(cx, objSelf, "prototype", &objProto)) {
8288     JS_ReportErrorASCII(cx, "CDataFinalizer.prototype does not exist");
8289     return false;
8290   }
8291 
8292   // Get arguments
8293   if (args.length() ==
8294       0) {  // Special case: the empty (already finalized) object
8295     JSObject* objResult =
8296         JS_NewObjectWithGivenProto(cx, &sCDataFinalizerClass, objProto);
8297     args.rval().setObject(*objResult);
8298     return true;
8299   }
8300 
8301   if (args.length() != 2) {
8302     return ArgumentLengthError(cx, "CDataFinalizer constructor", "two", "s");
8303   }
8304 
8305   JS::HandleValue valCodePtr = args[1];
8306   if (!valCodePtr.isObject()) {
8307     return TypeError(cx, "_a CData object_ of a function pointer type",
8308                      valCodePtr);
8309   }
8310   RootedObject objCodePtr(cx, &valCodePtr.toObject());
8311 
8312   // Note: Using a custom argument formatter here would be awkward (requires
8313   // a destructor just to uninstall the formatter).
8314 
8315   // 2. Extract argument type of |objCodePtr|
8316   if (!CData::IsCDataMaybeUnwrap(&objCodePtr)) {
8317     return TypeError(cx, "a _CData_ object of a function pointer type",
8318                      valCodePtr);
8319   }
8320   RootedObject objCodePtrType(cx, CData::GetCType(objCodePtr));
8321   RootedValue valCodePtrType(cx, ObjectValue(*objCodePtrType));
8322   MOZ_ASSERT(objCodePtrType);
8323 
8324   TypeCode typCodePtr = CType::GetTypeCode(objCodePtrType);
8325   if (typCodePtr != TYPE_pointer) {
8326     return TypeError(cx, "a CData object of a function _pointer_ type",
8327                      valCodePtr);
8328   }
8329 
8330   JSObject* objCodeType = PointerType::GetBaseType(objCodePtrType);
8331   MOZ_ASSERT(objCodeType);
8332 
8333   TypeCode typCode = CType::GetTypeCode(objCodeType);
8334   if (typCode != TYPE_function) {
8335     return TypeError(cx, "a CData object of a _function_ pointer type",
8336                      valCodePtr);
8337   }
8338   uintptr_t code = *reinterpret_cast<uintptr_t*>(CData::GetData(objCodePtr));
8339   if (!code) {
8340     return TypeError(cx, "a CData object of a _non-NULL_ function pointer type",
8341                      valCodePtr);
8342   }
8343 
8344   FunctionInfo* funInfoFinalizer = FunctionType::GetFunctionInfo(objCodeType);
8345   MOZ_ASSERT(funInfoFinalizer);
8346 
8347   if ((funInfoFinalizer->mArgTypes.length() != 1) ||
8348       (funInfoFinalizer->mIsVariadic)) {
8349     RootedValue valCodeType(cx, ObjectValue(*objCodeType));
8350     return TypeError(cx, "a function accepting exactly one argument",
8351                      valCodeType);
8352   }
8353   RootedObject objArgType(cx, funInfoFinalizer->mArgTypes[0]);
8354   RootedObject returnType(cx, funInfoFinalizer->mReturnType);
8355 
8356   // Invariant: At this stage, we know that funInfoFinalizer->mIsVariadic
8357   // is |false|. Therefore, funInfoFinalizer->mCIF has already been initialized.
8358 
8359   bool freePointer = false;
8360 
8361   // 3. Perform dynamic cast of |args[0]| into |objType|, store it in |cargs|
8362 
8363   size_t sizeArg;
8364   RootedValue valData(cx, args[0]);
8365   if (!CType::GetSafeSize(objArgType, &sizeArg)) {
8366     RootedValue valCodeType(cx, ObjectValue(*objCodeType));
8367     return TypeError(cx, "a function with one known size argument",
8368                      valCodeType);
8369   }
8370 
8371   UniquePtr<void, JS::FreePolicy> cargs(malloc(sizeArg));
8372 
8373   if (!ImplicitConvert(cx, valData, objArgType, cargs.get(),
8374                        ConversionType::Finalizer, &freePointer, objCodePtrType,
8375                        0)) {
8376     return false;
8377   }
8378   if (freePointer) {
8379     // Note: We could handle that case, if necessary.
8380     JS_ReportErrorASCII(
8381         cx,
8382         "Internal Error during CDataFinalizer. Object cannot be represented");
8383     return false;
8384   }
8385 
8386   // 4. Prepare buffer for holding return value
8387 
8388   UniquePtr<void, JS::FreePolicy> rvalue;
8389   if (CType::GetTypeCode(returnType) != TYPE_void_t) {
8390     rvalue.reset(malloc(Align(CType::GetSize(returnType), sizeof(ffi_arg))));
8391   }  // Otherwise, simply do not allocate
8392 
8393   // 5. Create |objResult|
8394 
8395   JSObject* objResult =
8396       JS_NewObjectWithGivenProto(cx, &sCDataFinalizerClass, objProto);
8397   if (!objResult) {
8398     return false;
8399   }
8400 
8401   // If our argument is a CData, it holds a type.
8402   // This is the type that we should capture, not that
8403   // of the function, which may be less precise.
8404   JSObject* objBestArgType = objArgType;
8405   if (valData.isObject()) {
8406     RootedObject objData(cx, &valData.toObject());
8407     if (CData::IsCDataMaybeUnwrap(&objData)) {
8408       objBestArgType = CData::GetCType(objData);
8409       size_t sizeBestArg;
8410       if (!CType::GetSafeSize(objBestArgType, &sizeBestArg)) {
8411         MOZ_CRASH("object with unknown size");
8412       }
8413       if (sizeBestArg != sizeArg) {
8414         return FinalizerSizeError(cx, objCodePtrType, valData);
8415       }
8416     }
8417   }
8418 
8419   // Used by GetCType
8420   JS_SetReservedSlot(objResult, SLOT_DATAFINALIZER_VALTYPE,
8421                      ObjectOrNullValue(objBestArgType));
8422 
8423   // Used by ToSource
8424   JS_SetReservedSlot(objResult, SLOT_DATAFINALIZER_CODETYPE,
8425                      ObjectValue(*objCodePtrType));
8426 
8427   RootedValue abiType(cx, ObjectOrNullValue(funInfoFinalizer->mABI));
8428   ffi_abi abi;
8429   if (!GetABI(cx, abiType, &abi)) {
8430     JS_ReportErrorASCII(cx,
8431                         "Internal Error: "
8432                         "Invalid ABI specification in CDataFinalizer");
8433     return false;
8434   }
8435 
8436   ffi_type* rtype = CType::GetFFIType(cx, funInfoFinalizer->mReturnType);
8437   if (!rtype) {
8438     JS_ReportErrorASCII(cx,
8439                         "Internal Error: "
8440                         "Could not access ffi type of CDataFinalizer");
8441     return false;
8442   }
8443 
8444   // 7. Store C information as private
8445   UniquePtr<CDataFinalizer::Private, JS::FreePolicy> p(
8446       (CDataFinalizer::Private*)malloc(sizeof(CDataFinalizer::Private)));
8447 
8448   memmove(&p->CIF, &funInfoFinalizer->mCIF, sizeof(ffi_cif));
8449 
8450   p->cargs = cargs.release();
8451   p->rvalue = rvalue.release();
8452   p->cargs_size = sizeArg;
8453   p->code = code;
8454 
8455   JS::SetPrivate(objResult, p.release());
8456   args.rval().setObject(*objResult);
8457   return true;
8458 }
8459 
8460 /*
8461  * Actually call the finalizer. Does not perform any cleanup on the object.
8462  *
8463  * Preconditions: |this| must be a |CDataFinalizer|, |p| must be non-null.
8464  * The function fails if |this| has gone through |Forget|/|Dispose|
8465  * or |Finalize|.
8466  *
8467  * This function does not alter the value of |errno|/|GetLastError|.
8468  *
8469  * If argument |errnoStatus| is non-nullptr, it receives the value of |errno|
8470  * immediately after the call. Under Windows, if argument |lastErrorStatus|
8471  * is non-nullptr, it receives the value of |GetLastError| immediately after
8472  * the call. On other platforms, |lastErrorStatus| is ignored.
8473  */
CallFinalizer(CDataFinalizer::Private * p,int * errnoStatus,int32_t * lastErrorStatus)8474 void CDataFinalizer::CallFinalizer(CDataFinalizer::Private* p, int* errnoStatus,
8475                                    int32_t* lastErrorStatus) {
8476   int savedErrno = errno;
8477   errno = 0;
8478 #if defined(XP_WIN)
8479   int32_t savedLastError = GetLastError();
8480   SetLastError(0);
8481 #endif  // defined(XP_WIN)
8482 
8483   void* args[1] = {p->cargs};
8484   ffi_call(&p->CIF, FFI_FN(p->code), p->rvalue, args);
8485 
8486   if (errnoStatus) {
8487     *errnoStatus = errno;
8488   }
8489   errno = savedErrno;
8490 #if defined(XP_WIN)
8491   if (lastErrorStatus) {
8492     *lastErrorStatus = GetLastError();
8493   }
8494   SetLastError(savedLastError);
8495 #endif  // defined(XP_WIN)
8496 }
8497 
8498 /*
8499  * Forget the value.
8500  *
8501  * Preconditions: |this| must be a |CDataFinalizer|.
8502  * The function fails if |this| has gone through |Forget|/|Dispose|
8503  * or |Finalize|.
8504  *
8505  * Does not call the finalizer. Cleans up the Private memory and releases all
8506  * strong references.
8507  */
Forget(JSContext * cx,unsigned argc,Value * vp)8508 bool CDataFinalizer::Methods::Forget(JSContext* cx, unsigned argc, Value* vp) {
8509   CallArgs args = CallArgsFromVp(argc, vp);
8510   if (args.length() != 0) {
8511     return ArgumentLengthError(cx, "CDataFinalizer.prototype.forget", "no",
8512                                "s");
8513   }
8514 
8515   RootedObject obj(cx,
8516                    GetThisObject(cx, args, "CDataFinalizer.prototype.forget"));
8517   if (!obj) {
8518     return false;
8519   }
8520   if (!CDataFinalizer::IsCDataFinalizer(obj)) {
8521     return IncompatibleThisProto(cx, "CDataFinalizer.prototype.forget",
8522                                  args.thisv());
8523   }
8524 
8525   auto* p = (CDataFinalizer::Private*)JS::GetPrivate(obj);
8526 
8527   if (!p) {
8528     return EmptyFinalizerCallError(cx, "CDataFinalizer.prototype.forget");
8529   }
8530 
8531   RootedValue valJSData(cx);
8532   RootedObject ctype(cx, GetCType(cx, obj));
8533   if (!ConvertToJS(cx, ctype, nullptr, p->cargs, false, true, &valJSData)) {
8534     JS_ReportErrorASCII(cx, "CDataFinalizer value cannot be represented");
8535     return false;
8536   }
8537 
8538   CDataFinalizer::Cleanup(p, obj);
8539 
8540   args.rval().set(valJSData);
8541   return true;
8542 }
8543 
8544 /*
8545  * Clean up the value.
8546  *
8547  * Preconditions: |this| must be a |CDataFinalizer|.
8548  * The function fails if |this| has gone through |Forget|/|Dispose|
8549  * or |Finalize|.
8550  *
8551  * Calls the finalizer, cleans up the Private memory and releases all
8552  * strong references.
8553  */
Dispose(JSContext * cx,unsigned argc,Value * vp)8554 bool CDataFinalizer::Methods::Dispose(JSContext* cx, unsigned argc, Value* vp) {
8555   CallArgs args = CallArgsFromVp(argc, vp);
8556   if (args.length() != 0) {
8557     return ArgumentLengthError(cx, "CDataFinalizer.prototype.dispose", "no",
8558                                "s");
8559   }
8560 
8561   RootedObject obj(cx,
8562                    GetThisObject(cx, args, "CDataFinalizer.prototype.dispose"));
8563   if (!obj) {
8564     return false;
8565   }
8566   if (!CDataFinalizer::IsCDataFinalizer(obj)) {
8567     return IncompatibleThisProto(cx, "CDataFinalizer.prototype.dispose",
8568                                  args.thisv());
8569   }
8570 
8571   auto* p = (CDataFinalizer::Private*)JS::GetPrivate(obj);
8572 
8573   if (!p) {
8574     return EmptyFinalizerCallError(cx, "CDataFinalizer.prototype.dispose");
8575   }
8576 
8577   Value valType = JS::GetReservedSlot(obj, SLOT_DATAFINALIZER_VALTYPE);
8578   MOZ_ASSERT(valType.isObject());
8579 
8580   RootedObject objCTypes(cx, CType::GetGlobalCTypes(cx, &valType.toObject()));
8581   if (!objCTypes) {
8582     return false;
8583   }
8584 
8585   Value valCodePtrType = JS::GetReservedSlot(obj, SLOT_DATAFINALIZER_CODETYPE);
8586   MOZ_ASSERT(valCodePtrType.isObject());
8587   JSObject* objCodePtrType = &valCodePtrType.toObject();
8588 
8589   JSObject* objCodeType = PointerType::GetBaseType(objCodePtrType);
8590   MOZ_ASSERT(objCodeType);
8591   MOZ_ASSERT(CType::GetTypeCode(objCodeType) == TYPE_function);
8592 
8593   RootedObject resultType(
8594       cx, FunctionType::GetFunctionInfo(objCodeType)->mReturnType);
8595   RootedValue result(cx);
8596 
8597   int errnoStatus;
8598 #if defined(XP_WIN)
8599   int32_t lastErrorStatus;
8600   CDataFinalizer::CallFinalizer(p, &errnoStatus, &lastErrorStatus);
8601 #else
8602   CDataFinalizer::CallFinalizer(p, &errnoStatus, nullptr);
8603 #endif  // defined(XP_WIN)
8604 
8605   JS_SetReservedSlot(objCTypes, SLOT_ERRNO, Int32Value(errnoStatus));
8606 #if defined(XP_WIN)
8607   JS_SetReservedSlot(objCTypes, SLOT_LASTERROR, Int32Value(lastErrorStatus));
8608 #endif  // defined(XP_WIN)
8609 
8610   if (ConvertToJS(cx, resultType, nullptr, p->rvalue, false, true, &result)) {
8611     CDataFinalizer::Cleanup(p, obj);
8612     args.rval().set(result);
8613     return true;
8614   }
8615   CDataFinalizer::Cleanup(p, obj);
8616   return false;
8617 }
8618 
8619 /*
8620  * Perform finalization.
8621  *
8622  * Preconditions: |this| must be the result of |CDataFinalizer|.
8623  * It may have gone through |Forget|/|Dispose|.
8624  *
8625  * If |this| has not gone through |Forget|/|Dispose|, calls the
8626  * finalizer, cleans up the Private memory and releases all
8627  * strong references.
8628  */
Finalize(JSFreeOp * fop,JSObject * obj)8629 void CDataFinalizer::Finalize(JSFreeOp* fop, JSObject* obj) {
8630   auto* p = (CDataFinalizer::Private*)JS::GetPrivate(obj);
8631 
8632   if (!p) {
8633     return;
8634   }
8635 
8636   CDataFinalizer::CallFinalizer(p, nullptr, nullptr);
8637   CDataFinalizer::Cleanup(p, nullptr);
8638 }
8639 
8640 /*
8641  * Perform cleanup of a CDataFinalizer
8642  *
8643  * Release strong references, cleanup |Private|.
8644  *
8645  * Argument |p| contains the private information of the CDataFinalizer. If
8646  * nullptr, this function does nothing.
8647  * Argument |obj| should contain |nullptr| during finalization (or in any
8648  * context in which the object itself should not be cleaned up), or a
8649  * CDataFinalizer object otherwise.
8650  */
Cleanup(CDataFinalizer::Private * p,JSObject * obj)8651 void CDataFinalizer::Cleanup(CDataFinalizer::Private* p, JSObject* obj) {
8652   if (!p) {
8653     return;  // We have already cleaned up
8654   }
8655 
8656   free(p->cargs);
8657   free(p->rvalue);
8658   free(p);
8659 
8660   if (!obj) {
8661     return;  // No slots to clean up
8662   }
8663 
8664   MOZ_ASSERT(CDataFinalizer::IsCDataFinalizer(obj));
8665 
8666   JS::SetPrivate(obj, nullptr);
8667   for (int i = 0; i < CDATAFINALIZER_SLOTS; ++i) {
8668     JS_SetReservedSlot(obj, i, JS::NullValue());
8669   }
8670 }
8671 
8672 /*******************************************************************************
8673 ** Int64 and UInt64 implementation
8674 *******************************************************************************/
8675 
Construct(JSContext * cx,HandleObject proto,uint64_t data,bool isUnsigned)8676 JSObject* Int64Base::Construct(JSContext* cx, HandleObject proto, uint64_t data,
8677                                bool isUnsigned) {
8678   const JSClass* clasp = isUnsigned ? &sUInt64Class : &sInt64Class;
8679   RootedObject result(cx, JS_NewObjectWithGivenProto(cx, clasp, proto));
8680   if (!result) {
8681     return nullptr;
8682   }
8683 
8684   // attach the Int64's data
8685   uint64_t* buffer = cx->new_<uint64_t>(data);
8686   if (!buffer) {
8687     return nullptr;
8688   }
8689 
8690   JS_InitReservedSlot(result, SLOT_INT64, buffer, JS::MemoryUse::CTypesInt64);
8691 
8692   if (!JS_FreezeObject(cx, result)) {
8693     return nullptr;
8694   }
8695 
8696   return result;
8697 }
8698 
Finalize(JSFreeOp * fop,JSObject * obj)8699 void Int64Base::Finalize(JSFreeOp* fop, JSObject* obj) {
8700   Value slot = JS::GetReservedSlot(obj, SLOT_INT64);
8701   if (slot.isUndefined()) {
8702     return;
8703   }
8704 
8705   uint64_t* buffer = static_cast<uint64_t*>(slot.toPrivate());
8706   fop->delete_(obj, buffer, MemoryUse::CTypesInt64);
8707 }
8708 
GetInt(JSObject * obj)8709 uint64_t Int64Base::GetInt(JSObject* obj) {
8710   MOZ_ASSERT(Int64::IsInt64(obj) || UInt64::IsUInt64(obj));
8711 
8712   Value slot = JS::GetReservedSlot(obj, SLOT_INT64);
8713   return *static_cast<uint64_t*>(slot.toPrivate());
8714 }
8715 
ToString(JSContext * cx,JSObject * obj,const CallArgs & args,bool isUnsigned)8716 bool Int64Base::ToString(JSContext* cx, JSObject* obj, const CallArgs& args,
8717                          bool isUnsigned) {
8718   if (args.length() > 1) {
8719     if (isUnsigned) {
8720       return ArgumentLengthError(cx, "UInt64.prototype.toString", "at most one",
8721                                  "");
8722     }
8723     return ArgumentLengthError(cx, "Int64.prototype.toString", "at most one",
8724                                "");
8725   }
8726 
8727   int radix = 10;
8728   if (args.length() == 1) {
8729     Value arg = args[0];
8730     if (arg.isInt32()) {
8731       radix = arg.toInt32();
8732     }
8733     if (!arg.isInt32() || radix < 2 || radix > 36) {
8734       if (isUnsigned) {
8735         return ArgumentRangeMismatch(
8736             cx, "UInt64.prototype.toString",
8737             "an integer at least 2 and no greater than 36");
8738       }
8739       return ArgumentRangeMismatch(
8740           cx, "Int64.prototype.toString",
8741           "an integer at least 2 and no greater than 36");
8742     }
8743   }
8744 
8745   AutoString intString;
8746   if (isUnsigned) {
8747     IntegerToString(GetInt(obj), radix, intString);
8748   } else {
8749     IntegerToString(static_cast<int64_t>(GetInt(obj)), radix, intString);
8750   }
8751 
8752   if (!intString) {
8753     return false;
8754   }
8755   JSString* result = NewUCString(cx, intString.finish());
8756   if (!result) {
8757     return false;
8758   }
8759 
8760   args.rval().setString(result);
8761   return true;
8762 }
8763 
ToSource(JSContext * cx,JSObject * obj,const CallArgs & args,bool isUnsigned)8764 bool Int64Base::ToSource(JSContext* cx, JSObject* obj, const CallArgs& args,
8765                          bool isUnsigned) {
8766   if (args.length() != 0) {
8767     if (isUnsigned) {
8768       return ArgumentLengthError(cx, "UInt64.prototype.toSource", "no", "s");
8769     }
8770     return ArgumentLengthError(cx, "Int64.prototype.toSource", "no", "s");
8771   }
8772 
8773   // Return a decimal string suitable for constructing the number.
8774   AutoString source;
8775   if (isUnsigned) {
8776     AppendString(cx, source, "ctypes.UInt64(\"");
8777     IntegerToString(GetInt(obj), 10, source);
8778   } else {
8779     AppendString(cx, source, "ctypes.Int64(\"");
8780     IntegerToString(static_cast<int64_t>(GetInt(obj)), 10, source);
8781   }
8782   AppendString(cx, source, "\")");
8783   if (!source) {
8784     return false;
8785   }
8786 
8787   JSString* result = NewUCString(cx, source.finish());
8788   if (!result) {
8789     return false;
8790   }
8791 
8792   args.rval().setString(result);
8793   return true;
8794 }
8795 
Construct(JSContext * cx,unsigned argc,Value * vp)8796 bool Int64::Construct(JSContext* cx, unsigned argc, Value* vp) {
8797   CallArgs args = CallArgsFromVp(argc, vp);
8798 
8799   // Construct and return a new Int64 object.
8800   if (args.length() != 1) {
8801     return ArgumentLengthError(cx, "Int64 constructor", "one", "");
8802   }
8803 
8804   int64_t i = 0;
8805   bool overflow = false;
8806   if (!jsvalToBigInteger(cx, args[0], true, &i, &overflow)) {
8807     if (overflow) {
8808       return TypeOverflow(cx, "int64", args[0]);
8809     }
8810     return ArgumentConvError(cx, args[0], "Int64", 0);
8811   }
8812 
8813   // Get ctypes.Int64.prototype from the 'prototype' property of the ctor.
8814   RootedValue slot(cx);
8815   RootedObject callee(cx, &args.callee());
8816   MOZ_ALWAYS_TRUE(JS_GetProperty(cx, callee, "prototype", &slot));
8817   RootedObject proto(cx, slot.toObjectOrNull());
8818   MOZ_ASSERT(proto->hasClass(&sInt64ProtoClass));
8819 
8820   JSObject* result = Int64Base::Construct(cx, proto, i, false);
8821   if (!result) {
8822     return false;
8823   }
8824 
8825   args.rval().setObject(*result);
8826   return true;
8827 }
8828 
IsInt64(JSObject * obj)8829 bool Int64::IsInt64(JSObject* obj) { return obj->hasClass(&sInt64Class); }
8830 
ToString(JSContext * cx,unsigned argc,Value * vp)8831 bool Int64::ToString(JSContext* cx, unsigned argc, Value* vp) {
8832   CallArgs args = CallArgsFromVp(argc, vp);
8833   RootedObject obj(cx, GetThisObject(cx, args, "Int64.prototype.toString"));
8834   if (!obj) {
8835     return false;
8836   }
8837   if (!Int64::IsInt64(obj)) {
8838     if (!CData::IsCDataMaybeUnwrap(&obj)) {
8839       return IncompatibleThisProto(cx, "Int64.prototype.toString",
8840                                    InformalValueTypeName(args.thisv()));
8841     }
8842     return IncompatibleThisType(cx, "Int64.prototype.toString",
8843                                 "non-Int64 CData");
8844   }
8845 
8846   return Int64Base::ToString(cx, obj, args, false);
8847 }
8848 
ToSource(JSContext * cx,unsigned argc,Value * vp)8849 bool Int64::ToSource(JSContext* cx, unsigned argc, Value* vp) {
8850   CallArgs args = CallArgsFromVp(argc, vp);
8851   RootedObject obj(cx, GetThisObject(cx, args, "Int64.prototype.toSource"));
8852   if (!obj) {
8853     return false;
8854   }
8855   if (!Int64::IsInt64(obj)) {
8856     if (!CData::IsCDataMaybeUnwrap(&obj)) {
8857       return IncompatibleThisProto(cx, "Int64.prototype.toSource",
8858                                    InformalValueTypeName(args.thisv()));
8859     }
8860     return IncompatibleThisType(cx, "Int64.prototype.toSource",
8861                                 "non-Int64 CData");
8862   }
8863 
8864   return Int64Base::ToSource(cx, obj, args, false);
8865 }
8866 
Compare(JSContext * cx,unsigned argc,Value * vp)8867 bool Int64::Compare(JSContext* cx, unsigned argc, Value* vp) {
8868   CallArgs args = CallArgsFromVp(argc, vp);
8869   if (args.length() != 2) {
8870     return ArgumentLengthError(cx, "Int64.compare", "two", "s");
8871   }
8872   if (args[0].isPrimitive() || !Int64::IsInt64(&args[0].toObject())) {
8873     return ArgumentTypeMismatch(cx, "first ", "Int64.compare", "a Int64");
8874   }
8875   if (args[1].isPrimitive() || !Int64::IsInt64(&args[1].toObject())) {
8876     return ArgumentTypeMismatch(cx, "second ", "Int64.compare", "a Int64");
8877   }
8878 
8879   JSObject* obj1 = &args[0].toObject();
8880   JSObject* obj2 = &args[1].toObject();
8881 
8882   int64_t i1 = Int64Base::GetInt(obj1);
8883   int64_t i2 = Int64Base::GetInt(obj2);
8884 
8885   if (i1 == i2) {
8886     args.rval().setInt32(0);
8887   } else if (i1 < i2) {
8888     args.rval().setInt32(-1);
8889   } else {
8890     args.rval().setInt32(1);
8891   }
8892 
8893   return true;
8894 }
8895 
8896 #define LO_MASK ((uint64_t(1) << 32) - 1)
8897 #define INT64_LO(i) ((i)&LO_MASK)
8898 #define INT64_HI(i) ((i) >> 32)
8899 
Lo(JSContext * cx,unsigned argc,Value * vp)8900 bool Int64::Lo(JSContext* cx, unsigned argc, Value* vp) {
8901   CallArgs args = CallArgsFromVp(argc, vp);
8902   if (args.length() != 1) {
8903     return ArgumentLengthError(cx, "Int64.lo", "one", "");
8904   }
8905   if (args[0].isPrimitive() || !Int64::IsInt64(&args[0].toObject())) {
8906     return ArgumentTypeMismatch(cx, "", "Int64.lo", "a Int64");
8907   }
8908 
8909   JSObject* obj = &args[0].toObject();
8910   int64_t u = Int64Base::GetInt(obj);
8911   double d = uint32_t(INT64_LO(u));
8912 
8913   args.rval().setNumber(d);
8914   return true;
8915 }
8916 
Hi(JSContext * cx,unsigned argc,Value * vp)8917 bool Int64::Hi(JSContext* cx, unsigned argc, Value* vp) {
8918   CallArgs args = CallArgsFromVp(argc, vp);
8919   if (args.length() != 1) {
8920     return ArgumentLengthError(cx, "Int64.hi", "one", "");
8921   }
8922   if (args[0].isPrimitive() || !Int64::IsInt64(&args[0].toObject())) {
8923     return ArgumentTypeMismatch(cx, "", "Int64.hi", "a Int64");
8924   }
8925 
8926   JSObject* obj = &args[0].toObject();
8927   int64_t u = Int64Base::GetInt(obj);
8928   double d = int32_t(INT64_HI(u));
8929 
8930   args.rval().setDouble(d);
8931   return true;
8932 }
8933 
Join(JSContext * cx,unsigned argc,Value * vp)8934 bool Int64::Join(JSContext* cx, unsigned argc, Value* vp) {
8935   CallArgs args = CallArgsFromVp(argc, vp);
8936   if (args.length() != 2) {
8937     return ArgumentLengthError(cx, "Int64.join", "two", "s");
8938   }
8939 
8940   int32_t hi;
8941   uint32_t lo;
8942   if (!jsvalToInteger(cx, args[0], &hi)) {
8943     return ArgumentConvError(cx, args[0], "Int64.join", 0);
8944   }
8945   if (!jsvalToInteger(cx, args[1], &lo)) {
8946     return ArgumentConvError(cx, args[1], "Int64.join", 1);
8947   }
8948 
8949   int64_t i = mozilla::WrapToSigned((uint64_t(hi) << 32) + lo);
8950 
8951   // Get Int64.prototype from the function's reserved slot.
8952   JSObject* callee = &args.callee();
8953 
8954   Value slot = js::GetFunctionNativeReserved(callee, SLOT_FN_INT64PROTO);
8955   RootedObject proto(cx, &slot.toObject());
8956   MOZ_ASSERT(proto->hasClass(&sInt64ProtoClass));
8957 
8958   JSObject* result = Int64Base::Construct(cx, proto, i, false);
8959   if (!result) {
8960     return false;
8961   }
8962 
8963   args.rval().setObject(*result);
8964   return true;
8965 }
8966 
Construct(JSContext * cx,unsigned argc,Value * vp)8967 bool UInt64::Construct(JSContext* cx, unsigned argc, Value* vp) {
8968   CallArgs args = CallArgsFromVp(argc, vp);
8969 
8970   // Construct and return a new UInt64 object.
8971   if (args.length() != 1) {
8972     return ArgumentLengthError(cx, "UInt64 constructor", "one", "");
8973   }
8974 
8975   uint64_t u = 0;
8976   bool overflow = false;
8977   if (!jsvalToBigInteger(cx, args[0], true, &u, &overflow)) {
8978     if (overflow) {
8979       return TypeOverflow(cx, "uint64", args[0]);
8980     }
8981     return ArgumentConvError(cx, args[0], "UInt64", 0);
8982   }
8983 
8984   // Get ctypes.UInt64.prototype from the 'prototype' property of the ctor.
8985   RootedValue slot(cx);
8986   RootedObject callee(cx, &args.callee());
8987   MOZ_ALWAYS_TRUE(JS_GetProperty(cx, callee, "prototype", &slot));
8988   RootedObject proto(cx, &slot.toObject());
8989   MOZ_ASSERT(proto->hasClass(&sUInt64ProtoClass));
8990 
8991   JSObject* result = Int64Base::Construct(cx, proto, u, true);
8992   if (!result) {
8993     return false;
8994   }
8995 
8996   args.rval().setObject(*result);
8997   return true;
8998 }
8999 
IsUInt64(JSObject * obj)9000 bool UInt64::IsUInt64(JSObject* obj) { return obj->hasClass(&sUInt64Class); }
9001 
ToString(JSContext * cx,unsigned argc,Value * vp)9002 bool UInt64::ToString(JSContext* cx, unsigned argc, Value* vp) {
9003   CallArgs args = CallArgsFromVp(argc, vp);
9004   RootedObject obj(cx, GetThisObject(cx, args, "UInt64.prototype.toString"));
9005   if (!obj) {
9006     return false;
9007   }
9008   if (!UInt64::IsUInt64(obj)) {
9009     if (!CData::IsCDataMaybeUnwrap(&obj)) {
9010       return IncompatibleThisProto(cx, "UInt64.prototype.toString",
9011                                    InformalValueTypeName(args.thisv()));
9012     }
9013     return IncompatibleThisType(cx, "UInt64.prototype.toString",
9014                                 "non-UInt64 CData");
9015   }
9016 
9017   return Int64Base::ToString(cx, obj, args, true);
9018 }
9019 
ToSource(JSContext * cx,unsigned argc,Value * vp)9020 bool UInt64::ToSource(JSContext* cx, unsigned argc, Value* vp) {
9021   CallArgs args = CallArgsFromVp(argc, vp);
9022   RootedObject obj(cx, GetThisObject(cx, args, "UInt64.prototype.toSource"));
9023   if (!obj) {
9024     return false;
9025   }
9026   if (!UInt64::IsUInt64(obj)) {
9027     if (!CData::IsCDataMaybeUnwrap(&obj)) {
9028       return IncompatibleThisProto(cx, "UInt64.prototype.toSource",
9029                                    InformalValueTypeName(args.thisv()));
9030     }
9031     return IncompatibleThisType(cx, "UInt64.prototype.toSource",
9032                                 "non-UInt64 CData");
9033   }
9034 
9035   return Int64Base::ToSource(cx, obj, args, true);
9036 }
9037 
Compare(JSContext * cx,unsigned argc,Value * vp)9038 bool UInt64::Compare(JSContext* cx, unsigned argc, Value* vp) {
9039   CallArgs args = CallArgsFromVp(argc, vp);
9040   if (args.length() != 2) {
9041     return ArgumentLengthError(cx, "UInt64.compare", "two", "s");
9042   }
9043   if (args[0].isPrimitive() || !UInt64::IsUInt64(&args[0].toObject())) {
9044     return ArgumentTypeMismatch(cx, "first ", "UInt64.compare", "a UInt64");
9045   }
9046   if (args[1].isPrimitive() || !UInt64::IsUInt64(&args[1].toObject())) {
9047     return ArgumentTypeMismatch(cx, "second ", "UInt64.compare", "a UInt64");
9048   }
9049 
9050   JSObject* obj1 = &args[0].toObject();
9051   JSObject* obj2 = &args[1].toObject();
9052 
9053   uint64_t u1 = Int64Base::GetInt(obj1);
9054   uint64_t u2 = Int64Base::GetInt(obj2);
9055 
9056   if (u1 == u2) {
9057     args.rval().setInt32(0);
9058   } else if (u1 < u2) {
9059     args.rval().setInt32(-1);
9060   } else {
9061     args.rval().setInt32(1);
9062   }
9063 
9064   return true;
9065 }
9066 
Lo(JSContext * cx,unsigned argc,Value * vp)9067 bool UInt64::Lo(JSContext* cx, unsigned argc, Value* vp) {
9068   CallArgs args = CallArgsFromVp(argc, vp);
9069   if (args.length() != 1) {
9070     return ArgumentLengthError(cx, "UInt64.lo", "one", "");
9071   }
9072   if (args[0].isPrimitive() || !UInt64::IsUInt64(&args[0].toObject())) {
9073     return ArgumentTypeMismatch(cx, "", "UInt64.lo", "a UInt64");
9074   }
9075 
9076   JSObject* obj = &args[0].toObject();
9077   uint64_t u = Int64Base::GetInt(obj);
9078   double d = uint32_t(INT64_LO(u));
9079 
9080   args.rval().setDouble(d);
9081   return true;
9082 }
9083 
Hi(JSContext * cx,unsigned argc,Value * vp)9084 bool UInt64::Hi(JSContext* cx, unsigned argc, Value* vp) {
9085   CallArgs args = CallArgsFromVp(argc, vp);
9086   if (args.length() != 1) {
9087     return ArgumentLengthError(cx, "UInt64.hi", "one", "");
9088   }
9089   if (args[0].isPrimitive() || !UInt64::IsUInt64(&args[0].toObject())) {
9090     return ArgumentTypeMismatch(cx, "", "UInt64.hi", "a UInt64");
9091   }
9092 
9093   JSObject* obj = &args[0].toObject();
9094   uint64_t u = Int64Base::GetInt(obj);
9095   double d = uint32_t(INT64_HI(u));
9096 
9097   args.rval().setDouble(d);
9098   return true;
9099 }
9100 
Join(JSContext * cx,unsigned argc,Value * vp)9101 bool UInt64::Join(JSContext* cx, unsigned argc, Value* vp) {
9102   CallArgs args = CallArgsFromVp(argc, vp);
9103   if (args.length() != 2) {
9104     return ArgumentLengthError(cx, "UInt64.join", "two", "s");
9105   }
9106 
9107   uint32_t hi;
9108   uint32_t lo;
9109   if (!jsvalToInteger(cx, args[0], &hi)) {
9110     return ArgumentConvError(cx, args[0], "UInt64.join", 0);
9111   }
9112   if (!jsvalToInteger(cx, args[1], &lo)) {
9113     return ArgumentConvError(cx, args[1], "UInt64.join", 1);
9114   }
9115 
9116   uint64_t u = (uint64_t(hi) << 32) + uint64_t(lo);
9117 
9118   // Get UInt64.prototype from the function's reserved slot.
9119   JSObject* callee = &args.callee();
9120 
9121   Value slot = js::GetFunctionNativeReserved(callee, SLOT_FN_INT64PROTO);
9122   RootedObject proto(cx, &slot.toObject());
9123   MOZ_ASSERT(proto->hasClass(&sUInt64ProtoClass));
9124 
9125   JSObject* result = Int64Base::Construct(cx, proto, u, true);
9126   if (!result) {
9127     return false;
9128   }
9129 
9130   args.rval().setObject(*result);
9131   return true;
9132 }
9133 
9134 }  // namespace ctypes
9135 }  // namespace js
9136