js/src/ctypes/CTypes.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial