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