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