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