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