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