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