1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/ctypes/CTypes.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,7727 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "ctypes/CTypes.h" 1.11 + 1.12 +#include "mozilla/FloatingPoint.h" 1.13 +#include "mozilla/MemoryReporting.h" 1.14 +#include "mozilla/NumericLimits.h" 1.15 + 1.16 +#include <math.h> 1.17 +#include <stdint.h> 1.18 + 1.19 +#if defined(XP_WIN) 1.20 +#include <float.h> 1.21 +#endif 1.22 + 1.23 +#if defined(SOLARIS) 1.24 +#include <ieeefp.h> 1.25 +#endif 1.26 + 1.27 +#ifdef HAVE_SSIZE_T 1.28 +#include <sys/types.h> 1.29 +#endif 1.30 + 1.31 +#if defined(XP_UNIX) 1.32 +#include <errno.h> 1.33 +#elif defined(XP_WIN) 1.34 +#include <windows.h> 1.35 +#endif 1.36 + 1.37 +#include "jscntxt.h" 1.38 +#include "jsfun.h" 1.39 +#include "jsnum.h" 1.40 +#include "jsprf.h" 1.41 + 1.42 +#include "builtin/TypedObject.h" 1.43 +#include "ctypes/Library.h" 1.44 + 1.45 +using namespace std; 1.46 +using mozilla::NumericLimits; 1.47 + 1.48 +namespace js { 1.49 +namespace ctypes { 1.50 + 1.51 +size_t 1.52 +GetDeflatedUTF8StringLength(JSContext *maybecx, const jschar *chars, 1.53 + size_t nchars) 1.54 +{ 1.55 + size_t nbytes; 1.56 + const jschar *end; 1.57 + unsigned c, c2; 1.58 + char buffer[10]; 1.59 + 1.60 + nbytes = nchars; 1.61 + for (end = chars + nchars; chars != end; chars++) { 1.62 + c = *chars; 1.63 + if (c < 0x80) 1.64 + continue; 1.65 + if (0xD800 <= c && c <= 0xDFFF) { 1.66 + /* Surrogate pair. */ 1.67 + chars++; 1.68 + 1.69 + /* nbytes sets 1 length since this is surrogate pair. */ 1.70 + nbytes--; 1.71 + if (c >= 0xDC00 || chars == end) 1.72 + goto bad_surrogate; 1.73 + c2 = *chars; 1.74 + if (c2 < 0xDC00 || c2 > 0xDFFF) 1.75 + goto bad_surrogate; 1.76 + c = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000; 1.77 + } 1.78 + c >>= 11; 1.79 + nbytes++; 1.80 + while (c) { 1.81 + c >>= 5; 1.82 + nbytes++; 1.83 + } 1.84 + } 1.85 + return nbytes; 1.86 + 1.87 + bad_surrogate: 1.88 + if (maybecx) { 1.89 + JS_snprintf(buffer, 10, "0x%x", c); 1.90 + JS_ReportErrorFlagsAndNumber(maybecx, JSREPORT_ERROR, js_GetErrorMessage, 1.91 + nullptr, JSMSG_BAD_SURROGATE_CHAR, buffer); 1.92 + } 1.93 + return (size_t) -1; 1.94 +} 1.95 + 1.96 +bool 1.97 +DeflateStringToUTF8Buffer(JSContext *maybecx, const jschar *src, size_t srclen, 1.98 + char *dst, size_t *dstlenp) 1.99 +{ 1.100 + size_t i, utf8Len; 1.101 + jschar c, c2; 1.102 + uint32_t v; 1.103 + uint8_t utf8buf[6]; 1.104 + 1.105 + size_t dstlen = *dstlenp; 1.106 + size_t origDstlen = dstlen; 1.107 + 1.108 + while (srclen) { 1.109 + c = *src++; 1.110 + srclen--; 1.111 + if (c >= 0xDC00 && c <= 0xDFFF) 1.112 + goto badSurrogate; 1.113 + if (c < 0xD800 || c > 0xDBFF) { 1.114 + v = c; 1.115 + } else { 1.116 + if (srclen < 1) 1.117 + goto badSurrogate; 1.118 + c2 = *src; 1.119 + if ((c2 < 0xDC00) || (c2 > 0xDFFF)) 1.120 + goto badSurrogate; 1.121 + src++; 1.122 + srclen--; 1.123 + v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000; 1.124 + } 1.125 + if (v < 0x0080) { 1.126 + /* no encoding necessary - performance hack */ 1.127 + if (dstlen == 0) 1.128 + goto bufferTooSmall; 1.129 + *dst++ = (char) v; 1.130 + utf8Len = 1; 1.131 + } else { 1.132 + utf8Len = js_OneUcs4ToUtf8Char(utf8buf, v); 1.133 + if (utf8Len > dstlen) 1.134 + goto bufferTooSmall; 1.135 + for (i = 0; i < utf8Len; i++) 1.136 + *dst++ = (char) utf8buf[i]; 1.137 + } 1.138 + dstlen -= utf8Len; 1.139 + } 1.140 + *dstlenp = (origDstlen - dstlen); 1.141 + return true; 1.142 + 1.143 +badSurrogate: 1.144 + *dstlenp = (origDstlen - dstlen); 1.145 + /* Delegate error reporting to the measurement function. */ 1.146 + if (maybecx) 1.147 + GetDeflatedUTF8StringLength(maybecx, src - 1, srclen + 1); 1.148 + return false; 1.149 + 1.150 +bufferTooSmall: 1.151 + *dstlenp = (origDstlen - dstlen); 1.152 + if (maybecx) { 1.153 + JS_ReportErrorNumber(maybecx, js_GetErrorMessage, nullptr, 1.154 + JSMSG_BUFFER_TOO_SMALL); 1.155 + } 1.156 + return false; 1.157 +} 1.158 + 1.159 +/******************************************************************************* 1.160 +** JSAPI function prototypes 1.161 +*******************************************************************************/ 1.162 + 1.163 +// We use an enclosing struct here out of paranoia about the ability of gcc 4.4 1.164 +// (and maybe 4.5) to correctly compile this if it were a template function. 1.165 +// See also the comments in dom/workers/Events.cpp (and other adjacent files) by 1.166 +// the |struct Property| there. 1.167 +template<JS::IsAcceptableThis Test, JS::NativeImpl Impl> 1.168 +struct Property 1.169 +{ 1.170 + static bool 1.171 + Fun(JSContext* cx, unsigned argc, JS::Value* vp) 1.172 + { 1.173 + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 1.174 + return JS::CallNonGenericMethod<Test, Impl>(cx, args); 1.175 + } 1.176 +}; 1.177 + 1.178 +static bool ConstructAbstract(JSContext* cx, unsigned argc, jsval* vp); 1.179 + 1.180 +namespace CType { 1.181 + static bool ConstructData(JSContext* cx, unsigned argc, jsval* vp); 1.182 + static bool ConstructBasic(JSContext* cx, HandleObject obj, const CallArgs& args); 1.183 + 1.184 + static void Trace(JSTracer* trc, JSObject* obj); 1.185 + static void Finalize(JSFreeOp *fop, JSObject* obj); 1.186 + 1.187 + bool IsCType(HandleValue v); 1.188 + bool IsCTypeOrProto(HandleValue v); 1.189 + 1.190 + bool PrototypeGetter(JSContext* cx, JS::CallArgs args); 1.191 + bool NameGetter(JSContext* cx, JS::CallArgs args); 1.192 + bool SizeGetter(JSContext* cx, JS::CallArgs args); 1.193 + bool PtrGetter(JSContext* cx, JS::CallArgs args); 1.194 + 1.195 + static bool CreateArray(JSContext* cx, unsigned argc, jsval* vp); 1.196 + static bool ToString(JSContext* cx, unsigned argc, jsval* vp); 1.197 + static bool ToSource(JSContext* cx, unsigned argc, jsval* vp); 1.198 + static bool HasInstance(JSContext* cx, HandleObject obj, MutableHandleValue v, bool* bp); 1.199 + 1.200 + 1.201 + /* 1.202 + * Get the global "ctypes" object. 1.203 + * 1.204 + * |obj| must be a CType object. 1.205 + * 1.206 + * This function never returns nullptr. 1.207 + */ 1.208 + static JSObject* GetGlobalCTypes(JSContext* cx, JSObject* obj); 1.209 + 1.210 +} 1.211 + 1.212 +namespace ABI { 1.213 + bool IsABI(JSObject* obj); 1.214 + static bool ToSource(JSContext* cx, unsigned argc, jsval* vp); 1.215 +} 1.216 + 1.217 +namespace PointerType { 1.218 + static bool Create(JSContext* cx, unsigned argc, jsval* vp); 1.219 + static bool ConstructData(JSContext* cx, HandleObject obj, const CallArgs& args); 1.220 + 1.221 + bool IsPointerType(HandleValue v); 1.222 + bool IsPointer(HandleValue v); 1.223 + 1.224 + bool TargetTypeGetter(JSContext* cx, JS::CallArgs args); 1.225 + bool ContentsGetter(JSContext* cx, JS::CallArgs args); 1.226 + bool ContentsSetter(JSContext* cx, JS::CallArgs args); 1.227 + 1.228 + static bool IsNull(JSContext* cx, unsigned argc, jsval* vp); 1.229 + static bool Increment(JSContext* cx, unsigned argc, jsval* vp); 1.230 + static bool Decrement(JSContext* cx, unsigned argc, jsval* vp); 1.231 + // The following is not an instance function, since we don't want to expose arbitrary 1.232 + // pointer arithmetic at this moment. 1.233 + static bool OffsetBy(JSContext* cx, const CallArgs& args, int offset); 1.234 +} 1.235 + 1.236 +namespace ArrayType { 1.237 + bool IsArrayType(HandleValue v); 1.238 + bool IsArrayOrArrayType(HandleValue v); 1.239 + 1.240 + static bool Create(JSContext* cx, unsigned argc, jsval* vp); 1.241 + static bool ConstructData(JSContext* cx, HandleObject obj, const CallArgs& args); 1.242 + 1.243 + bool ElementTypeGetter(JSContext* cx, JS::CallArgs args); 1.244 + bool LengthGetter(JSContext* cx, JS::CallArgs args); 1.245 + 1.246 + static bool Getter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp); 1.247 + static bool Setter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, MutableHandleValue vp); 1.248 + static bool AddressOfElement(JSContext* cx, unsigned argc, jsval* vp); 1.249 +} 1.250 + 1.251 +namespace StructType { 1.252 + bool IsStruct(HandleValue v); 1.253 + 1.254 + static bool Create(JSContext* cx, unsigned argc, jsval* vp); 1.255 + static bool ConstructData(JSContext* cx, HandleObject obj, const CallArgs& args); 1.256 + 1.257 + bool FieldsArrayGetter(JSContext* cx, JS::CallArgs args); 1.258 + 1.259 + static bool FieldGetter(JSContext* cx, HandleObject obj, HandleId idval, 1.260 + MutableHandleValue vp); 1.261 + static bool FieldSetter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, 1.262 + MutableHandleValue vp); 1.263 + static bool AddressOfField(JSContext* cx, unsigned argc, jsval* vp); 1.264 + static bool Define(JSContext* cx, unsigned argc, jsval* vp); 1.265 +} 1.266 + 1.267 +namespace FunctionType { 1.268 + static bool Create(JSContext* cx, unsigned argc, jsval* vp); 1.269 + static bool ConstructData(JSContext* cx, HandleObject typeObj, 1.270 + HandleObject dataObj, HandleObject fnObj, HandleObject thisObj, jsval errVal); 1.271 + 1.272 + static bool Call(JSContext* cx, unsigned argc, jsval* vp); 1.273 + 1.274 + bool IsFunctionType(HandleValue v); 1.275 + 1.276 + bool ArgTypesGetter(JSContext* cx, JS::CallArgs args); 1.277 + bool ReturnTypeGetter(JSContext* cx, JS::CallArgs args); 1.278 + bool ABIGetter(JSContext* cx, JS::CallArgs args); 1.279 + bool IsVariadicGetter(JSContext* cx, JS::CallArgs args); 1.280 +} 1.281 + 1.282 +namespace CClosure { 1.283 + static void Trace(JSTracer* trc, JSObject* obj); 1.284 + static void Finalize(JSFreeOp *fop, JSObject* obj); 1.285 + 1.286 + // libffi callback 1.287 + static void ClosureStub(ffi_cif* cif, void* result, void** args, 1.288 + void* userData); 1.289 +} 1.290 + 1.291 +namespace CData { 1.292 + static void Finalize(JSFreeOp *fop, JSObject* obj); 1.293 + 1.294 + bool ValueGetter(JSContext* cx, JS::CallArgs args); 1.295 + bool ValueSetter(JSContext* cx, JS::CallArgs args); 1.296 + 1.297 + static bool Address(JSContext* cx, unsigned argc, jsval* vp); 1.298 + static bool ReadString(JSContext* cx, unsigned argc, jsval* vp); 1.299 + static bool ReadStringReplaceMalformed(JSContext* cx, unsigned argc, jsval* vp); 1.300 + static bool ToSource(JSContext* cx, unsigned argc, jsval* vp); 1.301 + static JSString *GetSourceString(JSContext *cx, HandleObject typeObj, 1.302 + void *data); 1.303 + 1.304 + bool ErrnoGetter(JSContext* cx, JS::CallArgs args); 1.305 + 1.306 +#if defined(XP_WIN) 1.307 + bool LastErrorGetter(JSContext* cx, JS::CallArgs args); 1.308 +#endif // defined(XP_WIN) 1.309 +} 1.310 + 1.311 +namespace CDataFinalizer { 1.312 + /* 1.313 + * Attach a C function as a finalizer to a JS object. 1.314 + * 1.315 + * This function is available from JS as |ctypes.withFinalizer|. 1.316 + * 1.317 + * JavaScript signature: 1.318 + * function(CData, CData): CDataFinalizer 1.319 + * value finalizer finalizable 1.320 + * 1.321 + * Where |finalizer| is a one-argument function taking a value 1.322 + * with the same type as |value|. 1.323 + */ 1.324 + static bool Construct(JSContext* cx, unsigned argc, jsval *vp); 1.325 + 1.326 + /* 1.327 + * Private data held by |CDataFinalizer|. 1.328 + * 1.329 + * See also |enum CDataFinalizerSlot| for the slots of 1.330 + * |CDataFinalizer|. 1.331 + * 1.332 + * Note: the private data may be nullptr, if |dispose|, |forget| or the 1.333 + * finalizer has already been called. 1.334 + */ 1.335 + struct Private { 1.336 + /* 1.337 + * The C data to pass to the code. 1.338 + * Finalization/|dispose|/|forget| release this memory. 1.339 + */ 1.340 + void *cargs; 1.341 + 1.342 + /* 1.343 + * The total size of the buffer pointed by |cargs| 1.344 + */ 1.345 + size_t cargs_size; 1.346 + 1.347 + /* 1.348 + * Low-level signature information. 1.349 + * Finalization/|dispose|/|forget| release this memory. 1.350 + */ 1.351 + ffi_cif CIF; 1.352 + 1.353 + /* 1.354 + * The C function to invoke during finalization. 1.355 + * Do not deallocate this. 1.356 + */ 1.357 + uintptr_t code; 1.358 + 1.359 + /* 1.360 + * A buffer for holding the return value. 1.361 + * Finalization/|dispose|/|forget| release this memory. 1.362 + */ 1.363 + void *rvalue; 1.364 + }; 1.365 + 1.366 + /* 1.367 + * Methods of instances of |CDataFinalizer| 1.368 + */ 1.369 + namespace Methods { 1.370 + static bool Dispose(JSContext* cx, unsigned argc, jsval *vp); 1.371 + static bool Forget(JSContext* cx, unsigned argc, jsval *vp); 1.372 + static bool ToSource(JSContext* cx, unsigned argc, jsval *vp); 1.373 + static bool ToString(JSContext* cx, unsigned argc, jsval *vp); 1.374 + } 1.375 + 1.376 + /* 1.377 + * Utility functions 1.378 + * 1.379 + * @return true if |obj| is a CDataFinalizer, false otherwise. 1.380 + */ 1.381 + static bool IsCDataFinalizer(JSObject *obj); 1.382 + 1.383 + /* 1.384 + * Clean up the finalization information of a CDataFinalizer. 1.385 + * 1.386 + * Used by |Finalize|, |Dispose| and |Forget|. 1.387 + * 1.388 + * @param p The private information of the CDataFinalizer. If nullptr, 1.389 + * this function does nothing. 1.390 + * @param obj Either nullptr, if the object should not be cleaned up (i.e. 1.391 + * during finalization) or a CDataFinalizer JSObject. Always use nullptr 1.392 + * if you are calling from a finalizer. 1.393 + */ 1.394 + static void Cleanup(Private *p, JSObject *obj); 1.395 + 1.396 + /* 1.397 + * Perform the actual call to the finalizer code. 1.398 + */ 1.399 + static void CallFinalizer(CDataFinalizer::Private *p, 1.400 + int* errnoStatus, 1.401 + int32_t* lastErrorStatus); 1.402 + 1.403 + /* 1.404 + * Return the CType of a CDataFinalizer object, or nullptr if the object 1.405 + * has been cleaned-up already. 1.406 + */ 1.407 + static JSObject *GetCType(JSContext *cx, JSObject *obj); 1.408 + 1.409 + /* 1.410 + * Perform finalization of a |CDataFinalizer| 1.411 + */ 1.412 + static void Finalize(JSFreeOp *fop, JSObject *obj); 1.413 + 1.414 + /* 1.415 + * Return the jsval contained by this finalizer. 1.416 + * 1.417 + * Note that the jsval is actually not recorded, but converted back from C. 1.418 + */ 1.419 + static bool GetValue(JSContext *cx, JSObject *obj, jsval *result); 1.420 + 1.421 + static JSObject* GetCData(JSContext *cx, JSObject *obj); 1.422 + } 1.423 + 1.424 + 1.425 +// Int64Base provides functions common to Int64 and UInt64. 1.426 +namespace Int64Base { 1.427 + JSObject* Construct(JSContext* cx, HandleObject proto, uint64_t data, 1.428 + bool isUnsigned); 1.429 + 1.430 + uint64_t GetInt(JSObject* obj); 1.431 + 1.432 + bool ToString(JSContext* cx, JSObject* obj, const CallArgs& args, 1.433 + bool isUnsigned); 1.434 + 1.435 + bool ToSource(JSContext* cx, JSObject* obj, const CallArgs& args, 1.436 + bool isUnsigned); 1.437 + 1.438 + static void Finalize(JSFreeOp *fop, JSObject* obj); 1.439 +} 1.440 + 1.441 +namespace Int64 { 1.442 + static bool Construct(JSContext* cx, unsigned argc, jsval* vp); 1.443 + 1.444 + static bool ToString(JSContext* cx, unsigned argc, jsval* vp); 1.445 + static bool ToSource(JSContext* cx, unsigned argc, jsval* vp); 1.446 + 1.447 + static bool Compare(JSContext* cx, unsigned argc, jsval* vp); 1.448 + static bool Lo(JSContext* cx, unsigned argc, jsval* vp); 1.449 + static bool Hi(JSContext* cx, unsigned argc, jsval* vp); 1.450 + static bool Join(JSContext* cx, unsigned argc, jsval* vp); 1.451 +} 1.452 + 1.453 +namespace UInt64 { 1.454 + static bool Construct(JSContext* cx, unsigned argc, jsval* vp); 1.455 + 1.456 + static bool ToString(JSContext* cx, unsigned argc, jsval* vp); 1.457 + static bool ToSource(JSContext* cx, unsigned argc, jsval* vp); 1.458 + 1.459 + static bool Compare(JSContext* cx, unsigned argc, jsval* vp); 1.460 + static bool Lo(JSContext* cx, unsigned argc, jsval* vp); 1.461 + static bool Hi(JSContext* cx, unsigned argc, jsval* vp); 1.462 + static bool Join(JSContext* cx, unsigned argc, jsval* vp); 1.463 +} 1.464 + 1.465 +/******************************************************************************* 1.466 +** JSClass definitions and initialization functions 1.467 +*******************************************************************************/ 1.468 + 1.469 +// Class representing the 'ctypes' object itself. This exists to contain the 1.470 +// JSCTypesCallbacks set of function pointers. 1.471 +static const JSClass sCTypesGlobalClass = { 1.472 + "ctypes", 1.473 + JSCLASS_HAS_RESERVED_SLOTS(CTYPESGLOBAL_SLOTS), 1.474 + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, 1.475 + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub 1.476 +}; 1.477 + 1.478 +static const JSClass sCABIClass = { 1.479 + "CABI", 1.480 + JSCLASS_HAS_RESERVED_SLOTS(CABI_SLOTS), 1.481 + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, 1.482 + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub 1.483 +}; 1.484 + 1.485 +// Class representing ctypes.{C,Pointer,Array,Struct,Function}Type.prototype. 1.486 +// This exists to give said prototypes a class of "CType", and to provide 1.487 +// reserved slots for stashing various other prototype objects. 1.488 +static const JSClass sCTypeProtoClass = { 1.489 + "CType", 1.490 + JSCLASS_HAS_RESERVED_SLOTS(CTYPEPROTO_SLOTS), 1.491 + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, 1.492 + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, nullptr, 1.493 + ConstructAbstract, nullptr, ConstructAbstract 1.494 +}; 1.495 + 1.496 +// Class representing ctypes.CData.prototype and the 'prototype' properties 1.497 +// of CTypes. This exists to give said prototypes a class of "CData". 1.498 +static const JSClass sCDataProtoClass = { 1.499 + "CData", 1.500 + 0, 1.501 + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, 1.502 + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub 1.503 +}; 1.504 + 1.505 +static const JSClass sCTypeClass = { 1.506 + "CType", 1.507 + JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS), 1.508 + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, 1.509 + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CType::Finalize, 1.510 + CType::ConstructData, CType::HasInstance, CType::ConstructData, 1.511 + CType::Trace 1.512 +}; 1.513 + 1.514 +static const JSClass sCDataClass = { 1.515 + "CData", 1.516 + JSCLASS_HAS_RESERVED_SLOTS(CDATA_SLOTS), 1.517 + JS_PropertyStub, JS_DeletePropertyStub, ArrayType::Getter, ArrayType::Setter, 1.518 + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CData::Finalize, 1.519 + FunctionType::Call, nullptr, FunctionType::Call 1.520 +}; 1.521 + 1.522 +static const JSClass sCClosureClass = { 1.523 + "CClosure", 1.524 + JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS), 1.525 + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, 1.526 + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CClosure::Finalize, 1.527 + nullptr, nullptr, nullptr, CClosure::Trace 1.528 +}; 1.529 + 1.530 +/* 1.531 + * Class representing the prototype of CDataFinalizer. 1.532 + */ 1.533 +static const JSClass sCDataFinalizerProtoClass = { 1.534 + "CDataFinalizer", 1.535 + 0, 1.536 + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, 1.537 + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub 1.538 +}; 1.539 + 1.540 +/* 1.541 + * Class representing instances of CDataFinalizer. 1.542 + * 1.543 + * Instances of CDataFinalizer have both private data (with type 1.544 + * |CDataFinalizer::Private|) and slots (see |CDataFinalizerSlots|). 1.545 + */ 1.546 +static const JSClass sCDataFinalizerClass = { 1.547 + "CDataFinalizer", 1.548 + JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(CDATAFINALIZER_SLOTS), 1.549 + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, 1.550 + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CDataFinalizer::Finalize, 1.551 +}; 1.552 + 1.553 + 1.554 +#define CTYPESFN_FLAGS \ 1.555 + (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT) 1.556 + 1.557 +#define CTYPESCTOR_FLAGS \ 1.558 + (CTYPESFN_FLAGS | JSFUN_CONSTRUCTOR) 1.559 + 1.560 +#define CTYPESACC_FLAGS \ 1.561 + (JSPROP_ENUMERATE | JSPROP_PERMANENT) 1.562 + 1.563 +#define CABIFN_FLAGS \ 1.564 + (JSPROP_READONLY | JSPROP_PERMANENT) 1.565 + 1.566 +#define CDATAFN_FLAGS \ 1.567 + (JSPROP_READONLY | JSPROP_PERMANENT) 1.568 + 1.569 +#define CDATAFINALIZERFN_FLAGS \ 1.570 + (JSPROP_READONLY | JSPROP_PERMANENT) 1.571 + 1.572 +static const JSPropertySpec sCTypeProps[] = { 1.573 + JS_PSG("name", 1.574 + (Property<CType::IsCType, CType::NameGetter>::Fun), 1.575 + CTYPESACC_FLAGS), 1.576 + JS_PSG("size", 1.577 + (Property<CType::IsCType, CType::SizeGetter>::Fun), 1.578 + CTYPESACC_FLAGS), 1.579 + JS_PSG("ptr", 1.580 + (Property<CType::IsCType, CType::PtrGetter>::Fun), 1.581 + CTYPESACC_FLAGS), 1.582 + JS_PSG("prototype", 1.583 + (Property<CType::IsCTypeOrProto, CType::PrototypeGetter>::Fun), 1.584 + CTYPESACC_FLAGS), 1.585 + JS_PS_END 1.586 +}; 1.587 + 1.588 +static const JSFunctionSpec sCTypeFunctions[] = { 1.589 + JS_FN("array", CType::CreateArray, 0, CTYPESFN_FLAGS), 1.590 + JS_FN("toString", CType::ToString, 0, CTYPESFN_FLAGS), 1.591 + JS_FN("toSource", CType::ToSource, 0, CTYPESFN_FLAGS), 1.592 + JS_FS_END 1.593 +}; 1.594 + 1.595 +static const JSFunctionSpec sCABIFunctions[] = { 1.596 + JS_FN("toSource", ABI::ToSource, 0, CABIFN_FLAGS), 1.597 + JS_FN("toString", ABI::ToSource, 0, CABIFN_FLAGS), 1.598 + JS_FS_END 1.599 +}; 1.600 + 1.601 +static const JSPropertySpec sCDataProps[] = { 1.602 + JS_PSGS("value", 1.603 + (Property<CData::IsCData, CData::ValueGetter>::Fun), 1.604 + (Property<CData::IsCData, CData::ValueSetter>::Fun), 1.605 + JSPROP_PERMANENT), 1.606 + JS_PS_END 1.607 +}; 1.608 + 1.609 +static const JSFunctionSpec sCDataFunctions[] = { 1.610 + JS_FN("address", CData::Address, 0, CDATAFN_FLAGS), 1.611 + JS_FN("readString", CData::ReadString, 0, CDATAFN_FLAGS), 1.612 + JS_FN("readStringReplaceMalformed", CData::ReadStringReplaceMalformed, 0, CDATAFN_FLAGS), 1.613 + JS_FN("toSource", CData::ToSource, 0, CDATAFN_FLAGS), 1.614 + JS_FN("toString", CData::ToSource, 0, CDATAFN_FLAGS), 1.615 + JS_FS_END 1.616 +}; 1.617 + 1.618 +static const JSFunctionSpec sCDataFinalizerFunctions[] = { 1.619 + JS_FN("dispose", CDataFinalizer::Methods::Dispose, 0, CDATAFINALIZERFN_FLAGS), 1.620 + JS_FN("forget", CDataFinalizer::Methods::Forget, 0, CDATAFINALIZERFN_FLAGS), 1.621 + JS_FN("readString",CData::ReadString, 0, CDATAFINALIZERFN_FLAGS), 1.622 + JS_FN("toString", CDataFinalizer::Methods::ToString, 0, CDATAFINALIZERFN_FLAGS), 1.623 + JS_FN("toSource", CDataFinalizer::Methods::ToSource, 0, CDATAFINALIZERFN_FLAGS), 1.624 + JS_FS_END 1.625 +}; 1.626 + 1.627 +static const JSFunctionSpec sPointerFunction = 1.628 + JS_FN("PointerType", PointerType::Create, 1, CTYPESCTOR_FLAGS); 1.629 + 1.630 +static const JSPropertySpec sPointerProps[] = { 1.631 + JS_PSG("targetType", 1.632 + (Property<PointerType::IsPointerType, PointerType::TargetTypeGetter>::Fun), 1.633 + CTYPESACC_FLAGS), 1.634 + JS_PS_END 1.635 +}; 1.636 + 1.637 +static const JSFunctionSpec sPointerInstanceFunctions[] = { 1.638 + JS_FN("isNull", PointerType::IsNull, 0, CTYPESFN_FLAGS), 1.639 + JS_FN("increment", PointerType::Increment, 0, CTYPESFN_FLAGS), 1.640 + JS_FN("decrement", PointerType::Decrement, 0, CTYPESFN_FLAGS), 1.641 + JS_FS_END 1.642 +}; 1.643 + 1.644 +static const JSPropertySpec sPointerInstanceProps[] = { 1.645 + JS_PSGS("contents", 1.646 + (Property<PointerType::IsPointer, PointerType::ContentsGetter>::Fun), 1.647 + (Property<PointerType::IsPointer, PointerType::ContentsSetter>::Fun), 1.648 + JSPROP_PERMANENT), 1.649 + JS_PS_END 1.650 +}; 1.651 + 1.652 +static const JSFunctionSpec sArrayFunction = 1.653 + JS_FN("ArrayType", ArrayType::Create, 1, CTYPESCTOR_FLAGS); 1.654 + 1.655 +static const JSPropertySpec sArrayProps[] = { 1.656 + JS_PSG("elementType", 1.657 + (Property<ArrayType::IsArrayType, ArrayType::ElementTypeGetter>::Fun), 1.658 + CTYPESACC_FLAGS), 1.659 + JS_PSG("length", 1.660 + (Property<ArrayType::IsArrayOrArrayType, ArrayType::LengthGetter>::Fun), 1.661 + CTYPESACC_FLAGS), 1.662 + JS_PS_END 1.663 +}; 1.664 + 1.665 +static const JSFunctionSpec sArrayInstanceFunctions[] = { 1.666 + JS_FN("addressOfElement", ArrayType::AddressOfElement, 1, CDATAFN_FLAGS), 1.667 + JS_FS_END 1.668 +}; 1.669 + 1.670 +static const JSPropertySpec sArrayInstanceProps[] = { 1.671 + JS_PSG("length", 1.672 + (Property<ArrayType::IsArrayOrArrayType, ArrayType::LengthGetter>::Fun), 1.673 + JSPROP_PERMANENT), 1.674 + JS_PS_END 1.675 +}; 1.676 + 1.677 +static const JSFunctionSpec sStructFunction = 1.678 + JS_FN("StructType", StructType::Create, 2, CTYPESCTOR_FLAGS); 1.679 + 1.680 +static const JSPropertySpec sStructProps[] = { 1.681 + JS_PSG("fields", 1.682 + (Property<StructType::IsStruct, StructType::FieldsArrayGetter>::Fun), 1.683 + CTYPESACC_FLAGS), 1.684 + JS_PS_END 1.685 +}; 1.686 + 1.687 +static const JSFunctionSpec sStructFunctions[] = { 1.688 + JS_FN("define", StructType::Define, 1, CDATAFN_FLAGS), 1.689 + JS_FS_END 1.690 +}; 1.691 + 1.692 +static const JSFunctionSpec sStructInstanceFunctions[] = { 1.693 + JS_FN("addressOfField", StructType::AddressOfField, 1, CDATAFN_FLAGS), 1.694 + JS_FS_END 1.695 +}; 1.696 + 1.697 +static const JSFunctionSpec sFunctionFunction = 1.698 + JS_FN("FunctionType", FunctionType::Create, 2, CTYPESCTOR_FLAGS); 1.699 + 1.700 +static const JSPropertySpec sFunctionProps[] = { 1.701 + JS_PSG("argTypes", 1.702 + (Property<FunctionType::IsFunctionType, FunctionType::ArgTypesGetter>::Fun), 1.703 + CTYPESACC_FLAGS), 1.704 + JS_PSG("returnType", 1.705 + (Property<FunctionType::IsFunctionType, FunctionType::ReturnTypeGetter>::Fun), 1.706 + CTYPESACC_FLAGS), 1.707 + JS_PSG("abi", 1.708 + (Property<FunctionType::IsFunctionType, FunctionType::ABIGetter>::Fun), 1.709 + CTYPESACC_FLAGS), 1.710 + JS_PSG("isVariadic", 1.711 + (Property<FunctionType::IsFunctionType, FunctionType::IsVariadicGetter>::Fun), 1.712 + CTYPESACC_FLAGS), 1.713 + JS_PS_END 1.714 +}; 1.715 + 1.716 +static const JSFunctionSpec sFunctionInstanceFunctions[] = { 1.717 + JS_FN("call", js_fun_call, 1, CDATAFN_FLAGS), 1.718 + JS_FN("apply", js_fun_apply, 2, CDATAFN_FLAGS), 1.719 + JS_FS_END 1.720 +}; 1.721 + 1.722 +static const JSClass sInt64ProtoClass = { 1.723 + "Int64", 1.724 + 0, 1.725 + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, 1.726 + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub 1.727 +}; 1.728 + 1.729 +static const JSClass sUInt64ProtoClass = { 1.730 + "UInt64", 1.731 + 0, 1.732 + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, 1.733 + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub 1.734 +}; 1.735 + 1.736 +static const JSClass sInt64Class = { 1.737 + "Int64", 1.738 + JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS), 1.739 + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, 1.740 + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Int64Base::Finalize 1.741 +}; 1.742 + 1.743 +static const JSClass sUInt64Class = { 1.744 + "UInt64", 1.745 + JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS), 1.746 + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, 1.747 + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Int64Base::Finalize 1.748 +}; 1.749 + 1.750 +static const JSFunctionSpec sInt64StaticFunctions[] = { 1.751 + JS_FN("compare", Int64::Compare, 2, CTYPESFN_FLAGS), 1.752 + JS_FN("lo", Int64::Lo, 1, CTYPESFN_FLAGS), 1.753 + JS_FN("hi", Int64::Hi, 1, CTYPESFN_FLAGS), 1.754 + JS_FN("join", Int64::Join, 2, CTYPESFN_FLAGS), 1.755 + JS_FS_END 1.756 +}; 1.757 + 1.758 +static const JSFunctionSpec sUInt64StaticFunctions[] = { 1.759 + JS_FN("compare", UInt64::Compare, 2, CTYPESFN_FLAGS), 1.760 + JS_FN("lo", UInt64::Lo, 1, CTYPESFN_FLAGS), 1.761 + JS_FN("hi", UInt64::Hi, 1, CTYPESFN_FLAGS), 1.762 + JS_FN("join", UInt64::Join, 2, CTYPESFN_FLAGS), 1.763 + JS_FS_END 1.764 +}; 1.765 + 1.766 +static const JSFunctionSpec sInt64Functions[] = { 1.767 + JS_FN("toString", Int64::ToString, 0, CTYPESFN_FLAGS), 1.768 + JS_FN("toSource", Int64::ToSource, 0, CTYPESFN_FLAGS), 1.769 + JS_FS_END 1.770 +}; 1.771 + 1.772 +static const JSFunctionSpec sUInt64Functions[] = { 1.773 + JS_FN("toString", UInt64::ToString, 0, CTYPESFN_FLAGS), 1.774 + JS_FN("toSource", UInt64::ToSource, 0, CTYPESFN_FLAGS), 1.775 + JS_FS_END 1.776 +}; 1.777 + 1.778 +static const JSPropertySpec sModuleProps[] = { 1.779 + JS_PSG("errno", 1.780 + (Property<IsCTypesGlobal, CData::ErrnoGetter>::Fun), 1.781 + JSPROP_PERMANENT), 1.782 +#if defined(XP_WIN) 1.783 + JS_PSG("winLastError", 1.784 + (Property<IsCTypesGlobal, CData::LastErrorGetter>::Fun), 1.785 + JSPROP_PERMANENT), 1.786 +#endif // defined(XP_WIN) 1.787 + JS_PS_END 1.788 +}; 1.789 + 1.790 +static const JSFunctionSpec sModuleFunctions[] = { 1.791 + JS_FN("CDataFinalizer", CDataFinalizer::Construct, 2, CTYPESFN_FLAGS), 1.792 + JS_FN("open", Library::Open, 1, CTYPESFN_FLAGS), 1.793 + JS_FN("cast", CData::Cast, 2, CTYPESFN_FLAGS), 1.794 + JS_FN("getRuntime", CData::GetRuntime, 1, CTYPESFN_FLAGS), 1.795 + JS_FN("libraryName", Library::Name, 1, CTYPESFN_FLAGS), 1.796 + JS_FS_END 1.797 +}; 1.798 + 1.799 +static MOZ_ALWAYS_INLINE JSString* 1.800 +NewUCString(JSContext* cx, const AutoString& from) 1.801 +{ 1.802 + return JS_NewUCStringCopyN(cx, from.begin(), from.length()); 1.803 +} 1.804 + 1.805 +/* 1.806 + * Return a size rounded up to a multiple of a power of two. 1.807 + * 1.808 + * Note: |align| must be a power of 2. 1.809 + */ 1.810 +static MOZ_ALWAYS_INLINE size_t 1.811 +Align(size_t val, size_t align) 1.812 +{ 1.813 + // Ensure that align is a power of two. 1.814 + MOZ_ASSERT(align != 0 && (align & (align - 1)) == 0); 1.815 + return ((val - 1) | (align - 1)) + 1; 1.816 +} 1.817 + 1.818 +static ABICode 1.819 +GetABICode(JSObject* obj) 1.820 +{ 1.821 + // make sure we have an object representing a CABI class, 1.822 + // and extract the enumerated class type from the reserved slot. 1.823 + if (JS_GetClass(obj) != &sCABIClass) 1.824 + return INVALID_ABI; 1.825 + 1.826 + jsval result = JS_GetReservedSlot(obj, SLOT_ABICODE); 1.827 + return ABICode(JSVAL_TO_INT(result)); 1.828 +} 1.829 + 1.830 +static const JSErrorFormatString ErrorFormatString[CTYPESERR_LIMIT] = { 1.831 +#define MSG_DEF(name, number, count, exception, format) \ 1.832 + { format, count, exception } , 1.833 +#include "ctypes/ctypes.msg" 1.834 +#undef MSG_DEF 1.835 +}; 1.836 + 1.837 +static const JSErrorFormatString* 1.838 +GetErrorMessage(void* userRef, const char* locale, const unsigned errorNumber) 1.839 +{ 1.840 + if (0 < errorNumber && errorNumber < CTYPESERR_LIMIT) 1.841 + return &ErrorFormatString[errorNumber]; 1.842 + return nullptr; 1.843 +} 1.844 + 1.845 +static bool 1.846 +TypeError(JSContext* cx, const char* expected, HandleValue actual) 1.847 +{ 1.848 + JSString* str = JS_ValueToSource(cx, actual); 1.849 + JSAutoByteString bytes; 1.850 + 1.851 + const char* src; 1.852 + if (str) { 1.853 + src = bytes.encodeLatin1(cx, str); 1.854 + if (!src) 1.855 + return false; 1.856 + } else { 1.857 + JS_ClearPendingException(cx); 1.858 + src = "<<error converting value to string>>"; 1.859 + } 1.860 + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, 1.861 + CTYPESMSG_TYPE_ERROR, expected, src); 1.862 + return false; 1.863 +} 1.864 + 1.865 +static JSObject* 1.866 +InitCTypeClass(JSContext* cx, HandleObject parent) 1.867 +{ 1.868 + JSFunction *fun = JS_DefineFunction(cx, parent, "CType", ConstructAbstract, 0, 1.869 + CTYPESCTOR_FLAGS); 1.870 + if (!fun) 1.871 + return nullptr; 1.872 + 1.873 + RootedObject ctor(cx, JS_GetFunctionObject(fun)); 1.874 + RootedObject fnproto(cx); 1.875 + if (!JS_GetPrototype(cx, ctor, &fnproto)) 1.876 + return nullptr; 1.877 + JS_ASSERT(ctor); 1.878 + JS_ASSERT(fnproto); 1.879 + 1.880 + // Set up ctypes.CType.prototype. 1.881 + RootedObject prototype(cx, JS_NewObject(cx, &sCTypeProtoClass, fnproto, parent)); 1.882 + if (!prototype) 1.883 + return nullptr; 1.884 + 1.885 + if (!JS_DefineProperty(cx, ctor, "prototype", prototype, 1.886 + JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) 1.887 + return nullptr; 1.888 + 1.889 + if (!JS_DefineProperty(cx, prototype, "constructor", ctor, 1.890 + JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) 1.891 + return nullptr; 1.892 + 1.893 + // Define properties and functions common to all CTypes. 1.894 + if (!JS_DefineProperties(cx, prototype, sCTypeProps) || 1.895 + !JS_DefineFunctions(cx, prototype, sCTypeFunctions)) 1.896 + return nullptr; 1.897 + 1.898 + if (!JS_FreezeObject(cx, ctor) || !JS_FreezeObject(cx, prototype)) 1.899 + return nullptr; 1.900 + 1.901 + return prototype; 1.902 +} 1.903 + 1.904 +static JSObject* 1.905 +InitABIClass(JSContext* cx, JSObject* parent) 1.906 +{ 1.907 + RootedObject obj(cx, JS_NewObject(cx, nullptr, NullPtr(), NullPtr())); 1.908 + 1.909 + if (!obj) 1.910 + return nullptr; 1.911 + 1.912 + if (!JS_DefineFunctions(cx, obj, sCABIFunctions)) 1.913 + return nullptr; 1.914 + 1.915 + return obj; 1.916 +} 1.917 + 1.918 + 1.919 +static JSObject* 1.920 +InitCDataClass(JSContext* cx, HandleObject parent, HandleObject CTypeProto) 1.921 +{ 1.922 + JSFunction* fun = JS_DefineFunction(cx, parent, "CData", ConstructAbstract, 0, 1.923 + CTYPESCTOR_FLAGS); 1.924 + if (!fun) 1.925 + return nullptr; 1.926 + 1.927 + RootedObject ctor(cx, JS_GetFunctionObject(fun)); 1.928 + JS_ASSERT(ctor); 1.929 + 1.930 + // Set up ctypes.CData.__proto__ === ctypes.CType.prototype. 1.931 + // (Note that 'ctypes.CData instanceof Function' is still true, thanks to the 1.932 + // prototype chain.) 1.933 + if (!JS_SetPrototype(cx, ctor, CTypeProto)) 1.934 + return nullptr; 1.935 + 1.936 + // Set up ctypes.CData.prototype. 1.937 + RootedObject prototype(cx, JS_NewObject(cx, &sCDataProtoClass, NullPtr(), parent)); 1.938 + if (!prototype) 1.939 + return nullptr; 1.940 + 1.941 + if (!JS_DefineProperty(cx, ctor, "prototype", prototype, 1.942 + JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) 1.943 + return nullptr; 1.944 + 1.945 + if (!JS_DefineProperty(cx, prototype, "constructor", ctor, 1.946 + JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) 1.947 + return nullptr; 1.948 + 1.949 + // Define properties and functions common to all CDatas. 1.950 + if (!JS_DefineProperties(cx, prototype, sCDataProps) || 1.951 + !JS_DefineFunctions(cx, prototype, sCDataFunctions)) 1.952 + return nullptr; 1.953 + 1.954 + if (//!JS_FreezeObject(cx, prototype) || // XXX fixme - see bug 541212! 1.955 + !JS_FreezeObject(cx, ctor)) 1.956 + return nullptr; 1.957 + 1.958 + return prototype; 1.959 +} 1.960 + 1.961 +static bool 1.962 +DefineABIConstant(JSContext* cx, 1.963 + HandleObject parent, 1.964 + const char* name, 1.965 + ABICode code, 1.966 + HandleObject prototype) 1.967 +{ 1.968 + RootedObject obj(cx, JS_DefineObject(cx, parent, name, &sCABIClass, prototype, 1.969 + JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)); 1.970 + if (!obj) 1.971 + return false; 1.972 + JS_SetReservedSlot(obj, SLOT_ABICODE, INT_TO_JSVAL(code)); 1.973 + return JS_FreezeObject(cx, obj); 1.974 +} 1.975 + 1.976 +// Set up a single type constructor for 1.977 +// ctypes.{Pointer,Array,Struct,Function}Type. 1.978 +static bool 1.979 +InitTypeConstructor(JSContext* cx, 1.980 + HandleObject parent, 1.981 + HandleObject CTypeProto, 1.982 + HandleObject CDataProto, 1.983 + const JSFunctionSpec spec, 1.984 + const JSFunctionSpec* fns, 1.985 + const JSPropertySpec* props, 1.986 + const JSFunctionSpec* instanceFns, 1.987 + const JSPropertySpec* instanceProps, 1.988 + MutableHandleObject typeProto, 1.989 + MutableHandleObject dataProto) 1.990 +{ 1.991 + JSFunction* fun = js::DefineFunctionWithReserved(cx, parent, spec.name, spec.call.op, 1.992 + spec.nargs, spec.flags); 1.993 + if (!fun) 1.994 + return false; 1.995 + 1.996 + RootedObject obj(cx, JS_GetFunctionObject(fun)); 1.997 + if (!obj) 1.998 + return false; 1.999 + 1.1000 + // Set up the .prototype and .prototype.constructor properties. 1.1001 + typeProto.set(JS_NewObject(cx, &sCTypeProtoClass, CTypeProto, parent)); 1.1002 + if (!typeProto) 1.1003 + return false; 1.1004 + 1.1005 + // Define property before proceeding, for GC safety. 1.1006 + if (!JS_DefineProperty(cx, obj, "prototype", typeProto, 1.1007 + JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) 1.1008 + return false; 1.1009 + 1.1010 + if (fns && !JS_DefineFunctions(cx, typeProto, fns)) 1.1011 + return false; 1.1012 + 1.1013 + if (!JS_DefineProperties(cx, typeProto, props)) 1.1014 + return false; 1.1015 + 1.1016 + if (!JS_DefineProperty(cx, typeProto, "constructor", obj, 1.1017 + JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) 1.1018 + return false; 1.1019 + 1.1020 + // Stash ctypes.{Pointer,Array,Struct}Type.prototype on a reserved slot of 1.1021 + // the type constructor, for faster lookup. 1.1022 + js::SetFunctionNativeReserved(obj, SLOT_FN_CTORPROTO, OBJECT_TO_JSVAL(typeProto)); 1.1023 + 1.1024 + // Create an object to serve as the common ancestor for all CData objects 1.1025 + // created from the given type constructor. This has ctypes.CData.prototype 1.1026 + // as its prototype, such that it inherits the properties and functions 1.1027 + // common to all CDatas. 1.1028 + dataProto.set(JS_NewObject(cx, &sCDataProtoClass, CDataProto, parent)); 1.1029 + if (!dataProto) 1.1030 + return false; 1.1031 + 1.1032 + // Define functions and properties on the 'dataProto' object that are common 1.1033 + // to all CData objects created from this type constructor. (These will 1.1034 + // become functions and properties on CData objects created from this type.) 1.1035 + if (instanceFns && !JS_DefineFunctions(cx, dataProto, instanceFns)) 1.1036 + return false; 1.1037 + 1.1038 + if (instanceProps && !JS_DefineProperties(cx, dataProto, instanceProps)) 1.1039 + return false; 1.1040 + 1.1041 + // Link the type prototype to the data prototype. 1.1042 + JS_SetReservedSlot(typeProto, SLOT_OURDATAPROTO, OBJECT_TO_JSVAL(dataProto)); 1.1043 + 1.1044 + if (!JS_FreezeObject(cx, obj) || 1.1045 + //!JS_FreezeObject(cx, dataProto) || // XXX fixme - see bug 541212! 1.1046 + !JS_FreezeObject(cx, typeProto)) 1.1047 + return false; 1.1048 + 1.1049 + return true; 1.1050 +} 1.1051 + 1.1052 +static JSObject* 1.1053 +InitInt64Class(JSContext* cx, 1.1054 + HandleObject parent, 1.1055 + const JSClass* clasp, 1.1056 + JSNative construct, 1.1057 + const JSFunctionSpec* fs, 1.1058 + const JSFunctionSpec* static_fs) 1.1059 +{ 1.1060 + // Init type class and constructor 1.1061 + RootedObject prototype(cx, JS_InitClass(cx, parent, js::NullPtr(), clasp, construct, 1.1062 + 0, nullptr, fs, nullptr, static_fs)); 1.1063 + if (!prototype) 1.1064 + return nullptr; 1.1065 + 1.1066 + RootedObject ctor(cx, JS_GetConstructor(cx, prototype)); 1.1067 + if (!ctor) 1.1068 + return nullptr; 1.1069 + if (!JS_FreezeObject(cx, ctor)) 1.1070 + return nullptr; 1.1071 + 1.1072 + // Redefine the 'join' function as an extended native and stash 1.1073 + // ctypes.{Int64,UInt64}.prototype in a reserved slot of the new function. 1.1074 + JS_ASSERT(clasp == &sInt64ProtoClass || clasp == &sUInt64ProtoClass); 1.1075 + JSNative native = (clasp == &sInt64ProtoClass) ? Int64::Join : UInt64::Join; 1.1076 + JSFunction* fun = js::DefineFunctionWithReserved(cx, ctor, "join", native, 1.1077 + 2, CTYPESFN_FLAGS); 1.1078 + if (!fun) 1.1079 + return nullptr; 1.1080 + 1.1081 + js::SetFunctionNativeReserved(fun, SLOT_FN_INT64PROTO, 1.1082 + OBJECT_TO_JSVAL(prototype)); 1.1083 + 1.1084 + if (!JS_FreezeObject(cx, prototype)) 1.1085 + return nullptr; 1.1086 + 1.1087 + return prototype; 1.1088 +} 1.1089 + 1.1090 +static void 1.1091 +AttachProtos(JSObject* proto, const AutoObjectVector& protos) 1.1092 +{ 1.1093 + // For a given 'proto' of [[Class]] "CTypeProto", attach each of the 'protos' 1.1094 + // to the appropriate CTypeProtoSlot. (SLOT_CTYPES is the last slot 1.1095 + // of [[Class]] "CTypeProto" that we fill in this automated manner.) 1.1096 + for (uint32_t i = 0; i <= SLOT_CTYPES; ++i) 1.1097 + JS_SetReservedSlot(proto, i, OBJECT_TO_JSVAL(protos[i])); 1.1098 +} 1.1099 + 1.1100 +static bool 1.1101 +InitTypeClasses(JSContext* cx, HandleObject parent) 1.1102 +{ 1.1103 + // Initialize the ctypes.CType class. This acts as an abstract base class for 1.1104 + // the various types, and provides the common API functions. It has: 1.1105 + // * [[Class]] "Function" 1.1106 + // * __proto__ === Function.prototype 1.1107 + // * A constructor that throws a TypeError. (You can't construct an 1.1108 + // abstract type!) 1.1109 + // * 'prototype' property: 1.1110 + // * [[Class]] "CTypeProto" 1.1111 + // * __proto__ === Function.prototype 1.1112 + // * A constructor that throws a TypeError. (You can't construct an 1.1113 + // abstract type instance!) 1.1114 + // * 'constructor' property === ctypes.CType 1.1115 + // * Provides properties and functions common to all CTypes. 1.1116 + RootedObject CTypeProto(cx, InitCTypeClass(cx, parent)); 1.1117 + if (!CTypeProto) 1.1118 + return false; 1.1119 + 1.1120 + // Initialize the ctypes.CData class. This acts as an abstract base class for 1.1121 + // instances of the various types, and provides the common API functions. 1.1122 + // It has: 1.1123 + // * [[Class]] "Function" 1.1124 + // * __proto__ === Function.prototype 1.1125 + // * A constructor that throws a TypeError. (You can't construct an 1.1126 + // abstract type instance!) 1.1127 + // * 'prototype' property: 1.1128 + // * [[Class]] "CDataProto" 1.1129 + // * 'constructor' property === ctypes.CData 1.1130 + // * Provides properties and functions common to all CDatas. 1.1131 + RootedObject CDataProto(cx, InitCDataClass(cx, parent, CTypeProto)); 1.1132 + if (!CDataProto) 1.1133 + return false; 1.1134 + 1.1135 + // Link CTypeProto to CDataProto. 1.1136 + JS_SetReservedSlot(CTypeProto, SLOT_OURDATAPROTO, OBJECT_TO_JSVAL(CDataProto)); 1.1137 + 1.1138 + // Create and attach the special class constructors: ctypes.PointerType, 1.1139 + // ctypes.ArrayType, ctypes.StructType, and ctypes.FunctionType. 1.1140 + // Each of these constructors 'c' has, respectively: 1.1141 + // * [[Class]] "Function" 1.1142 + // * __proto__ === Function.prototype 1.1143 + // * A constructor that creates a user-defined type. 1.1144 + // * 'prototype' property: 1.1145 + // * [[Class]] "CTypeProto" 1.1146 + // * __proto__ === ctypes.CType.prototype 1.1147 + // * 'constructor' property === 'c' 1.1148 + // We also construct an object 'p' to serve, given a type object 't' 1.1149 + // constructed from one of these type constructors, as 1.1150 + // 't.prototype.__proto__'. This object has: 1.1151 + // * [[Class]] "CDataProto" 1.1152 + // * __proto__ === ctypes.CData.prototype 1.1153 + // * Properties and functions common to all CDatas. 1.1154 + // Therefore an instance 't' of ctypes.{Pointer,Array,Struct,Function}Type 1.1155 + // will have, resp.: 1.1156 + // * [[Class]] "CType" 1.1157 + // * __proto__ === ctypes.{Pointer,Array,Struct,Function}Type.prototype 1.1158 + // * A constructor which creates and returns a CData object, containing 1.1159 + // binary data of the given type. 1.1160 + // * 'prototype' property: 1.1161 + // * [[Class]] "CDataProto" 1.1162 + // * __proto__ === 'p', the prototype object from above 1.1163 + // * 'constructor' property === 't' 1.1164 + AutoObjectVector protos(cx); 1.1165 + protos.resize(CTYPEPROTO_SLOTS); 1.1166 + if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto, 1.1167 + sPointerFunction, nullptr, sPointerProps, 1.1168 + sPointerInstanceFunctions, sPointerInstanceProps, 1.1169 + protos.handleAt(SLOT_POINTERPROTO), protos.handleAt(SLOT_POINTERDATAPROTO))) 1.1170 + return false; 1.1171 + 1.1172 + if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto, 1.1173 + sArrayFunction, nullptr, sArrayProps, 1.1174 + sArrayInstanceFunctions, sArrayInstanceProps, 1.1175 + protos.handleAt(SLOT_ARRAYPROTO), protos.handleAt(SLOT_ARRAYDATAPROTO))) 1.1176 + return false; 1.1177 + 1.1178 + if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto, 1.1179 + sStructFunction, sStructFunctions, sStructProps, 1.1180 + sStructInstanceFunctions, nullptr, 1.1181 + protos.handleAt(SLOT_STRUCTPROTO), protos.handleAt(SLOT_STRUCTDATAPROTO))) 1.1182 + return false; 1.1183 + 1.1184 + if (!InitTypeConstructor(cx, parent, CTypeProto, protos.handleAt(SLOT_POINTERDATAPROTO), 1.1185 + sFunctionFunction, nullptr, sFunctionProps, sFunctionInstanceFunctions, nullptr, 1.1186 + protos.handleAt(SLOT_FUNCTIONPROTO), protos.handleAt(SLOT_FUNCTIONDATAPROTO))) 1.1187 + return false; 1.1188 + 1.1189 + protos[SLOT_CDATAPROTO] = CDataProto; 1.1190 + 1.1191 + // Create and attach the ctypes.{Int64,UInt64} constructors. 1.1192 + // Each of these has, respectively: 1.1193 + // * [[Class]] "Function" 1.1194 + // * __proto__ === Function.prototype 1.1195 + // * A constructor that creates a ctypes.{Int64,UInt64} object, respectively. 1.1196 + // * 'prototype' property: 1.1197 + // * [[Class]] {"Int64Proto","UInt64Proto"} 1.1198 + // * 'constructor' property === ctypes.{Int64,UInt64} 1.1199 + protos[SLOT_INT64PROTO] = InitInt64Class(cx, parent, &sInt64ProtoClass, 1.1200 + Int64::Construct, sInt64Functions, sInt64StaticFunctions); 1.1201 + if (!protos[SLOT_INT64PROTO]) 1.1202 + return false; 1.1203 + protos[SLOT_UINT64PROTO] = InitInt64Class(cx, parent, &sUInt64ProtoClass, 1.1204 + UInt64::Construct, sUInt64Functions, sUInt64StaticFunctions); 1.1205 + if (!protos[SLOT_UINT64PROTO]) 1.1206 + return false; 1.1207 + 1.1208 + // Finally, store a pointer to the global ctypes object. 1.1209 + // Note that there is no other reliable manner of locating this object. 1.1210 + protos[SLOT_CTYPES] = parent; 1.1211 + 1.1212 + // Attach the prototypes just created to each of ctypes.CType.prototype, 1.1213 + // and the special type constructors, so we can access them when constructing 1.1214 + // instances of those types. 1.1215 + AttachProtos(CTypeProto, protos); 1.1216 + AttachProtos(protos[SLOT_POINTERPROTO], protos); 1.1217 + AttachProtos(protos[SLOT_ARRAYPROTO], protos); 1.1218 + AttachProtos(protos[SLOT_STRUCTPROTO], protos); 1.1219 + AttachProtos(protos[SLOT_FUNCTIONPROTO], protos); 1.1220 + 1.1221 + RootedObject ABIProto(cx, InitABIClass(cx, parent)); 1.1222 + if (!ABIProto) 1.1223 + return false; 1.1224 + 1.1225 + // Attach objects representing ABI constants. 1.1226 + if (!DefineABIConstant(cx, parent, "default_abi", ABI_DEFAULT, ABIProto) || 1.1227 + !DefineABIConstant(cx, parent, "stdcall_abi", ABI_STDCALL, ABIProto) || 1.1228 + !DefineABIConstant(cx, parent, "winapi_abi", ABI_WINAPI, ABIProto)) 1.1229 + return false; 1.1230 + 1.1231 + // Create objects representing the builtin types, and attach them to the 1.1232 + // ctypes object. Each type object 't' has: 1.1233 + // * [[Class]] "CType" 1.1234 + // * __proto__ === ctypes.CType.prototype 1.1235 + // * A constructor which creates and returns a CData object, containing 1.1236 + // binary data of the given type. 1.1237 + // * 'prototype' property: 1.1238 + // * [[Class]] "CDataProto" 1.1239 + // * __proto__ === ctypes.CData.prototype 1.1240 + // * 'constructor' property === 't' 1.1241 +#define DEFINE_TYPE(name, type, ffiType) \ 1.1242 + RootedObject typeObj_##name(cx, \ 1.1243 + CType::DefineBuiltin(cx, parent, #name, CTypeProto, CDataProto, #name, \ 1.1244 + TYPE_##name, INT_TO_JSVAL(sizeof(type)), \ 1.1245 + INT_TO_JSVAL(ffiType.alignment), &ffiType)); \ 1.1246 + if (!typeObj_##name) \ 1.1247 + return false; 1.1248 +#include "ctypes/typedefs.h" 1.1249 + 1.1250 + // Alias 'ctypes.unsigned' as 'ctypes.unsigned_int', since they represent 1.1251 + // the same type in C. 1.1252 + if (!JS_DefineProperty(cx, parent, "unsigned", typeObj_unsigned_int, 1.1253 + JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) 1.1254 + return false; 1.1255 + 1.1256 + // Create objects representing the special types void_t and voidptr_t. 1.1257 + RootedObject typeObj(cx, 1.1258 + CType::DefineBuiltin(cx, parent, "void_t", CTypeProto, CDataProto, "void", 1.1259 + TYPE_void_t, JSVAL_VOID, JSVAL_VOID, &ffi_type_void)); 1.1260 + if (!typeObj) 1.1261 + return false; 1.1262 + 1.1263 + typeObj = PointerType::CreateInternal(cx, typeObj); 1.1264 + if (!typeObj) 1.1265 + return false; 1.1266 + if (!JS_DefineProperty(cx, parent, "voidptr_t", typeObj, 1.1267 + JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) 1.1268 + return false; 1.1269 + 1.1270 + return true; 1.1271 +} 1.1272 + 1.1273 +bool 1.1274 +IsCTypesGlobal(JSObject* obj) 1.1275 +{ 1.1276 + return JS_GetClass(obj) == &sCTypesGlobalClass; 1.1277 +} 1.1278 + 1.1279 +bool 1.1280 +IsCTypesGlobal(HandleValue v) 1.1281 +{ 1.1282 + return v.isObject() && IsCTypesGlobal(&v.toObject()); 1.1283 +} 1.1284 + 1.1285 +// Get the JSCTypesCallbacks struct from the 'ctypes' object 'obj'. 1.1286 +JSCTypesCallbacks* 1.1287 +GetCallbacks(JSObject* obj) 1.1288 +{ 1.1289 + JS_ASSERT(IsCTypesGlobal(obj)); 1.1290 + 1.1291 + jsval result = JS_GetReservedSlot(obj, SLOT_CALLBACKS); 1.1292 + if (JSVAL_IS_VOID(result)) 1.1293 + return nullptr; 1.1294 + 1.1295 + return static_cast<JSCTypesCallbacks*>(JSVAL_TO_PRIVATE(result)); 1.1296 +} 1.1297 + 1.1298 +// Utility function to access a property of an object as an object 1.1299 +// returns false and sets the error if the property does not exist 1.1300 +// or is not an object 1.1301 +static bool GetObjectProperty(JSContext *cx, HandleObject obj, 1.1302 + const char *property, MutableHandleObject result) 1.1303 +{ 1.1304 + RootedValue val(cx); 1.1305 + if (!JS_GetProperty(cx, obj, property, &val)) { 1.1306 + return false; 1.1307 + } 1.1308 + 1.1309 + if (JSVAL_IS_PRIMITIVE(val)) { 1.1310 + JS_ReportError(cx, "missing or non-object field"); 1.1311 + return false; 1.1312 + } 1.1313 + 1.1314 + result.set(JSVAL_TO_OBJECT(val)); 1.1315 + return true; 1.1316 +} 1.1317 + 1.1318 +} /* namespace ctypes */ 1.1319 +} /* namespace js */ 1.1320 + 1.1321 +using namespace js; 1.1322 +using namespace js::ctypes; 1.1323 + 1.1324 +JS_PUBLIC_API(bool) 1.1325 +JS_InitCTypesClass(JSContext* cx, HandleObject global) 1.1326 +{ 1.1327 + // attach ctypes property to global object 1.1328 + RootedObject ctypes(cx, JS_NewObject(cx, &sCTypesGlobalClass, NullPtr(), NullPtr())); 1.1329 + if (!ctypes) 1.1330 + return false; 1.1331 + 1.1332 + if (!JS_DefineProperty(cx, global, "ctypes", ctypes, JSPROP_READONLY | JSPROP_PERMANENT, 1.1333 + JS_PropertyStub, JS_StrictPropertyStub)){ 1.1334 + return false; 1.1335 + } 1.1336 + 1.1337 + if (!InitTypeClasses(cx, ctypes)) 1.1338 + return false; 1.1339 + 1.1340 + // attach API functions and properties 1.1341 + if (!JS_DefineFunctions(cx, ctypes, sModuleFunctions) || 1.1342 + !JS_DefineProperties(cx, ctypes, sModuleProps)) 1.1343 + return false; 1.1344 + 1.1345 + // Set up ctypes.CDataFinalizer.prototype. 1.1346 + RootedObject ctor(cx); 1.1347 + if (!GetObjectProperty(cx, ctypes, "CDataFinalizer", &ctor)) 1.1348 + return false; 1.1349 + 1.1350 + RootedObject prototype(cx, JS_NewObject(cx, &sCDataFinalizerProtoClass, NullPtr(), ctypes)); 1.1351 + if (!prototype) 1.1352 + return false; 1.1353 + 1.1354 + if (!JS_DefineFunctions(cx, prototype, sCDataFinalizerFunctions)) 1.1355 + return false; 1.1356 + 1.1357 + if (!JS_DefineProperty(cx, ctor, "prototype", prototype, 1.1358 + JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) 1.1359 + return false; 1.1360 + 1.1361 + if (!JS_DefineProperty(cx, prototype, "constructor", ctor, 1.1362 + JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) 1.1363 + return false; 1.1364 + 1.1365 + 1.1366 + // Seal the ctypes object, to prevent modification. 1.1367 + return JS_FreezeObject(cx, ctypes); 1.1368 +} 1.1369 + 1.1370 +JS_PUBLIC_API(void) 1.1371 +JS_SetCTypesCallbacks(JSObject *ctypesObj, JSCTypesCallbacks* callbacks) 1.1372 +{ 1.1373 + JS_ASSERT(callbacks); 1.1374 + JS_ASSERT(IsCTypesGlobal(ctypesObj)); 1.1375 + 1.1376 + // Set the callbacks on a reserved slot. 1.1377 + JS_SetReservedSlot(ctypesObj, SLOT_CALLBACKS, PRIVATE_TO_JSVAL(callbacks)); 1.1378 +} 1.1379 + 1.1380 +namespace js { 1.1381 + 1.1382 +JS_FRIEND_API(size_t) 1.1383 +SizeOfDataIfCDataObject(mozilla::MallocSizeOf mallocSizeOf, JSObject *obj) 1.1384 +{ 1.1385 + if (!CData::IsCData(obj)) 1.1386 + return 0; 1.1387 + 1.1388 + size_t n = 0; 1.1389 + jsval slot = JS_GetReservedSlot(obj, ctypes::SLOT_OWNS); 1.1390 + if (!JSVAL_IS_VOID(slot)) { 1.1391 + bool owns = JSVAL_TO_BOOLEAN(slot); 1.1392 + slot = JS_GetReservedSlot(obj, ctypes::SLOT_DATA); 1.1393 + if (!JSVAL_IS_VOID(slot)) { 1.1394 + char** buffer = static_cast<char**>(JSVAL_TO_PRIVATE(slot)); 1.1395 + n += mallocSizeOf(buffer); 1.1396 + if (owns) 1.1397 + n += mallocSizeOf(*buffer); 1.1398 + } 1.1399 + } 1.1400 + return n; 1.1401 +} 1.1402 + 1.1403 +namespace ctypes { 1.1404 + 1.1405 +/******************************************************************************* 1.1406 +** Type conversion functions 1.1407 +*******************************************************************************/ 1.1408 + 1.1409 +// Enforce some sanity checks on type widths and properties. 1.1410 +// Where the architecture is 64-bit, make sure it's LP64 or LLP64. (ctypes.int 1.1411 +// autoconverts to a primitive JS number; to support ILP64 architectures, it 1.1412 +// would need to autoconvert to an Int64 object instead. Therefore we enforce 1.1413 +// this invariant here.) 1.1414 +JS_STATIC_ASSERT(sizeof(bool) == 1 || sizeof(bool) == 4); 1.1415 +JS_STATIC_ASSERT(sizeof(char) == 1); 1.1416 +JS_STATIC_ASSERT(sizeof(short) == 2); 1.1417 +JS_STATIC_ASSERT(sizeof(int) == 4); 1.1418 +JS_STATIC_ASSERT(sizeof(unsigned) == 4); 1.1419 +JS_STATIC_ASSERT(sizeof(long) == 4 || sizeof(long) == 8); 1.1420 +JS_STATIC_ASSERT(sizeof(long long) == 8); 1.1421 +JS_STATIC_ASSERT(sizeof(size_t) == sizeof(uintptr_t)); 1.1422 +JS_STATIC_ASSERT(sizeof(float) == 4); 1.1423 +JS_STATIC_ASSERT(sizeof(PRFuncPtr) == sizeof(void*)); 1.1424 +JS_STATIC_ASSERT(NumericLimits<double>::is_signed); 1.1425 + 1.1426 +// Templated helper to convert FromType to TargetType, for the default case 1.1427 +// where the trivial POD constructor will do. 1.1428 +template<class TargetType, class FromType> 1.1429 +struct ConvertImpl { 1.1430 + static MOZ_ALWAYS_INLINE TargetType Convert(FromType d) { 1.1431 + return TargetType(d); 1.1432 + } 1.1433 +}; 1.1434 + 1.1435 +#ifdef _MSC_VER 1.1436 +// MSVC can't perform double to unsigned __int64 conversion when the 1.1437 +// double is greater than 2^63 - 1. Help it along a little. 1.1438 +template<> 1.1439 +struct ConvertImpl<uint64_t, double> { 1.1440 + static MOZ_ALWAYS_INLINE uint64_t Convert(double d) { 1.1441 + return d > 0x7fffffffffffffffui64 ? 1.1442 + uint64_t(d - 0x8000000000000000ui64) + 0x8000000000000000ui64 : 1.1443 + uint64_t(d); 1.1444 + } 1.1445 +}; 1.1446 +#endif 1.1447 + 1.1448 +// C++ doesn't guarantee that exact values are the only ones that will 1.1449 +// round-trip. In fact, on some platforms, including SPARC, there are pairs of 1.1450 +// values, a uint64_t and a double, such that neither value is exactly 1.1451 +// representable in the other type, but they cast to each other. 1.1452 +#if defined(SPARC) || defined(__powerpc__) 1.1453 +// Simulate x86 overflow behavior 1.1454 +template<> 1.1455 +struct ConvertImpl<uint64_t, double> { 1.1456 + static MOZ_ALWAYS_INLINE uint64_t Convert(double d) { 1.1457 + return d >= 0xffffffffffffffff ? 1.1458 + 0x8000000000000000 : uint64_t(d); 1.1459 + } 1.1460 +}; 1.1461 + 1.1462 +template<> 1.1463 +struct ConvertImpl<int64_t, double> { 1.1464 + static MOZ_ALWAYS_INLINE int64_t Convert(double d) { 1.1465 + return d >= 0x7fffffffffffffff ? 1.1466 + 0x8000000000000000 : int64_t(d); 1.1467 + } 1.1468 +}; 1.1469 +#endif 1.1470 + 1.1471 +template<class TargetType, class FromType> 1.1472 +static MOZ_ALWAYS_INLINE TargetType Convert(FromType d) 1.1473 +{ 1.1474 + return ConvertImpl<TargetType, FromType>::Convert(d); 1.1475 +} 1.1476 + 1.1477 +template<class TargetType, class FromType> 1.1478 +static MOZ_ALWAYS_INLINE bool IsAlwaysExact() 1.1479 +{ 1.1480 + // Return 'true' if TargetType can always exactly represent FromType. 1.1481 + // This means that: 1.1482 + // 1) TargetType must be the same or more bits wide as FromType. For integers 1.1483 + // represented in 'n' bits, unsigned variants will have 'n' digits while 1.1484 + // signed will have 'n - 1'. For floating point types, 'digits' is the 1.1485 + // mantissa width. 1.1486 + // 2) If FromType is signed, TargetType must also be signed. (Floating point 1.1487 + // types are always signed.) 1.1488 + // 3) If TargetType is an exact integral type, FromType must be also. 1.1489 + if (NumericLimits<TargetType>::digits < NumericLimits<FromType>::digits) 1.1490 + return false; 1.1491 + 1.1492 + if (NumericLimits<FromType>::is_signed && 1.1493 + !NumericLimits<TargetType>::is_signed) 1.1494 + return false; 1.1495 + 1.1496 + if (!NumericLimits<FromType>::is_exact && 1.1497 + NumericLimits<TargetType>::is_exact) 1.1498 + return false; 1.1499 + 1.1500 + return true; 1.1501 +} 1.1502 + 1.1503 +// Templated helper to determine if FromType 'i' converts losslessly to 1.1504 +// TargetType 'j'. Default case where both types are the same signedness. 1.1505 +template<class TargetType, class FromType, bool TargetSigned, bool FromSigned> 1.1506 +struct IsExactImpl { 1.1507 + static MOZ_ALWAYS_INLINE bool Test(FromType i, TargetType j) { 1.1508 + JS_STATIC_ASSERT(NumericLimits<TargetType>::is_exact); 1.1509 + return FromType(j) == i; 1.1510 + } 1.1511 +}; 1.1512 + 1.1513 +// Specialization where TargetType is unsigned, FromType is signed. 1.1514 +template<class TargetType, class FromType> 1.1515 +struct IsExactImpl<TargetType, FromType, false, true> { 1.1516 + static MOZ_ALWAYS_INLINE bool Test(FromType i, TargetType j) { 1.1517 + JS_STATIC_ASSERT(NumericLimits<TargetType>::is_exact); 1.1518 + return i >= 0 && FromType(j) == i; 1.1519 + } 1.1520 +}; 1.1521 + 1.1522 +// Specialization where TargetType is signed, FromType is unsigned. 1.1523 +template<class TargetType, class FromType> 1.1524 +struct IsExactImpl<TargetType, FromType, true, false> { 1.1525 + static MOZ_ALWAYS_INLINE bool Test(FromType i, TargetType j) { 1.1526 + JS_STATIC_ASSERT(NumericLimits<TargetType>::is_exact); 1.1527 + return TargetType(i) >= 0 && FromType(j) == i; 1.1528 + } 1.1529 +}; 1.1530 + 1.1531 +// Convert FromType 'i' to TargetType 'result', returning true iff 'result' 1.1532 +// is an exact representation of 'i'. 1.1533 +template<class TargetType, class FromType> 1.1534 +static MOZ_ALWAYS_INLINE bool ConvertExact(FromType i, TargetType* result) 1.1535 +{ 1.1536 + // Require that TargetType is integral, to simplify conversion. 1.1537 + JS_STATIC_ASSERT(NumericLimits<TargetType>::is_exact); 1.1538 + 1.1539 + *result = Convert<TargetType>(i); 1.1540 + 1.1541 + // See if we can avoid a dynamic check. 1.1542 + if (IsAlwaysExact<TargetType, FromType>()) 1.1543 + return true; 1.1544 + 1.1545 + // Return 'true' if 'i' is exactly representable in 'TargetType'. 1.1546 + return IsExactImpl<TargetType, 1.1547 + FromType, 1.1548 + NumericLimits<TargetType>::is_signed, 1.1549 + NumericLimits<FromType>::is_signed>::Test(i, *result); 1.1550 +} 1.1551 + 1.1552 +// Templated helper to determine if Type 'i' is negative. Default case 1.1553 +// where IntegerType is unsigned. 1.1554 +template<class Type, bool IsSigned> 1.1555 +struct IsNegativeImpl { 1.1556 + static MOZ_ALWAYS_INLINE bool Test(Type i) { 1.1557 + return false; 1.1558 + } 1.1559 +}; 1.1560 + 1.1561 +// Specialization where Type is signed. 1.1562 +template<class Type> 1.1563 +struct IsNegativeImpl<Type, true> { 1.1564 + static MOZ_ALWAYS_INLINE bool Test(Type i) { 1.1565 + return i < 0; 1.1566 + } 1.1567 +}; 1.1568 + 1.1569 +// Determine whether Type 'i' is negative. 1.1570 +template<class Type> 1.1571 +static MOZ_ALWAYS_INLINE bool IsNegative(Type i) 1.1572 +{ 1.1573 + return IsNegativeImpl<Type, NumericLimits<Type>::is_signed>::Test(i); 1.1574 +} 1.1575 + 1.1576 +// Implicitly convert val to bool, allowing bool, int, and double 1.1577 +// arguments numerically equal to 0 or 1. 1.1578 +static bool 1.1579 +jsvalToBool(JSContext* cx, jsval val, bool* result) 1.1580 +{ 1.1581 + if (JSVAL_IS_BOOLEAN(val)) { 1.1582 + *result = JSVAL_TO_BOOLEAN(val); 1.1583 + return true; 1.1584 + } 1.1585 + if (JSVAL_IS_INT(val)) { 1.1586 + int32_t i = JSVAL_TO_INT(val); 1.1587 + *result = i != 0; 1.1588 + return i == 0 || i == 1; 1.1589 + } 1.1590 + if (JSVAL_IS_DOUBLE(val)) { 1.1591 + double d = JSVAL_TO_DOUBLE(val); 1.1592 + *result = d != 0; 1.1593 + // Allow -0. 1.1594 + return d == 1 || d == 0; 1.1595 + } 1.1596 + // Don't silently convert null to bool. It's probably a mistake. 1.1597 + return false; 1.1598 +} 1.1599 + 1.1600 +// Implicitly convert val to IntegerType, allowing bool, int, double, 1.1601 +// Int64, UInt64, and CData integer types 't' where all values of 't' are 1.1602 +// representable by IntegerType. 1.1603 +template<class IntegerType> 1.1604 +static bool 1.1605 +jsvalToInteger(JSContext* cx, jsval val, IntegerType* result) 1.1606 +{ 1.1607 + JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact); 1.1608 + 1.1609 + if (JSVAL_IS_INT(val)) { 1.1610 + // Make sure the integer fits in the alotted precision, and has the right 1.1611 + // sign. 1.1612 + int32_t i = JSVAL_TO_INT(val); 1.1613 + return ConvertExact(i, result); 1.1614 + } 1.1615 + if (JSVAL_IS_DOUBLE(val)) { 1.1616 + // Don't silently lose bits here -- check that val really is an 1.1617 + // integer value, and has the right sign. 1.1618 + double d = JSVAL_TO_DOUBLE(val); 1.1619 + return ConvertExact(d, result); 1.1620 + } 1.1621 + if (!JSVAL_IS_PRIMITIVE(val)) { 1.1622 + JSObject* obj = JSVAL_TO_OBJECT(val); 1.1623 + if (CData::IsCData(obj)) { 1.1624 + JSObject* typeObj = CData::GetCType(obj); 1.1625 + void* data = CData::GetData(obj); 1.1626 + 1.1627 + // Check whether the source type is always representable, with exact 1.1628 + // precision, by the target type. If it is, convert the value. 1.1629 + switch (CType::GetTypeCode(typeObj)) { 1.1630 +#define DEFINE_INT_TYPE(name, fromType, ffiType) \ 1.1631 + case TYPE_##name: \ 1.1632 + if (!IsAlwaysExact<IntegerType, fromType>()) \ 1.1633 + return false; \ 1.1634 + *result = IntegerType(*static_cast<fromType*>(data)); \ 1.1635 + return true; 1.1636 +#define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) 1.1637 +#include "ctypes/typedefs.h" 1.1638 + case TYPE_void_t: 1.1639 + case TYPE_bool: 1.1640 + case TYPE_float: 1.1641 + case TYPE_double: 1.1642 + case TYPE_float32_t: 1.1643 + case TYPE_float64_t: 1.1644 + case TYPE_char: 1.1645 + case TYPE_signed_char: 1.1646 + case TYPE_unsigned_char: 1.1647 + case TYPE_jschar: 1.1648 + case TYPE_pointer: 1.1649 + case TYPE_function: 1.1650 + case TYPE_array: 1.1651 + case TYPE_struct: 1.1652 + // Not a compatible number type. 1.1653 + return false; 1.1654 + } 1.1655 + } 1.1656 + 1.1657 + if (Int64::IsInt64(obj)) { 1.1658 + // Make sure the integer fits in IntegerType. 1.1659 + int64_t i = Int64Base::GetInt(obj); 1.1660 + return ConvertExact(i, result); 1.1661 + } 1.1662 + 1.1663 + if (UInt64::IsUInt64(obj)) { 1.1664 + // Make sure the integer fits in IntegerType. 1.1665 + uint64_t i = Int64Base::GetInt(obj); 1.1666 + return ConvertExact(i, result); 1.1667 + } 1.1668 + 1.1669 + if (CDataFinalizer::IsCDataFinalizer(obj)) { 1.1670 + RootedValue innerData(cx); 1.1671 + if (!CDataFinalizer::GetValue(cx, obj, innerData.address())) { 1.1672 + return false; // Nothing to convert 1.1673 + } 1.1674 + return jsvalToInteger(cx, innerData, result); 1.1675 + } 1.1676 + 1.1677 + return false; 1.1678 + } 1.1679 + if (JSVAL_IS_BOOLEAN(val)) { 1.1680 + // Implicitly promote boolean values to 0 or 1, like C. 1.1681 + *result = JSVAL_TO_BOOLEAN(val); 1.1682 + JS_ASSERT(*result == 0 || *result == 1); 1.1683 + return true; 1.1684 + } 1.1685 + // Don't silently convert null to an integer. It's probably a mistake. 1.1686 + return false; 1.1687 +} 1.1688 + 1.1689 +// Implicitly convert val to FloatType, allowing int, double, 1.1690 +// Int64, UInt64, and CData numeric types 't' where all values of 't' are 1.1691 +// representable by FloatType. 1.1692 +template<class FloatType> 1.1693 +static bool 1.1694 +jsvalToFloat(JSContext *cx, jsval val, FloatType* result) 1.1695 +{ 1.1696 + JS_STATIC_ASSERT(!NumericLimits<FloatType>::is_exact); 1.1697 + 1.1698 + // The following casts may silently throw away some bits, but there's 1.1699 + // no good way around it. Sternly requiring that the 64-bit double 1.1700 + // argument be exactly representable as a 32-bit float is 1.1701 + // unrealistic: it would allow 1/2 to pass but not 1/3. 1.1702 + if (JSVAL_IS_INT(val)) { 1.1703 + *result = FloatType(JSVAL_TO_INT(val)); 1.1704 + return true; 1.1705 + } 1.1706 + if (JSVAL_IS_DOUBLE(val)) { 1.1707 + *result = FloatType(JSVAL_TO_DOUBLE(val)); 1.1708 + return true; 1.1709 + } 1.1710 + if (!JSVAL_IS_PRIMITIVE(val)) { 1.1711 + JSObject* obj = JSVAL_TO_OBJECT(val); 1.1712 + if (CData::IsCData(obj)) { 1.1713 + JSObject* typeObj = CData::GetCType(obj); 1.1714 + void* data = CData::GetData(obj); 1.1715 + 1.1716 + // Check whether the source type is always representable, with exact 1.1717 + // precision, by the target type. If it is, convert the value. 1.1718 + switch (CType::GetTypeCode(typeObj)) { 1.1719 +#define DEFINE_FLOAT_TYPE(name, fromType, ffiType) \ 1.1720 + case TYPE_##name: \ 1.1721 + if (!IsAlwaysExact<FloatType, fromType>()) \ 1.1722 + return false; \ 1.1723 + *result = FloatType(*static_cast<fromType*>(data)); \ 1.1724 + return true; 1.1725 +#define DEFINE_INT_TYPE(x, y, z) DEFINE_FLOAT_TYPE(x, y, z) 1.1726 +#define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) 1.1727 +#include "ctypes/typedefs.h" 1.1728 + case TYPE_void_t: 1.1729 + case TYPE_bool: 1.1730 + case TYPE_char: 1.1731 + case TYPE_signed_char: 1.1732 + case TYPE_unsigned_char: 1.1733 + case TYPE_jschar: 1.1734 + case TYPE_pointer: 1.1735 + case TYPE_function: 1.1736 + case TYPE_array: 1.1737 + case TYPE_struct: 1.1738 + // Not a compatible number type. 1.1739 + return false; 1.1740 + } 1.1741 + } 1.1742 + } 1.1743 + // Don't silently convert true to 1.0 or false to 0.0, even though C/C++ 1.1744 + // does it. It's likely to be a mistake. 1.1745 + return false; 1.1746 +} 1.1747 + 1.1748 +template<class IntegerType> 1.1749 +static bool 1.1750 +StringToInteger(JSContext* cx, JSString* string, IntegerType* result) 1.1751 +{ 1.1752 + JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact); 1.1753 + 1.1754 + const jschar* cp = string->getChars(nullptr); 1.1755 + if (!cp) 1.1756 + return false; 1.1757 + 1.1758 + const jschar* end = cp + string->length(); 1.1759 + if (cp == end) 1.1760 + return false; 1.1761 + 1.1762 + IntegerType sign = 1; 1.1763 + if (cp[0] == '-') { 1.1764 + if (!NumericLimits<IntegerType>::is_signed) 1.1765 + return false; 1.1766 + 1.1767 + sign = -1; 1.1768 + ++cp; 1.1769 + } 1.1770 + 1.1771 + // Assume base-10, unless the string begins with '0x' or '0X'. 1.1772 + IntegerType base = 10; 1.1773 + if (end - cp > 2 && cp[0] == '0' && (cp[1] == 'x' || cp[1] == 'X')) { 1.1774 + cp += 2; 1.1775 + base = 16; 1.1776 + } 1.1777 + 1.1778 + // Scan the string left to right and build the number, 1.1779 + // checking for valid characters 0 - 9, a - f, A - F and overflow. 1.1780 + IntegerType i = 0; 1.1781 + while (cp != end) { 1.1782 + jschar c = *cp++; 1.1783 + if (c >= '0' && c <= '9') 1.1784 + c -= '0'; 1.1785 + else if (base == 16 && c >= 'a' && c <= 'f') 1.1786 + c = c - 'a' + 10; 1.1787 + else if (base == 16 && c >= 'A' && c <= 'F') 1.1788 + c = c - 'A' + 10; 1.1789 + else 1.1790 + return false; 1.1791 + 1.1792 + IntegerType ii = i; 1.1793 + i = ii * base + sign * c; 1.1794 + if (i / base != ii) // overflow 1.1795 + return false; 1.1796 + } 1.1797 + 1.1798 + *result = i; 1.1799 + return true; 1.1800 +} 1.1801 + 1.1802 +// Implicitly convert val to IntegerType, allowing int, double, 1.1803 +// Int64, UInt64, and optionally a decimal or hexadecimal string argument. 1.1804 +// (This is common code shared by jsvalToSize and the Int64/UInt64 constructors.) 1.1805 +template<class IntegerType> 1.1806 +static bool 1.1807 +jsvalToBigInteger(JSContext* cx, 1.1808 + jsval val, 1.1809 + bool allowString, 1.1810 + IntegerType* result) 1.1811 +{ 1.1812 + JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact); 1.1813 + 1.1814 + if (JSVAL_IS_INT(val)) { 1.1815 + // Make sure the integer fits in the alotted precision, and has the right 1.1816 + // sign. 1.1817 + int32_t i = JSVAL_TO_INT(val); 1.1818 + return ConvertExact(i, result); 1.1819 + } 1.1820 + if (JSVAL_IS_DOUBLE(val)) { 1.1821 + // Don't silently lose bits here -- check that val really is an 1.1822 + // integer value, and has the right sign. 1.1823 + double d = JSVAL_TO_DOUBLE(val); 1.1824 + return ConvertExact(d, result); 1.1825 + } 1.1826 + if (allowString && JSVAL_IS_STRING(val)) { 1.1827 + // Allow conversion from base-10 or base-16 strings, provided the result 1.1828 + // fits in IntegerType. (This allows an Int64 or UInt64 object to be passed 1.1829 + // to the JS array element operator, which will automatically call 1.1830 + // toString() on the object for us.) 1.1831 + return StringToInteger(cx, JSVAL_TO_STRING(val), result); 1.1832 + } 1.1833 + if (!JSVAL_IS_PRIMITIVE(val)) { 1.1834 + // Allow conversion from an Int64 or UInt64 object directly. 1.1835 + JSObject* obj = JSVAL_TO_OBJECT(val); 1.1836 + 1.1837 + if (UInt64::IsUInt64(obj)) { 1.1838 + // Make sure the integer fits in IntegerType. 1.1839 + uint64_t i = Int64Base::GetInt(obj); 1.1840 + return ConvertExact(i, result); 1.1841 + } 1.1842 + 1.1843 + if (Int64::IsInt64(obj)) { 1.1844 + // Make sure the integer fits in IntegerType. 1.1845 + int64_t i = Int64Base::GetInt(obj); 1.1846 + return ConvertExact(i, result); 1.1847 + } 1.1848 + 1.1849 + if (CDataFinalizer::IsCDataFinalizer(obj)) { 1.1850 + RootedValue innerData(cx); 1.1851 + if (!CDataFinalizer::GetValue(cx, obj, innerData.address())) { 1.1852 + return false; // Nothing to convert 1.1853 + } 1.1854 + return jsvalToBigInteger(cx, innerData, allowString, result); 1.1855 + } 1.1856 + 1.1857 + } 1.1858 + return false; 1.1859 +} 1.1860 + 1.1861 +// Implicitly convert val to a size value, where the size value is represented 1.1862 +// by size_t but must also fit in a double. 1.1863 +static bool 1.1864 +jsvalToSize(JSContext* cx, jsval val, bool allowString, size_t* result) 1.1865 +{ 1.1866 + if (!jsvalToBigInteger(cx, val, allowString, result)) 1.1867 + return false; 1.1868 + 1.1869 + // Also check that the result fits in a double. 1.1870 + return Convert<size_t>(double(*result)) == *result; 1.1871 +} 1.1872 + 1.1873 +// Implicitly convert val to IntegerType, allowing int, double, 1.1874 +// Int64, UInt64, and optionally a decimal or hexadecimal string argument. 1.1875 +// (This is common code shared by jsvalToSize and the Int64/UInt64 constructors.) 1.1876 +template<class IntegerType> 1.1877 +static bool 1.1878 +jsidToBigInteger(JSContext* cx, 1.1879 + jsid val, 1.1880 + bool allowString, 1.1881 + IntegerType* result) 1.1882 +{ 1.1883 + JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact); 1.1884 + 1.1885 + if (JSID_IS_INT(val)) { 1.1886 + // Make sure the integer fits in the alotted precision, and has the right 1.1887 + // sign. 1.1888 + int32_t i = JSID_TO_INT(val); 1.1889 + return ConvertExact(i, result); 1.1890 + } 1.1891 + if (allowString && JSID_IS_STRING(val)) { 1.1892 + // Allow conversion from base-10 or base-16 strings, provided the result 1.1893 + // fits in IntegerType. (This allows an Int64 or UInt64 object to be passed 1.1894 + // to the JS array element operator, which will automatically call 1.1895 + // toString() on the object for us.) 1.1896 + return StringToInteger(cx, JSID_TO_STRING(val), result); 1.1897 + } 1.1898 + if (JSID_IS_OBJECT(val)) { 1.1899 + // Allow conversion from an Int64 or UInt64 object directly. 1.1900 + JSObject* obj = JSID_TO_OBJECT(val); 1.1901 + 1.1902 + if (UInt64::IsUInt64(obj)) { 1.1903 + // Make sure the integer fits in IntegerType. 1.1904 + uint64_t i = Int64Base::GetInt(obj); 1.1905 + return ConvertExact(i, result); 1.1906 + } 1.1907 + 1.1908 + if (Int64::IsInt64(obj)) { 1.1909 + // Make sure the integer fits in IntegerType. 1.1910 + int64_t i = Int64Base::GetInt(obj); 1.1911 + return ConvertExact(i, result); 1.1912 + } 1.1913 + } 1.1914 + return false; 1.1915 +} 1.1916 + 1.1917 +// Implicitly convert val to a size value, where the size value is represented 1.1918 +// by size_t but must also fit in a double. 1.1919 +static bool 1.1920 +jsidToSize(JSContext* cx, jsid val, bool allowString, size_t* result) 1.1921 +{ 1.1922 + if (!jsidToBigInteger(cx, val, allowString, result)) 1.1923 + return false; 1.1924 + 1.1925 + // Also check that the result fits in a double. 1.1926 + return Convert<size_t>(double(*result)) == *result; 1.1927 +} 1.1928 + 1.1929 +// Implicitly convert a size value to a jsval, ensuring that the size_t value 1.1930 +// fits in a double. 1.1931 +static bool 1.1932 +SizeTojsval(JSContext* cx, size_t size, jsval* result) 1.1933 +{ 1.1934 + if (Convert<size_t>(double(size)) != size) { 1.1935 + JS_ReportError(cx, "size overflow"); 1.1936 + return false; 1.1937 + } 1.1938 + 1.1939 + *result = JS_NumberValue(double(size)); 1.1940 + return true; 1.1941 +} 1.1942 + 1.1943 +// Forcefully convert val to IntegerType when explicitly requested. 1.1944 +template<class IntegerType> 1.1945 +static bool 1.1946 +jsvalToIntegerExplicit(jsval val, IntegerType* result) 1.1947 +{ 1.1948 + JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact); 1.1949 + 1.1950 + if (JSVAL_IS_DOUBLE(val)) { 1.1951 + // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast. 1.1952 + double d = JSVAL_TO_DOUBLE(val); 1.1953 + *result = mozilla::IsFinite(d) ? IntegerType(d) : 0; 1.1954 + return true; 1.1955 + } 1.1956 + if (!JSVAL_IS_PRIMITIVE(val)) { 1.1957 + // Convert Int64 and UInt64 values by C-style cast. 1.1958 + JSObject* obj = JSVAL_TO_OBJECT(val); 1.1959 + if (Int64::IsInt64(obj)) { 1.1960 + int64_t i = Int64Base::GetInt(obj); 1.1961 + *result = IntegerType(i); 1.1962 + return true; 1.1963 + } 1.1964 + if (UInt64::IsUInt64(obj)) { 1.1965 + uint64_t i = Int64Base::GetInt(obj); 1.1966 + *result = IntegerType(i); 1.1967 + return true; 1.1968 + } 1.1969 + } 1.1970 + return false; 1.1971 +} 1.1972 + 1.1973 +// Forcefully convert val to a pointer value when explicitly requested. 1.1974 +static bool 1.1975 +jsvalToPtrExplicit(JSContext* cx, jsval val, uintptr_t* result) 1.1976 +{ 1.1977 + if (JSVAL_IS_INT(val)) { 1.1978 + // int32_t always fits in intptr_t. If the integer is negative, cast through 1.1979 + // an intptr_t intermediate to sign-extend. 1.1980 + int32_t i = JSVAL_TO_INT(val); 1.1981 + *result = i < 0 ? uintptr_t(intptr_t(i)) : uintptr_t(i); 1.1982 + return true; 1.1983 + } 1.1984 + if (JSVAL_IS_DOUBLE(val)) { 1.1985 + double d = JSVAL_TO_DOUBLE(val); 1.1986 + if (d < 0) { 1.1987 + // Cast through an intptr_t intermediate to sign-extend. 1.1988 + intptr_t i = Convert<intptr_t>(d); 1.1989 + if (double(i) != d) 1.1990 + return false; 1.1991 + 1.1992 + *result = uintptr_t(i); 1.1993 + return true; 1.1994 + } 1.1995 + 1.1996 + // Don't silently lose bits here -- check that val really is an 1.1997 + // integer value, and has the right sign. 1.1998 + *result = Convert<uintptr_t>(d); 1.1999 + return double(*result) == d; 1.2000 + } 1.2001 + if (!JSVAL_IS_PRIMITIVE(val)) { 1.2002 + JSObject* obj = JSVAL_TO_OBJECT(val); 1.2003 + if (Int64::IsInt64(obj)) { 1.2004 + int64_t i = Int64Base::GetInt(obj); 1.2005 + intptr_t p = intptr_t(i); 1.2006 + 1.2007 + // Make sure the integer fits in the alotted precision. 1.2008 + if (int64_t(p) != i) 1.2009 + return false; 1.2010 + *result = uintptr_t(p); 1.2011 + return true; 1.2012 + } 1.2013 + 1.2014 + if (UInt64::IsUInt64(obj)) { 1.2015 + uint64_t i = Int64Base::GetInt(obj); 1.2016 + 1.2017 + // Make sure the integer fits in the alotted precision. 1.2018 + *result = uintptr_t(i); 1.2019 + return uint64_t(*result) == i; 1.2020 + } 1.2021 + } 1.2022 + return false; 1.2023 +} 1.2024 + 1.2025 +template<class IntegerType, class CharType, size_t N, class AP> 1.2026 +void 1.2027 +IntegerToString(IntegerType i, int radix, Vector<CharType, N, AP>& result) 1.2028 +{ 1.2029 + JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact); 1.2030 + 1.2031 + // The buffer must be big enough for all the bits of IntegerType to fit, 1.2032 + // in base-2, including '-'. 1.2033 + CharType buffer[sizeof(IntegerType) * 8 + 1]; 1.2034 + CharType* end = buffer + sizeof(buffer) / sizeof(CharType); 1.2035 + CharType* cp = end; 1.2036 + 1.2037 + // Build the string in reverse. We use multiplication and subtraction 1.2038 + // instead of modulus because that's much faster. 1.2039 + const bool isNegative = IsNegative(i); 1.2040 + size_t sign = isNegative ? -1 : 1; 1.2041 + do { 1.2042 + IntegerType ii = i / IntegerType(radix); 1.2043 + size_t index = sign * size_t(i - ii * IntegerType(radix)); 1.2044 + *--cp = "0123456789abcdefghijklmnopqrstuvwxyz"[index]; 1.2045 + i = ii; 1.2046 + } while (i != 0); 1.2047 + 1.2048 + if (isNegative) 1.2049 + *--cp = '-'; 1.2050 + 1.2051 + JS_ASSERT(cp >= buffer); 1.2052 + result.append(cp, end); 1.2053 +} 1.2054 + 1.2055 +template<class CharType> 1.2056 +static size_t 1.2057 +strnlen(const CharType* begin, size_t max) 1.2058 +{ 1.2059 + for (const CharType* s = begin; s != begin + max; ++s) 1.2060 + if (*s == 0) 1.2061 + return s - begin; 1.2062 + 1.2063 + return max; 1.2064 +} 1.2065 + 1.2066 +// Convert C binary value 'data' of CType 'typeObj' to a JS primitive, where 1.2067 +// possible; otherwise, construct and return a CData object. The following 1.2068 +// semantics apply when constructing a CData object for return: 1.2069 +// * If 'wantPrimitive' is true, the caller indicates that 'result' must be 1.2070 +// a JS primitive, and ConvertToJS will fail if 'result' would be a CData 1.2071 +// object. Otherwise: 1.2072 +// * If a CData object 'parentObj' is supplied, the new CData object is 1.2073 +// dependent on the given parent and its buffer refers to a slice of the 1.2074 +// parent's buffer. 1.2075 +// * If 'parentObj' is null, the new CData object may or may not own its 1.2076 +// resulting buffer depending on the 'ownResult' argument. 1.2077 +static bool 1.2078 +ConvertToJS(JSContext* cx, 1.2079 + HandleObject typeObj, 1.2080 + HandleObject parentObj, 1.2081 + void* data, 1.2082 + bool wantPrimitive, 1.2083 + bool ownResult, 1.2084 + jsval* result) 1.2085 +{ 1.2086 + JS_ASSERT(!parentObj || CData::IsCData(parentObj)); 1.2087 + JS_ASSERT(!parentObj || !ownResult); 1.2088 + JS_ASSERT(!wantPrimitive || !ownResult); 1.2089 + 1.2090 + TypeCode typeCode = CType::GetTypeCode(typeObj); 1.2091 + 1.2092 + switch (typeCode) { 1.2093 + case TYPE_void_t: 1.2094 + *result = JSVAL_VOID; 1.2095 + break; 1.2096 + case TYPE_bool: 1.2097 + *result = *static_cast<bool*>(data) ? JSVAL_TRUE : JSVAL_FALSE; 1.2098 + break; 1.2099 +#define DEFINE_INT_TYPE(name, type, ffiType) \ 1.2100 + case TYPE_##name: { \ 1.2101 + type value = *static_cast<type*>(data); \ 1.2102 + if (sizeof(type) < 4) \ 1.2103 + *result = INT_TO_JSVAL(int32_t(value)); \ 1.2104 + else \ 1.2105 + *result = JS_NumberValue(double(value)); \ 1.2106 + break; \ 1.2107 + } 1.2108 +#define DEFINE_WRAPPED_INT_TYPE(name, type, ffiType) \ 1.2109 + case TYPE_##name: { \ 1.2110 + /* Return an Int64 or UInt64 object - do not convert to a JS number. */ \ 1.2111 + uint64_t value; \ 1.2112 + RootedObject proto(cx); \ 1.2113 + if (!NumericLimits<type>::is_signed) { \ 1.2114 + value = *static_cast<type*>(data); \ 1.2115 + /* Get ctypes.UInt64.prototype from ctypes.CType.prototype. */ \ 1.2116 + proto = CType::GetProtoFromType(cx, typeObj, SLOT_UINT64PROTO); \ 1.2117 + if (!proto) \ 1.2118 + return false; \ 1.2119 + } else { \ 1.2120 + value = int64_t(*static_cast<type*>(data)); \ 1.2121 + /* Get ctypes.Int64.prototype from ctypes.CType.prototype. */ \ 1.2122 + proto = CType::GetProtoFromType(cx, typeObj, SLOT_INT64PROTO); \ 1.2123 + if (!proto) \ 1.2124 + return false; \ 1.2125 + } \ 1.2126 + \ 1.2127 + JSObject* obj = Int64Base::Construct(cx, proto, value, \ 1.2128 + !NumericLimits<type>::is_signed); \ 1.2129 + if (!obj) \ 1.2130 + return false; \ 1.2131 + *result = OBJECT_TO_JSVAL(obj); \ 1.2132 + break; \ 1.2133 + } 1.2134 +#define DEFINE_FLOAT_TYPE(name, type, ffiType) \ 1.2135 + case TYPE_##name: { \ 1.2136 + type value = *static_cast<type*>(data); \ 1.2137 + *result = JS_NumberValue(double(value)); \ 1.2138 + break; \ 1.2139 + } 1.2140 +#define DEFINE_CHAR_TYPE(name, type, ffiType) \ 1.2141 + case TYPE_##name: \ 1.2142 + /* Convert to an integer. We have no idea what character encoding to */ \ 1.2143 + /* use, if any. */ \ 1.2144 + *result = INT_TO_JSVAL(*static_cast<type*>(data)); \ 1.2145 + break; 1.2146 +#include "ctypes/typedefs.h" 1.2147 + case TYPE_jschar: { 1.2148 + // Convert the jschar to a 1-character string. 1.2149 + JSString* str = JS_NewUCStringCopyN(cx, static_cast<jschar*>(data), 1); 1.2150 + if (!str) 1.2151 + return false; 1.2152 + 1.2153 + *result = STRING_TO_JSVAL(str); 1.2154 + break; 1.2155 + } 1.2156 + case TYPE_pointer: 1.2157 + case TYPE_array: 1.2158 + case TYPE_struct: { 1.2159 + // We're about to create a new CData object to return. If the caller doesn't 1.2160 + // want this, return early. 1.2161 + if (wantPrimitive) { 1.2162 + JS_ReportError(cx, "cannot convert to primitive value"); 1.2163 + return false; 1.2164 + } 1.2165 + 1.2166 + JSObject* obj = CData::Create(cx, typeObj, parentObj, data, ownResult); 1.2167 + if (!obj) 1.2168 + return false; 1.2169 + 1.2170 + *result = OBJECT_TO_JSVAL(obj); 1.2171 + break; 1.2172 + } 1.2173 + case TYPE_function: 1.2174 + MOZ_ASSUME_UNREACHABLE("cannot return a FunctionType"); 1.2175 + } 1.2176 + 1.2177 + return true; 1.2178 +} 1.2179 + 1.2180 +// Determine if the contents of a typed array can be converted without 1.2181 +// ambiguity to a C type. Elements of a Int8Array are converted to 1.2182 +// ctypes.int8_t, UInt8Array to ctypes.uint8_t, etc. 1.2183 +bool CanConvertTypedArrayItemTo(JSObject *baseType, JSObject *valObj, JSContext *cx) { 1.2184 + TypeCode baseTypeCode = CType::GetTypeCode(baseType); 1.2185 + if (baseTypeCode == TYPE_void_t) { 1.2186 + return true; 1.2187 + } 1.2188 + TypeCode elementTypeCode; 1.2189 + switch (JS_GetArrayBufferViewType(valObj)) { 1.2190 + case ScalarTypeDescr::TYPE_INT8: 1.2191 + elementTypeCode = TYPE_int8_t; 1.2192 + break; 1.2193 + case ScalarTypeDescr::TYPE_UINT8: 1.2194 + case ScalarTypeDescr::TYPE_UINT8_CLAMPED: 1.2195 + elementTypeCode = TYPE_uint8_t; 1.2196 + break; 1.2197 + case ScalarTypeDescr::TYPE_INT16: 1.2198 + elementTypeCode = TYPE_int16_t; 1.2199 + break; 1.2200 + case ScalarTypeDescr::TYPE_UINT16: 1.2201 + elementTypeCode = TYPE_uint16_t; 1.2202 + break; 1.2203 + case ScalarTypeDescr::TYPE_INT32: 1.2204 + elementTypeCode = TYPE_int32_t; 1.2205 + break; 1.2206 + case ScalarTypeDescr::TYPE_UINT32: 1.2207 + elementTypeCode = TYPE_uint32_t; 1.2208 + break; 1.2209 + case ScalarTypeDescr::TYPE_FLOAT32: 1.2210 + elementTypeCode = TYPE_float32_t; 1.2211 + break; 1.2212 + case ScalarTypeDescr::TYPE_FLOAT64: 1.2213 + elementTypeCode = TYPE_float64_t; 1.2214 + break; 1.2215 + default: 1.2216 + return false; 1.2217 + } 1.2218 + return elementTypeCode == baseTypeCode; 1.2219 +} 1.2220 + 1.2221 +// Implicitly convert jsval 'val' to a C binary representation of CType 1.2222 +// 'targetType', storing the result in 'buffer'. Adequate space must be 1.2223 +// provided in 'buffer' by the caller. This function generally does minimal 1.2224 +// coercion between types. There are two cases in which this function is used: 1.2225 +// 1) The target buffer is internal to a CData object; we simply write data 1.2226 +// into it. 1.2227 +// 2) We are converting an argument for an ffi call, in which case 'isArgument' 1.2228 +// will be true. This allows us to handle a special case: if necessary, 1.2229 +// we can autoconvert a JS string primitive to a pointer-to-character type. 1.2230 +// In this case, ownership of the allocated string is handed off to the 1.2231 +// caller; 'freePointer' will be set to indicate this. 1.2232 +static bool 1.2233 +ImplicitConvert(JSContext* cx, 1.2234 + HandleValue val, 1.2235 + JSObject* targetType_, 1.2236 + void* buffer, 1.2237 + bool isArgument, 1.2238 + bool* freePointer) 1.2239 +{ 1.2240 + RootedObject targetType(cx, targetType_); 1.2241 + JS_ASSERT(CType::IsSizeDefined(targetType)); 1.2242 + 1.2243 + // First, check if val is either a CData object or a CDataFinalizer 1.2244 + // of type targetType. 1.2245 + JSObject* sourceData = nullptr; 1.2246 + JSObject* sourceType = nullptr; 1.2247 + RootedObject valObj(cx, nullptr); 1.2248 + if (!JSVAL_IS_PRIMITIVE(val)) { 1.2249 + valObj = JSVAL_TO_OBJECT(val); 1.2250 + if (CData::IsCData(valObj)) { 1.2251 + sourceData = valObj; 1.2252 + sourceType = CData::GetCType(sourceData); 1.2253 + 1.2254 + // If the types are equal, copy the buffer contained within the CData. 1.2255 + // (Note that the buffers may overlap partially or completely.) 1.2256 + if (CType::TypesEqual(sourceType, targetType)) { 1.2257 + size_t size = CType::GetSize(sourceType); 1.2258 + memmove(buffer, CData::GetData(sourceData), size); 1.2259 + return true; 1.2260 + } 1.2261 + } else if (CDataFinalizer::IsCDataFinalizer(valObj)) { 1.2262 + sourceData = valObj; 1.2263 + sourceType = CDataFinalizer::GetCType(cx, sourceData); 1.2264 + 1.2265 + CDataFinalizer::Private *p = (CDataFinalizer::Private *) 1.2266 + JS_GetPrivate(sourceData); 1.2267 + 1.2268 + if (!p) { 1.2269 + // We have called |dispose| or |forget| already. 1.2270 + JS_ReportError(cx, "Attempting to convert an empty CDataFinalizer"); 1.2271 + return false; 1.2272 + } 1.2273 + 1.2274 + // If the types are equal, copy the buffer contained within the CData. 1.2275 + if (CType::TypesEqual(sourceType, targetType)) { 1.2276 + memmove(buffer, p->cargs, p->cargs_size); 1.2277 + return true; 1.2278 + } 1.2279 + } 1.2280 + } 1.2281 + 1.2282 + TypeCode targetCode = CType::GetTypeCode(targetType); 1.2283 + 1.2284 + switch (targetCode) { 1.2285 + case TYPE_bool: { 1.2286 + // Do not implicitly lose bits, but allow the values 0, 1, and -0. 1.2287 + // Programs can convert explicitly, if needed, using `Boolean(v)` or `!!v`. 1.2288 + bool result; 1.2289 + if (!jsvalToBool(cx, val, &result)) 1.2290 + return TypeError(cx, "boolean", val); 1.2291 + *static_cast<bool*>(buffer) = result; 1.2292 + break; 1.2293 + } 1.2294 +#define DEFINE_INT_TYPE(name, type, ffiType) \ 1.2295 + case TYPE_##name: { \ 1.2296 + /* Do not implicitly lose bits. */ \ 1.2297 + type result; \ 1.2298 + if (!jsvalToInteger(cx, val, &result)) \ 1.2299 + return TypeError(cx, #name, val); \ 1.2300 + *static_cast<type*>(buffer) = result; \ 1.2301 + break; \ 1.2302 + } 1.2303 +#define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) 1.2304 +#define DEFINE_FLOAT_TYPE(name, type, ffiType) \ 1.2305 + case TYPE_##name: { \ 1.2306 + type result; \ 1.2307 + if (!jsvalToFloat(cx, val, &result)) \ 1.2308 + return TypeError(cx, #name, val); \ 1.2309 + *static_cast<type*>(buffer) = result; \ 1.2310 + break; \ 1.2311 + } 1.2312 +#define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) 1.2313 +#define DEFINE_JSCHAR_TYPE(name, type, ffiType) \ 1.2314 + case TYPE_##name: { \ 1.2315 + /* Convert from a 1-character string, regardless of encoding, */ \ 1.2316 + /* or from an integer, provided the result fits in 'type'. */ \ 1.2317 + type result; \ 1.2318 + if (JSVAL_IS_STRING(val)) { \ 1.2319 + JSString* str = JSVAL_TO_STRING(val); \ 1.2320 + if (str->length() != 1) \ 1.2321 + return TypeError(cx, #name, val); \ 1.2322 + const jschar *chars = str->getChars(cx); \ 1.2323 + if (!chars) \ 1.2324 + return false; \ 1.2325 + result = chars[0]; \ 1.2326 + } else if (!jsvalToInteger(cx, val, &result)) { \ 1.2327 + return TypeError(cx, #name, val); \ 1.2328 + } \ 1.2329 + *static_cast<type*>(buffer) = result; \ 1.2330 + break; \ 1.2331 + } 1.2332 +#include "ctypes/typedefs.h" 1.2333 + case TYPE_pointer: { 1.2334 + if (JSVAL_IS_NULL(val)) { 1.2335 + // Convert to a null pointer. 1.2336 + *static_cast<void**>(buffer) = nullptr; 1.2337 + break; 1.2338 + } 1.2339 + 1.2340 + JS::Rooted<JSObject*> baseType(cx, PointerType::GetBaseType(targetType)); 1.2341 + if (sourceData) { 1.2342 + // First, determine if the targetType is ctypes.void_t.ptr. 1.2343 + TypeCode sourceCode = CType::GetTypeCode(sourceType); 1.2344 + void* sourceBuffer = CData::GetData(sourceData); 1.2345 + bool voidptrTarget = CType::GetTypeCode(baseType) == TYPE_void_t; 1.2346 + 1.2347 + if (sourceCode == TYPE_pointer && voidptrTarget) { 1.2348 + // Autoconvert if targetType is ctypes.voidptr_t. 1.2349 + *static_cast<void**>(buffer) = *static_cast<void**>(sourceBuffer); 1.2350 + break; 1.2351 + } 1.2352 + if (sourceCode == TYPE_array) { 1.2353 + // Autoconvert an array to a ctypes.void_t.ptr or to 1.2354 + // sourceType.elementType.ptr, just like C. 1.2355 + JSObject* elementType = ArrayType::GetBaseType(sourceType); 1.2356 + if (voidptrTarget || CType::TypesEqual(baseType, elementType)) { 1.2357 + *static_cast<void**>(buffer) = sourceBuffer; 1.2358 + break; 1.2359 + } 1.2360 + } 1.2361 + 1.2362 + } else if (isArgument && JSVAL_IS_STRING(val)) { 1.2363 + // Convert the string for the ffi call. This requires allocating space 1.2364 + // which the caller assumes ownership of. 1.2365 + // TODO: Extend this so we can safely convert strings at other times also. 1.2366 + JSString* sourceString = JSVAL_TO_STRING(val); 1.2367 + size_t sourceLength = sourceString->length(); 1.2368 + const jschar* sourceChars = sourceString->getChars(cx); 1.2369 + if (!sourceChars) 1.2370 + return false; 1.2371 + 1.2372 + switch (CType::GetTypeCode(baseType)) { 1.2373 + case TYPE_char: 1.2374 + case TYPE_signed_char: 1.2375 + case TYPE_unsigned_char: { 1.2376 + // Convert from UTF-16 to UTF-8. 1.2377 + size_t nbytes = 1.2378 + GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength); 1.2379 + if (nbytes == (size_t) -1) 1.2380 + return false; 1.2381 + 1.2382 + char** charBuffer = static_cast<char**>(buffer); 1.2383 + *charBuffer = cx->pod_malloc<char>(nbytes + 1); 1.2384 + if (!*charBuffer) { 1.2385 + JS_ReportAllocationOverflow(cx); 1.2386 + return false; 1.2387 + } 1.2388 + 1.2389 + ASSERT_OK(DeflateStringToUTF8Buffer(cx, sourceChars, sourceLength, 1.2390 + *charBuffer, &nbytes)); 1.2391 + (*charBuffer)[nbytes] = 0; 1.2392 + *freePointer = true; 1.2393 + break; 1.2394 + } 1.2395 + case TYPE_jschar: { 1.2396 + // Copy the jschar string data. (We could provide direct access to the 1.2397 + // JSString's buffer, but this approach is safer if the caller happens 1.2398 + // to modify the string.) 1.2399 + jschar** jscharBuffer = static_cast<jschar**>(buffer); 1.2400 + *jscharBuffer = cx->pod_malloc<jschar>(sourceLength + 1); 1.2401 + if (!*jscharBuffer) { 1.2402 + JS_ReportAllocationOverflow(cx); 1.2403 + return false; 1.2404 + } 1.2405 + 1.2406 + *freePointer = true; 1.2407 + memcpy(*jscharBuffer, sourceChars, sourceLength * sizeof(jschar)); 1.2408 + (*jscharBuffer)[sourceLength] = 0; 1.2409 + break; 1.2410 + } 1.2411 + default: 1.2412 + return TypeError(cx, "string pointer", val); 1.2413 + } 1.2414 + break; 1.2415 + } else if (!JSVAL_IS_PRIMITIVE(val) && JS_IsArrayBufferObject(valObj)) { 1.2416 + // Convert ArrayBuffer to pointer without any copy. 1.2417 + // Just as with C arrays, we make no effort to 1.2418 + // keep the ArrayBuffer alive. 1.2419 + void* p = JS_GetStableArrayBufferData(cx, valObj); 1.2420 + if (!p) 1.2421 + return false; 1.2422 + *static_cast<void**>(buffer) = p; 1.2423 + break; 1.2424 + } if (!JSVAL_IS_PRIMITIVE(val) && JS_IsTypedArrayObject(valObj)) { 1.2425 + if(!CanConvertTypedArrayItemTo(baseType, valObj, cx)) { 1.2426 + return TypeError(cx, "typed array with the appropriate type", val); 1.2427 + } 1.2428 + 1.2429 + // Convert TypedArray to pointer without any copy. 1.2430 + // Just as with C arrays, we make no effort to 1.2431 + // keep the TypedArray alive. 1.2432 + *static_cast<void**>(buffer) = JS_GetArrayBufferViewData(valObj); 1.2433 + break; 1.2434 + } 1.2435 + return TypeError(cx, "pointer", val); 1.2436 + } 1.2437 + case TYPE_array: { 1.2438 + RootedObject baseType(cx, ArrayType::GetBaseType(targetType)); 1.2439 + size_t targetLength = ArrayType::GetLength(targetType); 1.2440 + 1.2441 + if (JSVAL_IS_STRING(val)) { 1.2442 + JSString* sourceString = JSVAL_TO_STRING(val); 1.2443 + size_t sourceLength = sourceString->length(); 1.2444 + const jschar* sourceChars = sourceString->getChars(cx); 1.2445 + if (!sourceChars) 1.2446 + return false; 1.2447 + 1.2448 + switch (CType::GetTypeCode(baseType)) { 1.2449 + case TYPE_char: 1.2450 + case TYPE_signed_char: 1.2451 + case TYPE_unsigned_char: { 1.2452 + // Convert from UTF-16 to UTF-8. 1.2453 + size_t nbytes = 1.2454 + GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength); 1.2455 + if (nbytes == (size_t) -1) 1.2456 + return false; 1.2457 + 1.2458 + if (targetLength < nbytes) { 1.2459 + JS_ReportError(cx, "ArrayType has insufficient length"); 1.2460 + return false; 1.2461 + } 1.2462 + 1.2463 + char* charBuffer = static_cast<char*>(buffer); 1.2464 + ASSERT_OK(DeflateStringToUTF8Buffer(cx, sourceChars, sourceLength, 1.2465 + charBuffer, &nbytes)); 1.2466 + 1.2467 + if (targetLength > nbytes) 1.2468 + charBuffer[nbytes] = 0; 1.2469 + 1.2470 + break; 1.2471 + } 1.2472 + case TYPE_jschar: { 1.2473 + // Copy the string data, jschar for jschar, including the terminator 1.2474 + // if there's space. 1.2475 + if (targetLength < sourceLength) { 1.2476 + JS_ReportError(cx, "ArrayType has insufficient length"); 1.2477 + return false; 1.2478 + } 1.2479 + 1.2480 + memcpy(buffer, sourceChars, sourceLength * sizeof(jschar)); 1.2481 + if (targetLength > sourceLength) 1.2482 + static_cast<jschar*>(buffer)[sourceLength] = 0; 1.2483 + 1.2484 + break; 1.2485 + } 1.2486 + default: 1.2487 + return TypeError(cx, "array", val); 1.2488 + } 1.2489 + 1.2490 + } else if (!JSVAL_IS_PRIMITIVE(val) && JS_IsArrayObject(cx, valObj)) { 1.2491 + // Convert each element of the array by calling ImplicitConvert. 1.2492 + uint32_t sourceLength; 1.2493 + if (!JS_GetArrayLength(cx, valObj, &sourceLength) || 1.2494 + targetLength != size_t(sourceLength)) { 1.2495 + JS_ReportError(cx, "ArrayType length does not match source array length"); 1.2496 + return false; 1.2497 + } 1.2498 + 1.2499 + // Convert into an intermediate, in case of failure. 1.2500 + size_t elementSize = CType::GetSize(baseType); 1.2501 + size_t arraySize = elementSize * targetLength; 1.2502 + AutoPtr<char> intermediate(cx->pod_malloc<char>(arraySize)); 1.2503 + if (!intermediate) { 1.2504 + JS_ReportAllocationOverflow(cx); 1.2505 + return false; 1.2506 + } 1.2507 + 1.2508 + for (uint32_t i = 0; i < sourceLength; ++i) { 1.2509 + RootedValue item(cx); 1.2510 + if (!JS_GetElement(cx, valObj, i, &item)) 1.2511 + return false; 1.2512 + 1.2513 + char* data = intermediate.get() + elementSize * i; 1.2514 + if (!ImplicitConvert(cx, item, baseType, data, false, nullptr)) 1.2515 + return false; 1.2516 + } 1.2517 + 1.2518 + memcpy(buffer, intermediate.get(), arraySize); 1.2519 + 1.2520 + } else if (!JSVAL_IS_PRIMITIVE(val) && 1.2521 + JS_IsArrayBufferObject(valObj)) { 1.2522 + // Check that array is consistent with type, then 1.2523 + // copy the array. 1.2524 + uint32_t sourceLength = JS_GetArrayBufferByteLength(valObj); 1.2525 + size_t elementSize = CType::GetSize(baseType); 1.2526 + size_t arraySize = elementSize * targetLength; 1.2527 + if (arraySize != size_t(sourceLength)) { 1.2528 + JS_ReportError(cx, "ArrayType length does not match source ArrayBuffer length"); 1.2529 + return false; 1.2530 + } 1.2531 + memcpy(buffer, JS_GetArrayBufferData(valObj), sourceLength); 1.2532 + break; 1.2533 + } else if (!JSVAL_IS_PRIMITIVE(val) && 1.2534 + JS_IsTypedArrayObject(valObj)) { 1.2535 + // Check that array is consistent with type, then 1.2536 + // copy the array. 1.2537 + if(!CanConvertTypedArrayItemTo(baseType, valObj, cx)) { 1.2538 + return TypeError(cx, "typed array with the appropriate type", val); 1.2539 + } 1.2540 + 1.2541 + uint32_t sourceLength = JS_GetTypedArrayByteLength(valObj); 1.2542 + size_t elementSize = CType::GetSize(baseType); 1.2543 + size_t arraySize = elementSize * targetLength; 1.2544 + if (arraySize != size_t(sourceLength)) { 1.2545 + JS_ReportError(cx, "typed array length does not match source TypedArray length"); 1.2546 + return false; 1.2547 + } 1.2548 + memcpy(buffer, JS_GetArrayBufferViewData(valObj), sourceLength); 1.2549 + break; 1.2550 + } else { 1.2551 + // Don't implicitly convert to string. Users can implicitly convert 1.2552 + // with `String(x)` or `""+x`. 1.2553 + return TypeError(cx, "array", val); 1.2554 + } 1.2555 + break; 1.2556 + } 1.2557 + case TYPE_struct: { 1.2558 + if (!JSVAL_IS_PRIMITIVE(val) && !sourceData) { 1.2559 + // Enumerate the properties of the object; if they match the struct 1.2560 + // specification, convert the fields. 1.2561 + RootedObject iter(cx, JS_NewPropertyIterator(cx, valObj)); 1.2562 + if (!iter) 1.2563 + return false; 1.2564 + 1.2565 + // Convert into an intermediate, in case of failure. 1.2566 + size_t structSize = CType::GetSize(targetType); 1.2567 + AutoPtr<char> intermediate(cx->pod_malloc<char>(structSize)); 1.2568 + if (!intermediate) { 1.2569 + JS_ReportAllocationOverflow(cx); 1.2570 + return false; 1.2571 + } 1.2572 + 1.2573 + RootedId id(cx); 1.2574 + size_t i = 0; 1.2575 + while (1) { 1.2576 + if (!JS_NextProperty(cx, iter, id.address())) 1.2577 + return false; 1.2578 + if (JSID_IS_VOID(id)) 1.2579 + break; 1.2580 + 1.2581 + if (!JSID_IS_STRING(id)) { 1.2582 + JS_ReportError(cx, "property name is not a string"); 1.2583 + return false; 1.2584 + } 1.2585 + 1.2586 + JSFlatString *name = JSID_TO_FLAT_STRING(id); 1.2587 + const FieldInfo* field = StructType::LookupField(cx, targetType, name); 1.2588 + if (!field) 1.2589 + return false; 1.2590 + 1.2591 + RootedValue prop(cx); 1.2592 + if (!JS_GetPropertyById(cx, valObj, id, &prop)) 1.2593 + return false; 1.2594 + 1.2595 + // Convert the field via ImplicitConvert(). 1.2596 + char* fieldData = intermediate.get() + field->mOffset; 1.2597 + if (!ImplicitConvert(cx, prop, field->mType, fieldData, false, nullptr)) 1.2598 + return false; 1.2599 + 1.2600 + ++i; 1.2601 + } 1.2602 + 1.2603 + const FieldInfoHash* fields = StructType::GetFieldInfo(targetType); 1.2604 + if (i != fields->count()) { 1.2605 + JS_ReportError(cx, "missing fields"); 1.2606 + return false; 1.2607 + } 1.2608 + 1.2609 + memcpy(buffer, intermediate.get(), structSize); 1.2610 + break; 1.2611 + } 1.2612 + 1.2613 + return TypeError(cx, "struct", val); 1.2614 + } 1.2615 + case TYPE_void_t: 1.2616 + case TYPE_function: 1.2617 + MOZ_ASSUME_UNREACHABLE("invalid type"); 1.2618 + } 1.2619 + 1.2620 + return true; 1.2621 +} 1.2622 + 1.2623 +// Convert jsval 'val' to a C binary representation of CType 'targetType', 1.2624 +// storing the result in 'buffer'. This function is more forceful than 1.2625 +// ImplicitConvert. 1.2626 +static bool 1.2627 +ExplicitConvert(JSContext* cx, HandleValue val, HandleObject targetType, void* buffer) 1.2628 +{ 1.2629 + // If ImplicitConvert succeeds, use that result. 1.2630 + if (ImplicitConvert(cx, val, targetType, buffer, false, nullptr)) 1.2631 + return true; 1.2632 + 1.2633 + // If ImplicitConvert failed, and there is no pending exception, then assume 1.2634 + // hard failure (out of memory, or some other similarly serious condition). 1.2635 + // We store any pending exception in case we need to re-throw it. 1.2636 + RootedValue ex(cx); 1.2637 + if (!JS_GetPendingException(cx, &ex)) 1.2638 + return false; 1.2639 + 1.2640 + // Otherwise, assume soft failure. Clear the pending exception so that we 1.2641 + // can throw a different one as required. 1.2642 + JS_ClearPendingException(cx); 1.2643 + 1.2644 + TypeCode type = CType::GetTypeCode(targetType); 1.2645 + 1.2646 + switch (type) { 1.2647 + case TYPE_bool: { 1.2648 + *static_cast<bool*>(buffer) = ToBoolean(val); 1.2649 + break; 1.2650 + } 1.2651 +#define DEFINE_INT_TYPE(name, type, ffiType) \ 1.2652 + case TYPE_##name: { \ 1.2653 + /* Convert numeric values with a C-style cast, and */ \ 1.2654 + /* allow conversion from a base-10 or base-16 string. */ \ 1.2655 + type result; \ 1.2656 + if (!jsvalToIntegerExplicit(val, &result) && \ 1.2657 + (!JSVAL_IS_STRING(val) || \ 1.2658 + !StringToInteger(cx, JSVAL_TO_STRING(val), &result))) \ 1.2659 + return TypeError(cx, #name, val); \ 1.2660 + *static_cast<type*>(buffer) = result; \ 1.2661 + break; \ 1.2662 + } 1.2663 +#define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) 1.2664 +#define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) 1.2665 +#define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_CHAR_TYPE(x, y, z) 1.2666 +#include "ctypes/typedefs.h" 1.2667 + case TYPE_pointer: { 1.2668 + // Convert a number, Int64 object, or UInt64 object to a pointer. 1.2669 + uintptr_t result; 1.2670 + if (!jsvalToPtrExplicit(cx, val, &result)) 1.2671 + return TypeError(cx, "pointer", val); 1.2672 + *static_cast<uintptr_t*>(buffer) = result; 1.2673 + break; 1.2674 + } 1.2675 + case TYPE_float32_t: 1.2676 + case TYPE_float64_t: 1.2677 + case TYPE_float: 1.2678 + case TYPE_double: 1.2679 + case TYPE_array: 1.2680 + case TYPE_struct: 1.2681 + // ImplicitConvert is sufficient. Re-throw the exception it generated. 1.2682 + JS_SetPendingException(cx, ex); 1.2683 + return false; 1.2684 + case TYPE_void_t: 1.2685 + case TYPE_function: 1.2686 + MOZ_ASSUME_UNREACHABLE("invalid type"); 1.2687 + } 1.2688 + return true; 1.2689 +} 1.2690 + 1.2691 +// Given a CType 'typeObj', generate a string describing the C type declaration 1.2692 +// corresponding to 'typeObj'. For instance, the CType constructed from 1.2693 +// 'ctypes.int32_t.ptr.array(4).ptr.ptr' will result in the type string 1.2694 +// 'int32_t*(**)[4]'. 1.2695 +static JSString* 1.2696 +BuildTypeName(JSContext* cx, JSObject* typeObj_) 1.2697 +{ 1.2698 + AutoString result; 1.2699 + RootedObject typeObj(cx, typeObj_); 1.2700 + 1.2701 + // Walk the hierarchy of types, outermost to innermost, building up the type 1.2702 + // string. This consists of the base type, which goes on the left. 1.2703 + // Derived type modifiers (* and []) build from the inside outward, with 1.2704 + // pointers on the left and arrays on the right. An excellent description 1.2705 + // of the rules for building C type declarations can be found at: 1.2706 + // http://unixwiz.net/techtips/reading-cdecl.html 1.2707 + TypeCode prevGrouping = CType::GetTypeCode(typeObj), currentGrouping; 1.2708 + while (1) { 1.2709 + currentGrouping = CType::GetTypeCode(typeObj); 1.2710 + switch (currentGrouping) { 1.2711 + case TYPE_pointer: { 1.2712 + // Pointer types go on the left. 1.2713 + PrependString(result, "*"); 1.2714 + 1.2715 + typeObj = PointerType::GetBaseType(typeObj); 1.2716 + prevGrouping = currentGrouping; 1.2717 + continue; 1.2718 + } 1.2719 + case TYPE_array: { 1.2720 + if (prevGrouping == TYPE_pointer) { 1.2721 + // Outer type is pointer, inner type is array. Grouping is required. 1.2722 + PrependString(result, "("); 1.2723 + AppendString(result, ")"); 1.2724 + } 1.2725 + 1.2726 + // Array types go on the right. 1.2727 + AppendString(result, "["); 1.2728 + size_t length; 1.2729 + if (ArrayType::GetSafeLength(typeObj, &length)) 1.2730 + IntegerToString(length, 10, result); 1.2731 + 1.2732 + AppendString(result, "]"); 1.2733 + 1.2734 + typeObj = ArrayType::GetBaseType(typeObj); 1.2735 + prevGrouping = currentGrouping; 1.2736 + continue; 1.2737 + } 1.2738 + case TYPE_function: { 1.2739 + FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj); 1.2740 + 1.2741 + // Add in the calling convention, if it's not cdecl. 1.2742 + // There's no trailing or leading space needed here, as none of the 1.2743 + // modifiers can produce a string beginning with an identifier --- 1.2744 + // except for TYPE_function itself, which is fine because functions 1.2745 + // can't return functions. 1.2746 + ABICode abi = GetABICode(fninfo->mABI); 1.2747 + if (abi == ABI_STDCALL) 1.2748 + PrependString(result, "__stdcall"); 1.2749 + else if (abi == ABI_WINAPI) 1.2750 + PrependString(result, "WINAPI"); 1.2751 + 1.2752 + // Function application binds more tightly than dereferencing, so 1.2753 + // wrap pointer types in parens. Functions can't return functions 1.2754 + // (only pointers to them), and arrays can't hold functions 1.2755 + // (similarly), so we don't need to address those cases. 1.2756 + if (prevGrouping == TYPE_pointer) { 1.2757 + PrependString(result, "("); 1.2758 + AppendString(result, ")"); 1.2759 + } 1.2760 + 1.2761 + // Argument list goes on the right. 1.2762 + AppendString(result, "("); 1.2763 + for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) { 1.2764 + RootedObject argType(cx, fninfo->mArgTypes[i]); 1.2765 + JSString* argName = CType::GetName(cx, argType); 1.2766 + AppendString(result, argName); 1.2767 + if (i != fninfo->mArgTypes.length() - 1 || 1.2768 + fninfo->mIsVariadic) 1.2769 + AppendString(result, ", "); 1.2770 + } 1.2771 + if (fninfo->mIsVariadic) 1.2772 + AppendString(result, "..."); 1.2773 + AppendString(result, ")"); 1.2774 + 1.2775 + // Set 'typeObj' to the return type, and let the loop process it. 1.2776 + // 'prevGrouping' doesn't matter here, because functions cannot return 1.2777 + // arrays -- thus the parenthetical rules don't get tickled. 1.2778 + typeObj = fninfo->mReturnType; 1.2779 + continue; 1.2780 + } 1.2781 + default: 1.2782 + // Either a basic or struct type. Use the type's name as the base type. 1.2783 + break; 1.2784 + } 1.2785 + break; 1.2786 + } 1.2787 + 1.2788 + // If prepending the base type name directly would splice two 1.2789 + // identifiers, insert a space. 1.2790 + if (('a' <= result[0] && result[0] <= 'z') || 1.2791 + ('A' <= result[0] && result[0] <= 'Z') || 1.2792 + (result[0] == '_')) 1.2793 + PrependString(result, " "); 1.2794 + 1.2795 + // Stick the base type and derived type parts together. 1.2796 + JSString* baseName = CType::GetName(cx, typeObj); 1.2797 + PrependString(result, baseName); 1.2798 + return NewUCString(cx, result); 1.2799 +} 1.2800 + 1.2801 +// Given a CType 'typeObj', generate a string 'result' such that 'eval(result)' 1.2802 +// would construct the same CType. If 'makeShort' is true, assume that any 1.2803 +// StructType 't' is bound to an in-scope variable of name 't.name', and use 1.2804 +// that variable in place of generating a string to construct the type 't'. 1.2805 +// (This means the type comparison function CType::TypesEqual will return true 1.2806 +// when comparing the input and output of BuildTypeSource, since struct 1.2807 +// equality is determined by strict JSObject pointer equality.) 1.2808 +static void 1.2809 +BuildTypeSource(JSContext* cx, 1.2810 + JSObject* typeObj_, 1.2811 + bool makeShort, 1.2812 + AutoString& result) 1.2813 +{ 1.2814 + RootedObject typeObj(cx, typeObj_); 1.2815 + 1.2816 + // Walk the types, building up the toSource() string. 1.2817 + switch (CType::GetTypeCode(typeObj)) { 1.2818 + case TYPE_void_t: 1.2819 +#define DEFINE_TYPE(name, type, ffiType) \ 1.2820 + case TYPE_##name: 1.2821 +#include "ctypes/typedefs.h" 1.2822 + { 1.2823 + AppendString(result, "ctypes."); 1.2824 + JSString* nameStr = CType::GetName(cx, typeObj); 1.2825 + AppendString(result, nameStr); 1.2826 + break; 1.2827 + } 1.2828 + case TYPE_pointer: { 1.2829 + RootedObject baseType(cx, PointerType::GetBaseType(typeObj)); 1.2830 + 1.2831 + // Specialcase ctypes.voidptr_t. 1.2832 + if (CType::GetTypeCode(baseType) == TYPE_void_t) { 1.2833 + AppendString(result, "ctypes.voidptr_t"); 1.2834 + break; 1.2835 + } 1.2836 + 1.2837 + // Recursively build the source string, and append '.ptr'. 1.2838 + BuildTypeSource(cx, baseType, makeShort, result); 1.2839 + AppendString(result, ".ptr"); 1.2840 + break; 1.2841 + } 1.2842 + case TYPE_function: { 1.2843 + FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj); 1.2844 + 1.2845 + AppendString(result, "ctypes.FunctionType("); 1.2846 + 1.2847 + switch (GetABICode(fninfo->mABI)) { 1.2848 + case ABI_DEFAULT: 1.2849 + AppendString(result, "ctypes.default_abi, "); 1.2850 + break; 1.2851 + case ABI_STDCALL: 1.2852 + AppendString(result, "ctypes.stdcall_abi, "); 1.2853 + break; 1.2854 + case ABI_WINAPI: 1.2855 + AppendString(result, "ctypes.winapi_abi, "); 1.2856 + break; 1.2857 + case INVALID_ABI: 1.2858 + MOZ_ASSUME_UNREACHABLE("invalid abi"); 1.2859 + } 1.2860 + 1.2861 + // Recursively build the source string describing the function return and 1.2862 + // argument types. 1.2863 + BuildTypeSource(cx, fninfo->mReturnType, true, result); 1.2864 + 1.2865 + if (fninfo->mArgTypes.length() > 0) { 1.2866 + AppendString(result, ", ["); 1.2867 + for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) { 1.2868 + BuildTypeSource(cx, fninfo->mArgTypes[i], true, result); 1.2869 + if (i != fninfo->mArgTypes.length() - 1 || 1.2870 + fninfo->mIsVariadic) 1.2871 + AppendString(result, ", "); 1.2872 + } 1.2873 + if (fninfo->mIsVariadic) 1.2874 + AppendString(result, "\"...\""); 1.2875 + AppendString(result, "]"); 1.2876 + } 1.2877 + 1.2878 + AppendString(result, ")"); 1.2879 + break; 1.2880 + } 1.2881 + case TYPE_array: { 1.2882 + // Recursively build the source string, and append '.array(n)', 1.2883 + // where n is the array length, or the empty string if the array length 1.2884 + // is undefined. 1.2885 + JSObject* baseType = ArrayType::GetBaseType(typeObj); 1.2886 + BuildTypeSource(cx, baseType, makeShort, result); 1.2887 + AppendString(result, ".array("); 1.2888 + 1.2889 + size_t length; 1.2890 + if (ArrayType::GetSafeLength(typeObj, &length)) 1.2891 + IntegerToString(length, 10, result); 1.2892 + 1.2893 + AppendString(result, ")"); 1.2894 + break; 1.2895 + } 1.2896 + case TYPE_struct: { 1.2897 + JSString* name = CType::GetName(cx, typeObj); 1.2898 + 1.2899 + if (makeShort) { 1.2900 + // Shorten the type declaration by assuming that StructType 't' is bound 1.2901 + // to an in-scope variable of name 't.name'. 1.2902 + AppendString(result, name); 1.2903 + break; 1.2904 + } 1.2905 + 1.2906 + // Write the full struct declaration. 1.2907 + AppendString(result, "ctypes.StructType(\""); 1.2908 + AppendString(result, name); 1.2909 + AppendString(result, "\""); 1.2910 + 1.2911 + // If it's an opaque struct, we're done. 1.2912 + if (!CType::IsSizeDefined(typeObj)) { 1.2913 + AppendString(result, ")"); 1.2914 + break; 1.2915 + } 1.2916 + 1.2917 + AppendString(result, ", ["); 1.2918 + 1.2919 + const FieldInfoHash* fields = StructType::GetFieldInfo(typeObj); 1.2920 + size_t length = fields->count(); 1.2921 + Array<const FieldInfoHash::Entry*, 64> fieldsArray; 1.2922 + if (!fieldsArray.resize(length)) 1.2923 + break; 1.2924 + 1.2925 + for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) 1.2926 + fieldsArray[r.front().value().mIndex] = &r.front(); 1.2927 + 1.2928 + for (size_t i = 0; i < length; ++i) { 1.2929 + const FieldInfoHash::Entry* entry = fieldsArray[i]; 1.2930 + AppendString(result, "{ \""); 1.2931 + AppendString(result, entry->key()); 1.2932 + AppendString(result, "\": "); 1.2933 + BuildTypeSource(cx, entry->value().mType, true, result); 1.2934 + AppendString(result, " }"); 1.2935 + if (i != length - 1) 1.2936 + AppendString(result, ", "); 1.2937 + } 1.2938 + 1.2939 + AppendString(result, "])"); 1.2940 + break; 1.2941 + } 1.2942 + } 1.2943 +} 1.2944 + 1.2945 +// Given a CData object of CType 'typeObj' with binary value 'data', generate a 1.2946 +// string 'result' such that 'eval(result)' would construct a CData object with 1.2947 +// the same CType and containing the same binary value. This assumes that any 1.2948 +// StructType 't' is bound to an in-scope variable of name 't.name'. (This means 1.2949 +// the type comparison function CType::TypesEqual will return true when 1.2950 +// comparing the types, since struct equality is determined by strict JSObject 1.2951 +// pointer equality.) Further, if 'isImplicit' is true, ensure that the 1.2952 +// resulting string can ImplicitConvert successfully if passed to another data 1.2953 +// constructor. (This is important when called recursively, since fields of 1.2954 +// structs and arrays are converted with ImplicitConvert.) 1.2955 +static bool 1.2956 +BuildDataSource(JSContext* cx, 1.2957 + HandleObject typeObj, 1.2958 + void* data, 1.2959 + bool isImplicit, 1.2960 + AutoString& result) 1.2961 +{ 1.2962 + TypeCode type = CType::GetTypeCode(typeObj); 1.2963 + switch (type) { 1.2964 + case TYPE_bool: 1.2965 + if (*static_cast<bool*>(data)) 1.2966 + AppendString(result, "true"); 1.2967 + else 1.2968 + AppendString(result, "false"); 1.2969 + break; 1.2970 +#define DEFINE_INT_TYPE(name, type, ffiType) \ 1.2971 + case TYPE_##name: \ 1.2972 + /* Serialize as a primitive decimal integer. */ \ 1.2973 + IntegerToString(*static_cast<type*>(data), 10, result); \ 1.2974 + break; 1.2975 +#define DEFINE_WRAPPED_INT_TYPE(name, type, ffiType) \ 1.2976 + case TYPE_##name: \ 1.2977 + /* Serialize as a wrapped decimal integer. */ \ 1.2978 + if (!NumericLimits<type>::is_signed) \ 1.2979 + AppendString(result, "ctypes.UInt64(\""); \ 1.2980 + else \ 1.2981 + AppendString(result, "ctypes.Int64(\""); \ 1.2982 + \ 1.2983 + IntegerToString(*static_cast<type*>(data), 10, result); \ 1.2984 + AppendString(result, "\")"); \ 1.2985 + break; 1.2986 +#define DEFINE_FLOAT_TYPE(name, type, ffiType) \ 1.2987 + case TYPE_##name: { \ 1.2988 + /* Serialize as a primitive double. */ \ 1.2989 + double fp = *static_cast<type*>(data); \ 1.2990 + ToCStringBuf cbuf; \ 1.2991 + char* str = NumberToCString(cx, &cbuf, fp); \ 1.2992 + if (!str) { \ 1.2993 + JS_ReportOutOfMemory(cx); \ 1.2994 + return false; \ 1.2995 + } \ 1.2996 + \ 1.2997 + result.append(str, strlen(str)); \ 1.2998 + break; \ 1.2999 + } 1.3000 +#define DEFINE_CHAR_TYPE(name, type, ffiType) \ 1.3001 + case TYPE_##name: \ 1.3002 + /* Serialize as an integer. */ \ 1.3003 + IntegerToString(*static_cast<type*>(data), 10, result); \ 1.3004 + break; 1.3005 +#include "ctypes/typedefs.h" 1.3006 + case TYPE_jschar: { 1.3007 + // Serialize as a 1-character JS string. 1.3008 + JSString* str = JS_NewUCStringCopyN(cx, static_cast<jschar*>(data), 1); 1.3009 + if (!str) 1.3010 + return false; 1.3011 + 1.3012 + // Escape characters, and quote as necessary. 1.3013 + RootedValue valStr(cx, StringValue(str)); 1.3014 + JSString* src = JS_ValueToSource(cx, valStr); 1.3015 + if (!src) 1.3016 + return false; 1.3017 + 1.3018 + AppendString(result, src); 1.3019 + break; 1.3020 + } 1.3021 + case TYPE_pointer: 1.3022 + case TYPE_function: { 1.3023 + if (isImplicit) { 1.3024 + // The result must be able to ImplicitConvert successfully. 1.3025 + // Wrap in a type constructor, then serialize for ExplicitConvert. 1.3026 + BuildTypeSource(cx, typeObj, true, result); 1.3027 + AppendString(result, "("); 1.3028 + } 1.3029 + 1.3030 + // Serialize the pointer value as a wrapped hexadecimal integer. 1.3031 + uintptr_t ptr = *static_cast<uintptr_t*>(data); 1.3032 + AppendString(result, "ctypes.UInt64(\"0x"); 1.3033 + IntegerToString(ptr, 16, result); 1.3034 + AppendString(result, "\")"); 1.3035 + 1.3036 + if (isImplicit) 1.3037 + AppendString(result, ")"); 1.3038 + 1.3039 + break; 1.3040 + } 1.3041 + case TYPE_array: { 1.3042 + // Serialize each element of the array recursively. Each element must 1.3043 + // be able to ImplicitConvert successfully. 1.3044 + RootedObject baseType(cx, ArrayType::GetBaseType(typeObj)); 1.3045 + AppendString(result, "["); 1.3046 + 1.3047 + size_t length = ArrayType::GetLength(typeObj); 1.3048 + size_t elementSize = CType::GetSize(baseType); 1.3049 + for (size_t i = 0; i < length; ++i) { 1.3050 + char* element = static_cast<char*>(data) + elementSize * i; 1.3051 + if (!BuildDataSource(cx, baseType, element, true, result)) 1.3052 + return false; 1.3053 + 1.3054 + if (i + 1 < length) 1.3055 + AppendString(result, ", "); 1.3056 + } 1.3057 + AppendString(result, "]"); 1.3058 + break; 1.3059 + } 1.3060 + case TYPE_struct: { 1.3061 + if (isImplicit) { 1.3062 + // The result must be able to ImplicitConvert successfully. 1.3063 + // Serialize the data as an object with properties, rather than 1.3064 + // a sequence of arguments to the StructType constructor. 1.3065 + AppendString(result, "{"); 1.3066 + } 1.3067 + 1.3068 + // Serialize each field of the struct recursively. Each field must 1.3069 + // be able to ImplicitConvert successfully. 1.3070 + const FieldInfoHash* fields = StructType::GetFieldInfo(typeObj); 1.3071 + size_t length = fields->count(); 1.3072 + Array<const FieldInfoHash::Entry*, 64> fieldsArray; 1.3073 + if (!fieldsArray.resize(length)) 1.3074 + return false; 1.3075 + 1.3076 + for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) 1.3077 + fieldsArray[r.front().value().mIndex] = &r.front(); 1.3078 + 1.3079 + for (size_t i = 0; i < length; ++i) { 1.3080 + const FieldInfoHash::Entry* entry = fieldsArray[i]; 1.3081 + 1.3082 + if (isImplicit) { 1.3083 + AppendString(result, "\""); 1.3084 + AppendString(result, entry->key()); 1.3085 + AppendString(result, "\": "); 1.3086 + } 1.3087 + 1.3088 + char* fieldData = static_cast<char*>(data) + entry->value().mOffset; 1.3089 + RootedObject entryType(cx, entry->value().mType); 1.3090 + if (!BuildDataSource(cx, entryType, fieldData, true, result)) 1.3091 + return false; 1.3092 + 1.3093 + if (i + 1 != length) 1.3094 + AppendString(result, ", "); 1.3095 + } 1.3096 + 1.3097 + if (isImplicit) 1.3098 + AppendString(result, "}"); 1.3099 + 1.3100 + break; 1.3101 + } 1.3102 + case TYPE_void_t: 1.3103 + MOZ_ASSUME_UNREACHABLE("invalid type"); 1.3104 + } 1.3105 + 1.3106 + return true; 1.3107 +} 1.3108 + 1.3109 +/******************************************************************************* 1.3110 +** JSAPI callback function implementations 1.3111 +*******************************************************************************/ 1.3112 + 1.3113 +bool 1.3114 +ConstructAbstract(JSContext* cx, 1.3115 + unsigned argc, 1.3116 + jsval* vp) 1.3117 +{ 1.3118 + // Calling an abstract base class constructor is disallowed. 1.3119 + JS_ReportError(cx, "cannot construct from abstract type"); 1.3120 + return false; 1.3121 +} 1.3122 + 1.3123 +/******************************************************************************* 1.3124 +** CType implementation 1.3125 +*******************************************************************************/ 1.3126 + 1.3127 +bool 1.3128 +CType::ConstructData(JSContext* cx, 1.3129 + unsigned argc, 1.3130 + jsval* vp) 1.3131 +{ 1.3132 + CallArgs args = CallArgsFromVp(argc, vp); 1.3133 + // get the callee object... 1.3134 + RootedObject obj(cx, &args.callee()); 1.3135 + if (!CType::IsCType(obj)) { 1.3136 + JS_ReportError(cx, "not a CType"); 1.3137 + return false; 1.3138 + } 1.3139 + 1.3140 + // How we construct the CData object depends on what type we represent. 1.3141 + // An instance 'd' of a CData object of type 't' has: 1.3142 + // * [[Class]] "CData" 1.3143 + // * __proto__ === t.prototype 1.3144 + switch (GetTypeCode(obj)) { 1.3145 + case TYPE_void_t: 1.3146 + JS_ReportError(cx, "cannot construct from void_t"); 1.3147 + return false; 1.3148 + case TYPE_function: 1.3149 + JS_ReportError(cx, "cannot construct from FunctionType; use FunctionType.ptr instead"); 1.3150 + return false; 1.3151 + case TYPE_pointer: 1.3152 + return PointerType::ConstructData(cx, obj, args); 1.3153 + case TYPE_array: 1.3154 + return ArrayType::ConstructData(cx, obj, args); 1.3155 + case TYPE_struct: 1.3156 + return StructType::ConstructData(cx, obj, args); 1.3157 + default: 1.3158 + return ConstructBasic(cx, obj, args); 1.3159 + } 1.3160 +} 1.3161 + 1.3162 +bool 1.3163 +CType::ConstructBasic(JSContext* cx, 1.3164 + HandleObject obj, 1.3165 + const CallArgs& args) 1.3166 +{ 1.3167 + if (args.length() > 1) { 1.3168 + JS_ReportError(cx, "CType constructor takes zero or one argument"); 1.3169 + return false; 1.3170 + } 1.3171 + 1.3172 + // construct a CData object 1.3173 + RootedObject result(cx, CData::Create(cx, obj, NullPtr(), nullptr, true)); 1.3174 + if (!result) 1.3175 + return false; 1.3176 + 1.3177 + if (args.length() == 1) { 1.3178 + if (!ExplicitConvert(cx, args[0], obj, CData::GetData(result))) 1.3179 + return false; 1.3180 + } 1.3181 + 1.3182 + args.rval().setObject(*result); 1.3183 + return true; 1.3184 +} 1.3185 + 1.3186 +JSObject* 1.3187 +CType::Create(JSContext* cx, 1.3188 + HandleObject typeProto, 1.3189 + HandleObject dataProto, 1.3190 + TypeCode type, 1.3191 + JSString* name_, 1.3192 + jsval size_, 1.3193 + jsval align_, 1.3194 + ffi_type* ffiType) 1.3195 +{ 1.3196 + RootedString name(cx, name_); 1.3197 + RootedValue size(cx, size_); 1.3198 + RootedValue align(cx, align_); 1.3199 + RootedObject parent(cx, JS_GetParent(typeProto)); 1.3200 + JS_ASSERT(parent); 1.3201 + 1.3202 + // Create a CType object with the properties and slots common to all CTypes. 1.3203 + // Each type object 't' has: 1.3204 + // * [[Class]] "CType" 1.3205 + // * __proto__ === 'typeProto'; one of ctypes.{CType,PointerType,ArrayType, 1.3206 + // StructType}.prototype 1.3207 + // * A constructor which creates and returns a CData object, containing 1.3208 + // binary data of the given type. 1.3209 + // * 'prototype' property: 1.3210 + // * [[Class]] "CDataProto" 1.3211 + // * __proto__ === 'dataProto'; an object containing properties and 1.3212 + // functions common to all CData objects of types derived from 1.3213 + // 'typeProto'. (For instance, this could be ctypes.CData.prototype 1.3214 + // for simple types, or something representing structs for StructTypes.) 1.3215 + // * 'constructor' property === 't' 1.3216 + // * Additional properties specified by 'ps', as appropriate for the 1.3217 + // specific type instance 't'. 1.3218 + RootedObject typeObj(cx, JS_NewObject(cx, &sCTypeClass, typeProto, parent)); 1.3219 + if (!typeObj) 1.3220 + return nullptr; 1.3221 + 1.3222 + // Set up the reserved slots. 1.3223 + JS_SetReservedSlot(typeObj, SLOT_TYPECODE, INT_TO_JSVAL(type)); 1.3224 + if (ffiType) 1.3225 + JS_SetReservedSlot(typeObj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(ffiType)); 1.3226 + if (name) 1.3227 + JS_SetReservedSlot(typeObj, SLOT_NAME, STRING_TO_JSVAL(name)); 1.3228 + JS_SetReservedSlot(typeObj, SLOT_SIZE, size); 1.3229 + JS_SetReservedSlot(typeObj, SLOT_ALIGN, align); 1.3230 + 1.3231 + if (dataProto) { 1.3232 + // Set up the 'prototype' and 'prototype.constructor' properties. 1.3233 + RootedObject prototype(cx, JS_NewObject(cx, &sCDataProtoClass, dataProto, parent)); 1.3234 + if (!prototype) 1.3235 + return nullptr; 1.3236 + 1.3237 + if (!JS_DefineProperty(cx, prototype, "constructor", typeObj, 1.3238 + JSPROP_READONLY | JSPROP_PERMANENT)) 1.3239 + return nullptr; 1.3240 + 1.3241 + // Set the 'prototype' object. 1.3242 + //if (!JS_FreezeObject(cx, prototype)) // XXX fixme - see bug 541212! 1.3243 + // return nullptr; 1.3244 + JS_SetReservedSlot(typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype)); 1.3245 + } 1.3246 + 1.3247 + if (!JS_FreezeObject(cx, typeObj)) 1.3248 + return nullptr; 1.3249 + 1.3250 + // Assert a sanity check on size and alignment: size % alignment should always 1.3251 + // be zero. 1.3252 + JS_ASSERT_IF(IsSizeDefined(typeObj), 1.3253 + GetSize(typeObj) % GetAlignment(typeObj) == 0); 1.3254 + 1.3255 + return typeObj; 1.3256 +} 1.3257 + 1.3258 +JSObject* 1.3259 +CType::DefineBuiltin(JSContext* cx, 1.3260 + JSObject* parent_, 1.3261 + const char* propName, 1.3262 + JSObject* typeProto_, 1.3263 + JSObject* dataProto_, 1.3264 + const char* name, 1.3265 + TypeCode type, 1.3266 + jsval size_, 1.3267 + jsval align_, 1.3268 + ffi_type* ffiType) 1.3269 +{ 1.3270 + RootedObject parent(cx, parent_); 1.3271 + RootedObject typeProto(cx, typeProto_); 1.3272 + RootedObject dataProto(cx, dataProto_); 1.3273 + RootedValue size(cx, size_); 1.3274 + RootedValue align(cx, align_); 1.3275 + 1.3276 + RootedString nameStr(cx, JS_NewStringCopyZ(cx, name)); 1.3277 + if (!nameStr) 1.3278 + return nullptr; 1.3279 + 1.3280 + // Create a new CType object with the common properties and slots. 1.3281 + RootedObject typeObj(cx, Create(cx, typeProto, dataProto, type, nameStr, size, align, ffiType)); 1.3282 + if (!typeObj) 1.3283 + return nullptr; 1.3284 + 1.3285 + // Define the CType as a 'propName' property on 'parent'. 1.3286 + if (!JS_DefineProperty(cx, parent, propName, typeObj, 1.3287 + JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) 1.3288 + return nullptr; 1.3289 + 1.3290 + return typeObj; 1.3291 +} 1.3292 + 1.3293 +void 1.3294 +CType::Finalize(JSFreeOp *fop, JSObject* obj) 1.3295 +{ 1.3296 + // Make sure our TypeCode slot is legit. If it's not, bail. 1.3297 + jsval slot = JS_GetReservedSlot(obj, SLOT_TYPECODE); 1.3298 + if (JSVAL_IS_VOID(slot)) 1.3299 + return; 1.3300 + 1.3301 + // The contents of our slots depends on what kind of type we are. 1.3302 + switch (TypeCode(JSVAL_TO_INT(slot))) { 1.3303 + case TYPE_function: { 1.3304 + // Free the FunctionInfo. 1.3305 + slot = JS_GetReservedSlot(obj, SLOT_FNINFO); 1.3306 + if (!JSVAL_IS_VOID(slot)) 1.3307 + FreeOp::get(fop)->delete_(static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot))); 1.3308 + break; 1.3309 + } 1.3310 + 1.3311 + case TYPE_struct: { 1.3312 + // Free the FieldInfoHash table. 1.3313 + slot = JS_GetReservedSlot(obj, SLOT_FIELDINFO); 1.3314 + if (!JSVAL_IS_VOID(slot)) { 1.3315 + void* info = JSVAL_TO_PRIVATE(slot); 1.3316 + FreeOp::get(fop)->delete_(static_cast<FieldInfoHash*>(info)); 1.3317 + } 1.3318 + } 1.3319 + 1.3320 + // Fall through. 1.3321 + case TYPE_array: { 1.3322 + // Free the ffi_type info. 1.3323 + slot = JS_GetReservedSlot(obj, SLOT_FFITYPE); 1.3324 + if (!JSVAL_IS_VOID(slot)) { 1.3325 + ffi_type* ffiType = static_cast<ffi_type*>(JSVAL_TO_PRIVATE(slot)); 1.3326 + FreeOp::get(fop)->free_(ffiType->elements); 1.3327 + FreeOp::get(fop)->delete_(ffiType); 1.3328 + } 1.3329 + 1.3330 + break; 1.3331 + } 1.3332 + default: 1.3333 + // Nothing to do here. 1.3334 + break; 1.3335 + } 1.3336 +} 1.3337 + 1.3338 +void 1.3339 +CType::Trace(JSTracer* trc, JSObject* obj) 1.3340 +{ 1.3341 + // Make sure our TypeCode slot is legit. If it's not, bail. 1.3342 + jsval slot = obj->getSlot(SLOT_TYPECODE); 1.3343 + if (JSVAL_IS_VOID(slot)) 1.3344 + return; 1.3345 + 1.3346 + // The contents of our slots depends on what kind of type we are. 1.3347 + switch (TypeCode(JSVAL_TO_INT(slot))) { 1.3348 + case TYPE_struct: { 1.3349 + slot = obj->getReservedSlot(SLOT_FIELDINFO); 1.3350 + if (JSVAL_IS_VOID(slot)) 1.3351 + return; 1.3352 + 1.3353 + FieldInfoHash* fields = 1.3354 + static_cast<FieldInfoHash*>(JSVAL_TO_PRIVATE(slot)); 1.3355 + for (FieldInfoHash::Enum e(*fields); !e.empty(); e.popFront()) { 1.3356 + JSString *key = e.front().key(); 1.3357 + JS_CallStringTracer(trc, &key, "fieldName"); 1.3358 + if (key != e.front().key()) 1.3359 + e.rekeyFront(JS_ASSERT_STRING_IS_FLAT(key)); 1.3360 + JS_CallHeapObjectTracer(trc, &e.front().value().mType, "fieldType"); 1.3361 + } 1.3362 + 1.3363 + break; 1.3364 + } 1.3365 + case TYPE_function: { 1.3366 + // Check if we have a FunctionInfo. 1.3367 + slot = obj->getReservedSlot(SLOT_FNINFO); 1.3368 + if (JSVAL_IS_VOID(slot)) 1.3369 + return; 1.3370 + 1.3371 + FunctionInfo* fninfo = static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot)); 1.3372 + JS_ASSERT(fninfo); 1.3373 + 1.3374 + // Identify our objects to the tracer. 1.3375 + JS_CallHeapObjectTracer(trc, &fninfo->mABI, "abi"); 1.3376 + JS_CallHeapObjectTracer(trc, &fninfo->mReturnType, "returnType"); 1.3377 + for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) 1.3378 + JS_CallHeapObjectTracer(trc, &fninfo->mArgTypes[i], "argType"); 1.3379 + 1.3380 + break; 1.3381 + } 1.3382 + default: 1.3383 + // Nothing to do here. 1.3384 + break; 1.3385 + } 1.3386 +} 1.3387 + 1.3388 +bool 1.3389 +CType::IsCType(JSObject* obj) 1.3390 +{ 1.3391 + return JS_GetClass(obj) == &sCTypeClass; 1.3392 +} 1.3393 + 1.3394 +bool 1.3395 +CType::IsCTypeProto(JSObject* obj) 1.3396 +{ 1.3397 + return JS_GetClass(obj) == &sCTypeProtoClass; 1.3398 +} 1.3399 + 1.3400 +TypeCode 1.3401 +CType::GetTypeCode(JSObject* typeObj) 1.3402 +{ 1.3403 + JS_ASSERT(IsCType(typeObj)); 1.3404 + 1.3405 + jsval result = JS_GetReservedSlot(typeObj, SLOT_TYPECODE); 1.3406 + return TypeCode(JSVAL_TO_INT(result)); 1.3407 +} 1.3408 + 1.3409 +bool 1.3410 +CType::TypesEqual(JSObject* t1, JSObject* t2) 1.3411 +{ 1.3412 + JS_ASSERT(IsCType(t1) && IsCType(t2)); 1.3413 + 1.3414 + // Fast path: check for object equality. 1.3415 + if (t1 == t2) 1.3416 + return true; 1.3417 + 1.3418 + // First, perform shallow comparison. 1.3419 + TypeCode c1 = GetTypeCode(t1); 1.3420 + TypeCode c2 = GetTypeCode(t2); 1.3421 + if (c1 != c2) 1.3422 + return false; 1.3423 + 1.3424 + // Determine whether the types require shallow or deep comparison. 1.3425 + switch (c1) { 1.3426 + case TYPE_pointer: { 1.3427 + // Compare base types. 1.3428 + JSObject* b1 = PointerType::GetBaseType(t1); 1.3429 + JSObject* b2 = PointerType::GetBaseType(t2); 1.3430 + return TypesEqual(b1, b2); 1.3431 + } 1.3432 + case TYPE_function: { 1.3433 + FunctionInfo* f1 = FunctionType::GetFunctionInfo(t1); 1.3434 + FunctionInfo* f2 = FunctionType::GetFunctionInfo(t2); 1.3435 + 1.3436 + // Compare abi, return type, and argument types. 1.3437 + if (f1->mABI != f2->mABI) 1.3438 + return false; 1.3439 + 1.3440 + if (!TypesEqual(f1->mReturnType, f2->mReturnType)) 1.3441 + return false; 1.3442 + 1.3443 + if (f1->mArgTypes.length() != f2->mArgTypes.length()) 1.3444 + return false; 1.3445 + 1.3446 + if (f1->mIsVariadic != f2->mIsVariadic) 1.3447 + return false; 1.3448 + 1.3449 + for (size_t i = 0; i < f1->mArgTypes.length(); ++i) { 1.3450 + if (!TypesEqual(f1->mArgTypes[i], f2->mArgTypes[i])) 1.3451 + return false; 1.3452 + } 1.3453 + 1.3454 + return true; 1.3455 + } 1.3456 + case TYPE_array: { 1.3457 + // Compare length, then base types. 1.3458 + // An undefined length array matches other undefined length arrays. 1.3459 + size_t s1 = 0, s2 = 0; 1.3460 + bool d1 = ArrayType::GetSafeLength(t1, &s1); 1.3461 + bool d2 = ArrayType::GetSafeLength(t2, &s2); 1.3462 + if (d1 != d2 || (d1 && s1 != s2)) 1.3463 + return false; 1.3464 + 1.3465 + JSObject* b1 = ArrayType::GetBaseType(t1); 1.3466 + JSObject* b2 = ArrayType::GetBaseType(t2); 1.3467 + return TypesEqual(b1, b2); 1.3468 + } 1.3469 + case TYPE_struct: 1.3470 + // Require exact type object equality. 1.3471 + return false; 1.3472 + default: 1.3473 + // Shallow comparison is sufficient. 1.3474 + return true; 1.3475 + } 1.3476 +} 1.3477 + 1.3478 +bool 1.3479 +CType::GetSafeSize(JSObject* obj, size_t* result) 1.3480 +{ 1.3481 + JS_ASSERT(CType::IsCType(obj)); 1.3482 + 1.3483 + jsval size = JS_GetReservedSlot(obj, SLOT_SIZE); 1.3484 + 1.3485 + // The "size" property can be an int, a double, or JSVAL_VOID 1.3486 + // (for arrays of undefined length), and must always fit in a size_t. 1.3487 + if (JSVAL_IS_INT(size)) { 1.3488 + *result = JSVAL_TO_INT(size); 1.3489 + return true; 1.3490 + } 1.3491 + if (JSVAL_IS_DOUBLE(size)) { 1.3492 + *result = Convert<size_t>(JSVAL_TO_DOUBLE(size)); 1.3493 + return true; 1.3494 + } 1.3495 + 1.3496 + JS_ASSERT(JSVAL_IS_VOID(size)); 1.3497 + return false; 1.3498 +} 1.3499 + 1.3500 +size_t 1.3501 +CType::GetSize(JSObject* obj) 1.3502 +{ 1.3503 + JS_ASSERT(CType::IsCType(obj)); 1.3504 + 1.3505 + jsval size = JS_GetReservedSlot(obj, SLOT_SIZE); 1.3506 + 1.3507 + JS_ASSERT(!JSVAL_IS_VOID(size)); 1.3508 + 1.3509 + // The "size" property can be an int, a double, or JSVAL_VOID 1.3510 + // (for arrays of undefined length), and must always fit in a size_t. 1.3511 + // For callers who know it can never be JSVAL_VOID, return a size_t directly. 1.3512 + if (JSVAL_IS_INT(size)) 1.3513 + return JSVAL_TO_INT(size); 1.3514 + return Convert<size_t>(JSVAL_TO_DOUBLE(size)); 1.3515 +} 1.3516 + 1.3517 +bool 1.3518 +CType::IsSizeDefined(JSObject* obj) 1.3519 +{ 1.3520 + JS_ASSERT(CType::IsCType(obj)); 1.3521 + 1.3522 + jsval size = JS_GetReservedSlot(obj, SLOT_SIZE); 1.3523 + 1.3524 + // The "size" property can be an int, a double, or JSVAL_VOID 1.3525 + // (for arrays of undefined length), and must always fit in a size_t. 1.3526 + JS_ASSERT(JSVAL_IS_INT(size) || JSVAL_IS_DOUBLE(size) || JSVAL_IS_VOID(size)); 1.3527 + return !JSVAL_IS_VOID(size); 1.3528 +} 1.3529 + 1.3530 +size_t 1.3531 +CType::GetAlignment(JSObject* obj) 1.3532 +{ 1.3533 + JS_ASSERT(CType::IsCType(obj)); 1.3534 + 1.3535 + jsval slot = JS_GetReservedSlot(obj, SLOT_ALIGN); 1.3536 + return static_cast<size_t>(JSVAL_TO_INT(slot)); 1.3537 +} 1.3538 + 1.3539 +ffi_type* 1.3540 +CType::GetFFIType(JSContext* cx, JSObject* obj) 1.3541 +{ 1.3542 + JS_ASSERT(CType::IsCType(obj)); 1.3543 + 1.3544 + jsval slot = JS_GetReservedSlot(obj, SLOT_FFITYPE); 1.3545 + 1.3546 + if (!JSVAL_IS_VOID(slot)) { 1.3547 + return static_cast<ffi_type*>(JSVAL_TO_PRIVATE(slot)); 1.3548 + } 1.3549 + 1.3550 + AutoPtr<ffi_type> result; 1.3551 + switch (CType::GetTypeCode(obj)) { 1.3552 + case TYPE_array: 1.3553 + result = ArrayType::BuildFFIType(cx, obj); 1.3554 + break; 1.3555 + 1.3556 + case TYPE_struct: 1.3557 + result = StructType::BuildFFIType(cx, obj); 1.3558 + break; 1.3559 + 1.3560 + default: 1.3561 + MOZ_ASSUME_UNREACHABLE("simple types must have an ffi_type"); 1.3562 + } 1.3563 + 1.3564 + if (!result) 1.3565 + return nullptr; 1.3566 + JS_SetReservedSlot(obj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(result.get())); 1.3567 + return result.forget(); 1.3568 +} 1.3569 + 1.3570 +JSString* 1.3571 +CType::GetName(JSContext* cx, HandleObject obj) 1.3572 +{ 1.3573 + JS_ASSERT(CType::IsCType(obj)); 1.3574 + 1.3575 + jsval string = JS_GetReservedSlot(obj, SLOT_NAME); 1.3576 + if (!JSVAL_IS_VOID(string)) 1.3577 + return JSVAL_TO_STRING(string); 1.3578 + 1.3579 + // Build the type name lazily. 1.3580 + JSString* name = BuildTypeName(cx, obj); 1.3581 + if (!name) 1.3582 + return nullptr; 1.3583 + JS_SetReservedSlot(obj, SLOT_NAME, STRING_TO_JSVAL(name)); 1.3584 + return name; 1.3585 +} 1.3586 + 1.3587 +JSObject* 1.3588 +CType::GetProtoFromCtor(JSObject* obj, CTypeProtoSlot slot) 1.3589 +{ 1.3590 + // Get ctypes.{Pointer,Array,Struct}Type.prototype from a reserved slot 1.3591 + // on the type constructor. 1.3592 + jsval protoslot = js::GetFunctionNativeReserved(obj, SLOT_FN_CTORPROTO); 1.3593 + JSObject* proto = &protoslot.toObject(); 1.3594 + JS_ASSERT(proto); 1.3595 + JS_ASSERT(CType::IsCTypeProto(proto)); 1.3596 + 1.3597 + // Get the desired prototype. 1.3598 + jsval result = JS_GetReservedSlot(proto, slot); 1.3599 + return &result.toObject(); 1.3600 +} 1.3601 + 1.3602 +JSObject* 1.3603 +CType::GetProtoFromType(JSContext* cx, JSObject* objArg, CTypeProtoSlot slot) 1.3604 +{ 1.3605 + JS_ASSERT(IsCType(objArg)); 1.3606 + RootedObject obj(cx, objArg); 1.3607 + 1.3608 + // Get the prototype of the type object. 1.3609 + RootedObject proto(cx); 1.3610 + if (!JS_GetPrototype(cx, obj, &proto)) 1.3611 + return nullptr; 1.3612 + JS_ASSERT(proto); 1.3613 + JS_ASSERT(CType::IsCTypeProto(proto)); 1.3614 + 1.3615 + // Get the requested ctypes.{Pointer,Array,Struct,Function}Type.prototype. 1.3616 + jsval result = JS_GetReservedSlot(proto, slot); 1.3617 + JS_ASSERT(!JSVAL_IS_PRIMITIVE(result)); 1.3618 + return JSVAL_TO_OBJECT(result); 1.3619 +} 1.3620 + 1.3621 +bool 1.3622 +CType::IsCTypeOrProto(HandleValue v) 1.3623 +{ 1.3624 + if (!v.isObject()) 1.3625 + return false; 1.3626 + JSObject* obj = &v.toObject(); 1.3627 + return CType::IsCType(obj) || CType::IsCTypeProto(obj); 1.3628 +} 1.3629 + 1.3630 +bool 1.3631 +CType::PrototypeGetter(JSContext* cx, JS::CallArgs args) 1.3632 +{ 1.3633 + RootedObject obj(cx, &args.thisv().toObject()); 1.3634 + unsigned slot = CType::IsCTypeProto(obj) ? (unsigned) SLOT_OURDATAPROTO 1.3635 + : (unsigned) SLOT_PROTO; 1.3636 + args.rval().set(JS_GetReservedSlot(obj, slot)); 1.3637 + MOZ_ASSERT(args.rval().isObject() || args.rval().isUndefined()); 1.3638 + return true; 1.3639 +} 1.3640 + 1.3641 +bool 1.3642 +CType::IsCType(HandleValue v) 1.3643 +{ 1.3644 + return v.isObject() && CType::IsCType(&v.toObject()); 1.3645 +} 1.3646 + 1.3647 +bool 1.3648 +CType::NameGetter(JSContext* cx, JS::CallArgs args) 1.3649 +{ 1.3650 + RootedObject obj(cx, &args.thisv().toObject()); 1.3651 + JSString* name = CType::GetName(cx, obj); 1.3652 + if (!name) 1.3653 + return false; 1.3654 + 1.3655 + args.rval().setString(name); 1.3656 + return true; 1.3657 +} 1.3658 + 1.3659 +bool 1.3660 +CType::SizeGetter(JSContext* cx, JS::CallArgs args) 1.3661 +{ 1.3662 + RootedObject obj(cx, &args.thisv().toObject()); 1.3663 + args.rval().set(JS_GetReservedSlot(obj, SLOT_SIZE)); 1.3664 + MOZ_ASSERT(args.rval().isNumber() || args.rval().isUndefined()); 1.3665 + return true; 1.3666 +} 1.3667 + 1.3668 +bool 1.3669 +CType::PtrGetter(JSContext* cx, JS::CallArgs args) 1.3670 +{ 1.3671 + RootedObject obj(cx, &args.thisv().toObject()); 1.3672 + JSObject* pointerType = PointerType::CreateInternal(cx, obj); 1.3673 + if (!pointerType) 1.3674 + return false; 1.3675 + 1.3676 + args.rval().setObject(*pointerType); 1.3677 + return true; 1.3678 +} 1.3679 + 1.3680 +bool 1.3681 +CType::CreateArray(JSContext* cx, unsigned argc, jsval* vp) 1.3682 +{ 1.3683 + CallArgs args = CallArgsFromVp(argc, vp); 1.3684 + RootedObject baseType(cx, JS_THIS_OBJECT(cx, vp)); 1.3685 + if (!baseType) 1.3686 + return false; 1.3687 + if (!CType::IsCType(baseType)) { 1.3688 + JS_ReportError(cx, "not a CType"); 1.3689 + return false; 1.3690 + } 1.3691 + 1.3692 + // Construct and return a new ArrayType object. 1.3693 + if (args.length() > 1) { 1.3694 + JS_ReportError(cx, "array takes zero or one argument"); 1.3695 + return false; 1.3696 + } 1.3697 + 1.3698 + // Convert the length argument to a size_t. 1.3699 + size_t length = 0; 1.3700 + if (args.length() == 1 && !jsvalToSize(cx, args[0], false, &length)) { 1.3701 + JS_ReportError(cx, "argument must be a nonnegative integer"); 1.3702 + return false; 1.3703 + } 1.3704 + 1.3705 + JSObject* result = ArrayType::CreateInternal(cx, baseType, length, args.length() == 1); 1.3706 + if (!result) 1.3707 + return false; 1.3708 + 1.3709 + args.rval().setObject(*result); 1.3710 + return true; 1.3711 +} 1.3712 + 1.3713 +bool 1.3714 +CType::ToString(JSContext* cx, unsigned argc, jsval* vp) 1.3715 +{ 1.3716 + CallArgs args = CallArgsFromVp(argc, vp); 1.3717 + RootedObject obj(cx, JS_THIS_OBJECT(cx, vp)); 1.3718 + if (!obj) 1.3719 + return false; 1.3720 + if (!CType::IsCType(obj) && !CType::IsCTypeProto(obj)) { 1.3721 + JS_ReportError(cx, "not a CType"); 1.3722 + return false; 1.3723 + } 1.3724 + 1.3725 + // Create the appropriate string depending on whether we're sCTypeClass or 1.3726 + // sCTypeProtoClass. 1.3727 + JSString* result; 1.3728 + if (CType::IsCType(obj)) { 1.3729 + AutoString type; 1.3730 + AppendString(type, "type "); 1.3731 + AppendString(type, GetName(cx, obj)); 1.3732 + result = NewUCString(cx, type); 1.3733 + } 1.3734 + else { 1.3735 + result = JS_NewStringCopyZ(cx, "[CType proto object]"); 1.3736 + } 1.3737 + if (!result) 1.3738 + return false; 1.3739 + 1.3740 + args.rval().setString(result); 1.3741 + return true; 1.3742 +} 1.3743 + 1.3744 +bool 1.3745 +CType::ToSource(JSContext* cx, unsigned argc, jsval* vp) 1.3746 +{ 1.3747 + CallArgs args = CallArgsFromVp(argc, vp); 1.3748 + JSObject* obj = JS_THIS_OBJECT(cx, vp); 1.3749 + if (!obj) 1.3750 + return false; 1.3751 + if (!CType::IsCType(obj) && !CType::IsCTypeProto(obj)) 1.3752 + { 1.3753 + JS_ReportError(cx, "not a CType"); 1.3754 + return false; 1.3755 + } 1.3756 + 1.3757 + // Create the appropriate string depending on whether we're sCTypeClass or 1.3758 + // sCTypeProtoClass. 1.3759 + JSString* result; 1.3760 + if (CType::IsCType(obj)) { 1.3761 + AutoString source; 1.3762 + BuildTypeSource(cx, obj, false, source); 1.3763 + result = NewUCString(cx, source); 1.3764 + } else { 1.3765 + result = JS_NewStringCopyZ(cx, "[CType proto object]"); 1.3766 + } 1.3767 + if (!result) 1.3768 + return false; 1.3769 + 1.3770 + args.rval().setString(result); 1.3771 + return true; 1.3772 +} 1.3773 + 1.3774 +bool 1.3775 +CType::HasInstance(JSContext* cx, HandleObject obj, MutableHandleValue v, bool* bp) 1.3776 +{ 1.3777 + JS_ASSERT(CType::IsCType(obj)); 1.3778 + 1.3779 + jsval slot = JS_GetReservedSlot(obj, SLOT_PROTO); 1.3780 + JS::Rooted<JSObject*> prototype(cx, &slot.toObject()); 1.3781 + JS_ASSERT(prototype); 1.3782 + JS_ASSERT(CData::IsCDataProto(prototype)); 1.3783 + 1.3784 + *bp = false; 1.3785 + if (JSVAL_IS_PRIMITIVE(v)) 1.3786 + return true; 1.3787 + 1.3788 + RootedObject proto(cx, &v.toObject()); 1.3789 + for (;;) { 1.3790 + if (!JS_GetPrototype(cx, proto, &proto)) 1.3791 + return false; 1.3792 + if (!proto) 1.3793 + break; 1.3794 + if (proto == prototype) { 1.3795 + *bp = true; 1.3796 + break; 1.3797 + } 1.3798 + } 1.3799 + return true; 1.3800 +} 1.3801 + 1.3802 +static JSObject* 1.3803 +CType::GetGlobalCTypes(JSContext* cx, JSObject* objArg) 1.3804 +{ 1.3805 + JS_ASSERT(CType::IsCType(objArg)); 1.3806 + 1.3807 + RootedObject obj(cx, objArg); 1.3808 + RootedObject objTypeProto(cx); 1.3809 + if (!JS_GetPrototype(cx, obj, &objTypeProto)) 1.3810 + return nullptr; 1.3811 + JS_ASSERT(objTypeProto); 1.3812 + JS_ASSERT(CType::IsCTypeProto(objTypeProto)); 1.3813 + 1.3814 + jsval valCTypes = JS_GetReservedSlot(objTypeProto, SLOT_CTYPES); 1.3815 + JS_ASSERT(!JSVAL_IS_PRIMITIVE(valCTypes)); 1.3816 + 1.3817 + JS_ASSERT(!JSVAL_IS_PRIMITIVE(valCTypes)); 1.3818 + return &valCTypes.toObject(); 1.3819 +} 1.3820 + 1.3821 +/******************************************************************************* 1.3822 +** ABI implementation 1.3823 +*******************************************************************************/ 1.3824 + 1.3825 +bool 1.3826 +ABI::IsABI(JSObject* obj) 1.3827 +{ 1.3828 + return JS_GetClass(obj) == &sCABIClass; 1.3829 +} 1.3830 + 1.3831 +bool 1.3832 +ABI::ToSource(JSContext* cx, unsigned argc, jsval* vp) 1.3833 +{ 1.3834 + CallArgs args = CallArgsFromVp(argc, vp); 1.3835 + if (args.length() != 0) { 1.3836 + JS_ReportError(cx, "toSource takes zero arguments"); 1.3837 + return false; 1.3838 + } 1.3839 + 1.3840 + JSObject* obj = JS_THIS_OBJECT(cx, vp); 1.3841 + if (!obj) 1.3842 + return false; 1.3843 + if (!ABI::IsABI(obj)) { 1.3844 + JS_ReportError(cx, "not an ABI"); 1.3845 + return false; 1.3846 + } 1.3847 + 1.3848 + JSString* result; 1.3849 + switch (GetABICode(obj)) { 1.3850 + case ABI_DEFAULT: 1.3851 + result = JS_NewStringCopyZ(cx, "ctypes.default_abi"); 1.3852 + break; 1.3853 + case ABI_STDCALL: 1.3854 + result = JS_NewStringCopyZ(cx, "ctypes.stdcall_abi"); 1.3855 + break; 1.3856 + case ABI_WINAPI: 1.3857 + result = JS_NewStringCopyZ(cx, "ctypes.winapi_abi"); 1.3858 + break; 1.3859 + default: 1.3860 + JS_ReportError(cx, "not a valid ABICode"); 1.3861 + return false; 1.3862 + } 1.3863 + if (!result) 1.3864 + return false; 1.3865 + 1.3866 + args.rval().setString(result); 1.3867 + return true; 1.3868 +} 1.3869 + 1.3870 + 1.3871 +/******************************************************************************* 1.3872 +** PointerType implementation 1.3873 +*******************************************************************************/ 1.3874 + 1.3875 +bool 1.3876 +PointerType::Create(JSContext* cx, unsigned argc, jsval* vp) 1.3877 +{ 1.3878 + CallArgs args = CallArgsFromVp(argc, vp); 1.3879 + // Construct and return a new PointerType object. 1.3880 + if (args.length() != 1) { 1.3881 + JS_ReportError(cx, "PointerType takes one argument"); 1.3882 + return false; 1.3883 + } 1.3884 + 1.3885 + jsval arg = args[0]; 1.3886 + RootedObject obj(cx); 1.3887 + if (JSVAL_IS_PRIMITIVE(arg) || !CType::IsCType(obj = &arg.toObject())) { 1.3888 + JS_ReportError(cx, "first argument must be a CType"); 1.3889 + return false; 1.3890 + } 1.3891 + 1.3892 + JSObject* result = CreateInternal(cx, obj); 1.3893 + if (!result) 1.3894 + return false; 1.3895 + 1.3896 + args.rval().setObject(*result); 1.3897 + return true; 1.3898 +} 1.3899 + 1.3900 +JSObject* 1.3901 +PointerType::CreateInternal(JSContext* cx, HandleObject baseType) 1.3902 +{ 1.3903 + // check if we have a cached PointerType on our base CType. 1.3904 + jsval slot = JS_GetReservedSlot(baseType, SLOT_PTR); 1.3905 + if (!slot.isUndefined()) 1.3906 + return &slot.toObject(); 1.3907 + 1.3908 + // Get ctypes.PointerType.prototype and the common prototype for CData objects 1.3909 + // of this type, or ctypes.FunctionType.prototype for function pointers. 1.3910 + CTypeProtoSlot slotId = CType::GetTypeCode(baseType) == TYPE_function ? 1.3911 + SLOT_FUNCTIONDATAPROTO : SLOT_POINTERDATAPROTO; 1.3912 + RootedObject dataProto(cx, CType::GetProtoFromType(cx, baseType, slotId)); 1.3913 + if (!dataProto) 1.3914 + return nullptr; 1.3915 + RootedObject typeProto(cx, CType::GetProtoFromType(cx, baseType, SLOT_POINTERPROTO)); 1.3916 + if (!typeProto) 1.3917 + return nullptr; 1.3918 + 1.3919 + // Create a new CType object with the common properties and slots. 1.3920 + JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_pointer, 1.3921 + nullptr, INT_TO_JSVAL(sizeof(void*)), 1.3922 + INT_TO_JSVAL(ffi_type_pointer.alignment), 1.3923 + &ffi_type_pointer); 1.3924 + if (!typeObj) 1.3925 + return nullptr; 1.3926 + 1.3927 + // Set the target type. (This will be 'null' for an opaque pointer type.) 1.3928 + JS_SetReservedSlot(typeObj, SLOT_TARGET_T, OBJECT_TO_JSVAL(baseType)); 1.3929 + 1.3930 + // Finally, cache our newly-created PointerType on our pointed-to CType. 1.3931 + JS_SetReservedSlot(baseType, SLOT_PTR, OBJECT_TO_JSVAL(typeObj)); 1.3932 + 1.3933 + return typeObj; 1.3934 +} 1.3935 + 1.3936 +bool 1.3937 +PointerType::ConstructData(JSContext* cx, 1.3938 + HandleObject obj, 1.3939 + const CallArgs& args) 1.3940 +{ 1.3941 + if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_pointer) { 1.3942 + JS_ReportError(cx, "not a PointerType"); 1.3943 + return false; 1.3944 + } 1.3945 + 1.3946 + if (args.length() > 3) { 1.3947 + JS_ReportError(cx, "constructor takes 0, 1, 2, or 3 arguments"); 1.3948 + return false; 1.3949 + } 1.3950 + 1.3951 + RootedObject result(cx, CData::Create(cx, obj, NullPtr(), nullptr, true)); 1.3952 + if (!result) 1.3953 + return false; 1.3954 + 1.3955 + // Set return value early, must not observe *vp after 1.3956 + args.rval().setObject(*result); 1.3957 + 1.3958 + // There are 3 things that we might be creating here: 1.3959 + // 1 - A null pointer (no arguments) 1.3960 + // 2 - An initialized pointer (1 argument) 1.3961 + // 3 - A closure (1-3 arguments) 1.3962 + // 1.3963 + // The API doesn't give us a perfect way to distinguish 2 and 3, but the 1.3964 + // heuristics we use should be fine. 1.3965 + 1.3966 + // 1.3967 + // Case 1 - Null pointer 1.3968 + // 1.3969 + if (args.length() == 0) 1.3970 + return true; 1.3971 + 1.3972 + // Analyze the arguments a bit to decide what to do next. 1.3973 + RootedObject baseObj(cx, PointerType::GetBaseType(obj)); 1.3974 + bool looksLikeClosure = CType::GetTypeCode(baseObj) == TYPE_function && 1.3975 + args[0].isObject() && 1.3976 + JS_ObjectIsCallable(cx, &args[0].toObject()); 1.3977 + 1.3978 + // 1.3979 + // Case 2 - Initialized pointer 1.3980 + // 1.3981 + if (!looksLikeClosure) { 1.3982 + if (args.length() != 1) { 1.3983 + JS_ReportError(cx, "first argument must be a function"); 1.3984 + return false; 1.3985 + } 1.3986 + return ExplicitConvert(cx, args[0], obj, CData::GetData(result)); 1.3987 + } 1.3988 + 1.3989 + // 1.3990 + // Case 3 - Closure 1.3991 + // 1.3992 + 1.3993 + // The second argument is an optional 'this' parameter with which to invoke 1.3994 + // the given js function. Callers may leave this blank, or pass null if they 1.3995 + // wish to pass the third argument. 1.3996 + RootedObject thisObj(cx, nullptr); 1.3997 + if (args.length() >= 2) { 1.3998 + if (args[1].isNull()) { 1.3999 + thisObj = nullptr; 1.4000 + } else if (!JSVAL_IS_PRIMITIVE(args[1])) { 1.4001 + thisObj = &args[1].toObject(); 1.4002 + } else if (!JS_ValueToObject(cx, args[1], &thisObj)) { 1.4003 + return false; 1.4004 + } 1.4005 + } 1.4006 + 1.4007 + // The third argument is an optional error sentinel that js-ctypes will return 1.4008 + // if an exception is raised while executing the closure. The type must match 1.4009 + // the return type of the callback. 1.4010 + jsval errVal = JSVAL_VOID; 1.4011 + if (args.length() == 3) 1.4012 + errVal = args[2]; 1.4013 + 1.4014 + RootedObject fnObj(cx, &args[0].toObject()); 1.4015 + return FunctionType::ConstructData(cx, baseObj, result, fnObj, thisObj, errVal); 1.4016 +} 1.4017 + 1.4018 +JSObject* 1.4019 +PointerType::GetBaseType(JSObject* obj) 1.4020 +{ 1.4021 + JS_ASSERT(CType::GetTypeCode(obj) == TYPE_pointer); 1.4022 + 1.4023 + jsval type = JS_GetReservedSlot(obj, SLOT_TARGET_T); 1.4024 + JS_ASSERT(!type.isNull()); 1.4025 + return &type.toObject(); 1.4026 +} 1.4027 + 1.4028 +bool 1.4029 +PointerType::IsPointerType(HandleValue v) 1.4030 +{ 1.4031 + if (!v.isObject()) 1.4032 + return false; 1.4033 + JSObject* obj = &v.toObject(); 1.4034 + return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_pointer; 1.4035 +} 1.4036 + 1.4037 +bool 1.4038 +PointerType::IsPointer(HandleValue v) 1.4039 +{ 1.4040 + if (!v.isObject()) 1.4041 + return false; 1.4042 + JSObject* obj = &v.toObject(); 1.4043 + return CData::IsCData(obj) && CType::GetTypeCode(CData::GetCType(obj)) == TYPE_pointer; 1.4044 +} 1.4045 + 1.4046 +bool 1.4047 +PointerType::TargetTypeGetter(JSContext* cx, JS::CallArgs args) 1.4048 +{ 1.4049 + RootedObject obj(cx, &args.thisv().toObject()); 1.4050 + args.rval().set(JS_GetReservedSlot(obj, SLOT_TARGET_T)); 1.4051 + MOZ_ASSERT(args.rval().isObject()); 1.4052 + return true; 1.4053 +} 1.4054 + 1.4055 +bool 1.4056 +PointerType::IsNull(JSContext* cx, unsigned argc, jsval* vp) 1.4057 +{ 1.4058 + CallArgs args = CallArgsFromVp(argc, vp); 1.4059 + JSObject* obj = JS_THIS_OBJECT(cx, vp); 1.4060 + if (!obj) 1.4061 + return false; 1.4062 + if (!CData::IsCData(obj)) { 1.4063 + JS_ReportError(cx, "not a CData"); 1.4064 + return false; 1.4065 + } 1.4066 + 1.4067 + // Get pointer type and base type. 1.4068 + JSObject* typeObj = CData::GetCType(obj); 1.4069 + if (CType::GetTypeCode(typeObj) != TYPE_pointer) { 1.4070 + JS_ReportError(cx, "not a PointerType"); 1.4071 + return false; 1.4072 + } 1.4073 + 1.4074 + void* data = *static_cast<void**>(CData::GetData(obj)); 1.4075 + args.rval().setBoolean(data == nullptr); 1.4076 + return true; 1.4077 +} 1.4078 + 1.4079 +bool 1.4080 +PointerType::OffsetBy(JSContext* cx, const CallArgs& args, int offset) 1.4081 +{ 1.4082 + JSObject* obj = JS_THIS_OBJECT(cx, args.base()); 1.4083 + if (!obj) 1.4084 + return false; 1.4085 + if (!CData::IsCData(obj)) { 1.4086 + JS_ReportError(cx, "not a CData"); 1.4087 + return false; 1.4088 + } 1.4089 + 1.4090 + RootedObject typeObj(cx, CData::GetCType(obj)); 1.4091 + if (CType::GetTypeCode(typeObj) != TYPE_pointer) { 1.4092 + JS_ReportError(cx, "not a PointerType"); 1.4093 + return false; 1.4094 + } 1.4095 + 1.4096 + RootedObject baseType(cx, PointerType::GetBaseType(typeObj)); 1.4097 + if (!CType::IsSizeDefined(baseType)) { 1.4098 + JS_ReportError(cx, "cannot modify pointer of undefined size"); 1.4099 + return false; 1.4100 + } 1.4101 + 1.4102 + size_t elementSize = CType::GetSize(baseType); 1.4103 + char* data = static_cast<char*>(*static_cast<void**>(CData::GetData(obj))); 1.4104 + void* address = data + offset * elementSize; 1.4105 + 1.4106 + // Create a PointerType CData object containing the new address. 1.4107 + JSObject* result = CData::Create(cx, typeObj, NullPtr(), &address, true); 1.4108 + if (!result) 1.4109 + return false; 1.4110 + 1.4111 + args.rval().setObject(*result); 1.4112 + return true; 1.4113 +} 1.4114 + 1.4115 +bool 1.4116 +PointerType::Increment(JSContext* cx, unsigned argc, jsval* vp) 1.4117 +{ 1.4118 + CallArgs args = CallArgsFromVp(argc, vp); 1.4119 + return OffsetBy(cx, args, 1); 1.4120 +} 1.4121 + 1.4122 +bool 1.4123 +PointerType::Decrement(JSContext* cx, unsigned argc, jsval* vp) 1.4124 +{ 1.4125 + CallArgs args = CallArgsFromVp(argc, vp); 1.4126 + return OffsetBy(cx, args, -1); 1.4127 +} 1.4128 + 1.4129 +bool 1.4130 +PointerType::ContentsGetter(JSContext* cx, JS::CallArgs args) 1.4131 +{ 1.4132 + RootedObject obj(cx, &args.thisv().toObject()); 1.4133 + RootedObject baseType(cx, GetBaseType(CData::GetCType(obj))); 1.4134 + if (!CType::IsSizeDefined(baseType)) { 1.4135 + JS_ReportError(cx, "cannot get contents of undefined size"); 1.4136 + return false; 1.4137 + } 1.4138 + 1.4139 + void* data = *static_cast<void**>(CData::GetData(obj)); 1.4140 + if (data == nullptr) { 1.4141 + JS_ReportError(cx, "cannot read contents of null pointer"); 1.4142 + return false; 1.4143 + } 1.4144 + 1.4145 + RootedValue result(cx); 1.4146 + if (!ConvertToJS(cx, baseType, NullPtr(), data, false, false, result.address())) 1.4147 + return false; 1.4148 + 1.4149 + args.rval().set(result); 1.4150 + return true; 1.4151 +} 1.4152 + 1.4153 +bool 1.4154 +PointerType::ContentsSetter(JSContext* cx, JS::CallArgs args) 1.4155 +{ 1.4156 + RootedObject obj(cx, &args.thisv().toObject()); 1.4157 + RootedObject baseType(cx, GetBaseType(CData::GetCType(obj))); 1.4158 + if (!CType::IsSizeDefined(baseType)) { 1.4159 + JS_ReportError(cx, "cannot set contents of undefined size"); 1.4160 + return false; 1.4161 + } 1.4162 + 1.4163 + void* data = *static_cast<void**>(CData::GetData(obj)); 1.4164 + if (data == nullptr) { 1.4165 + JS_ReportError(cx, "cannot write contents to null pointer"); 1.4166 + return false; 1.4167 + } 1.4168 + 1.4169 + args.rval().setUndefined(); 1.4170 + return ImplicitConvert(cx, args.get(0), baseType, data, false, nullptr); 1.4171 +} 1.4172 + 1.4173 +/******************************************************************************* 1.4174 +** ArrayType implementation 1.4175 +*******************************************************************************/ 1.4176 + 1.4177 +bool 1.4178 +ArrayType::Create(JSContext* cx, unsigned argc, jsval* vp) 1.4179 +{ 1.4180 + CallArgs args = CallArgsFromVp(argc, vp); 1.4181 + // Construct and return a new ArrayType object. 1.4182 + if (args.length() < 1 || args.length() > 2) { 1.4183 + JS_ReportError(cx, "ArrayType takes one or two arguments"); 1.4184 + return false; 1.4185 + } 1.4186 + 1.4187 + if (JSVAL_IS_PRIMITIVE(args[0]) || 1.4188 + !CType::IsCType(&args[0].toObject())) { 1.4189 + JS_ReportError(cx, "first argument must be a CType"); 1.4190 + return false; 1.4191 + } 1.4192 + 1.4193 + // Convert the length argument to a size_t. 1.4194 + size_t length = 0; 1.4195 + if (args.length() == 2 && !jsvalToSize(cx, args[1], false, &length)) { 1.4196 + JS_ReportError(cx, "second argument must be a nonnegative integer"); 1.4197 + return false; 1.4198 + } 1.4199 + 1.4200 + RootedObject baseType(cx, &args[0].toObject()); 1.4201 + JSObject* result = CreateInternal(cx, baseType, length, args.length() == 2); 1.4202 + if (!result) 1.4203 + return false; 1.4204 + 1.4205 + args.rval().setObject(*result); 1.4206 + return true; 1.4207 +} 1.4208 + 1.4209 +JSObject* 1.4210 +ArrayType::CreateInternal(JSContext* cx, 1.4211 + HandleObject baseType, 1.4212 + size_t length, 1.4213 + bool lengthDefined) 1.4214 +{ 1.4215 + // Get ctypes.ArrayType.prototype and the common prototype for CData objects 1.4216 + // of this type, from ctypes.CType.prototype. 1.4217 + RootedObject typeProto(cx, CType::GetProtoFromType(cx, baseType, SLOT_ARRAYPROTO)); 1.4218 + if (!typeProto) 1.4219 + return nullptr; 1.4220 + RootedObject dataProto(cx, CType::GetProtoFromType(cx, baseType, SLOT_ARRAYDATAPROTO)); 1.4221 + if (!dataProto) 1.4222 + return nullptr; 1.4223 + 1.4224 + // Determine the size of the array from the base type, if possible. 1.4225 + // The size of the base type must be defined. 1.4226 + // If our length is undefined, both our size and length will be undefined. 1.4227 + size_t baseSize; 1.4228 + if (!CType::GetSafeSize(baseType, &baseSize)) { 1.4229 + JS_ReportError(cx, "base size must be defined"); 1.4230 + return nullptr; 1.4231 + } 1.4232 + 1.4233 + RootedValue sizeVal(cx, JSVAL_VOID); 1.4234 + RootedValue lengthVal(cx, JSVAL_VOID); 1.4235 + if (lengthDefined) { 1.4236 + // Check for overflow, and convert to an int or double as required. 1.4237 + size_t size = length * baseSize; 1.4238 + if (length > 0 && size / length != baseSize) { 1.4239 + JS_ReportError(cx, "size overflow"); 1.4240 + return nullptr; 1.4241 + } 1.4242 + if (!SizeTojsval(cx, size, sizeVal.address()) || 1.4243 + !SizeTojsval(cx, length, lengthVal.address())) 1.4244 + return nullptr; 1.4245 + } 1.4246 + 1.4247 + size_t align = CType::GetAlignment(baseType); 1.4248 + 1.4249 + // Create a new CType object with the common properties and slots. 1.4250 + JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_array, nullptr, 1.4251 + sizeVal, INT_TO_JSVAL(align), nullptr); 1.4252 + if (!typeObj) 1.4253 + return nullptr; 1.4254 + 1.4255 + // Set the element type. 1.4256 + JS_SetReservedSlot(typeObj, SLOT_ELEMENT_T, OBJECT_TO_JSVAL(baseType)); 1.4257 + 1.4258 + // Set the length. 1.4259 + JS_SetReservedSlot(typeObj, SLOT_LENGTH, lengthVal); 1.4260 + 1.4261 + return typeObj; 1.4262 +} 1.4263 + 1.4264 +bool 1.4265 +ArrayType::ConstructData(JSContext* cx, 1.4266 + HandleObject obj_, 1.4267 + const CallArgs& args) 1.4268 +{ 1.4269 + RootedObject obj(cx, obj_); // Make a mutable version 1.4270 + 1.4271 + if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_array) { 1.4272 + JS_ReportError(cx, "not an ArrayType"); 1.4273 + return false; 1.4274 + } 1.4275 + 1.4276 + // Decide whether we have an object to initialize from. We'll override this 1.4277 + // if we get a length argument instead. 1.4278 + bool convertObject = args.length() == 1; 1.4279 + 1.4280 + // Check if we're an array of undefined length. If we are, allow construction 1.4281 + // with a length argument, or with an actual JS array. 1.4282 + if (CType::IsSizeDefined(obj)) { 1.4283 + if (args.length() > 1) { 1.4284 + JS_ReportError(cx, "constructor takes zero or one argument"); 1.4285 + return false; 1.4286 + } 1.4287 + 1.4288 + } else { 1.4289 + if (args.length() != 1) { 1.4290 + JS_ReportError(cx, "constructor takes one argument"); 1.4291 + return false; 1.4292 + } 1.4293 + 1.4294 + RootedObject baseType(cx, GetBaseType(obj)); 1.4295 + 1.4296 + size_t length; 1.4297 + if (jsvalToSize(cx, args[0], false, &length)) { 1.4298 + // Have a length, rather than an object to initialize from. 1.4299 + convertObject = false; 1.4300 + 1.4301 + } else if (!JSVAL_IS_PRIMITIVE(args[0])) { 1.4302 + // We were given an object with a .length property. 1.4303 + // This could be a JS array, or a CData array. 1.4304 + RootedObject arg(cx, &args[0].toObject()); 1.4305 + RootedValue lengthVal(cx); 1.4306 + if (!JS_GetProperty(cx, arg, "length", &lengthVal) || 1.4307 + !jsvalToSize(cx, lengthVal, false, &length)) { 1.4308 + JS_ReportError(cx, "argument must be an array object or length"); 1.4309 + return false; 1.4310 + } 1.4311 + 1.4312 + } else if (args[0].isString()) { 1.4313 + // We were given a string. Size the array to the appropriate length, 1.4314 + // including space for the terminator. 1.4315 + JSString* sourceString = args[0].toString(); 1.4316 + size_t sourceLength = sourceString->length(); 1.4317 + const jschar* sourceChars = sourceString->getChars(cx); 1.4318 + if (!sourceChars) 1.4319 + return false; 1.4320 + 1.4321 + switch (CType::GetTypeCode(baseType)) { 1.4322 + case TYPE_char: 1.4323 + case TYPE_signed_char: 1.4324 + case TYPE_unsigned_char: { 1.4325 + // Determine the UTF-8 length. 1.4326 + length = GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength); 1.4327 + if (length == (size_t) -1) 1.4328 + return false; 1.4329 + 1.4330 + ++length; 1.4331 + break; 1.4332 + } 1.4333 + case TYPE_jschar: 1.4334 + length = sourceLength + 1; 1.4335 + break; 1.4336 + default: 1.4337 + return TypeError(cx, "array", args[0]); 1.4338 + } 1.4339 + 1.4340 + } else { 1.4341 + JS_ReportError(cx, "argument must be an array object or length"); 1.4342 + return false; 1.4343 + } 1.4344 + 1.4345 + // Construct a new ArrayType of defined length, for the new CData object. 1.4346 + obj = CreateInternal(cx, baseType, length, true); 1.4347 + if (!obj) 1.4348 + return false; 1.4349 + } 1.4350 + 1.4351 + JSObject* result = CData::Create(cx, obj, NullPtr(), nullptr, true); 1.4352 + if (!result) 1.4353 + return false; 1.4354 + 1.4355 + args.rval().setObject(*result); 1.4356 + 1.4357 + if (convertObject) { 1.4358 + if (!ExplicitConvert(cx, args[0], obj, CData::GetData(result))) 1.4359 + return false; 1.4360 + } 1.4361 + 1.4362 + return true; 1.4363 +} 1.4364 + 1.4365 +JSObject* 1.4366 +ArrayType::GetBaseType(JSObject* obj) 1.4367 +{ 1.4368 + JS_ASSERT(CType::IsCType(obj)); 1.4369 + JS_ASSERT(CType::GetTypeCode(obj) == TYPE_array); 1.4370 + 1.4371 + jsval type = JS_GetReservedSlot(obj, SLOT_ELEMENT_T); 1.4372 + JS_ASSERT(!JSVAL_IS_NULL(type)); 1.4373 + return &type.toObject(); 1.4374 +} 1.4375 + 1.4376 +bool 1.4377 +ArrayType::GetSafeLength(JSObject* obj, size_t* result) 1.4378 +{ 1.4379 + JS_ASSERT(CType::IsCType(obj)); 1.4380 + JS_ASSERT(CType::GetTypeCode(obj) == TYPE_array); 1.4381 + 1.4382 + jsval length = JS_GetReservedSlot(obj, SLOT_LENGTH); 1.4383 + 1.4384 + // The "length" property can be an int, a double, or JSVAL_VOID 1.4385 + // (for arrays of undefined length), and must always fit in a size_t. 1.4386 + if (length.isInt32()) { 1.4387 + *result = length.toInt32();; 1.4388 + return true; 1.4389 + } 1.4390 + if (length.isDouble()) { 1.4391 + *result = Convert<size_t>(length.toDouble()); 1.4392 + return true; 1.4393 + } 1.4394 + 1.4395 + JS_ASSERT(length.isUndefined()); 1.4396 + return false; 1.4397 +} 1.4398 + 1.4399 +size_t 1.4400 +ArrayType::GetLength(JSObject* obj) 1.4401 +{ 1.4402 + JS_ASSERT(CType::IsCType(obj)); 1.4403 + JS_ASSERT(CType::GetTypeCode(obj) == TYPE_array); 1.4404 + 1.4405 + jsval length = JS_GetReservedSlot(obj, SLOT_LENGTH); 1.4406 + 1.4407 + JS_ASSERT(!length.isUndefined()); 1.4408 + 1.4409 + // The "length" property can be an int, a double, or JSVAL_VOID 1.4410 + // (for arrays of undefined length), and must always fit in a size_t. 1.4411 + // For callers who know it can never be JSVAL_VOID, return a size_t directly. 1.4412 + if (length.isInt32()) 1.4413 + return length.toInt32();; 1.4414 + return Convert<size_t>(length.toDouble()); 1.4415 +} 1.4416 + 1.4417 +ffi_type* 1.4418 +ArrayType::BuildFFIType(JSContext* cx, JSObject* obj) 1.4419 +{ 1.4420 + JS_ASSERT(CType::IsCType(obj)); 1.4421 + JS_ASSERT(CType::GetTypeCode(obj) == TYPE_array); 1.4422 + JS_ASSERT(CType::IsSizeDefined(obj)); 1.4423 + 1.4424 + JSObject* baseType = ArrayType::GetBaseType(obj); 1.4425 + ffi_type* ffiBaseType = CType::GetFFIType(cx, baseType); 1.4426 + if (!ffiBaseType) 1.4427 + return nullptr; 1.4428 + 1.4429 + size_t length = ArrayType::GetLength(obj); 1.4430 + 1.4431 + // Create an ffi_type to represent the array. This is necessary for the case 1.4432 + // where the array is part of a struct. Since libffi has no intrinsic 1.4433 + // support for array types, we approximate it by creating a struct type 1.4434 + // with elements of type 'baseType' and with appropriate size and alignment 1.4435 + // values. It would be nice to not do all the work of setting up 'elements', 1.4436 + // but some libffi platforms currently require that it be meaningful. I'm 1.4437 + // looking at you, x86_64. 1.4438 + AutoPtr<ffi_type> ffiType(cx->new_<ffi_type>()); 1.4439 + if (!ffiType) { 1.4440 + JS_ReportOutOfMemory(cx); 1.4441 + return nullptr; 1.4442 + } 1.4443 + 1.4444 + ffiType->type = FFI_TYPE_STRUCT; 1.4445 + ffiType->size = CType::GetSize(obj); 1.4446 + ffiType->alignment = CType::GetAlignment(obj); 1.4447 + ffiType->elements = cx->pod_malloc<ffi_type*>(length + 1); 1.4448 + if (!ffiType->elements) { 1.4449 + JS_ReportAllocationOverflow(cx); 1.4450 + return nullptr; 1.4451 + } 1.4452 + 1.4453 + for (size_t i = 0; i < length; ++i) 1.4454 + ffiType->elements[i] = ffiBaseType; 1.4455 + ffiType->elements[length] = nullptr; 1.4456 + 1.4457 + return ffiType.forget(); 1.4458 +} 1.4459 + 1.4460 +bool 1.4461 +ArrayType::IsArrayType(HandleValue v) 1.4462 +{ 1.4463 + if (!v.isObject()) 1.4464 + return false; 1.4465 + JSObject* obj = &v.toObject(); 1.4466 + return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_array; 1.4467 +} 1.4468 + 1.4469 +bool 1.4470 +ArrayType::IsArrayOrArrayType(HandleValue v) 1.4471 +{ 1.4472 + if (!v.isObject()) 1.4473 + return false; 1.4474 + JSObject* obj = &v.toObject(); 1.4475 + 1.4476 + // Allow both CTypes and CDatas of the ArrayType persuasion by extracting the 1.4477 + // CType if we're dealing with a CData. 1.4478 + if (CData::IsCData(obj)) { 1.4479 + obj = CData::GetCType(obj); 1.4480 + } 1.4481 + return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_array; 1.4482 +} 1.4483 + 1.4484 +bool 1.4485 +ArrayType::ElementTypeGetter(JSContext* cx, JS::CallArgs args) 1.4486 +{ 1.4487 + RootedObject obj(cx, &args.thisv().toObject()); 1.4488 + args.rval().set(JS_GetReservedSlot(obj, SLOT_ELEMENT_T)); 1.4489 + MOZ_ASSERT(args.rval().isObject()); 1.4490 + return true; 1.4491 +} 1.4492 + 1.4493 +bool 1.4494 +ArrayType::LengthGetter(JSContext* cx, JS::CallArgs args) 1.4495 +{ 1.4496 + JSObject *obj = &args.thisv().toObject(); 1.4497 + 1.4498 + // This getter exists for both CTypes and CDatas of the ArrayType persuasion. 1.4499 + // If we're dealing with a CData, get the CType from it. 1.4500 + if (CData::IsCData(obj)) 1.4501 + obj = CData::GetCType(obj); 1.4502 + 1.4503 + args.rval().set(JS_GetReservedSlot(obj, SLOT_LENGTH)); 1.4504 + JS_ASSERT(args.rval().isNumber() || args.rval().isUndefined()); 1.4505 + return true; 1.4506 +} 1.4507 + 1.4508 +bool 1.4509 +ArrayType::Getter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp) 1.4510 +{ 1.4511 + // This should never happen, but we'll check to be safe. 1.4512 + if (!CData::IsCData(obj)) { 1.4513 + JS_ReportError(cx, "not a CData"); 1.4514 + return false; 1.4515 + } 1.4516 + 1.4517 + // Bail early if we're not an ArrayType. (This setter is present for all 1.4518 + // CData, regardless of CType.) 1.4519 + JSObject* typeObj = CData::GetCType(obj); 1.4520 + if (CType::GetTypeCode(typeObj) != TYPE_array) 1.4521 + return true; 1.4522 + 1.4523 + // Convert the index to a size_t and bounds-check it. 1.4524 + size_t index; 1.4525 + size_t length = GetLength(typeObj); 1.4526 + bool ok = jsidToSize(cx, idval, true, &index); 1.4527 + int32_t dummy; 1.4528 + if (!ok && JSID_IS_STRING(idval) && !StringToInteger(cx, JSID_TO_STRING(idval), &dummy)) { 1.4529 + // String either isn't a number, or doesn't fit in size_t. 1.4530 + // Chances are it's a regular property lookup, so return. 1.4531 + return true; 1.4532 + } 1.4533 + if (!ok || index >= length) { 1.4534 + JS_ReportError(cx, "invalid index"); 1.4535 + return false; 1.4536 + } 1.4537 + 1.4538 + RootedObject baseType(cx, GetBaseType(typeObj)); 1.4539 + size_t elementSize = CType::GetSize(baseType); 1.4540 + char* data = static_cast<char*>(CData::GetData(obj)) + elementSize * index; 1.4541 + return ConvertToJS(cx, baseType, obj, data, false, false, vp.address()); 1.4542 +} 1.4543 + 1.4544 +bool 1.4545 +ArrayType::Setter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, MutableHandleValue vp) 1.4546 +{ 1.4547 + // This should never happen, but we'll check to be safe. 1.4548 + if (!CData::IsCData(obj)) { 1.4549 + JS_ReportError(cx, "not a CData"); 1.4550 + return false; 1.4551 + } 1.4552 + 1.4553 + // Bail early if we're not an ArrayType. (This setter is present for all 1.4554 + // CData, regardless of CType.) 1.4555 + JSObject* typeObj = CData::GetCType(obj); 1.4556 + if (CType::GetTypeCode(typeObj) != TYPE_array) 1.4557 + return true; 1.4558 + 1.4559 + // Convert the index to a size_t and bounds-check it. 1.4560 + size_t index; 1.4561 + size_t length = GetLength(typeObj); 1.4562 + bool ok = jsidToSize(cx, idval, true, &index); 1.4563 + int32_t dummy; 1.4564 + if (!ok && JSID_IS_STRING(idval) && !StringToInteger(cx, JSID_TO_STRING(idval), &dummy)) { 1.4565 + // String either isn't a number, or doesn't fit in size_t. 1.4566 + // Chances are it's a regular property lookup, so return. 1.4567 + return true; 1.4568 + } 1.4569 + if (!ok || index >= length) { 1.4570 + JS_ReportError(cx, "invalid index"); 1.4571 + return false; 1.4572 + } 1.4573 + 1.4574 + JSObject* baseType = GetBaseType(typeObj); 1.4575 + size_t elementSize = CType::GetSize(baseType); 1.4576 + char* data = static_cast<char*>(CData::GetData(obj)) + elementSize * index; 1.4577 + return ImplicitConvert(cx, vp, baseType, data, false, nullptr); 1.4578 +} 1.4579 + 1.4580 +bool 1.4581 +ArrayType::AddressOfElement(JSContext* cx, unsigned argc, jsval* vp) 1.4582 +{ 1.4583 + CallArgs args = CallArgsFromVp(argc, vp); 1.4584 + RootedObject obj(cx, JS_THIS_OBJECT(cx, vp)); 1.4585 + if (!obj) 1.4586 + return false; 1.4587 + if (!CData::IsCData(obj)) { 1.4588 + JS_ReportError(cx, "not a CData"); 1.4589 + return false; 1.4590 + } 1.4591 + 1.4592 + RootedObject typeObj(cx, CData::GetCType(obj)); 1.4593 + if (CType::GetTypeCode(typeObj) != TYPE_array) { 1.4594 + JS_ReportError(cx, "not an ArrayType"); 1.4595 + return false; 1.4596 + } 1.4597 + 1.4598 + if (args.length() != 1) { 1.4599 + JS_ReportError(cx, "addressOfElement takes one argument"); 1.4600 + return false; 1.4601 + } 1.4602 + 1.4603 + RootedObject baseType(cx, GetBaseType(typeObj)); 1.4604 + RootedObject pointerType(cx, PointerType::CreateInternal(cx, baseType)); 1.4605 + if (!pointerType) 1.4606 + return false; 1.4607 + 1.4608 + // Create a PointerType CData object containing null. 1.4609 + RootedObject result(cx, CData::Create(cx, pointerType, NullPtr(), nullptr, true)); 1.4610 + if (!result) 1.4611 + return false; 1.4612 + 1.4613 + args.rval().setObject(*result); 1.4614 + 1.4615 + // Convert the index to a size_t and bounds-check it. 1.4616 + size_t index; 1.4617 + size_t length = GetLength(typeObj); 1.4618 + if (!jsvalToSize(cx, args[0], false, &index) || 1.4619 + index >= length) { 1.4620 + JS_ReportError(cx, "invalid index"); 1.4621 + return false; 1.4622 + } 1.4623 + 1.4624 + // Manually set the pointer inside the object, so we skip the conversion step. 1.4625 + void** data = static_cast<void**>(CData::GetData(result)); 1.4626 + size_t elementSize = CType::GetSize(baseType); 1.4627 + *data = static_cast<char*>(CData::GetData(obj)) + elementSize * index; 1.4628 + return true; 1.4629 +} 1.4630 + 1.4631 +/******************************************************************************* 1.4632 +** StructType implementation 1.4633 +*******************************************************************************/ 1.4634 + 1.4635 +// For a struct field descriptor 'val' of the form { name : type }, extract 1.4636 +// 'name' and 'type'. 1.4637 +static JSFlatString* 1.4638 +ExtractStructField(JSContext* cx, jsval val, JSObject** typeObj) 1.4639 +{ 1.4640 + if (JSVAL_IS_PRIMITIVE(val)) { 1.4641 + JS_ReportError(cx, "struct field descriptors require a valid name and type"); 1.4642 + return nullptr; 1.4643 + } 1.4644 + 1.4645 + RootedObject obj(cx, JSVAL_TO_OBJECT(val)); 1.4646 + RootedObject iter(cx, JS_NewPropertyIterator(cx, obj)); 1.4647 + if (!iter) 1.4648 + return nullptr; 1.4649 + 1.4650 + RootedId nameid(cx); 1.4651 + if (!JS_NextProperty(cx, iter, nameid.address())) 1.4652 + return nullptr; 1.4653 + if (JSID_IS_VOID(nameid)) { 1.4654 + JS_ReportError(cx, "struct field descriptors require a valid name and type"); 1.4655 + return nullptr; 1.4656 + } 1.4657 + 1.4658 + if (!JSID_IS_STRING(nameid)) { 1.4659 + JS_ReportError(cx, "struct field descriptors require a valid name and type"); 1.4660 + return nullptr; 1.4661 + } 1.4662 + 1.4663 + // make sure we have one, and only one, property 1.4664 + jsid id; 1.4665 + if (!JS_NextProperty(cx, iter, &id)) 1.4666 + return nullptr; 1.4667 + if (!JSID_IS_VOID(id)) { 1.4668 + JS_ReportError(cx, "struct field descriptors must contain one property"); 1.4669 + return nullptr; 1.4670 + } 1.4671 + 1.4672 + RootedValue propVal(cx); 1.4673 + if (!JS_GetPropertyById(cx, obj, nameid, &propVal)) 1.4674 + return nullptr; 1.4675 + 1.4676 + if (propVal.isPrimitive() || !CType::IsCType(&propVal.toObject())) { 1.4677 + JS_ReportError(cx, "struct field descriptors require a valid name and type"); 1.4678 + return nullptr; 1.4679 + } 1.4680 + 1.4681 + // Undefined size or zero size struct members are illegal. 1.4682 + // (Zero-size arrays are legal as struct members in C++, but libffi will 1.4683 + // choke on a zero-size struct, so we disallow them.) 1.4684 + *typeObj = &propVal.toObject(); 1.4685 + size_t size; 1.4686 + if (!CType::GetSafeSize(*typeObj, &size) || size == 0) { 1.4687 + JS_ReportError(cx, "struct field types must have defined and nonzero size"); 1.4688 + return nullptr; 1.4689 + } 1.4690 + 1.4691 + return JSID_TO_FLAT_STRING(nameid); 1.4692 +} 1.4693 + 1.4694 +// For a struct field with 'name' and 'type', add an element of the form 1.4695 +// { name : type }. 1.4696 +static bool 1.4697 +AddFieldToArray(JSContext* cx, 1.4698 + jsval* element, 1.4699 + JSFlatString* name_, 1.4700 + JSObject* typeObj_) 1.4701 +{ 1.4702 + RootedObject typeObj(cx, typeObj_); 1.4703 + Rooted<JSFlatString*> name(cx, name_); 1.4704 + RootedObject fieldObj(cx, JS_NewObject(cx, nullptr, NullPtr(), NullPtr())); 1.4705 + if (!fieldObj) 1.4706 + return false; 1.4707 + 1.4708 + *element = OBJECT_TO_JSVAL(fieldObj); 1.4709 + 1.4710 + if (!JS_DefineUCProperty(cx, fieldObj, 1.4711 + name->chars(), name->length(), 1.4712 + OBJECT_TO_JSVAL(typeObj), nullptr, nullptr, 1.4713 + JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) 1.4714 + return false; 1.4715 + 1.4716 + return JS_FreezeObject(cx, fieldObj); 1.4717 +} 1.4718 + 1.4719 +bool 1.4720 +StructType::Create(JSContext* cx, unsigned argc, jsval* vp) 1.4721 +{ 1.4722 + CallArgs args = CallArgsFromVp(argc, vp); 1.4723 + 1.4724 + // Construct and return a new StructType object. 1.4725 + if (args.length() < 1 || args.length() > 2) { 1.4726 + JS_ReportError(cx, "StructType takes one or two arguments"); 1.4727 + return false; 1.4728 + } 1.4729 + 1.4730 + jsval name = args[0]; 1.4731 + if (!name.isString()) { 1.4732 + JS_ReportError(cx, "first argument must be a string"); 1.4733 + return false; 1.4734 + } 1.4735 + 1.4736 + // Get ctypes.StructType.prototype from the ctypes.StructType constructor. 1.4737 + RootedObject typeProto(cx, CType::GetProtoFromCtor(&args.callee(), SLOT_STRUCTPROTO)); 1.4738 + 1.4739 + // Create a simple StructType with no defined fields. The result will be 1.4740 + // non-instantiable as CData, will have no 'prototype' property, and will 1.4741 + // have undefined size and alignment and no ffi_type. 1.4742 + RootedObject result(cx, CType::Create(cx, typeProto, NullPtr(), TYPE_struct, 1.4743 + JSVAL_TO_STRING(name), JSVAL_VOID, JSVAL_VOID, nullptr)); 1.4744 + if (!result) 1.4745 + return false; 1.4746 + 1.4747 + if (args.length() == 2) { 1.4748 + RootedObject arr(cx, JSVAL_IS_PRIMITIVE(args[1]) ? nullptr : &args[1].toObject()); 1.4749 + if (!arr || !JS_IsArrayObject(cx, arr)) { 1.4750 + JS_ReportError(cx, "second argument must be an array"); 1.4751 + return false; 1.4752 + } 1.4753 + 1.4754 + // Define the struct fields. 1.4755 + if (!DefineInternal(cx, result, arr)) 1.4756 + return false; 1.4757 + } 1.4758 + 1.4759 + args.rval().setObject(*result); 1.4760 + return true; 1.4761 +} 1.4762 + 1.4763 +static void 1.4764 +PostBarrierCallback(JSTracer *trc, JSString *key, void *data) 1.4765 +{ 1.4766 + typedef HashMap<JSFlatString*, 1.4767 + UnbarrieredFieldInfo, 1.4768 + FieldHashPolicy, 1.4769 + SystemAllocPolicy> UnbarrieredFieldInfoHash; 1.4770 + 1.4771 + UnbarrieredFieldInfoHash *table = reinterpret_cast<UnbarrieredFieldInfoHash*>(data); 1.4772 + JSString *prior = key; 1.4773 + JS_CallStringTracer(trc, &key, "CType fieldName"); 1.4774 + table->rekeyIfMoved(JS_ASSERT_STRING_IS_FLAT(prior), JS_ASSERT_STRING_IS_FLAT(key)); 1.4775 +} 1.4776 + 1.4777 +bool 1.4778 +StructType::DefineInternal(JSContext* cx, JSObject* typeObj_, JSObject* fieldsObj_) 1.4779 +{ 1.4780 + RootedObject typeObj(cx, typeObj_); 1.4781 + RootedObject fieldsObj(cx, fieldsObj_); 1.4782 + 1.4783 + uint32_t len; 1.4784 + ASSERT_OK(JS_GetArrayLength(cx, fieldsObj, &len)); 1.4785 + 1.4786 + // Get the common prototype for CData objects of this type from 1.4787 + // ctypes.CType.prototype. 1.4788 + RootedObject dataProto(cx, CType::GetProtoFromType(cx, typeObj, SLOT_STRUCTDATAPROTO)); 1.4789 + if (!dataProto) 1.4790 + return false; 1.4791 + 1.4792 + // Set up the 'prototype' and 'prototype.constructor' properties. 1.4793 + // The prototype will reflect the struct fields as properties on CData objects 1.4794 + // created from this type. 1.4795 + RootedObject prototype(cx, JS_NewObject(cx, &sCDataProtoClass, dataProto, NullPtr())); 1.4796 + if (!prototype) 1.4797 + return false; 1.4798 + 1.4799 + if (!JS_DefineProperty(cx, prototype, "constructor", typeObj, 1.4800 + JSPROP_READONLY | JSPROP_PERMANENT)) 1.4801 + return false; 1.4802 + 1.4803 + // Create a FieldInfoHash to stash on the type object, and an array to root 1.4804 + // its constituents. (We cannot simply stash the hash in a reserved slot now 1.4805 + // to get GC safety for free, since if anything in this function fails we 1.4806 + // do not want to mutate 'typeObj'.) 1.4807 + AutoPtr<FieldInfoHash> fields(cx->new_<FieldInfoHash>()); 1.4808 + if (!fields || !fields->init(len)) { 1.4809 + JS_ReportOutOfMemory(cx); 1.4810 + return false; 1.4811 + } 1.4812 + JS::AutoValueVector fieldRoots(cx); 1.4813 + if (!fieldRoots.resize(len)) { 1.4814 + JS_ReportOutOfMemory(cx); 1.4815 + return false; 1.4816 + } 1.4817 + 1.4818 + // Process the field types. 1.4819 + size_t structSize, structAlign; 1.4820 + if (len != 0) { 1.4821 + structSize = 0; 1.4822 + structAlign = 0; 1.4823 + 1.4824 + for (uint32_t i = 0; i < len; ++i) { 1.4825 + RootedValue item(cx); 1.4826 + if (!JS_GetElement(cx, fieldsObj, i, &item)) 1.4827 + return false; 1.4828 + 1.4829 + RootedObject fieldType(cx, nullptr); 1.4830 + Rooted<JSFlatString*> name(cx, ExtractStructField(cx, item, fieldType.address())); 1.4831 + if (!name) 1.4832 + return false; 1.4833 + fieldRoots[i] = JS::ObjectValue(*fieldType); 1.4834 + 1.4835 + // Make sure each field name is unique 1.4836 + FieldInfoHash::AddPtr entryPtr = fields->lookupForAdd(name); 1.4837 + if (entryPtr) { 1.4838 + JS_ReportError(cx, "struct fields must have unique names"); 1.4839 + return false; 1.4840 + } 1.4841 + 1.4842 + // Add the field to the StructType's 'prototype' property. 1.4843 + if (!JS_DefineUCProperty(cx, prototype, 1.4844 + name->chars(), name->length(), JSVAL_VOID, 1.4845 + StructType::FieldGetter, StructType::FieldSetter, 1.4846 + JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_PERMANENT)) 1.4847 + return false; 1.4848 + 1.4849 + size_t fieldSize = CType::GetSize(fieldType); 1.4850 + size_t fieldAlign = CType::GetAlignment(fieldType); 1.4851 + size_t fieldOffset = Align(structSize, fieldAlign); 1.4852 + // Check for overflow. Since we hold invariant that fieldSize % fieldAlign 1.4853 + // be zero, we can safely check fieldOffset + fieldSize without first 1.4854 + // checking fieldOffset for overflow. 1.4855 + if (fieldOffset + fieldSize < structSize) { 1.4856 + JS_ReportError(cx, "size overflow"); 1.4857 + return false; 1.4858 + } 1.4859 + 1.4860 + // Add field name to the hash 1.4861 + FieldInfo info; 1.4862 + info.mType = fieldType; 1.4863 + info.mIndex = i; 1.4864 + info.mOffset = fieldOffset; 1.4865 + ASSERT_OK(fields->add(entryPtr, name, info)); 1.4866 + JS_StoreStringPostBarrierCallback(cx, PostBarrierCallback, name, fields.get()); 1.4867 + 1.4868 + structSize = fieldOffset + fieldSize; 1.4869 + 1.4870 + if (fieldAlign > structAlign) 1.4871 + structAlign = fieldAlign; 1.4872 + } 1.4873 + 1.4874 + // Pad the struct tail according to struct alignment. 1.4875 + size_t structTail = Align(structSize, structAlign); 1.4876 + if (structTail < structSize) { 1.4877 + JS_ReportError(cx, "size overflow"); 1.4878 + return false; 1.4879 + } 1.4880 + structSize = structTail; 1.4881 + 1.4882 + } else { 1.4883 + // Empty structs are illegal in C, but are legal and have a size of 1.4884 + // 1 byte in C++. We're going to allow them, and trick libffi into 1.4885 + // believing this by adding a char member. The resulting struct will have 1.4886 + // no getters or setters, and will be initialized to zero. 1.4887 + structSize = 1; 1.4888 + structAlign = 1; 1.4889 + } 1.4890 + 1.4891 + RootedValue sizeVal(cx); 1.4892 + if (!SizeTojsval(cx, structSize, sizeVal.address())) 1.4893 + return false; 1.4894 + 1.4895 + JS_SetReservedSlot(typeObj, SLOT_FIELDINFO, PRIVATE_TO_JSVAL(fields.forget())); 1.4896 + 1.4897 + JS_SetReservedSlot(typeObj, SLOT_SIZE, sizeVal); 1.4898 + JS_SetReservedSlot(typeObj, SLOT_ALIGN, INT_TO_JSVAL(structAlign)); 1.4899 + //if (!JS_FreezeObject(cx, prototype)0 // XXX fixme - see bug 541212! 1.4900 + // return false; 1.4901 + JS_SetReservedSlot(typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype)); 1.4902 + return true; 1.4903 +} 1.4904 + 1.4905 +ffi_type* 1.4906 +StructType::BuildFFIType(JSContext* cx, JSObject* obj) 1.4907 +{ 1.4908 + JS_ASSERT(CType::IsCType(obj)); 1.4909 + JS_ASSERT(CType::GetTypeCode(obj) == TYPE_struct); 1.4910 + JS_ASSERT(CType::IsSizeDefined(obj)); 1.4911 + 1.4912 + const FieldInfoHash* fields = GetFieldInfo(obj); 1.4913 + size_t len = fields->count(); 1.4914 + 1.4915 + size_t structSize = CType::GetSize(obj); 1.4916 + size_t structAlign = CType::GetAlignment(obj); 1.4917 + 1.4918 + AutoPtr<ffi_type> ffiType(cx->new_<ffi_type>()); 1.4919 + if (!ffiType) { 1.4920 + JS_ReportOutOfMemory(cx); 1.4921 + return nullptr; 1.4922 + } 1.4923 + ffiType->type = FFI_TYPE_STRUCT; 1.4924 + 1.4925 + AutoPtr<ffi_type*> elements; 1.4926 + if (len != 0) { 1.4927 + elements = cx->pod_malloc<ffi_type*>(len + 1); 1.4928 + if (!elements) { 1.4929 + JS_ReportOutOfMemory(cx); 1.4930 + return nullptr; 1.4931 + } 1.4932 + elements[len] = nullptr; 1.4933 + 1.4934 + for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) { 1.4935 + const FieldInfoHash::Entry& entry = r.front(); 1.4936 + ffi_type* fieldType = CType::GetFFIType(cx, entry.value().mType); 1.4937 + if (!fieldType) 1.4938 + return nullptr; 1.4939 + elements[entry.value().mIndex] = fieldType; 1.4940 + } 1.4941 + 1.4942 + } else { 1.4943 + // Represent an empty struct as having a size of 1 byte, just like C++. 1.4944 + JS_ASSERT(structSize == 1); 1.4945 + JS_ASSERT(structAlign == 1); 1.4946 + elements = cx->pod_malloc<ffi_type*>(2); 1.4947 + if (!elements) { 1.4948 + JS_ReportOutOfMemory(cx); 1.4949 + return nullptr; 1.4950 + } 1.4951 + elements[0] = &ffi_type_uint8; 1.4952 + elements[1] = nullptr; 1.4953 + } 1.4954 + 1.4955 + ffiType->elements = elements.get(); 1.4956 + 1.4957 +#ifdef DEBUG 1.4958 + // Perform a sanity check: the result of our struct size and alignment 1.4959 + // calculations should match libffi's. We force it to do this calculation 1.4960 + // by calling ffi_prep_cif. 1.4961 + ffi_cif cif; 1.4962 + ffiType->size = 0; 1.4963 + ffiType->alignment = 0; 1.4964 + ffi_status status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 0, ffiType.get(), nullptr); 1.4965 + JS_ASSERT(status == FFI_OK); 1.4966 + JS_ASSERT(structSize == ffiType->size); 1.4967 + JS_ASSERT(structAlign == ffiType->alignment); 1.4968 +#else 1.4969 + // Fill in the ffi_type's size and align fields. This makes libffi treat the 1.4970 + // type as initialized; it will not recompute the values. (We assume 1.4971 + // everything agrees; if it doesn't, we really want to know about it, which 1.4972 + // is the purpose of the above debug-only check.) 1.4973 + ffiType->size = structSize; 1.4974 + ffiType->alignment = structAlign; 1.4975 +#endif 1.4976 + 1.4977 + elements.forget(); 1.4978 + return ffiType.forget(); 1.4979 +} 1.4980 + 1.4981 +bool 1.4982 +StructType::Define(JSContext* cx, unsigned argc, jsval* vp) 1.4983 +{ 1.4984 + CallArgs args = CallArgsFromVp(argc, vp); 1.4985 + RootedObject obj(cx, JS_THIS_OBJECT(cx, vp)); 1.4986 + if (!obj) 1.4987 + return false; 1.4988 + if (!CType::IsCType(obj) || 1.4989 + CType::GetTypeCode(obj) != TYPE_struct) { 1.4990 + JS_ReportError(cx, "not a StructType"); 1.4991 + return false; 1.4992 + } 1.4993 + 1.4994 + if (CType::IsSizeDefined(obj)) { 1.4995 + JS_ReportError(cx, "StructType has already been defined"); 1.4996 + return false; 1.4997 + } 1.4998 + 1.4999 + if (args.length() != 1) { 1.5000 + JS_ReportError(cx, "define takes one argument"); 1.5001 + return false; 1.5002 + } 1.5003 + 1.5004 + jsval arg = args[0]; 1.5005 + if (JSVAL_IS_PRIMITIVE(arg)) { 1.5006 + JS_ReportError(cx, "argument must be an array"); 1.5007 + return false; 1.5008 + } 1.5009 + RootedObject arr(cx, JSVAL_TO_OBJECT(arg)); 1.5010 + if (!JS_IsArrayObject(cx, arr)) { 1.5011 + JS_ReportError(cx, "argument must be an array"); 1.5012 + return false; 1.5013 + } 1.5014 + 1.5015 + return DefineInternal(cx, obj, arr); 1.5016 +} 1.5017 + 1.5018 +bool 1.5019 +StructType::ConstructData(JSContext* cx, 1.5020 + HandleObject obj, 1.5021 + const CallArgs& args) 1.5022 +{ 1.5023 + if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_struct) { 1.5024 + JS_ReportError(cx, "not a StructType"); 1.5025 + return false; 1.5026 + } 1.5027 + 1.5028 + if (!CType::IsSizeDefined(obj)) { 1.5029 + JS_ReportError(cx, "cannot construct an opaque StructType"); 1.5030 + return false; 1.5031 + } 1.5032 + 1.5033 + JSObject* result = CData::Create(cx, obj, NullPtr(), nullptr, true); 1.5034 + if (!result) 1.5035 + return false; 1.5036 + 1.5037 + args.rval().setObject(*result); 1.5038 + 1.5039 + if (args.length() == 0) 1.5040 + return true; 1.5041 + 1.5042 + char* buffer = static_cast<char*>(CData::GetData(result)); 1.5043 + const FieldInfoHash* fields = GetFieldInfo(obj); 1.5044 + 1.5045 + if (args.length() == 1) { 1.5046 + // There are two possible interpretations of the argument: 1.5047 + // 1) It may be an object '{ ... }' with properties representing the 1.5048 + // struct fields intended to ExplicitConvert wholesale to our StructType. 1.5049 + // 2) If the struct contains one field, the arg may be intended to 1.5050 + // ImplicitConvert directly to that arg's CType. 1.5051 + // Thankfully, the conditions for these two possibilities to succeed 1.5052 + // are mutually exclusive, so we can pick the right one. 1.5053 + 1.5054 + // Try option 1) first. 1.5055 + if (ExplicitConvert(cx, args[0], obj, buffer)) 1.5056 + return true; 1.5057 + 1.5058 + if (fields->count() != 1) 1.5059 + return false; 1.5060 + 1.5061 + // If ExplicitConvert failed, and there is no pending exception, then assume 1.5062 + // hard failure (out of memory, or some other similarly serious condition). 1.5063 + if (!JS_IsExceptionPending(cx)) 1.5064 + return false; 1.5065 + 1.5066 + // Otherwise, assume soft failure, and clear the pending exception so that we 1.5067 + // can throw a different one as required. 1.5068 + JS_ClearPendingException(cx); 1.5069 + 1.5070 + // Fall through to try option 2). 1.5071 + } 1.5072 + 1.5073 + // We have a type constructor of the form 'ctypes.StructType(a, b, c, ...)'. 1.5074 + // ImplicitConvert each field. 1.5075 + if (args.length() == fields->count()) { 1.5076 + for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) { 1.5077 + const FieldInfo& field = r.front().value(); 1.5078 + STATIC_ASSUME(field.mIndex < fields->count()); /* Quantified invariant */ 1.5079 + if (!ImplicitConvert(cx, args[field.mIndex], field.mType, 1.5080 + buffer + field.mOffset, 1.5081 + false, nullptr)) 1.5082 + return false; 1.5083 + } 1.5084 + 1.5085 + return true; 1.5086 + } 1.5087 + 1.5088 + JS_ReportError(cx, "constructor takes 0, 1, or %u arguments", 1.5089 + fields->count()); 1.5090 + return false; 1.5091 +} 1.5092 + 1.5093 +const FieldInfoHash* 1.5094 +StructType::GetFieldInfo(JSObject* obj) 1.5095 +{ 1.5096 + JS_ASSERT(CType::IsCType(obj)); 1.5097 + JS_ASSERT(CType::GetTypeCode(obj) == TYPE_struct); 1.5098 + 1.5099 + jsval slot = JS_GetReservedSlot(obj, SLOT_FIELDINFO); 1.5100 + JS_ASSERT(!JSVAL_IS_VOID(slot) && JSVAL_TO_PRIVATE(slot)); 1.5101 + 1.5102 + return static_cast<const FieldInfoHash*>(JSVAL_TO_PRIVATE(slot)); 1.5103 +} 1.5104 + 1.5105 +const FieldInfo* 1.5106 +StructType::LookupField(JSContext* cx, JSObject* obj, JSFlatString *name) 1.5107 +{ 1.5108 + JS_ASSERT(CType::IsCType(obj)); 1.5109 + JS_ASSERT(CType::GetTypeCode(obj) == TYPE_struct); 1.5110 + 1.5111 + FieldInfoHash::Ptr ptr = GetFieldInfo(obj)->lookup(name); 1.5112 + if (ptr) 1.5113 + return &ptr->value(); 1.5114 + 1.5115 + JSAutoByteString bytes(cx, name); 1.5116 + if (!bytes) 1.5117 + return nullptr; 1.5118 + 1.5119 + JS_ReportError(cx, "%s does not name a field", bytes.ptr()); 1.5120 + return nullptr; 1.5121 +} 1.5122 + 1.5123 +JSObject* 1.5124 +StructType::BuildFieldsArray(JSContext* cx, JSObject* obj) 1.5125 +{ 1.5126 + JS_ASSERT(CType::IsCType(obj)); 1.5127 + JS_ASSERT(CType::GetTypeCode(obj) == TYPE_struct); 1.5128 + JS_ASSERT(CType::IsSizeDefined(obj)); 1.5129 + 1.5130 + const FieldInfoHash* fields = GetFieldInfo(obj); 1.5131 + size_t len = fields->count(); 1.5132 + 1.5133 + // Prepare a new array for the 'fields' property of the StructType. 1.5134 + JS::AutoValueVector fieldsVec(cx); 1.5135 + if (!fieldsVec.resize(len)) 1.5136 + return nullptr; 1.5137 + 1.5138 + for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) { 1.5139 + const FieldInfoHash::Entry& entry = r.front(); 1.5140 + // Add the field descriptor to the array. 1.5141 + if (!AddFieldToArray(cx, &fieldsVec[entry.value().mIndex], 1.5142 + entry.key(), entry.value().mType)) 1.5143 + return nullptr; 1.5144 + } 1.5145 + 1.5146 + RootedObject fieldsProp(cx, JS_NewArrayObject(cx, fieldsVec)); 1.5147 + if (!fieldsProp) 1.5148 + return nullptr; 1.5149 + 1.5150 + // Seal the fields array. 1.5151 + if (!JS_FreezeObject(cx, fieldsProp)) 1.5152 + return nullptr; 1.5153 + 1.5154 + return fieldsProp; 1.5155 +} 1.5156 + 1.5157 +/* static */ bool 1.5158 +StructType::IsStruct(HandleValue v) 1.5159 +{ 1.5160 + if (!v.isObject()) 1.5161 + return false; 1.5162 + JSObject* obj = &v.toObject(); 1.5163 + return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_struct; 1.5164 +} 1.5165 + 1.5166 +bool 1.5167 +StructType::FieldsArrayGetter(JSContext* cx, JS::CallArgs args) 1.5168 +{ 1.5169 + RootedObject obj(cx, &args.thisv().toObject()); 1.5170 + 1.5171 + args.rval().set(JS_GetReservedSlot(obj, SLOT_FIELDS)); 1.5172 + 1.5173 + if (!CType::IsSizeDefined(obj)) { 1.5174 + MOZ_ASSERT(args.rval().isUndefined()); 1.5175 + return true; 1.5176 + } 1.5177 + 1.5178 + if (args.rval().isUndefined()) { 1.5179 + // Build the 'fields' array lazily. 1.5180 + JSObject* fields = BuildFieldsArray(cx, obj); 1.5181 + if (!fields) 1.5182 + return false; 1.5183 + JS_SetReservedSlot(obj, SLOT_FIELDS, OBJECT_TO_JSVAL(fields)); 1.5184 + 1.5185 + args.rval().setObject(*fields); 1.5186 + } 1.5187 + 1.5188 + MOZ_ASSERT(args.rval().isObject()); 1.5189 + MOZ_ASSERT(JS_IsArrayObject(cx, args.rval())); 1.5190 + return true; 1.5191 +} 1.5192 + 1.5193 +bool 1.5194 +StructType::FieldGetter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp) 1.5195 +{ 1.5196 + if (!CData::IsCData(obj)) { 1.5197 + JS_ReportError(cx, "not a CData"); 1.5198 + return false; 1.5199 + } 1.5200 + 1.5201 + JSObject* typeObj = CData::GetCType(obj); 1.5202 + if (CType::GetTypeCode(typeObj) != TYPE_struct) { 1.5203 + JS_ReportError(cx, "not a StructType"); 1.5204 + return false; 1.5205 + } 1.5206 + 1.5207 + const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_FLAT_STRING(idval)); 1.5208 + if (!field) 1.5209 + return false; 1.5210 + 1.5211 + char* data = static_cast<char*>(CData::GetData(obj)) + field->mOffset; 1.5212 + RootedObject fieldType(cx, field->mType); 1.5213 + return ConvertToJS(cx, fieldType, obj, data, false, false, vp.address()); 1.5214 +} 1.5215 + 1.5216 +bool 1.5217 +StructType::FieldSetter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, MutableHandleValue vp) 1.5218 +{ 1.5219 + if (!CData::IsCData(obj)) { 1.5220 + JS_ReportError(cx, "not a CData"); 1.5221 + return false; 1.5222 + } 1.5223 + 1.5224 + JSObject* typeObj = CData::GetCType(obj); 1.5225 + if (CType::GetTypeCode(typeObj) != TYPE_struct) { 1.5226 + JS_ReportError(cx, "not a StructType"); 1.5227 + return false; 1.5228 + } 1.5229 + 1.5230 + const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_FLAT_STRING(idval)); 1.5231 + if (!field) 1.5232 + return false; 1.5233 + 1.5234 + char* data = static_cast<char*>(CData::GetData(obj)) + field->mOffset; 1.5235 + return ImplicitConvert(cx, vp, field->mType, data, false, nullptr); 1.5236 +} 1.5237 + 1.5238 +bool 1.5239 +StructType::AddressOfField(JSContext* cx, unsigned argc, jsval* vp) 1.5240 +{ 1.5241 + CallArgs args = CallArgsFromVp(argc, vp); 1.5242 + RootedObject obj(cx, JS_THIS_OBJECT(cx, vp)); 1.5243 + if (!obj) 1.5244 + return false; 1.5245 + if (!CData::IsCData(obj)) { 1.5246 + JS_ReportError(cx, "not a CData"); 1.5247 + return false; 1.5248 + } 1.5249 + 1.5250 + JSObject* typeObj = CData::GetCType(obj); 1.5251 + if (CType::GetTypeCode(typeObj) != TYPE_struct) { 1.5252 + JS_ReportError(cx, "not a StructType"); 1.5253 + return false; 1.5254 + } 1.5255 + 1.5256 + if (args.length() != 1) { 1.5257 + JS_ReportError(cx, "addressOfField takes one argument"); 1.5258 + return false; 1.5259 + } 1.5260 + 1.5261 + JSFlatString *str = JS_FlattenString(cx, args[0].toString()); 1.5262 + if (!str) 1.5263 + return false; 1.5264 + 1.5265 + const FieldInfo* field = LookupField(cx, typeObj, str); 1.5266 + if (!field) 1.5267 + return false; 1.5268 + 1.5269 + RootedObject baseType(cx, field->mType); 1.5270 + RootedObject pointerType(cx, PointerType::CreateInternal(cx, baseType)); 1.5271 + if (!pointerType) 1.5272 + return false; 1.5273 + 1.5274 + // Create a PointerType CData object containing null. 1.5275 + JSObject* result = CData::Create(cx, pointerType, NullPtr(), nullptr, true); 1.5276 + if (!result) 1.5277 + return false; 1.5278 + 1.5279 + args.rval().setObject(*result); 1.5280 + 1.5281 + // Manually set the pointer inside the object, so we skip the conversion step. 1.5282 + void** data = static_cast<void**>(CData::GetData(result)); 1.5283 + *data = static_cast<char*>(CData::GetData(obj)) + field->mOffset; 1.5284 + return true; 1.5285 +} 1.5286 + 1.5287 +/******************************************************************************* 1.5288 +** FunctionType implementation 1.5289 +*******************************************************************************/ 1.5290 + 1.5291 +// Helper class for handling allocation of function arguments. 1.5292 +struct AutoValue 1.5293 +{ 1.5294 + AutoValue() : mData(nullptr) { } 1.5295 + 1.5296 + ~AutoValue() 1.5297 + { 1.5298 + js_free(mData); 1.5299 + } 1.5300 + 1.5301 + bool SizeToType(JSContext* cx, JSObject* type) 1.5302 + { 1.5303 + // Allocate a minimum of sizeof(ffi_arg) to handle small integers. 1.5304 + size_t size = Align(CType::GetSize(type), sizeof(ffi_arg)); 1.5305 + mData = js_malloc(size); 1.5306 + if (mData) 1.5307 + memset(mData, 0, size); 1.5308 + return mData != nullptr; 1.5309 + } 1.5310 + 1.5311 + void* mData; 1.5312 +}; 1.5313 + 1.5314 +static bool 1.5315 +GetABI(JSContext* cx, jsval abiType, ffi_abi* result) 1.5316 +{ 1.5317 + if (JSVAL_IS_PRIMITIVE(abiType)) 1.5318 + return false; 1.5319 + 1.5320 + ABICode abi = GetABICode(JSVAL_TO_OBJECT(abiType)); 1.5321 + 1.5322 + // determine the ABI from the subset of those available on the 1.5323 + // given platform. ABI_DEFAULT specifies the default 1.5324 + // C calling convention (cdecl) on each platform. 1.5325 + switch (abi) { 1.5326 + case ABI_DEFAULT: 1.5327 + *result = FFI_DEFAULT_ABI; 1.5328 + return true; 1.5329 + case ABI_STDCALL: 1.5330 + case ABI_WINAPI: 1.5331 +#if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2) 1.5332 + *result = FFI_STDCALL; 1.5333 + return true; 1.5334 +#elif (defined(_WIN64)) 1.5335 + // We'd like the same code to work across Win32 and Win64, so stdcall_api 1.5336 + // and winapi_abi become aliases to the lone Win64 ABI. 1.5337 + *result = FFI_WIN64; 1.5338 + return true; 1.5339 +#endif 1.5340 + case INVALID_ABI: 1.5341 + break; 1.5342 + } 1.5343 + return false; 1.5344 +} 1.5345 + 1.5346 +static JSObject* 1.5347 +PrepareType(JSContext* cx, jsval type) 1.5348 +{ 1.5349 + if (JSVAL_IS_PRIMITIVE(type) || 1.5350 + !CType::IsCType(JSVAL_TO_OBJECT(type))) { 1.5351 + JS_ReportError(cx, "not a ctypes type"); 1.5352 + return nullptr; 1.5353 + } 1.5354 + 1.5355 + JSObject* result = JSVAL_TO_OBJECT(type); 1.5356 + TypeCode typeCode = CType::GetTypeCode(result); 1.5357 + 1.5358 + if (typeCode == TYPE_array) { 1.5359 + // convert array argument types to pointers, just like C. 1.5360 + // ImplicitConvert will do the same, when passing an array as data. 1.5361 + RootedObject baseType(cx, ArrayType::GetBaseType(result)); 1.5362 + result = PointerType::CreateInternal(cx, baseType); 1.5363 + if (!result) 1.5364 + return nullptr; 1.5365 + 1.5366 + } else if (typeCode == TYPE_void_t || typeCode == TYPE_function) { 1.5367 + // disallow void or function argument types 1.5368 + JS_ReportError(cx, "Cannot have void or function argument type"); 1.5369 + return nullptr; 1.5370 + } 1.5371 + 1.5372 + if (!CType::IsSizeDefined(result)) { 1.5373 + JS_ReportError(cx, "Argument type must have defined size"); 1.5374 + return nullptr; 1.5375 + } 1.5376 + 1.5377 + // libffi cannot pass types of zero size by value. 1.5378 + JS_ASSERT(CType::GetSize(result) != 0); 1.5379 + 1.5380 + return result; 1.5381 +} 1.5382 + 1.5383 +static JSObject* 1.5384 +PrepareReturnType(JSContext* cx, jsval type) 1.5385 +{ 1.5386 + if (JSVAL_IS_PRIMITIVE(type) || 1.5387 + !CType::IsCType(JSVAL_TO_OBJECT(type))) { 1.5388 + JS_ReportError(cx, "not a ctypes type"); 1.5389 + return nullptr; 1.5390 + } 1.5391 + 1.5392 + JSObject* result = JSVAL_TO_OBJECT(type); 1.5393 + TypeCode typeCode = CType::GetTypeCode(result); 1.5394 + 1.5395 + // Arrays and functions can never be return types. 1.5396 + if (typeCode == TYPE_array || typeCode == TYPE_function) { 1.5397 + JS_ReportError(cx, "Return type cannot be an array or function"); 1.5398 + return nullptr; 1.5399 + } 1.5400 + 1.5401 + if (typeCode != TYPE_void_t && !CType::IsSizeDefined(result)) { 1.5402 + JS_ReportError(cx, "Return type must have defined size"); 1.5403 + return nullptr; 1.5404 + } 1.5405 + 1.5406 + // libffi cannot pass types of zero size by value. 1.5407 + JS_ASSERT(typeCode == TYPE_void_t || CType::GetSize(result) != 0); 1.5408 + 1.5409 + return result; 1.5410 +} 1.5411 + 1.5412 +static MOZ_ALWAYS_INLINE bool 1.5413 +IsEllipsis(JSContext* cx, jsval v, bool* isEllipsis) 1.5414 +{ 1.5415 + *isEllipsis = false; 1.5416 + if (!JSVAL_IS_STRING(v)) 1.5417 + return true; 1.5418 + JSString* str = JSVAL_TO_STRING(v); 1.5419 + if (str->length() != 3) 1.5420 + return true; 1.5421 + const jschar* chars = str->getChars(cx); 1.5422 + if (!chars) 1.5423 + return false; 1.5424 + jschar dot = '.'; 1.5425 + *isEllipsis = (chars[0] == dot && 1.5426 + chars[1] == dot && 1.5427 + chars[2] == dot); 1.5428 + return true; 1.5429 +} 1.5430 + 1.5431 +static bool 1.5432 +PrepareCIF(JSContext* cx, 1.5433 + FunctionInfo* fninfo) 1.5434 +{ 1.5435 + ffi_abi abi; 1.5436 + if (!GetABI(cx, OBJECT_TO_JSVAL(fninfo->mABI), &abi)) { 1.5437 + JS_ReportError(cx, "Invalid ABI specification"); 1.5438 + return false; 1.5439 + } 1.5440 + 1.5441 + ffi_type* rtype = CType::GetFFIType(cx, fninfo->mReturnType); 1.5442 + if (!rtype) 1.5443 + return false; 1.5444 + 1.5445 + ffi_status status = 1.5446 + ffi_prep_cif(&fninfo->mCIF, 1.5447 + abi, 1.5448 + fninfo->mFFITypes.length(), 1.5449 + rtype, 1.5450 + fninfo->mFFITypes.begin()); 1.5451 + 1.5452 + switch (status) { 1.5453 + case FFI_OK: 1.5454 + return true; 1.5455 + case FFI_BAD_ABI: 1.5456 + JS_ReportError(cx, "Invalid ABI specification"); 1.5457 + return false; 1.5458 + case FFI_BAD_TYPEDEF: 1.5459 + JS_ReportError(cx, "Invalid type specification"); 1.5460 + return false; 1.5461 + default: 1.5462 + JS_ReportError(cx, "Unknown libffi error"); 1.5463 + return false; 1.5464 + } 1.5465 +} 1.5466 + 1.5467 +void 1.5468 +FunctionType::BuildSymbolName(JSString* name, 1.5469 + JSObject* typeObj, 1.5470 + AutoCString& result) 1.5471 +{ 1.5472 + FunctionInfo* fninfo = GetFunctionInfo(typeObj); 1.5473 + 1.5474 + switch (GetABICode(fninfo->mABI)) { 1.5475 + case ABI_DEFAULT: 1.5476 + case ABI_WINAPI: 1.5477 + // For cdecl or WINAPI functions, no mangling is necessary. 1.5478 + AppendString(result, name); 1.5479 + break; 1.5480 + 1.5481 + case ABI_STDCALL: { 1.5482 +#if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2) 1.5483 + // On WIN32, stdcall functions look like: 1.5484 + // _foo@40 1.5485 + // where 'foo' is the function name, and '40' is the aligned size of the 1.5486 + // arguments. 1.5487 + AppendString(result, "_"); 1.5488 + AppendString(result, name); 1.5489 + AppendString(result, "@"); 1.5490 + 1.5491 + // Compute the suffix by aligning each argument to sizeof(ffi_arg). 1.5492 + size_t size = 0; 1.5493 + for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) { 1.5494 + JSObject* argType = fninfo->mArgTypes[i]; 1.5495 + size += Align(CType::GetSize(argType), sizeof(ffi_arg)); 1.5496 + } 1.5497 + 1.5498 + IntegerToString(size, 10, result); 1.5499 +#elif defined(_WIN64) 1.5500 + // On Win64, stdcall is an alias to the default ABI for compatibility, so no 1.5501 + // mangling is done. 1.5502 + AppendString(result, name); 1.5503 +#endif 1.5504 + break; 1.5505 + } 1.5506 + 1.5507 + case INVALID_ABI: 1.5508 + MOZ_ASSUME_UNREACHABLE("invalid abi"); 1.5509 + } 1.5510 +} 1.5511 + 1.5512 +static FunctionInfo* 1.5513 +NewFunctionInfo(JSContext* cx, 1.5514 + jsval abiType, 1.5515 + jsval returnType, 1.5516 + jsval* argTypes, 1.5517 + unsigned argLength) 1.5518 +{ 1.5519 + AutoPtr<FunctionInfo> fninfo(cx->new_<FunctionInfo>()); 1.5520 + if (!fninfo) { 1.5521 + JS_ReportOutOfMemory(cx); 1.5522 + return nullptr; 1.5523 + } 1.5524 + 1.5525 + ffi_abi abi; 1.5526 + if (!GetABI(cx, abiType, &abi)) { 1.5527 + JS_ReportError(cx, "Invalid ABI specification"); 1.5528 + return nullptr; 1.5529 + } 1.5530 + fninfo->mABI = JSVAL_TO_OBJECT(abiType); 1.5531 + 1.5532 + // prepare the result type 1.5533 + fninfo->mReturnType = PrepareReturnType(cx, returnType); 1.5534 + if (!fninfo->mReturnType) 1.5535 + return nullptr; 1.5536 + 1.5537 + // prepare the argument types 1.5538 + if (!fninfo->mArgTypes.reserve(argLength) || 1.5539 + !fninfo->mFFITypes.reserve(argLength)) { 1.5540 + JS_ReportOutOfMemory(cx); 1.5541 + return nullptr; 1.5542 + } 1.5543 + 1.5544 + fninfo->mIsVariadic = false; 1.5545 + 1.5546 + for (uint32_t i = 0; i < argLength; ++i) { 1.5547 + bool isEllipsis; 1.5548 + if (!IsEllipsis(cx, argTypes[i], &isEllipsis)) 1.5549 + return nullptr; 1.5550 + if (isEllipsis) { 1.5551 + fninfo->mIsVariadic = true; 1.5552 + if (i < 1) { 1.5553 + JS_ReportError(cx, "\"...\" may not be the first and only parameter " 1.5554 + "type of a variadic function declaration"); 1.5555 + return nullptr; 1.5556 + } 1.5557 + if (i < argLength - 1) { 1.5558 + JS_ReportError(cx, "\"...\" must be the last parameter type of a " 1.5559 + "variadic function declaration"); 1.5560 + return nullptr; 1.5561 + } 1.5562 + if (GetABICode(fninfo->mABI) != ABI_DEFAULT) { 1.5563 + JS_ReportError(cx, "Variadic functions must use the __cdecl calling " 1.5564 + "convention"); 1.5565 + return nullptr; 1.5566 + } 1.5567 + break; 1.5568 + } 1.5569 + 1.5570 + JSObject* argType = PrepareType(cx, argTypes[i]); 1.5571 + if (!argType) 1.5572 + return nullptr; 1.5573 + 1.5574 + ffi_type* ffiType = CType::GetFFIType(cx, argType); 1.5575 + if (!ffiType) 1.5576 + return nullptr; 1.5577 + 1.5578 + fninfo->mArgTypes.infallibleAppend(argType); 1.5579 + fninfo->mFFITypes.infallibleAppend(ffiType); 1.5580 + } 1.5581 + 1.5582 + if (fninfo->mIsVariadic) 1.5583 + // wait to PrepareCIF until function is called 1.5584 + return fninfo.forget(); 1.5585 + 1.5586 + if (!PrepareCIF(cx, fninfo.get())) 1.5587 + return nullptr; 1.5588 + 1.5589 + return fninfo.forget(); 1.5590 +} 1.5591 + 1.5592 +bool 1.5593 +FunctionType::Create(JSContext* cx, unsigned argc, jsval* vp) 1.5594 +{ 1.5595 + // Construct and return a new FunctionType object. 1.5596 + CallArgs args = CallArgsFromVp(argc, vp); 1.5597 + if (args.length() < 2 || args.length() > 3) { 1.5598 + JS_ReportError(cx, "FunctionType takes two or three arguments"); 1.5599 + return false; 1.5600 + } 1.5601 + 1.5602 + AutoValueVector argTypes(cx); 1.5603 + RootedObject arrayObj(cx, nullptr); 1.5604 + 1.5605 + if (args.length() == 3) { 1.5606 + // Prepare an array of jsvals for the arguments. 1.5607 + if (!JSVAL_IS_PRIMITIVE(args[2])) 1.5608 + arrayObj = &args[2].toObject(); 1.5609 + if (!arrayObj || !JS_IsArrayObject(cx, arrayObj)) { 1.5610 + JS_ReportError(cx, "third argument must be an array"); 1.5611 + return false; 1.5612 + } 1.5613 + 1.5614 + uint32_t len; 1.5615 + ASSERT_OK(JS_GetArrayLength(cx, arrayObj, &len)); 1.5616 + 1.5617 + if (!argTypes.resize(len)) { 1.5618 + JS_ReportOutOfMemory(cx); 1.5619 + return false; 1.5620 + } 1.5621 + } 1.5622 + 1.5623 + // Pull out the argument types from the array, if any. 1.5624 + JS_ASSERT_IF(argTypes.length(), arrayObj); 1.5625 + for (uint32_t i = 0; i < argTypes.length(); ++i) { 1.5626 + if (!JS_GetElement(cx, arrayObj, i, argTypes.handleAt(i))) 1.5627 + return false; 1.5628 + } 1.5629 + 1.5630 + JSObject* result = CreateInternal(cx, args[0], args[1], 1.5631 + argTypes.begin(), argTypes.length()); 1.5632 + if (!result) 1.5633 + return false; 1.5634 + 1.5635 + args.rval().setObject(*result); 1.5636 + return true; 1.5637 +} 1.5638 + 1.5639 +JSObject* 1.5640 +FunctionType::CreateInternal(JSContext* cx, 1.5641 + jsval abi, 1.5642 + jsval rtype, 1.5643 + jsval* argtypes, 1.5644 + unsigned arglen) 1.5645 +{ 1.5646 + // Determine and check the types, and prepare the function CIF. 1.5647 + AutoPtr<FunctionInfo> fninfo(NewFunctionInfo(cx, abi, rtype, argtypes, arglen)); 1.5648 + if (!fninfo) 1.5649 + return nullptr; 1.5650 + 1.5651 + // Get ctypes.FunctionType.prototype and the common prototype for CData objects 1.5652 + // of this type, from ctypes.CType.prototype. 1.5653 + RootedObject typeProto(cx, CType::GetProtoFromType(cx, fninfo->mReturnType, 1.5654 + SLOT_FUNCTIONPROTO)); 1.5655 + if (!typeProto) 1.5656 + return nullptr; 1.5657 + RootedObject dataProto(cx, CType::GetProtoFromType(cx, fninfo->mReturnType, 1.5658 + SLOT_FUNCTIONDATAPROTO)); 1.5659 + if (!dataProto) 1.5660 + return nullptr; 1.5661 + 1.5662 + // Create a new CType object with the common properties and slots. 1.5663 + JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_function, 1.5664 + nullptr, JSVAL_VOID, JSVAL_VOID, nullptr); 1.5665 + if (!typeObj) 1.5666 + return nullptr; 1.5667 + 1.5668 + // Stash the FunctionInfo in a reserved slot. 1.5669 + JS_SetReservedSlot(typeObj, SLOT_FNINFO, PRIVATE_TO_JSVAL(fninfo.forget())); 1.5670 + 1.5671 + return typeObj; 1.5672 +} 1.5673 + 1.5674 +// Construct a function pointer to a JS function (see CClosure::Create()). 1.5675 +// Regular function pointers are constructed directly in 1.5676 +// PointerType::ConstructData(). 1.5677 +bool 1.5678 +FunctionType::ConstructData(JSContext* cx, 1.5679 + HandleObject typeObj, 1.5680 + HandleObject dataObj, 1.5681 + HandleObject fnObj, 1.5682 + HandleObject thisObj, 1.5683 + jsval errVal) 1.5684 +{ 1.5685 + JS_ASSERT(CType::GetTypeCode(typeObj) == TYPE_function); 1.5686 + 1.5687 + PRFuncPtr* data = static_cast<PRFuncPtr*>(CData::GetData(dataObj)); 1.5688 + 1.5689 + FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj); 1.5690 + if (fninfo->mIsVariadic) { 1.5691 + JS_ReportError(cx, "Can't declare a variadic callback function"); 1.5692 + return false; 1.5693 + } 1.5694 + if (GetABICode(fninfo->mABI) == ABI_WINAPI) { 1.5695 + JS_ReportError(cx, "Can't declare a ctypes.winapi_abi callback function, " 1.5696 + "use ctypes.stdcall_abi instead"); 1.5697 + return false; 1.5698 + } 1.5699 + 1.5700 + RootedObject closureObj(cx, CClosure::Create(cx, typeObj, fnObj, thisObj, errVal, data)); 1.5701 + if (!closureObj) 1.5702 + return false; 1.5703 + 1.5704 + // Set the closure object as the referent of the new CData object. 1.5705 + JS_SetReservedSlot(dataObj, SLOT_REFERENT, OBJECT_TO_JSVAL(closureObj)); 1.5706 + 1.5707 + // Seal the CData object, to prevent modification of the function pointer. 1.5708 + // This permanently associates this object with the closure, and avoids 1.5709 + // having to do things like reset SLOT_REFERENT when someone tries to 1.5710 + // change the pointer value. 1.5711 + // XXX This will need to change when bug 541212 is fixed -- CData::ValueSetter 1.5712 + // could be called on a frozen object. 1.5713 + return JS_FreezeObject(cx, dataObj); 1.5714 +} 1.5715 + 1.5716 +typedef Array<AutoValue, 16> AutoValueAutoArray; 1.5717 + 1.5718 +static bool 1.5719 +ConvertArgument(JSContext* cx, 1.5720 + HandleValue arg, 1.5721 + JSObject* type, 1.5722 + AutoValue* value, 1.5723 + AutoValueAutoArray* strings) 1.5724 +{ 1.5725 + if (!value->SizeToType(cx, type)) { 1.5726 + JS_ReportAllocationOverflow(cx); 1.5727 + return false; 1.5728 + } 1.5729 + 1.5730 + bool freePointer = false; 1.5731 + if (!ImplicitConvert(cx, arg, type, value->mData, true, &freePointer)) 1.5732 + return false; 1.5733 + 1.5734 + if (freePointer) { 1.5735 + // ImplicitConvert converted a string for us, which we have to free. 1.5736 + // Keep track of it. 1.5737 + if (!strings->growBy(1)) { 1.5738 + JS_ReportOutOfMemory(cx); 1.5739 + return false; 1.5740 + } 1.5741 + strings->back().mData = *static_cast<char**>(value->mData); 1.5742 + } 1.5743 + 1.5744 + return true; 1.5745 +} 1.5746 + 1.5747 +bool 1.5748 +FunctionType::Call(JSContext* cx, 1.5749 + unsigned argc, 1.5750 + jsval* vp) 1.5751 +{ 1.5752 + CallArgs args = CallArgsFromVp(argc, vp); 1.5753 + // get the callee object... 1.5754 + RootedObject obj(cx, &args.callee()); 1.5755 + if (!CData::IsCData(obj)) { 1.5756 + JS_ReportError(cx, "not a CData"); 1.5757 + return false; 1.5758 + } 1.5759 + 1.5760 + RootedObject typeObj(cx, CData::GetCType(obj)); 1.5761 + if (CType::GetTypeCode(typeObj) != TYPE_pointer) { 1.5762 + JS_ReportError(cx, "not a FunctionType.ptr"); 1.5763 + return false; 1.5764 + } 1.5765 + 1.5766 + typeObj = PointerType::GetBaseType(typeObj); 1.5767 + if (CType::GetTypeCode(typeObj) != TYPE_function) { 1.5768 + JS_ReportError(cx, "not a FunctionType.ptr"); 1.5769 + return false; 1.5770 + } 1.5771 + 1.5772 + FunctionInfo* fninfo = GetFunctionInfo(typeObj); 1.5773 + uint32_t argcFixed = fninfo->mArgTypes.length(); 1.5774 + 1.5775 + if ((!fninfo->mIsVariadic && args.length() != argcFixed) || 1.5776 + (fninfo->mIsVariadic && args.length() < argcFixed)) { 1.5777 + JS_ReportError(cx, "Number of arguments does not match declaration"); 1.5778 + return false; 1.5779 + } 1.5780 + 1.5781 + // Check if we have a Library object. If we do, make sure it's open. 1.5782 + jsval slot = JS_GetReservedSlot(obj, SLOT_REFERENT); 1.5783 + if (!slot.isUndefined() && Library::IsLibrary(&slot.toObject())) { 1.5784 + PRLibrary* library = Library::GetLibrary(&slot.toObject()); 1.5785 + if (!library) { 1.5786 + JS_ReportError(cx, "library is not open"); 1.5787 + return false; 1.5788 + } 1.5789 + } 1.5790 + 1.5791 + // prepare the values for each argument 1.5792 + AutoValueAutoArray values; 1.5793 + AutoValueAutoArray strings; 1.5794 + if (!values.resize(args.length())) { 1.5795 + JS_ReportOutOfMemory(cx); 1.5796 + return false; 1.5797 + } 1.5798 + 1.5799 + for (unsigned i = 0; i < argcFixed; ++i) 1.5800 + if (!ConvertArgument(cx, args[i], fninfo->mArgTypes[i], &values[i], &strings)) 1.5801 + return false; 1.5802 + 1.5803 + if (fninfo->mIsVariadic) { 1.5804 + if (!fninfo->mFFITypes.resize(args.length())) { 1.5805 + JS_ReportOutOfMemory(cx); 1.5806 + return false; 1.5807 + } 1.5808 + 1.5809 + RootedObject obj(cx); // Could reuse obj instead of declaring a second 1.5810 + RootedObject type(cx); // RootedObject, but readability would suffer. 1.5811 + 1.5812 + for (uint32_t i = argcFixed; i < args.length(); ++i) { 1.5813 + if (JSVAL_IS_PRIMITIVE(args[i]) || 1.5814 + !CData::IsCData(obj = &args[i].toObject())) { 1.5815 + // Since we know nothing about the CTypes of the ... arguments, 1.5816 + // they absolutely must be CData objects already. 1.5817 + JS_ReportError(cx, "argument %d of type %s is not a CData object", 1.5818 + i, JS_GetTypeName(cx, JS_TypeOfValue(cx, args[i]))); 1.5819 + return false; 1.5820 + } 1.5821 + if (!(type = CData::GetCType(obj)) || 1.5822 + !(type = PrepareType(cx, OBJECT_TO_JSVAL(type))) || 1.5823 + // Relying on ImplicitConvert only for the limited purpose of 1.5824 + // converting one CType to another (e.g., T[] to T*). 1.5825 + !ConvertArgument(cx, args[i], type, &values[i], &strings) || 1.5826 + !(fninfo->mFFITypes[i] = CType::GetFFIType(cx, type))) { 1.5827 + // These functions report their own errors. 1.5828 + return false; 1.5829 + } 1.5830 + } 1.5831 + if (!PrepareCIF(cx, fninfo)) 1.5832 + return false; 1.5833 + } 1.5834 + 1.5835 + // initialize a pointer to an appropriate location, for storing the result 1.5836 + AutoValue returnValue; 1.5837 + TypeCode typeCode = CType::GetTypeCode(fninfo->mReturnType); 1.5838 + if (typeCode != TYPE_void_t && 1.5839 + !returnValue.SizeToType(cx, fninfo->mReturnType)) { 1.5840 + JS_ReportAllocationOverflow(cx); 1.5841 + return false; 1.5842 + } 1.5843 + 1.5844 + // Let the runtime callback know that we are about to call into C. 1.5845 + js::AutoCTypesActivityCallback autoCallback(cx, js::CTYPES_CALL_BEGIN, js::CTYPES_CALL_END); 1.5846 + 1.5847 + uintptr_t fn = *reinterpret_cast<uintptr_t*>(CData::GetData(obj)); 1.5848 + 1.5849 +#if defined(XP_WIN) 1.5850 + int32_t lastErrorStatus; // The status as defined by |GetLastError| 1.5851 + int32_t savedLastError = GetLastError(); 1.5852 + SetLastError(0); 1.5853 +#endif //defined(XP_WIN) 1.5854 + int errnoStatus; // The status as defined by |errno| 1.5855 + int savedErrno = errno; 1.5856 + errno = 0; 1.5857 + 1.5858 + ffi_call(&fninfo->mCIF, FFI_FN(fn), returnValue.mData, 1.5859 + reinterpret_cast<void**>(values.begin())); 1.5860 + 1.5861 + // Save error value. 1.5862 + // We need to save it before leaving the scope of |suspend| as destructing 1.5863 + // |suspend| has the side-effect of clearing |GetLastError| 1.5864 + // (see bug 684017). 1.5865 + 1.5866 + errnoStatus = errno; 1.5867 +#if defined(XP_WIN) 1.5868 + lastErrorStatus = GetLastError(); 1.5869 + SetLastError(savedLastError); 1.5870 +#endif // defined(XP_WIN) 1.5871 + 1.5872 + errno = savedErrno; 1.5873 + 1.5874 + // We're no longer calling into C. 1.5875 + autoCallback.DoEndCallback(); 1.5876 + 1.5877 + // Store the error value for later consultation with |ctypes.getStatus| 1.5878 + JSObject *objCTypes = CType::GetGlobalCTypes(cx, typeObj); 1.5879 + if (!objCTypes) 1.5880 + return false; 1.5881 + 1.5882 + JS_SetReservedSlot(objCTypes, SLOT_ERRNO, INT_TO_JSVAL(errnoStatus)); 1.5883 +#if defined(XP_WIN) 1.5884 + JS_SetReservedSlot(objCTypes, SLOT_LASTERROR, INT_TO_JSVAL(lastErrorStatus)); 1.5885 +#endif // defined(XP_WIN) 1.5886 + 1.5887 + // Small integer types get returned as a word-sized ffi_arg. Coerce it back 1.5888 + // into the correct size for ConvertToJS. 1.5889 + switch (typeCode) { 1.5890 +#define DEFINE_INT_TYPE(name, type, ffiType) \ 1.5891 + case TYPE_##name: \ 1.5892 + if (sizeof(type) < sizeof(ffi_arg)) { \ 1.5893 + ffi_arg data = *static_cast<ffi_arg*>(returnValue.mData); \ 1.5894 + *static_cast<type*>(returnValue.mData) = static_cast<type>(data); \ 1.5895 + } \ 1.5896 + break; 1.5897 +#define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) 1.5898 +#define DEFINE_BOOL_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) 1.5899 +#define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) 1.5900 +#define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) 1.5901 +#include "ctypes/typedefs.h" 1.5902 + default: 1.5903 + break; 1.5904 + } 1.5905 + 1.5906 + // prepare a JS object from the result 1.5907 + RootedObject returnType(cx, fninfo->mReturnType); 1.5908 + return ConvertToJS(cx, returnType, NullPtr(), returnValue.mData, false, true, vp); 1.5909 +} 1.5910 + 1.5911 +FunctionInfo* 1.5912 +FunctionType::GetFunctionInfo(JSObject* obj) 1.5913 +{ 1.5914 + JS_ASSERT(CType::IsCType(obj)); 1.5915 + JS_ASSERT(CType::GetTypeCode(obj) == TYPE_function); 1.5916 + 1.5917 + jsval slot = JS_GetReservedSlot(obj, SLOT_FNINFO); 1.5918 + JS_ASSERT(!JSVAL_IS_VOID(slot) && JSVAL_TO_PRIVATE(slot)); 1.5919 + 1.5920 + return static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot)); 1.5921 +} 1.5922 + 1.5923 +bool 1.5924 +FunctionType::IsFunctionType(HandleValue v) 1.5925 +{ 1.5926 + if (!v.isObject()) 1.5927 + return false; 1.5928 + JSObject* obj = &v.toObject(); 1.5929 + return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_function; 1.5930 +} 1.5931 + 1.5932 +bool 1.5933 +FunctionType::ArgTypesGetter(JSContext* cx, JS::CallArgs args) 1.5934 +{ 1.5935 + JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject()); 1.5936 + 1.5937 + args.rval().set(JS_GetReservedSlot(obj, SLOT_ARGS_T)); 1.5938 + if (!args.rval().isUndefined()) 1.5939 + return true; 1.5940 + 1.5941 + FunctionInfo* fninfo = GetFunctionInfo(obj); 1.5942 + size_t len = fninfo->mArgTypes.length(); 1.5943 + 1.5944 + // Prepare a new array. 1.5945 + JS::Rooted<JSObject*> argTypes(cx); 1.5946 + { 1.5947 + JS::AutoValueVector vec(cx); 1.5948 + if (!vec.resize(len)) 1.5949 + return false; 1.5950 + 1.5951 + for (size_t i = 0; i < len; ++i) 1.5952 + vec[i] = JS::ObjectValue(*fninfo->mArgTypes[i]); 1.5953 + 1.5954 + argTypes = JS_NewArrayObject(cx, vec); 1.5955 + if (!argTypes) 1.5956 + return false; 1.5957 + } 1.5958 + 1.5959 + // Seal and cache it. 1.5960 + if (!JS_FreezeObject(cx, argTypes)) 1.5961 + return false; 1.5962 + JS_SetReservedSlot(obj, SLOT_ARGS_T, JS::ObjectValue(*argTypes)); 1.5963 + 1.5964 + args.rval().setObject(*argTypes); 1.5965 + return true; 1.5966 +} 1.5967 + 1.5968 +bool 1.5969 +FunctionType::ReturnTypeGetter(JSContext* cx, JS::CallArgs args) 1.5970 +{ 1.5971 + // Get the returnType object from the FunctionInfo. 1.5972 + args.rval().setObject(*GetFunctionInfo(&args.thisv().toObject())->mReturnType); 1.5973 + return true; 1.5974 +} 1.5975 + 1.5976 +bool 1.5977 +FunctionType::ABIGetter(JSContext* cx, JS::CallArgs args) 1.5978 +{ 1.5979 + // Get the abi object from the FunctionInfo. 1.5980 + args.rval().setObject(*GetFunctionInfo(&args.thisv().toObject())->mABI); 1.5981 + return true; 1.5982 +} 1.5983 + 1.5984 +bool 1.5985 +FunctionType::IsVariadicGetter(JSContext* cx, JS::CallArgs args) 1.5986 +{ 1.5987 + args.rval().setBoolean(GetFunctionInfo(&args.thisv().toObject())->mIsVariadic); 1.5988 + return true; 1.5989 +} 1.5990 + 1.5991 +/******************************************************************************* 1.5992 +** CClosure implementation 1.5993 +*******************************************************************************/ 1.5994 + 1.5995 +JSObject* 1.5996 +CClosure::Create(JSContext* cx, 1.5997 + HandleObject typeObj, 1.5998 + HandleObject fnObj, 1.5999 + HandleObject thisObj, 1.6000 + jsval errVal_, 1.6001 + PRFuncPtr* fnptr) 1.6002 +{ 1.6003 + RootedValue errVal(cx, errVal_); 1.6004 + JS_ASSERT(fnObj); 1.6005 + 1.6006 + RootedObject result(cx, JS_NewObject(cx, &sCClosureClass, NullPtr(), NullPtr())); 1.6007 + if (!result) 1.6008 + return nullptr; 1.6009 + 1.6010 + // Get the FunctionInfo from the FunctionType. 1.6011 + FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj); 1.6012 + JS_ASSERT(!fninfo->mIsVariadic); 1.6013 + JS_ASSERT(GetABICode(fninfo->mABI) != ABI_WINAPI); 1.6014 + 1.6015 + AutoPtr<ClosureInfo> cinfo(cx->new_<ClosureInfo>(JS_GetRuntime(cx))); 1.6016 + if (!cinfo) { 1.6017 + JS_ReportOutOfMemory(cx); 1.6018 + return nullptr; 1.6019 + } 1.6020 + 1.6021 + // Get the prototype of the FunctionType object, of class CTypeProto, 1.6022 + // which stores our JSContext for use with the closure. 1.6023 + RootedObject proto(cx); 1.6024 + if (!JS_GetPrototype(cx, typeObj, &proto)) 1.6025 + return nullptr; 1.6026 + JS_ASSERT(proto); 1.6027 + JS_ASSERT(CType::IsCTypeProto(proto)); 1.6028 + 1.6029 + // Get a JSContext for use with the closure. 1.6030 + cinfo->cx = js::DefaultJSContext(JS_GetRuntime(cx)); 1.6031 + 1.6032 + // Prepare the error sentinel value. It's important to do this now, because 1.6033 + // we might be unable to convert the value to the proper type. If so, we want 1.6034 + // the caller to know about it _now_, rather than some uncertain time in the 1.6035 + // future when the error sentinel is actually needed. 1.6036 + if (!JSVAL_IS_VOID(errVal)) { 1.6037 + 1.6038 + // Make sure the callback returns something. 1.6039 + if (CType::GetTypeCode(fninfo->mReturnType) == TYPE_void_t) { 1.6040 + JS_ReportError(cx, "A void callback can't pass an error sentinel"); 1.6041 + return nullptr; 1.6042 + } 1.6043 + 1.6044 + // With the exception of void, the FunctionType constructor ensures that 1.6045 + // the return type has a defined size. 1.6046 + JS_ASSERT(CType::IsSizeDefined(fninfo->mReturnType)); 1.6047 + 1.6048 + // Allocate a buffer for the return value. 1.6049 + size_t rvSize = CType::GetSize(fninfo->mReturnType); 1.6050 + cinfo->errResult = cx->malloc_(rvSize); 1.6051 + if (!cinfo->errResult) 1.6052 + return nullptr; 1.6053 + 1.6054 + // Do the value conversion. This might fail, in which case we throw. 1.6055 + if (!ImplicitConvert(cx, errVal, fninfo->mReturnType, cinfo->errResult, 1.6056 + false, nullptr)) 1.6057 + return nullptr; 1.6058 + } else { 1.6059 + cinfo->errResult = nullptr; 1.6060 + } 1.6061 + 1.6062 + // Copy the important bits of context into cinfo. 1.6063 + cinfo->closureObj = result; 1.6064 + cinfo->typeObj = typeObj; 1.6065 + cinfo->thisObj = thisObj; 1.6066 + cinfo->jsfnObj = fnObj; 1.6067 + 1.6068 + // Create an ffi_closure object and initialize it. 1.6069 + void* code; 1.6070 + cinfo->closure = 1.6071 + static_cast<ffi_closure*>(ffi_closure_alloc(sizeof(ffi_closure), &code)); 1.6072 + if (!cinfo->closure || !code) { 1.6073 + JS_ReportError(cx, "couldn't create closure - libffi error"); 1.6074 + return nullptr; 1.6075 + } 1.6076 + 1.6077 + ffi_status status = ffi_prep_closure_loc(cinfo->closure, &fninfo->mCIF, 1.6078 + CClosure::ClosureStub, cinfo.get(), code); 1.6079 + if (status != FFI_OK) { 1.6080 + JS_ReportError(cx, "couldn't create closure - libffi error"); 1.6081 + return nullptr; 1.6082 + } 1.6083 + 1.6084 + // Stash the ClosureInfo struct on our new object. 1.6085 + JS_SetReservedSlot(result, SLOT_CLOSUREINFO, PRIVATE_TO_JSVAL(cinfo.forget())); 1.6086 + 1.6087 + // Casting between void* and a function pointer is forbidden in C and C++. 1.6088 + // Do it via an integral type. 1.6089 + *fnptr = reinterpret_cast<PRFuncPtr>(reinterpret_cast<uintptr_t>(code)); 1.6090 + return result; 1.6091 +} 1.6092 + 1.6093 +void 1.6094 +CClosure::Trace(JSTracer* trc, JSObject* obj) 1.6095 +{ 1.6096 + // Make sure our ClosureInfo slot is legit. If it's not, bail. 1.6097 + jsval slot = JS_GetReservedSlot(obj, SLOT_CLOSUREINFO); 1.6098 + if (JSVAL_IS_VOID(slot)) 1.6099 + return; 1.6100 + 1.6101 + ClosureInfo* cinfo = static_cast<ClosureInfo*>(JSVAL_TO_PRIVATE(slot)); 1.6102 + 1.6103 + // Identify our objects to the tracer. (There's no need to identify 1.6104 + // 'closureObj', since that's us.) 1.6105 + JS_CallHeapObjectTracer(trc, &cinfo->typeObj, "typeObj"); 1.6106 + JS_CallHeapObjectTracer(trc, &cinfo->jsfnObj, "jsfnObj"); 1.6107 + if (cinfo->thisObj) 1.6108 + JS_CallHeapObjectTracer(trc, &cinfo->thisObj, "thisObj"); 1.6109 +} 1.6110 + 1.6111 +void 1.6112 +CClosure::Finalize(JSFreeOp *fop, JSObject* obj) 1.6113 +{ 1.6114 + // Make sure our ClosureInfo slot is legit. If it's not, bail. 1.6115 + jsval slot = JS_GetReservedSlot(obj, SLOT_CLOSUREINFO); 1.6116 + if (JSVAL_IS_VOID(slot)) 1.6117 + return; 1.6118 + 1.6119 + ClosureInfo* cinfo = static_cast<ClosureInfo*>(JSVAL_TO_PRIVATE(slot)); 1.6120 + FreeOp::get(fop)->delete_(cinfo); 1.6121 +} 1.6122 + 1.6123 +void 1.6124 +CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData) 1.6125 +{ 1.6126 + JS_ASSERT(cif); 1.6127 + JS_ASSERT(result); 1.6128 + JS_ASSERT(args); 1.6129 + JS_ASSERT(userData); 1.6130 + 1.6131 + // Retrieve the essentials from our closure object. 1.6132 + ClosureInfo* cinfo = static_cast<ClosureInfo*>(userData); 1.6133 + JSContext* cx = cinfo->cx; 1.6134 + 1.6135 + // Let the runtime callback know that we are about to call into JS again. The end callback will 1.6136 + // fire automatically when we exit this function. 1.6137 + js::AutoCTypesActivityCallback autoCallback(cx, js::CTYPES_CALLBACK_BEGIN, 1.6138 + js::CTYPES_CALLBACK_END); 1.6139 + 1.6140 + RootedObject typeObj(cx, cinfo->typeObj); 1.6141 + RootedObject thisObj(cx, cinfo->thisObj); 1.6142 + RootedValue jsfnVal(cx, ObjectValue(*cinfo->jsfnObj)); 1.6143 + 1.6144 + JS_AbortIfWrongThread(JS_GetRuntime(cx)); 1.6145 + 1.6146 + JSAutoRequest ar(cx); 1.6147 + JSAutoCompartment ac(cx, cinfo->jsfnObj); 1.6148 + 1.6149 + // Assert that our CIFs agree. 1.6150 + FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj); 1.6151 + JS_ASSERT(cif == &fninfo->mCIF); 1.6152 + 1.6153 + TypeCode typeCode = CType::GetTypeCode(fninfo->mReturnType); 1.6154 + 1.6155 + // Initialize the result to zero, in case something fails. Small integer types 1.6156 + // are promoted to a word-sized ffi_arg, so we must be careful to zero the 1.6157 + // whole word. 1.6158 + size_t rvSize = 0; 1.6159 + if (cif->rtype != &ffi_type_void) { 1.6160 + rvSize = cif->rtype->size; 1.6161 + switch (typeCode) { 1.6162 +#define DEFINE_INT_TYPE(name, type, ffiType) \ 1.6163 + case TYPE_##name: 1.6164 +#define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) 1.6165 +#define DEFINE_BOOL_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) 1.6166 +#define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) 1.6167 +#define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) 1.6168 +#include "ctypes/typedefs.h" 1.6169 + rvSize = Align(rvSize, sizeof(ffi_arg)); 1.6170 + break; 1.6171 + default: 1.6172 + break; 1.6173 + } 1.6174 + memset(result, 0, rvSize); 1.6175 + } 1.6176 + 1.6177 + // Set up an array for converted arguments. 1.6178 + JS::AutoValueVector argv(cx); 1.6179 + if (!argv.resize(cif->nargs)) { 1.6180 + JS_ReportOutOfMemory(cx); 1.6181 + return; 1.6182 + } 1.6183 + 1.6184 + for (uint32_t i = 0; i < cif->nargs; ++i) { 1.6185 + // Convert each argument, and have any CData objects created depend on 1.6186 + // the existing buffers. 1.6187 + RootedObject argType(cx, fninfo->mArgTypes[i]); 1.6188 + if (!ConvertToJS(cx, argType, NullPtr(), args[i], false, false, &argv[i])) 1.6189 + return; 1.6190 + } 1.6191 + 1.6192 + // Call the JS function. 'thisObj' may be nullptr, in which case the JS 1.6193 + // engine will find an appropriate object to use. 1.6194 + RootedValue rval(cx); 1.6195 + bool success = JS_CallFunctionValue(cx, thisObj, jsfnVal, argv, &rval); 1.6196 + 1.6197 + // Convert the result. Note that we pass 'isArgument = false', such that 1.6198 + // ImplicitConvert will *not* autoconvert a JS string into a pointer-to-char 1.6199 + // type, which would require an allocation that we can't track. The JS 1.6200 + // function must perform this conversion itself and return a PointerType 1.6201 + // CData; thusly, the burden of freeing the data is left to the user. 1.6202 + if (success && cif->rtype != &ffi_type_void) 1.6203 + success = ImplicitConvert(cx, rval, fninfo->mReturnType, result, false, 1.6204 + nullptr); 1.6205 + 1.6206 + if (!success) { 1.6207 + // Something failed. The callee may have thrown, or it may not have 1.6208 + // returned a value that ImplicitConvert() was happy with. Depending on how 1.6209 + // prudent the consumer has been, we may or may not have a recovery plan. 1.6210 + 1.6211 + // In any case, a JS exception cannot be passed to C code, so report the 1.6212 + // exception if any and clear it from the cx. 1.6213 + if (JS_IsExceptionPending(cx)) 1.6214 + JS_ReportPendingException(cx); 1.6215 + 1.6216 + if (cinfo->errResult) { 1.6217 + // Good case: we have a sentinel that we can return. Copy it in place of 1.6218 + // the actual return value, and then proceed. 1.6219 + 1.6220 + // The buffer we're returning might be larger than the size of the return 1.6221 + // type, due to libffi alignment issues (see above). But it should never 1.6222 + // be smaller. 1.6223 + size_t copySize = CType::GetSize(fninfo->mReturnType); 1.6224 + JS_ASSERT(copySize <= rvSize); 1.6225 + memcpy(result, cinfo->errResult, copySize); 1.6226 + } else { 1.6227 + // Bad case: not much we can do here. The rv is already zeroed out, so we 1.6228 + // just report (another) error and hope for the best. JS_ReportError will 1.6229 + // actually throw an exception here, so then we have to report it. Again. 1.6230 + // Ugh. 1.6231 + JS_ReportError(cx, "JavaScript callback failed, and an error sentinel " 1.6232 + "was not specified."); 1.6233 + if (JS_IsExceptionPending(cx)) 1.6234 + JS_ReportPendingException(cx); 1.6235 + 1.6236 + return; 1.6237 + } 1.6238 + } 1.6239 + 1.6240 + // Small integer types must be returned as a word-sized ffi_arg. Coerce it 1.6241 + // back into the size libffi expects. 1.6242 + switch (typeCode) { 1.6243 +#define DEFINE_INT_TYPE(name, type, ffiType) \ 1.6244 + case TYPE_##name: \ 1.6245 + if (sizeof(type) < sizeof(ffi_arg)) { \ 1.6246 + ffi_arg data = *static_cast<type*>(result); \ 1.6247 + *static_cast<ffi_arg*>(result) = data; \ 1.6248 + } \ 1.6249 + break; 1.6250 +#define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) 1.6251 +#define DEFINE_BOOL_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) 1.6252 +#define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) 1.6253 +#define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) 1.6254 +#include "ctypes/typedefs.h" 1.6255 + default: 1.6256 + break; 1.6257 + } 1.6258 +} 1.6259 + 1.6260 +/******************************************************************************* 1.6261 +** CData implementation 1.6262 +*******************************************************************************/ 1.6263 + 1.6264 +// Create a new CData object of type 'typeObj' containing binary data supplied 1.6265 +// in 'source', optionally with a referent object 'refObj'. 1.6266 +// 1.6267 +// * 'typeObj' must be a CType of defined (but possibly zero) size. 1.6268 +// 1.6269 +// * If an object 'refObj' is supplied, the new CData object stores the 1.6270 +// referent object in a reserved slot for GC safety, such that 'refObj' will 1.6271 +// be held alive by the resulting CData object. 'refObj' may or may not be 1.6272 +// a CData object; merely an object we want to keep alive. 1.6273 +// * If 'refObj' is a CData object, 'ownResult' must be false. 1.6274 +// * Otherwise, 'refObj' is a Library or CClosure object, and 'ownResult' 1.6275 +// may be true or false. 1.6276 +// * Otherwise 'refObj' is nullptr. In this case, 'ownResult' may be true or 1.6277 +// false. 1.6278 +// 1.6279 +// * If 'ownResult' is true, the CData object will allocate an appropriately 1.6280 +// sized buffer, and free it upon finalization. If 'source' data is 1.6281 +// supplied, the data will be copied from 'source' into the buffer; 1.6282 +// otherwise, the entirety of the new buffer will be initialized to zero. 1.6283 +// * If 'ownResult' is false, the new CData's buffer refers to a slice of 1.6284 +// another buffer kept alive by 'refObj'. 'source' data must be provided, 1.6285 +// and the new CData's buffer will refer to 'source'. 1.6286 +JSObject* 1.6287 +CData::Create(JSContext* cx, 1.6288 + HandleObject typeObj, 1.6289 + HandleObject refObj, 1.6290 + void* source, 1.6291 + bool ownResult) 1.6292 +{ 1.6293 + JS_ASSERT(typeObj); 1.6294 + JS_ASSERT(CType::IsCType(typeObj)); 1.6295 + JS_ASSERT(CType::IsSizeDefined(typeObj)); 1.6296 + JS_ASSERT(ownResult || source); 1.6297 + JS_ASSERT_IF(refObj && CData::IsCData(refObj), !ownResult); 1.6298 + 1.6299 + // Get the 'prototype' property from the type. 1.6300 + jsval slot = JS_GetReservedSlot(typeObj, SLOT_PROTO); 1.6301 + JS_ASSERT(!JSVAL_IS_PRIMITIVE(slot)); 1.6302 + 1.6303 + RootedObject proto(cx, JSVAL_TO_OBJECT(slot)); 1.6304 + RootedObject parent(cx, JS_GetParent(typeObj)); 1.6305 + JS_ASSERT(parent); 1.6306 + 1.6307 + RootedObject dataObj(cx, JS_NewObject(cx, &sCDataClass, proto, parent)); 1.6308 + if (!dataObj) 1.6309 + return nullptr; 1.6310 + 1.6311 + // set the CData's associated type 1.6312 + JS_SetReservedSlot(dataObj, SLOT_CTYPE, OBJECT_TO_JSVAL(typeObj)); 1.6313 + 1.6314 + // Stash the referent object, if any, for GC safety. 1.6315 + if (refObj) 1.6316 + JS_SetReservedSlot(dataObj, SLOT_REFERENT, OBJECT_TO_JSVAL(refObj)); 1.6317 + 1.6318 + // Set our ownership flag. 1.6319 + JS_SetReservedSlot(dataObj, SLOT_OWNS, BOOLEAN_TO_JSVAL(ownResult)); 1.6320 + 1.6321 + // attach the buffer. since it might not be 2-byte aligned, we need to 1.6322 + // allocate an aligned space for it and store it there. :( 1.6323 + char** buffer = cx->new_<char*>(); 1.6324 + if (!buffer) { 1.6325 + JS_ReportOutOfMemory(cx); 1.6326 + return nullptr; 1.6327 + } 1.6328 + 1.6329 + char* data; 1.6330 + if (!ownResult) { 1.6331 + data = static_cast<char*>(source); 1.6332 + } else { 1.6333 + // Initialize our own buffer. 1.6334 + size_t size = CType::GetSize(typeObj); 1.6335 + data = (char*)cx->malloc_(size); 1.6336 + if (!data) { 1.6337 + // Report a catchable allocation error. 1.6338 + JS_ReportAllocationOverflow(cx); 1.6339 + js_free(buffer); 1.6340 + return nullptr; 1.6341 + } 1.6342 + 1.6343 + if (!source) 1.6344 + memset(data, 0, size); 1.6345 + else 1.6346 + memcpy(data, source, size); 1.6347 + } 1.6348 + 1.6349 + *buffer = data; 1.6350 + JS_SetReservedSlot(dataObj, SLOT_DATA, PRIVATE_TO_JSVAL(buffer)); 1.6351 + 1.6352 + return dataObj; 1.6353 +} 1.6354 + 1.6355 +void 1.6356 +CData::Finalize(JSFreeOp *fop, JSObject* obj) 1.6357 +{ 1.6358 + // Delete our buffer, and the data it contains if we own it. 1.6359 + jsval slot = JS_GetReservedSlot(obj, SLOT_OWNS); 1.6360 + if (JSVAL_IS_VOID(slot)) 1.6361 + return; 1.6362 + 1.6363 + bool owns = JSVAL_TO_BOOLEAN(slot); 1.6364 + 1.6365 + slot = JS_GetReservedSlot(obj, SLOT_DATA); 1.6366 + if (JSVAL_IS_VOID(slot)) 1.6367 + return; 1.6368 + char** buffer = static_cast<char**>(JSVAL_TO_PRIVATE(slot)); 1.6369 + 1.6370 + if (owns) 1.6371 + FreeOp::get(fop)->free_(*buffer); 1.6372 + FreeOp::get(fop)->delete_(buffer); 1.6373 +} 1.6374 + 1.6375 +JSObject* 1.6376 +CData::GetCType(JSObject* dataObj) 1.6377 +{ 1.6378 + JS_ASSERT(CData::IsCData(dataObj)); 1.6379 + 1.6380 + jsval slot = JS_GetReservedSlot(dataObj, SLOT_CTYPE); 1.6381 + JSObject* typeObj = JSVAL_TO_OBJECT(slot); 1.6382 + JS_ASSERT(CType::IsCType(typeObj)); 1.6383 + return typeObj; 1.6384 +} 1.6385 + 1.6386 +void* 1.6387 +CData::GetData(JSObject* dataObj) 1.6388 +{ 1.6389 + JS_ASSERT(CData::IsCData(dataObj)); 1.6390 + 1.6391 + jsval slot = JS_GetReservedSlot(dataObj, SLOT_DATA); 1.6392 + 1.6393 + void** buffer = static_cast<void**>(JSVAL_TO_PRIVATE(slot)); 1.6394 + JS_ASSERT(buffer); 1.6395 + JS_ASSERT(*buffer); 1.6396 + return *buffer; 1.6397 +} 1.6398 + 1.6399 +bool 1.6400 +CData::IsCData(JSObject* obj) 1.6401 +{ 1.6402 + return JS_GetClass(obj) == &sCDataClass; 1.6403 +} 1.6404 + 1.6405 +bool 1.6406 +CData::IsCData(HandleValue v) 1.6407 +{ 1.6408 + return v.isObject() && CData::IsCData(&v.toObject()); 1.6409 +} 1.6410 + 1.6411 +bool 1.6412 +CData::IsCDataProto(JSObject* obj) 1.6413 +{ 1.6414 + return JS_GetClass(obj) == &sCDataProtoClass; 1.6415 +} 1.6416 + 1.6417 +bool 1.6418 +CData::ValueGetter(JSContext* cx, JS::CallArgs args) 1.6419 +{ 1.6420 + RootedObject obj(cx, &args.thisv().toObject()); 1.6421 + 1.6422 + // Convert the value to a primitive; do not create a new CData object. 1.6423 + RootedObject ctype(cx, GetCType(obj)); 1.6424 + return ConvertToJS(cx, ctype, NullPtr(), GetData(obj), true, false, args.rval().address()); 1.6425 +} 1.6426 + 1.6427 +bool 1.6428 +CData::ValueSetter(JSContext* cx, JS::CallArgs args) 1.6429 +{ 1.6430 + RootedObject obj(cx, &args.thisv().toObject()); 1.6431 + args.rval().setUndefined(); 1.6432 + return ImplicitConvert(cx, args.get(0), GetCType(obj), GetData(obj), false, nullptr); 1.6433 +} 1.6434 + 1.6435 +bool 1.6436 +CData::Address(JSContext* cx, unsigned argc, jsval* vp) 1.6437 +{ 1.6438 + CallArgs args = CallArgsFromVp(argc, vp); 1.6439 + if (args.length() != 0) { 1.6440 + JS_ReportError(cx, "address takes zero arguments"); 1.6441 + return false; 1.6442 + } 1.6443 + 1.6444 + RootedObject obj(cx, JS_THIS_OBJECT(cx, vp)); 1.6445 + if (!obj) 1.6446 + return false; 1.6447 + if (!IsCData(obj)) { 1.6448 + JS_ReportError(cx, "not a CData"); 1.6449 + return false; 1.6450 + } 1.6451 + 1.6452 + RootedObject typeObj(cx, CData::GetCType(obj)); 1.6453 + RootedObject pointerType(cx, PointerType::CreateInternal(cx, typeObj)); 1.6454 + if (!pointerType) 1.6455 + return false; 1.6456 + 1.6457 + // Create a PointerType CData object containing null. 1.6458 + JSObject* result = CData::Create(cx, pointerType, NullPtr(), nullptr, true); 1.6459 + if (!result) 1.6460 + return false; 1.6461 + 1.6462 + args.rval().setObject(*result); 1.6463 + 1.6464 + // Manually set the pointer inside the object, so we skip the conversion step. 1.6465 + void** data = static_cast<void**>(GetData(result)); 1.6466 + *data = GetData(obj); 1.6467 + return true; 1.6468 +} 1.6469 + 1.6470 +bool 1.6471 +CData::Cast(JSContext* cx, unsigned argc, jsval* vp) 1.6472 +{ 1.6473 + CallArgs args = CallArgsFromVp(argc, vp); 1.6474 + if (args.length() != 2) { 1.6475 + JS_ReportError(cx, "cast takes two arguments"); 1.6476 + return false; 1.6477 + } 1.6478 + 1.6479 + if (JSVAL_IS_PRIMITIVE(args[0]) || 1.6480 + !CData::IsCData(&args[0].toObject())) { 1.6481 + JS_ReportError(cx, "first argument must be a CData"); 1.6482 + return false; 1.6483 + } 1.6484 + RootedObject sourceData(cx, &args[0].toObject()); 1.6485 + JSObject* sourceType = CData::GetCType(sourceData); 1.6486 + 1.6487 + if (JSVAL_IS_PRIMITIVE(args[1]) || 1.6488 + !CType::IsCType(&args[1].toObject())) { 1.6489 + JS_ReportError(cx, "second argument must be a CType"); 1.6490 + return false; 1.6491 + } 1.6492 + 1.6493 + RootedObject targetType(cx, &args[1].toObject()); 1.6494 + size_t targetSize; 1.6495 + if (!CType::GetSafeSize(targetType, &targetSize) || 1.6496 + targetSize > CType::GetSize(sourceType)) { 1.6497 + JS_ReportError(cx, 1.6498 + "target CType has undefined or larger size than source CType"); 1.6499 + return false; 1.6500 + } 1.6501 + 1.6502 + // Construct a new CData object with a type of 'targetType' and a referent 1.6503 + // of 'sourceData'. 1.6504 + void* data = CData::GetData(sourceData); 1.6505 + JSObject* result = CData::Create(cx, targetType, sourceData, data, false); 1.6506 + if (!result) 1.6507 + return false; 1.6508 + 1.6509 + args.rval().setObject(*result); 1.6510 + return true; 1.6511 +} 1.6512 + 1.6513 +bool 1.6514 +CData::GetRuntime(JSContext* cx, unsigned argc, jsval* vp) 1.6515 +{ 1.6516 + CallArgs args = CallArgsFromVp(argc, vp); 1.6517 + if (args.length() != 1) { 1.6518 + JS_ReportError(cx, "getRuntime takes one argument"); 1.6519 + return false; 1.6520 + } 1.6521 + 1.6522 + if (JSVAL_IS_PRIMITIVE(args[0]) || 1.6523 + !CType::IsCType(&args[0].toObject())) { 1.6524 + JS_ReportError(cx, "first argument must be a CType"); 1.6525 + return false; 1.6526 + } 1.6527 + 1.6528 + RootedObject targetType(cx, &args[0].toObject()); 1.6529 + size_t targetSize; 1.6530 + if (!CType::GetSafeSize(targetType, &targetSize) || 1.6531 + targetSize != sizeof(void*)) { 1.6532 + JS_ReportError(cx, "target CType has non-pointer size"); 1.6533 + return false; 1.6534 + } 1.6535 + 1.6536 + void* data = static_cast<void*>(cx->runtime()); 1.6537 + JSObject* result = CData::Create(cx, targetType, NullPtr(), &data, true); 1.6538 + if (!result) 1.6539 + return false; 1.6540 + 1.6541 + args.rval().setObject(*result); 1.6542 + return true; 1.6543 +} 1.6544 + 1.6545 +typedef JS::TwoByteCharsZ (*InflateUTF8Method)(JSContext *, const JS::UTF8Chars, size_t *); 1.6546 + 1.6547 +static bool 1.6548 +ReadStringCommon(JSContext* cx, InflateUTF8Method inflateUTF8, unsigned argc, jsval *vp) 1.6549 +{ 1.6550 + CallArgs args = CallArgsFromVp(argc, vp); 1.6551 + if (args.length() != 0) { 1.6552 + JS_ReportError(cx, "readString takes zero arguments"); 1.6553 + return false; 1.6554 + } 1.6555 + 1.6556 + JSObject* obj = CDataFinalizer::GetCData(cx, JS_THIS_OBJECT(cx, vp)); 1.6557 + if (!obj || !CData::IsCData(obj)) { 1.6558 + JS_ReportError(cx, "not a CData"); 1.6559 + return false; 1.6560 + } 1.6561 + 1.6562 + // Make sure we are a pointer to, or an array of, an 8-bit or 16-bit 1.6563 + // character or integer type. 1.6564 + JSObject* baseType; 1.6565 + JSObject* typeObj = CData::GetCType(obj); 1.6566 + TypeCode typeCode = CType::GetTypeCode(typeObj); 1.6567 + void* data; 1.6568 + size_t maxLength = -1; 1.6569 + switch (typeCode) { 1.6570 + case TYPE_pointer: 1.6571 + baseType = PointerType::GetBaseType(typeObj); 1.6572 + data = *static_cast<void**>(CData::GetData(obj)); 1.6573 + if (data == nullptr) { 1.6574 + JS_ReportError(cx, "cannot read contents of null pointer"); 1.6575 + return false; 1.6576 + } 1.6577 + break; 1.6578 + case TYPE_array: 1.6579 + baseType = ArrayType::GetBaseType(typeObj); 1.6580 + data = CData::GetData(obj); 1.6581 + maxLength = ArrayType::GetLength(typeObj); 1.6582 + break; 1.6583 + default: 1.6584 + JS_ReportError(cx, "not a PointerType or ArrayType"); 1.6585 + return false; 1.6586 + } 1.6587 + 1.6588 + // Convert the string buffer, taking care to determine the correct string 1.6589 + // length in the case of arrays (which may contain embedded nulls). 1.6590 + JSString* result; 1.6591 + switch (CType::GetTypeCode(baseType)) { 1.6592 + case TYPE_int8_t: 1.6593 + case TYPE_uint8_t: 1.6594 + case TYPE_char: 1.6595 + case TYPE_signed_char: 1.6596 + case TYPE_unsigned_char: { 1.6597 + char* bytes = static_cast<char*>(data); 1.6598 + size_t length = strnlen(bytes, maxLength); 1.6599 + 1.6600 + // Determine the length. 1.6601 + jschar *dst = inflateUTF8(cx, JS::UTF8Chars(bytes, length), &length).get(); 1.6602 + if (!dst) 1.6603 + return false; 1.6604 + 1.6605 + result = JS_NewUCString(cx, dst, length); 1.6606 + break; 1.6607 + } 1.6608 + case TYPE_int16_t: 1.6609 + case TYPE_uint16_t: 1.6610 + case TYPE_short: 1.6611 + case TYPE_unsigned_short: 1.6612 + case TYPE_jschar: { 1.6613 + jschar* chars = static_cast<jschar*>(data); 1.6614 + size_t length = strnlen(chars, maxLength); 1.6615 + result = JS_NewUCStringCopyN(cx, chars, length); 1.6616 + break; 1.6617 + } 1.6618 + default: 1.6619 + JS_ReportError(cx, 1.6620 + "base type is not an 8-bit or 16-bit integer or character type"); 1.6621 + return false; 1.6622 + } 1.6623 + 1.6624 + if (!result) 1.6625 + return false; 1.6626 + 1.6627 + args.rval().setString(result); 1.6628 + return true; 1.6629 +} 1.6630 + 1.6631 +bool 1.6632 +CData::ReadString(JSContext* cx, unsigned argc, jsval* vp) 1.6633 +{ 1.6634 + return ReadStringCommon(cx, JS::UTF8CharsToNewTwoByteCharsZ, argc, vp); 1.6635 +} 1.6636 + 1.6637 +bool 1.6638 +CData::ReadStringReplaceMalformed(JSContext* cx, unsigned argc, jsval* vp) 1.6639 +{ 1.6640 + return ReadStringCommon(cx, JS::LossyUTF8CharsToNewTwoByteCharsZ, argc, vp); 1.6641 +} 1.6642 + 1.6643 +JSString * 1.6644 +CData::GetSourceString(JSContext *cx, HandleObject typeObj, void *data) 1.6645 +{ 1.6646 + // Walk the types, building up the toSource() string. 1.6647 + // First, we build up the type expression: 1.6648 + // 't.ptr' for pointers; 1.6649 + // 't.array([n])' for arrays; 1.6650 + // 'n' for structs, where n = t.name, the struct's name. (We assume this is 1.6651 + // bound to a variable in the current scope.) 1.6652 + AutoString source; 1.6653 + BuildTypeSource(cx, typeObj, true, source); 1.6654 + AppendString(source, "("); 1.6655 + if (!BuildDataSource(cx, typeObj, data, false, source)) 1.6656 + return nullptr; 1.6657 + 1.6658 + AppendString(source, ")"); 1.6659 + 1.6660 + return NewUCString(cx, source); 1.6661 +} 1.6662 + 1.6663 +bool 1.6664 +CData::ToSource(JSContext* cx, unsigned argc, jsval* vp) 1.6665 +{ 1.6666 + CallArgs args = CallArgsFromVp(argc, vp); 1.6667 + if (args.length() != 0) { 1.6668 + JS_ReportError(cx, "toSource takes zero arguments"); 1.6669 + return false; 1.6670 + } 1.6671 + 1.6672 + JSObject* obj = JS_THIS_OBJECT(cx, vp); 1.6673 + if (!obj) 1.6674 + return false; 1.6675 + if (!CData::IsCData(obj) && !CData::IsCDataProto(obj)) { 1.6676 + JS_ReportError(cx, "not a CData"); 1.6677 + return false; 1.6678 + } 1.6679 + 1.6680 + JSString* result; 1.6681 + if (CData::IsCData(obj)) { 1.6682 + RootedObject typeObj(cx, CData::GetCType(obj)); 1.6683 + void* data = CData::GetData(obj); 1.6684 + 1.6685 + result = CData::GetSourceString(cx, typeObj, data); 1.6686 + } else { 1.6687 + result = JS_NewStringCopyZ(cx, "[CData proto object]"); 1.6688 + } 1.6689 + 1.6690 + if (!result) 1.6691 + return false; 1.6692 + 1.6693 + args.rval().setString(result); 1.6694 + return true; 1.6695 +} 1.6696 + 1.6697 +bool 1.6698 +CData::ErrnoGetter(JSContext* cx, JS::CallArgs args) 1.6699 +{ 1.6700 + args.rval().set(JS_GetReservedSlot(&args.thisv().toObject(), SLOT_ERRNO)); 1.6701 + return true; 1.6702 +} 1.6703 + 1.6704 +#if defined(XP_WIN) 1.6705 +bool 1.6706 +CData::LastErrorGetter(JSContext* cx, JS::CallArgs args) 1.6707 +{ 1.6708 + args.rval().set(JS_GetReservedSlot(&args.thisv().toObject(), SLOT_LASTERROR)); 1.6709 + return true; 1.6710 +} 1.6711 +#endif // defined(XP_WIN) 1.6712 + 1.6713 +bool 1.6714 +CDataFinalizer::Methods::ToSource(JSContext *cx, unsigned argc, jsval *vp) 1.6715 +{ 1.6716 + CallArgs args = CallArgsFromVp(argc, vp); 1.6717 + RootedObject objThis(cx, JS_THIS_OBJECT(cx, vp)); 1.6718 + if (!objThis) 1.6719 + return false; 1.6720 + if (!CDataFinalizer::IsCDataFinalizer(objThis)) { 1.6721 + JS_ReportError(cx, "not a CDataFinalizer"); 1.6722 + return false; 1.6723 + } 1.6724 + 1.6725 + CDataFinalizer::Private *p = (CDataFinalizer::Private *) 1.6726 + JS_GetPrivate(objThis); 1.6727 + 1.6728 + JSString *strMessage; 1.6729 + if (!p) { 1.6730 + strMessage = JS_NewStringCopyZ(cx, "ctypes.CDataFinalizer()"); 1.6731 + } else { 1.6732 + RootedObject objType(cx, CDataFinalizer::GetCType(cx, objThis)); 1.6733 + if (!objType) { 1.6734 + JS_ReportError(cx, "CDataFinalizer has no type"); 1.6735 + return false; 1.6736 + } 1.6737 + 1.6738 + AutoString source; 1.6739 + AppendString(source, "ctypes.CDataFinalizer("); 1.6740 + JSString *srcValue = CData::GetSourceString(cx, objType, p->cargs); 1.6741 + if (!srcValue) { 1.6742 + return false; 1.6743 + } 1.6744 + AppendString(source, srcValue); 1.6745 + AppendString(source, ", "); 1.6746 + jsval valCodePtrType = JS_GetReservedSlot(objThis, 1.6747 + SLOT_DATAFINALIZER_CODETYPE); 1.6748 + if (JSVAL_IS_PRIMITIVE(valCodePtrType)) { 1.6749 + return false; 1.6750 + } 1.6751 + 1.6752 + RootedObject typeObj(cx, JSVAL_TO_OBJECT(valCodePtrType)); 1.6753 + JSString *srcDispose = CData::GetSourceString(cx, typeObj, &(p->code)); 1.6754 + if (!srcDispose) { 1.6755 + return false; 1.6756 + } 1.6757 + 1.6758 + AppendString(source, srcDispose); 1.6759 + AppendString(source, ")"); 1.6760 + strMessage = NewUCString(cx, source); 1.6761 + } 1.6762 + 1.6763 + if (!strMessage) { 1.6764 + // This is a memory issue, no error message 1.6765 + return false; 1.6766 + } 1.6767 + 1.6768 + args.rval().setString(strMessage); 1.6769 + return true; 1.6770 +} 1.6771 + 1.6772 +bool 1.6773 +CDataFinalizer::Methods::ToString(JSContext *cx, unsigned argc, jsval *vp) 1.6774 +{ 1.6775 + CallArgs args = CallArgsFromVp(argc, vp); 1.6776 + JSObject* objThis = JS_THIS_OBJECT(cx, vp); 1.6777 + if (!objThis) 1.6778 + return false; 1.6779 + if (!CDataFinalizer::IsCDataFinalizer(objThis)) { 1.6780 + JS_ReportError(cx, "not a CDataFinalizer"); 1.6781 + return false; 1.6782 + } 1.6783 + 1.6784 + JSString *strMessage; 1.6785 + RootedValue value(cx); 1.6786 + if (!JS_GetPrivate(objThis)) { 1.6787 + // Pre-check whether CDataFinalizer::GetValue can fail 1.6788 + // to avoid reporting an error when not appropriate. 1.6789 + strMessage = JS_NewStringCopyZ(cx, "[CDataFinalizer - empty]"); 1.6790 + if (!strMessage) { 1.6791 + return false; 1.6792 + } 1.6793 + } else if (!CDataFinalizer::GetValue(cx, objThis, value.address())) { 1.6794 + MOZ_ASSUME_UNREACHABLE("Could not convert an empty CDataFinalizer"); 1.6795 + } else { 1.6796 + strMessage = ToString(cx, value); 1.6797 + if (!strMessage) { 1.6798 + return false; 1.6799 + } 1.6800 + } 1.6801 + args.rval().setString(strMessage); 1.6802 + return true; 1.6803 +} 1.6804 + 1.6805 +bool 1.6806 +CDataFinalizer::IsCDataFinalizer(JSObject *obj) 1.6807 +{ 1.6808 + return JS_GetClass(obj) == &sCDataFinalizerClass; 1.6809 +} 1.6810 + 1.6811 + 1.6812 +JSObject * 1.6813 +CDataFinalizer::GetCType(JSContext *cx, JSObject *obj) 1.6814 +{ 1.6815 + MOZ_ASSERT(IsCDataFinalizer(obj)); 1.6816 + 1.6817 + jsval valData = JS_GetReservedSlot(obj, 1.6818 + SLOT_DATAFINALIZER_VALTYPE); 1.6819 + if (JSVAL_IS_VOID(valData)) { 1.6820 + return nullptr; 1.6821 + } 1.6822 + 1.6823 + return JSVAL_TO_OBJECT(valData); 1.6824 +} 1.6825 + 1.6826 +JSObject* 1.6827 +CDataFinalizer::GetCData(JSContext *cx, JSObject *obj) 1.6828 +{ 1.6829 + if (!obj) { 1.6830 + JS_ReportError(cx, "No C data"); 1.6831 + return nullptr; 1.6832 + } 1.6833 + if (CData::IsCData(obj)) { 1.6834 + return obj; 1.6835 + } 1.6836 + if (!CDataFinalizer::IsCDataFinalizer(obj)) { 1.6837 + JS_ReportError(cx, "Not C data"); 1.6838 + return nullptr; 1.6839 + } 1.6840 + RootedValue val(cx); 1.6841 + if (!CDataFinalizer::GetValue(cx, obj, val.address()) || JSVAL_IS_PRIMITIVE(val)) { 1.6842 + JS_ReportError(cx, "Empty CDataFinalizer"); 1.6843 + return nullptr; 1.6844 + } 1.6845 + return JSVAL_TO_OBJECT(val); 1.6846 +} 1.6847 + 1.6848 +bool 1.6849 +CDataFinalizer::GetValue(JSContext *cx, JSObject *obj, jsval *aResult) 1.6850 +{ 1.6851 + MOZ_ASSERT(IsCDataFinalizer(obj)); 1.6852 + 1.6853 + CDataFinalizer::Private *p = (CDataFinalizer::Private *) 1.6854 + JS_GetPrivate(obj); 1.6855 + 1.6856 + if (!p) { 1.6857 + JS_ReportError(cx, "Attempting to get the value of an empty CDataFinalizer"); 1.6858 + return false; // We have called |dispose| or |forget| already. 1.6859 + } 1.6860 + 1.6861 + RootedObject ctype(cx, GetCType(cx, obj)); 1.6862 + return ConvertToJS(cx, ctype, /*parent*/NullPtr(), p -> cargs, false, true, aResult); 1.6863 +} 1.6864 + 1.6865 +/* 1.6866 + * Attach a C function as a finalizer to a JS object. 1.6867 + * 1.6868 + * Pseudo-JS signature: 1.6869 + * function(CData<T>, CData<T -> U>): CDataFinalizer<T> 1.6870 + * value, finalizer 1.6871 + * 1.6872 + * This function attaches strong references to the following values: 1.6873 + * - the CType of |value| 1.6874 + * 1.6875 + * Note: This function takes advantage of the fact that non-variadic 1.6876 + * CData functions are initialized during creation. 1.6877 + */ 1.6878 +bool 1.6879 +CDataFinalizer::Construct(JSContext* cx, unsigned argc, jsval *vp) 1.6880 +{ 1.6881 + CallArgs args = CallArgsFromVp(argc, vp); 1.6882 + RootedObject objSelf(cx, &args.callee()); 1.6883 + RootedObject objProto(cx); 1.6884 + if (!GetObjectProperty(cx, objSelf, "prototype", &objProto)) { 1.6885 + JS_ReportError(cx, "CDataFinalizer.prototype does not exist"); 1.6886 + return false; 1.6887 + } 1.6888 + 1.6889 + // Get arguments 1.6890 + if (args.length() == 0) { // Special case: the empty (already finalized) object 1.6891 + JSObject *objResult = JS_NewObject(cx, &sCDataFinalizerClass, objProto, NullPtr()); 1.6892 + args.rval().setObject(*objResult); 1.6893 + return true; 1.6894 + } 1.6895 + 1.6896 + if (args.length() != 2) { 1.6897 + JS_ReportError(cx, "CDataFinalizer takes 2 arguments"); 1.6898 + return false; 1.6899 + } 1.6900 + 1.6901 + JS::HandleValue valCodePtr = args[1]; 1.6902 + if (!valCodePtr.isObject()) { 1.6903 + return TypeError(cx, "_a CData object_ of a function pointer type", 1.6904 + valCodePtr); 1.6905 + } 1.6906 + JSObject *objCodePtr = &valCodePtr.toObject(); 1.6907 + 1.6908 + //Note: Using a custom argument formatter here would be awkward (requires 1.6909 + //a destructor just to uninstall the formatter). 1.6910 + 1.6911 + // 2. Extract argument type of |objCodePtr| 1.6912 + if (!CData::IsCData(objCodePtr)) { 1.6913 + return TypeError(cx, "a _CData_ object of a function pointer type", 1.6914 + valCodePtr); 1.6915 + } 1.6916 + RootedObject objCodePtrType(cx, CData::GetCType(objCodePtr)); 1.6917 + RootedValue valCodePtrType(cx, ObjectValue(*objCodePtrType)); 1.6918 + MOZ_ASSERT(objCodePtrType); 1.6919 + 1.6920 + TypeCode typCodePtr = CType::GetTypeCode(objCodePtrType); 1.6921 + if (typCodePtr != TYPE_pointer) { 1.6922 + return TypeError(cx, "a CData object of a function _pointer_ type", 1.6923 + valCodePtrType); 1.6924 + } 1.6925 + 1.6926 + JSObject *objCodeType = PointerType::GetBaseType(objCodePtrType); 1.6927 + MOZ_ASSERT(objCodeType); 1.6928 + 1.6929 + TypeCode typCode = CType::GetTypeCode(objCodeType); 1.6930 + if (typCode != TYPE_function) { 1.6931 + return TypeError(cx, "a CData object of a _function_ pointer type", 1.6932 + valCodePtrType); 1.6933 + } 1.6934 + uintptr_t code = *reinterpret_cast<uintptr_t*>(CData::GetData(objCodePtr)); 1.6935 + if (!code) { 1.6936 + return TypeError(cx, "a CData object of a _non-NULL_ function pointer type", 1.6937 + valCodePtrType); 1.6938 + } 1.6939 + 1.6940 + FunctionInfo* funInfoFinalizer = 1.6941 + FunctionType::GetFunctionInfo(objCodeType); 1.6942 + MOZ_ASSERT(funInfoFinalizer); 1.6943 + 1.6944 + if ((funInfoFinalizer->mArgTypes.length() != 1) 1.6945 + || (funInfoFinalizer->mIsVariadic)) { 1.6946 + RootedValue valCodeType(cx, ObjectValue(*objCodeType)); 1.6947 + return TypeError(cx, "a function accepting exactly one argument", 1.6948 + valCodeType); 1.6949 + } 1.6950 + RootedObject objArgType(cx, funInfoFinalizer->mArgTypes[0]); 1.6951 + RootedObject returnType(cx, funInfoFinalizer->mReturnType); 1.6952 + 1.6953 + // Invariant: At this stage, we know that funInfoFinalizer->mIsVariadic 1.6954 + // is |false|. Therefore, funInfoFinalizer->mCIF has already been initialized. 1.6955 + 1.6956 + bool freePointer = false; 1.6957 + 1.6958 + // 3. Perform dynamic cast of |args[0]| into |objType|, store it in |cargs| 1.6959 + 1.6960 + size_t sizeArg; 1.6961 + RootedValue valData(cx, args[0]); 1.6962 + if (!CType::GetSafeSize(objArgType, &sizeArg)) { 1.6963 + return TypeError(cx, "(an object with known size)", valData); 1.6964 + } 1.6965 + 1.6966 + ScopedJSFreePtr<void> cargs(malloc(sizeArg)); 1.6967 + 1.6968 + if (!ImplicitConvert(cx, valData, objArgType, cargs.get(), 1.6969 + false, &freePointer)) { 1.6970 + RootedValue valArgType(cx, ObjectValue(*objArgType)); 1.6971 + return TypeError(cx, "(an object that can be converted to the following type)", 1.6972 + valArgType); 1.6973 + } 1.6974 + if (freePointer) { 1.6975 + // Note: We could handle that case, if necessary. 1.6976 + JS_ReportError(cx, "Internal Error during CDataFinalizer. Object cannot be represented"); 1.6977 + return false; 1.6978 + } 1.6979 + 1.6980 + // 4. Prepare buffer for holding return value 1.6981 + 1.6982 + ScopedJSFreePtr<void> rvalue; 1.6983 + if (CType::GetTypeCode(returnType) != TYPE_void_t) { 1.6984 + rvalue = malloc(Align(CType::GetSize(returnType), 1.6985 + sizeof(ffi_arg))); 1.6986 + } //Otherwise, simply do not allocate 1.6987 + 1.6988 + // 5. Create |objResult| 1.6989 + 1.6990 + JSObject *objResult = JS_NewObject(cx, &sCDataFinalizerClass, objProto, NullPtr()); 1.6991 + if (!objResult) { 1.6992 + return false; 1.6993 + } 1.6994 + 1.6995 + // If our argument is a CData, it holds a type. 1.6996 + // This is the type that we should capture, not that 1.6997 + // of the function, which may be less precise. 1.6998 + JSObject *objBestArgType = objArgType; 1.6999 + if (!JSVAL_IS_PRIMITIVE(valData)) { 1.7000 + JSObject *objData = &valData.toObject(); 1.7001 + if (CData::IsCData(objData)) { 1.7002 + objBestArgType = CData::GetCType(objData); 1.7003 + size_t sizeBestArg; 1.7004 + if (!CType::GetSafeSize(objBestArgType, &sizeBestArg)) { 1.7005 + MOZ_ASSUME_UNREACHABLE("object with unknown size"); 1.7006 + } 1.7007 + if (sizeBestArg != sizeArg) { 1.7008 + return TypeError(cx, "(an object with the same size as that expected by the C finalization function)", valData); 1.7009 + } 1.7010 + } 1.7011 + } 1.7012 + 1.7013 + // Used by GetCType 1.7014 + JS_SetReservedSlot(objResult, 1.7015 + SLOT_DATAFINALIZER_VALTYPE, 1.7016 + OBJECT_TO_JSVAL(objBestArgType)); 1.7017 + 1.7018 + // Used by ToSource 1.7019 + JS_SetReservedSlot(objResult, 1.7020 + SLOT_DATAFINALIZER_CODETYPE, 1.7021 + OBJECT_TO_JSVAL(objCodePtrType)); 1.7022 + 1.7023 + ffi_abi abi; 1.7024 + if (!GetABI(cx, OBJECT_TO_JSVAL(funInfoFinalizer->mABI), &abi)) { 1.7025 + JS_ReportError(cx, "Internal Error: " 1.7026 + "Invalid ABI specification in CDataFinalizer"); 1.7027 + return false; 1.7028 + } 1.7029 + 1.7030 + ffi_type* rtype = CType::GetFFIType(cx, funInfoFinalizer->mReturnType); 1.7031 + if (!rtype) { 1.7032 + JS_ReportError(cx, "Internal Error: " 1.7033 + "Could not access ffi type of CDataFinalizer"); 1.7034 + return false; 1.7035 + } 1.7036 + 1.7037 + // 7. Store C information as private 1.7038 + ScopedJSFreePtr<CDataFinalizer::Private> 1.7039 + p((CDataFinalizer::Private*)malloc(sizeof(CDataFinalizer::Private))); 1.7040 + 1.7041 + memmove(&p->CIF, &funInfoFinalizer->mCIF, sizeof(ffi_cif)); 1.7042 + 1.7043 + p->cargs = cargs.forget(); 1.7044 + p->rvalue = rvalue.forget(); 1.7045 + p->cargs_size = sizeArg; 1.7046 + p->code = code; 1.7047 + 1.7048 + 1.7049 + JS_SetPrivate(objResult, p.forget()); 1.7050 + args.rval().setObject(*objResult); 1.7051 + return true; 1.7052 +} 1.7053 + 1.7054 + 1.7055 +/* 1.7056 + * Actually call the finalizer. Does not perform any cleanup on the object. 1.7057 + * 1.7058 + * Preconditions: |this| must be a |CDataFinalizer|, |p| must be non-null. 1.7059 + * The function fails if |this| has gone through |Forget|/|Dispose| 1.7060 + * or |Finalize|. 1.7061 + * 1.7062 + * This function does not alter the value of |errno|/|GetLastError|. 1.7063 + * 1.7064 + * If argument |errnoStatus| is non-nullptr, it receives the value of |errno| 1.7065 + * immediately after the call. Under Windows, if argument |lastErrorStatus| 1.7066 + * is non-nullptr, it receives the value of |GetLastError| immediately after 1.7067 + * the call. On other platforms, |lastErrorStatus| is ignored. 1.7068 + */ 1.7069 +void 1.7070 +CDataFinalizer::CallFinalizer(CDataFinalizer::Private *p, 1.7071 + int* errnoStatus, 1.7072 + int32_t* lastErrorStatus) 1.7073 +{ 1.7074 + int savedErrno = errno; 1.7075 + errno = 0; 1.7076 +#if defined(XP_WIN) 1.7077 + int32_t savedLastError = GetLastError(); 1.7078 + SetLastError(0); 1.7079 +#endif // defined(XP_WIN) 1.7080 + 1.7081 + ffi_call(&p->CIF, FFI_FN(p->code), p->rvalue, &p->cargs); 1.7082 + 1.7083 + if (errnoStatus) { 1.7084 + *errnoStatus = errno; 1.7085 + } 1.7086 + errno = savedErrno; 1.7087 +#if defined(XP_WIN) 1.7088 + if (lastErrorStatus) { 1.7089 + *lastErrorStatus = GetLastError(); 1.7090 + } 1.7091 + SetLastError(savedLastError); 1.7092 +#endif // defined(XP_WIN) 1.7093 +} 1.7094 + 1.7095 +/* 1.7096 + * Forget the value. 1.7097 + * 1.7098 + * Preconditions: |this| must be a |CDataFinalizer|. 1.7099 + * The function fails if |this| has gone through |Forget|/|Dispose| 1.7100 + * or |Finalize|. 1.7101 + * 1.7102 + * Does not call the finalizer. Cleans up the Private memory and releases all 1.7103 + * strong references. 1.7104 + */ 1.7105 +bool 1.7106 +CDataFinalizer::Methods::Forget(JSContext* cx, unsigned argc, jsval *vp) 1.7107 +{ 1.7108 + CallArgs args = CallArgsFromVp(argc, vp); 1.7109 + if (args.length() != 0) { 1.7110 + JS_ReportError(cx, "CDataFinalizer.prototype.forget takes no arguments"); 1.7111 + return false; 1.7112 + } 1.7113 + 1.7114 + JS::Rooted<JSObject*> obj(cx, args.thisv().toObjectOrNull()); 1.7115 + if (!obj) 1.7116 + return false; 1.7117 + if (!CDataFinalizer::IsCDataFinalizer(obj)) { 1.7118 + RootedValue val(cx, ObjectValue(*obj)); 1.7119 + return TypeError(cx, "a CDataFinalizer", val); 1.7120 + } 1.7121 + 1.7122 + CDataFinalizer::Private *p = (CDataFinalizer::Private *) 1.7123 + JS_GetPrivate(obj); 1.7124 + 1.7125 + if (!p) { 1.7126 + JS_ReportError(cx, "forget called on an empty CDataFinalizer"); 1.7127 + return false; 1.7128 + } 1.7129 + 1.7130 + RootedValue valJSData(cx); 1.7131 + RootedObject ctype(cx, GetCType(cx, obj)); 1.7132 + if (!ConvertToJS(cx, ctype, NullPtr(), p->cargs, false, true, valJSData.address())) { 1.7133 + JS_ReportError(cx, "CDataFinalizer value cannot be represented"); 1.7134 + return false; 1.7135 + } 1.7136 + 1.7137 + CDataFinalizer::Cleanup(p, obj); 1.7138 + 1.7139 + args.rval().set(valJSData); 1.7140 + return true; 1.7141 +} 1.7142 + 1.7143 +/* 1.7144 + * Clean up the value. 1.7145 + * 1.7146 + * Preconditions: |this| must be a |CDataFinalizer|. 1.7147 + * The function fails if |this| has gone through |Forget|/|Dispose| 1.7148 + * or |Finalize|. 1.7149 + * 1.7150 + * Calls the finalizer, cleans up the Private memory and releases all 1.7151 + * strong references. 1.7152 + */ 1.7153 +bool 1.7154 +CDataFinalizer::Methods::Dispose(JSContext* cx, unsigned argc, jsval *vp) 1.7155 +{ 1.7156 + CallArgs args = CallArgsFromVp(argc, vp); 1.7157 + if (args.length() != 0) { 1.7158 + JS_ReportError(cx, "CDataFinalizer.prototype.dispose takes no arguments"); 1.7159 + return false; 1.7160 + } 1.7161 + 1.7162 + RootedObject obj(cx, JS_THIS_OBJECT(cx, vp)); 1.7163 + if (!obj) 1.7164 + return false; 1.7165 + if (!CDataFinalizer::IsCDataFinalizer(obj)) { 1.7166 + RootedValue val(cx, ObjectValue(*obj)); 1.7167 + return TypeError(cx, "a CDataFinalizer", val); 1.7168 + } 1.7169 + 1.7170 + CDataFinalizer::Private *p = (CDataFinalizer::Private *) 1.7171 + JS_GetPrivate(obj); 1.7172 + 1.7173 + if (!p) { 1.7174 + JS_ReportError(cx, "dispose called on an empty CDataFinalizer."); 1.7175 + return false; 1.7176 + } 1.7177 + 1.7178 + jsval valType = JS_GetReservedSlot(obj, SLOT_DATAFINALIZER_VALTYPE); 1.7179 + JS_ASSERT(!JSVAL_IS_PRIMITIVE(valType)); 1.7180 + 1.7181 + JSObject *objCTypes = CType::GetGlobalCTypes(cx, &valType.toObject()); 1.7182 + if (!objCTypes) 1.7183 + return false; 1.7184 + 1.7185 + jsval valCodePtrType = JS_GetReservedSlot(obj, SLOT_DATAFINALIZER_CODETYPE); 1.7186 + JS_ASSERT(!JSVAL_IS_PRIMITIVE(valCodePtrType)); 1.7187 + JSObject *objCodePtrType = &valCodePtrType.toObject(); 1.7188 + 1.7189 + JSObject *objCodeType = PointerType::GetBaseType(objCodePtrType); 1.7190 + JS_ASSERT(objCodeType); 1.7191 + JS_ASSERT(CType::GetTypeCode(objCodeType) == TYPE_function); 1.7192 + 1.7193 + RootedObject resultType(cx, FunctionType::GetFunctionInfo(objCodeType)->mReturnType); 1.7194 + RootedValue result(cx, JSVAL_VOID); 1.7195 + 1.7196 + int errnoStatus; 1.7197 +#if defined(XP_WIN) 1.7198 + int32_t lastErrorStatus; 1.7199 + CDataFinalizer::CallFinalizer(p, &errnoStatus, &lastErrorStatus); 1.7200 +#else 1.7201 + CDataFinalizer::CallFinalizer(p, &errnoStatus, nullptr); 1.7202 +#endif // defined(XP_WIN) 1.7203 + 1.7204 + JS_SetReservedSlot(objCTypes, SLOT_ERRNO, INT_TO_JSVAL(errnoStatus)); 1.7205 +#if defined(XP_WIN) 1.7206 + JS_SetReservedSlot(objCTypes, SLOT_LASTERROR, INT_TO_JSVAL(lastErrorStatus)); 1.7207 +#endif // defined(XP_WIN) 1.7208 + 1.7209 + if (ConvertToJS(cx, resultType, NullPtr(), p->rvalue, false, true, result.address())) { 1.7210 + CDataFinalizer::Cleanup(p, obj); 1.7211 + args.rval().set(result); 1.7212 + return true; 1.7213 + } 1.7214 + CDataFinalizer::Cleanup(p, obj); 1.7215 + return false; 1.7216 +} 1.7217 + 1.7218 +/* 1.7219 + * Perform finalization. 1.7220 + * 1.7221 + * Preconditions: |this| must be the result of |CDataFinalizer|. 1.7222 + * It may have gone through |Forget|/|Dispose|. 1.7223 + * 1.7224 + * If |this| has not gone through |Forget|/|Dispose|, calls the 1.7225 + * finalizer, cleans up the Private memory and releases all 1.7226 + * strong references. 1.7227 + */ 1.7228 +void 1.7229 +CDataFinalizer::Finalize(JSFreeOp* fop, JSObject* obj) 1.7230 +{ 1.7231 + CDataFinalizer::Private *p = (CDataFinalizer::Private *) 1.7232 + JS_GetPrivate(obj); 1.7233 + 1.7234 + if (!p) { 1.7235 + return; 1.7236 + } 1.7237 + 1.7238 + CDataFinalizer::CallFinalizer(p, nullptr, nullptr); 1.7239 + CDataFinalizer::Cleanup(p, nullptr); 1.7240 +} 1.7241 + 1.7242 +/* 1.7243 + * Perform cleanup of a CDataFinalizer 1.7244 + * 1.7245 + * Release strong references, cleanup |Private|. 1.7246 + * 1.7247 + * Argument |p| contains the private information of the CDataFinalizer. If 1.7248 + * nullptr, this function does nothing. 1.7249 + * Argument |obj| should contain |nullptr| during finalization (or in any 1.7250 + * context in which the object itself should not be cleaned up), or a 1.7251 + * CDataFinalizer object otherwise. 1.7252 + */ 1.7253 +void 1.7254 +CDataFinalizer::Cleanup(CDataFinalizer::Private *p, JSObject *obj) 1.7255 +{ 1.7256 + if (!p) { 1.7257 + return; // We have already cleaned up 1.7258 + } 1.7259 + 1.7260 + free(p->cargs); 1.7261 + free(p->rvalue); 1.7262 + free(p); 1.7263 + 1.7264 + if (!obj) { 1.7265 + return; // No slots to clean up 1.7266 + } 1.7267 + 1.7268 + JS_ASSERT(CDataFinalizer::IsCDataFinalizer(obj)); 1.7269 + 1.7270 + JS_SetPrivate(obj, nullptr); 1.7271 + for (int i = 0; i < CDATAFINALIZER_SLOTS; ++i) { 1.7272 + JS_SetReservedSlot(obj, i, JSVAL_NULL); 1.7273 + } 1.7274 +} 1.7275 + 1.7276 + 1.7277 +/******************************************************************************* 1.7278 +** Int64 and UInt64 implementation 1.7279 +*******************************************************************************/ 1.7280 + 1.7281 +JSObject* 1.7282 +Int64Base::Construct(JSContext* cx, 1.7283 + HandleObject proto, 1.7284 + uint64_t data, 1.7285 + bool isUnsigned) 1.7286 +{ 1.7287 + const JSClass* clasp = isUnsigned ? &sUInt64Class : &sInt64Class; 1.7288 + RootedObject parent(cx, JS_GetParent(proto)); 1.7289 + RootedObject result(cx, JS_NewObject(cx, clasp, proto, parent)); 1.7290 + if (!result) 1.7291 + return nullptr; 1.7292 + 1.7293 + // attach the Int64's data 1.7294 + uint64_t* buffer = cx->new_<uint64_t>(data); 1.7295 + if (!buffer) { 1.7296 + JS_ReportOutOfMemory(cx); 1.7297 + return nullptr; 1.7298 + } 1.7299 + 1.7300 + JS_SetReservedSlot(result, SLOT_INT64, PRIVATE_TO_JSVAL(buffer)); 1.7301 + 1.7302 + if (!JS_FreezeObject(cx, result)) 1.7303 + return nullptr; 1.7304 + 1.7305 + return result; 1.7306 +} 1.7307 + 1.7308 +void 1.7309 +Int64Base::Finalize(JSFreeOp *fop, JSObject* obj) 1.7310 +{ 1.7311 + jsval slot = JS_GetReservedSlot(obj, SLOT_INT64); 1.7312 + if (JSVAL_IS_VOID(slot)) 1.7313 + return; 1.7314 + 1.7315 + FreeOp::get(fop)->delete_(static_cast<uint64_t*>(JSVAL_TO_PRIVATE(slot))); 1.7316 +} 1.7317 + 1.7318 +uint64_t 1.7319 +Int64Base::GetInt(JSObject* obj) { 1.7320 + JS_ASSERT(Int64::IsInt64(obj) || UInt64::IsUInt64(obj)); 1.7321 + 1.7322 + jsval slot = JS_GetReservedSlot(obj, SLOT_INT64); 1.7323 + return *static_cast<uint64_t*>(JSVAL_TO_PRIVATE(slot)); 1.7324 +} 1.7325 + 1.7326 +bool 1.7327 +Int64Base::ToString(JSContext* cx, 1.7328 + JSObject* obj, 1.7329 + const CallArgs& args, 1.7330 + bool isUnsigned) 1.7331 +{ 1.7332 + if (args.length() > 1) { 1.7333 + JS_ReportError(cx, "toString takes zero or one argument"); 1.7334 + return false; 1.7335 + } 1.7336 + 1.7337 + int radix = 10; 1.7338 + if (args.length() == 1) { 1.7339 + jsval arg = args[0]; 1.7340 + if (arg.isInt32()) 1.7341 + radix = arg.toInt32(); 1.7342 + if (!arg.isInt32() || radix < 2 || radix > 36) { 1.7343 + JS_ReportError(cx, "radix argument must be an integer between 2 and 36"); 1.7344 + return false; 1.7345 + } 1.7346 + } 1.7347 + 1.7348 + AutoString intString; 1.7349 + if (isUnsigned) { 1.7350 + IntegerToString(GetInt(obj), radix, intString); 1.7351 + } else { 1.7352 + IntegerToString(static_cast<int64_t>(GetInt(obj)), radix, intString); 1.7353 + } 1.7354 + 1.7355 + JSString *result = NewUCString(cx, intString); 1.7356 + if (!result) 1.7357 + return false; 1.7358 + 1.7359 + args.rval().setString(result); 1.7360 + return true; 1.7361 +} 1.7362 + 1.7363 +bool 1.7364 +Int64Base::ToSource(JSContext* cx, 1.7365 + JSObject* obj, 1.7366 + const CallArgs& args, 1.7367 + bool isUnsigned) 1.7368 +{ 1.7369 + if (args.length() != 0) { 1.7370 + JS_ReportError(cx, "toSource takes zero arguments"); 1.7371 + return false; 1.7372 + } 1.7373 + 1.7374 + // Return a decimal string suitable for constructing the number. 1.7375 + AutoString source; 1.7376 + if (isUnsigned) { 1.7377 + AppendString(source, "ctypes.UInt64(\""); 1.7378 + IntegerToString(GetInt(obj), 10, source); 1.7379 + } else { 1.7380 + AppendString(source, "ctypes.Int64(\""); 1.7381 + IntegerToString(static_cast<int64_t>(GetInt(obj)), 10, source); 1.7382 + } 1.7383 + AppendString(source, "\")"); 1.7384 + 1.7385 + JSString *result = NewUCString(cx, source); 1.7386 + if (!result) 1.7387 + return false; 1.7388 + 1.7389 + args.rval().setString(result); 1.7390 + return true; 1.7391 +} 1.7392 + 1.7393 +bool 1.7394 +Int64::Construct(JSContext* cx, 1.7395 + unsigned argc, 1.7396 + jsval* vp) 1.7397 +{ 1.7398 + CallArgs args = CallArgsFromVp(argc, vp); 1.7399 + 1.7400 + // Construct and return a new Int64 object. 1.7401 + if (args.length() != 1) { 1.7402 + JS_ReportError(cx, "Int64 takes one argument"); 1.7403 + return false; 1.7404 + } 1.7405 + 1.7406 + int64_t i = 0; 1.7407 + if (!jsvalToBigInteger(cx, args[0], true, &i)) 1.7408 + return TypeError(cx, "int64", args[0]); 1.7409 + 1.7410 + // Get ctypes.Int64.prototype from the 'prototype' property of the ctor. 1.7411 + RootedValue slot(cx); 1.7412 + RootedObject callee(cx, &args.callee()); 1.7413 + ASSERT_OK(JS_GetProperty(cx, callee, "prototype", &slot)); 1.7414 + RootedObject proto(cx, JSVAL_TO_OBJECT(slot)); 1.7415 + JS_ASSERT(JS_GetClass(proto) == &sInt64ProtoClass); 1.7416 + 1.7417 + JSObject* result = Int64Base::Construct(cx, proto, i, false); 1.7418 + if (!result) 1.7419 + return false; 1.7420 + 1.7421 + args.rval().setObject(*result); 1.7422 + return true; 1.7423 +} 1.7424 + 1.7425 +bool 1.7426 +Int64::IsInt64(JSObject* obj) 1.7427 +{ 1.7428 + return JS_GetClass(obj) == &sInt64Class; 1.7429 +} 1.7430 + 1.7431 +bool 1.7432 +Int64::ToString(JSContext* cx, unsigned argc, jsval* vp) 1.7433 +{ 1.7434 + CallArgs args = CallArgsFromVp(argc, vp); 1.7435 + JSObject* obj = JS_THIS_OBJECT(cx, vp); 1.7436 + if (!obj) 1.7437 + return false; 1.7438 + if (!Int64::IsInt64(obj)) { 1.7439 + JS_ReportError(cx, "not an Int64"); 1.7440 + return false; 1.7441 + } 1.7442 + 1.7443 + return Int64Base::ToString(cx, obj, args, false); 1.7444 +} 1.7445 + 1.7446 +bool 1.7447 +Int64::ToSource(JSContext* cx, unsigned argc, jsval* vp) 1.7448 +{ 1.7449 + CallArgs args = CallArgsFromVp(argc, vp); 1.7450 + JSObject* obj = JS_THIS_OBJECT(cx, vp); 1.7451 + if (!obj) 1.7452 + return false; 1.7453 + if (!Int64::IsInt64(obj)) { 1.7454 + JS_ReportError(cx, "not an Int64"); 1.7455 + return false; 1.7456 + } 1.7457 + 1.7458 + return Int64Base::ToSource(cx, obj, args, false); 1.7459 +} 1.7460 + 1.7461 +bool 1.7462 +Int64::Compare(JSContext* cx, unsigned argc, jsval* vp) 1.7463 +{ 1.7464 + CallArgs args = CallArgsFromVp(argc, vp); 1.7465 + if (args.length() != 2 || 1.7466 + JSVAL_IS_PRIMITIVE(args[0]) || 1.7467 + JSVAL_IS_PRIMITIVE(args[1]) || 1.7468 + !Int64::IsInt64(&args[0].toObject()) || 1.7469 + !Int64::IsInt64(&args[1].toObject())) { 1.7470 + JS_ReportError(cx, "compare takes two Int64 arguments"); 1.7471 + return false; 1.7472 + } 1.7473 + 1.7474 + JSObject* obj1 = &args[0].toObject(); 1.7475 + JSObject* obj2 = &args[1].toObject(); 1.7476 + 1.7477 + int64_t i1 = Int64Base::GetInt(obj1); 1.7478 + int64_t i2 = Int64Base::GetInt(obj2); 1.7479 + 1.7480 + if (i1 == i2) 1.7481 + args.rval().setInt32(0); 1.7482 + else if (i1 < i2) 1.7483 + args.rval().setInt32(-1); 1.7484 + else 1.7485 + args.rval().setInt32(1); 1.7486 + 1.7487 + return true; 1.7488 +} 1.7489 + 1.7490 +#define LO_MASK ((uint64_t(1) << 32) - 1) 1.7491 +#define INT64_LO(i) ((i) & LO_MASK) 1.7492 +#define INT64_HI(i) ((i) >> 32) 1.7493 + 1.7494 +bool 1.7495 +Int64::Lo(JSContext* cx, unsigned argc, jsval* vp) 1.7496 +{ 1.7497 + CallArgs args = CallArgsFromVp(argc, vp); 1.7498 + if (args.length() != 1 || JSVAL_IS_PRIMITIVE(args[0]) || 1.7499 + !Int64::IsInt64(&args[0].toObject())) { 1.7500 + JS_ReportError(cx, "lo takes one Int64 argument"); 1.7501 + return false; 1.7502 + } 1.7503 + 1.7504 + JSObject* obj = &args[0].toObject(); 1.7505 + int64_t u = Int64Base::GetInt(obj); 1.7506 + double d = uint32_t(INT64_LO(u)); 1.7507 + 1.7508 + args.rval().setNumber(d); 1.7509 + return true; 1.7510 +} 1.7511 + 1.7512 +bool 1.7513 +Int64::Hi(JSContext* cx, unsigned argc, jsval* vp) 1.7514 +{ 1.7515 + CallArgs args = CallArgsFromVp(argc, vp); 1.7516 + if (args.length() != 1 || JSVAL_IS_PRIMITIVE(args[0]) || 1.7517 + !Int64::IsInt64(&args[0].toObject())) { 1.7518 + JS_ReportError(cx, "hi takes one Int64 argument"); 1.7519 + return false; 1.7520 + } 1.7521 + 1.7522 + JSObject* obj = &args[0].toObject(); 1.7523 + int64_t u = Int64Base::GetInt(obj); 1.7524 + double d = int32_t(INT64_HI(u)); 1.7525 + 1.7526 + args.rval().setDouble(d); 1.7527 + return true; 1.7528 +} 1.7529 + 1.7530 +bool 1.7531 +Int64::Join(JSContext* cx, unsigned argc, jsval* vp) 1.7532 +{ 1.7533 + CallArgs args = CallArgsFromVp(argc, vp); 1.7534 + if (args.length() != 2) { 1.7535 + JS_ReportError(cx, "join takes two arguments"); 1.7536 + return false; 1.7537 + } 1.7538 + 1.7539 + int32_t hi; 1.7540 + uint32_t lo; 1.7541 + if (!jsvalToInteger(cx, args[0], &hi)) 1.7542 + return TypeError(cx, "int32", args[0]); 1.7543 + if (!jsvalToInteger(cx, args[1], &lo)) 1.7544 + return TypeError(cx, "uint32", args[1]); 1.7545 + 1.7546 + int64_t i = (int64_t(hi) << 32) + int64_t(lo); 1.7547 + 1.7548 + // Get Int64.prototype from the function's reserved slot. 1.7549 + JSObject* callee = &args.callee(); 1.7550 + 1.7551 + jsval slot = js::GetFunctionNativeReserved(callee, SLOT_FN_INT64PROTO); 1.7552 + RootedObject proto(cx, &slot.toObject()); 1.7553 + JS_ASSERT(JS_GetClass(proto) == &sInt64ProtoClass); 1.7554 + 1.7555 + JSObject* result = Int64Base::Construct(cx, proto, i, false); 1.7556 + if (!result) 1.7557 + return false; 1.7558 + 1.7559 + args.rval().setObject(*result); 1.7560 + return true; 1.7561 +} 1.7562 + 1.7563 +bool 1.7564 +UInt64::Construct(JSContext* cx, 1.7565 + unsigned argc, 1.7566 + jsval* vp) 1.7567 +{ 1.7568 + CallArgs args = CallArgsFromVp(argc, vp); 1.7569 + 1.7570 + // Construct and return a new UInt64 object. 1.7571 + if (args.length() != 1) { 1.7572 + JS_ReportError(cx, "UInt64 takes one argument"); 1.7573 + return false; 1.7574 + } 1.7575 + 1.7576 + uint64_t u = 0; 1.7577 + if (!jsvalToBigInteger(cx, args[0], true, &u)) 1.7578 + return TypeError(cx, "uint64", args[0]); 1.7579 + 1.7580 + // Get ctypes.UInt64.prototype from the 'prototype' property of the ctor. 1.7581 + RootedValue slot(cx); 1.7582 + RootedObject callee(cx, &args.callee()); 1.7583 + ASSERT_OK(JS_GetProperty(cx, callee, "prototype", &slot)); 1.7584 + RootedObject proto(cx, &slot.toObject()); 1.7585 + JS_ASSERT(JS_GetClass(proto) == &sUInt64ProtoClass); 1.7586 + 1.7587 + JSObject* result = Int64Base::Construct(cx, proto, u, true); 1.7588 + if (!result) 1.7589 + return false; 1.7590 + 1.7591 + args.rval().setObject(*result); 1.7592 + return true; 1.7593 +} 1.7594 + 1.7595 +bool 1.7596 +UInt64::IsUInt64(JSObject* obj) 1.7597 +{ 1.7598 + return JS_GetClass(obj) == &sUInt64Class; 1.7599 +} 1.7600 + 1.7601 +bool 1.7602 +UInt64::ToString(JSContext* cx, unsigned argc, jsval* vp) 1.7603 +{ 1.7604 + CallArgs args = CallArgsFromVp(argc, vp); 1.7605 + JSObject* obj = JS_THIS_OBJECT(cx, vp); 1.7606 + if (!obj) 1.7607 + return false; 1.7608 + if (!UInt64::IsUInt64(obj)) { 1.7609 + JS_ReportError(cx, "not a UInt64"); 1.7610 + return false; 1.7611 + } 1.7612 + 1.7613 + return Int64Base::ToString(cx, obj, args, true); 1.7614 +} 1.7615 + 1.7616 +bool 1.7617 +UInt64::ToSource(JSContext* cx, unsigned argc, jsval* vp) 1.7618 +{ 1.7619 + CallArgs args = CallArgsFromVp(argc, vp); 1.7620 + JSObject* obj = JS_THIS_OBJECT(cx, vp); 1.7621 + if (!obj) 1.7622 + return false; 1.7623 + if (!UInt64::IsUInt64(obj)) { 1.7624 + JS_ReportError(cx, "not a UInt64"); 1.7625 + return false; 1.7626 + } 1.7627 + 1.7628 + return Int64Base::ToSource(cx, obj, args, true); 1.7629 +} 1.7630 + 1.7631 +bool 1.7632 +UInt64::Compare(JSContext* cx, unsigned argc, jsval* vp) 1.7633 +{ 1.7634 + CallArgs args = CallArgsFromVp(argc, vp); 1.7635 + if (args.length() != 2 || 1.7636 + JSVAL_IS_PRIMITIVE(args[0]) || 1.7637 + JSVAL_IS_PRIMITIVE(args[1]) || 1.7638 + !UInt64::IsUInt64(&args[0].toObject()) || 1.7639 + !UInt64::IsUInt64(&args[1].toObject())) { 1.7640 + JS_ReportError(cx, "compare takes two UInt64 arguments"); 1.7641 + return false; 1.7642 + } 1.7643 + 1.7644 + JSObject* obj1 = &args[0].toObject(); 1.7645 + JSObject* obj2 = &args[1].toObject(); 1.7646 + 1.7647 + uint64_t u1 = Int64Base::GetInt(obj1); 1.7648 + uint64_t u2 = Int64Base::GetInt(obj2); 1.7649 + 1.7650 + if (u1 == u2) 1.7651 + args.rval().setInt32(0); 1.7652 + else if (u1 < u2) 1.7653 + args.rval().setInt32(-1); 1.7654 + else 1.7655 + args.rval().setInt32(1); 1.7656 + 1.7657 + return true; 1.7658 +} 1.7659 + 1.7660 +bool 1.7661 +UInt64::Lo(JSContext* cx, unsigned argc, jsval* vp) 1.7662 +{ 1.7663 + CallArgs args = CallArgsFromVp(argc, vp); 1.7664 + if (args.length() != 1 || JSVAL_IS_PRIMITIVE(args[0]) || 1.7665 + !UInt64::IsUInt64(&args[0].toObject())) { 1.7666 + JS_ReportError(cx, "lo takes one UInt64 argument"); 1.7667 + return false; 1.7668 + } 1.7669 + 1.7670 + JSObject* obj = &args[0].toObject(); 1.7671 + uint64_t u = Int64Base::GetInt(obj); 1.7672 + double d = uint32_t(INT64_LO(u)); 1.7673 + 1.7674 + args.rval().setDouble(d); 1.7675 + return true; 1.7676 +} 1.7677 + 1.7678 +bool 1.7679 +UInt64::Hi(JSContext* cx, unsigned argc, jsval* vp) 1.7680 +{ 1.7681 + CallArgs args = CallArgsFromVp(argc, vp); 1.7682 + if (args.length() != 1 || JSVAL_IS_PRIMITIVE(args[0]) || 1.7683 + !UInt64::IsUInt64(&args[0].toObject())) { 1.7684 + JS_ReportError(cx, "hi takes one UInt64 argument"); 1.7685 + return false; 1.7686 + } 1.7687 + 1.7688 + JSObject* obj = &args[0].toObject(); 1.7689 + uint64_t u = Int64Base::GetInt(obj); 1.7690 + double d = uint32_t(INT64_HI(u)); 1.7691 + 1.7692 + args.rval().setDouble(d); 1.7693 + return true; 1.7694 +} 1.7695 + 1.7696 +bool 1.7697 +UInt64::Join(JSContext* cx, unsigned argc, jsval* vp) 1.7698 +{ 1.7699 + CallArgs args = CallArgsFromVp(argc, vp); 1.7700 + if (args.length() != 2) { 1.7701 + JS_ReportError(cx, "join takes two arguments"); 1.7702 + return false; 1.7703 + } 1.7704 + 1.7705 + uint32_t hi; 1.7706 + uint32_t lo; 1.7707 + if (!jsvalToInteger(cx, args[0], &hi)) 1.7708 + return TypeError(cx, "uint32_t", args[0]); 1.7709 + if (!jsvalToInteger(cx, args[1], &lo)) 1.7710 + return TypeError(cx, "uint32_t", args[1]); 1.7711 + 1.7712 + uint64_t u = (uint64_t(hi) << 32) + uint64_t(lo); 1.7713 + 1.7714 + // Get UInt64.prototype from the function's reserved slot. 1.7715 + JSObject* callee = &args.callee(); 1.7716 + 1.7717 + jsval slot = js::GetFunctionNativeReserved(callee, SLOT_FN_INT64PROTO); 1.7718 + RootedObject proto(cx, &slot.toObject()); 1.7719 + JS_ASSERT(JS_GetClass(proto) == &sUInt64ProtoClass); 1.7720 + 1.7721 + JSObject* result = Int64Base::Construct(cx, proto, u, true); 1.7722 + if (!result) 1.7723 + return false; 1.7724 + 1.7725 + args.rval().setObject(*result); 1.7726 + return true; 1.7727 +} 1.7728 + 1.7729 +} 1.7730 +}