michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "ctypes/CTypes.h" michael@0: michael@0: #include "mozilla/FloatingPoint.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/NumericLimits.h" michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #if defined(XP_WIN) michael@0: #include michael@0: #endif michael@0: michael@0: #if defined(SOLARIS) michael@0: #include michael@0: #endif michael@0: michael@0: #ifdef HAVE_SSIZE_T michael@0: #include michael@0: #endif michael@0: michael@0: #if defined(XP_UNIX) michael@0: #include michael@0: #elif defined(XP_WIN) michael@0: #include michael@0: #endif michael@0: michael@0: #include "jscntxt.h" michael@0: #include "jsfun.h" michael@0: #include "jsnum.h" michael@0: #include "jsprf.h" michael@0: michael@0: #include "builtin/TypedObject.h" michael@0: #include "ctypes/Library.h" michael@0: michael@0: using namespace std; michael@0: using mozilla::NumericLimits; michael@0: michael@0: namespace js { michael@0: namespace ctypes { michael@0: michael@0: size_t michael@0: GetDeflatedUTF8StringLength(JSContext *maybecx, const jschar *chars, michael@0: size_t nchars) michael@0: { michael@0: size_t nbytes; michael@0: const jschar *end; michael@0: unsigned c, c2; michael@0: char buffer[10]; michael@0: michael@0: nbytes = nchars; michael@0: for (end = chars + nchars; chars != end; chars++) { michael@0: c = *chars; michael@0: if (c < 0x80) michael@0: continue; michael@0: if (0xD800 <= c && c <= 0xDFFF) { michael@0: /* Surrogate pair. */ michael@0: chars++; michael@0: michael@0: /* nbytes sets 1 length since this is surrogate pair. */ michael@0: nbytes--; michael@0: if (c >= 0xDC00 || chars == end) michael@0: goto bad_surrogate; michael@0: c2 = *chars; michael@0: if (c2 < 0xDC00 || c2 > 0xDFFF) michael@0: goto bad_surrogate; michael@0: c = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000; michael@0: } michael@0: c >>= 11; michael@0: nbytes++; michael@0: while (c) { michael@0: c >>= 5; michael@0: nbytes++; michael@0: } michael@0: } michael@0: return nbytes; michael@0: michael@0: bad_surrogate: michael@0: if (maybecx) { michael@0: JS_snprintf(buffer, 10, "0x%x", c); michael@0: JS_ReportErrorFlagsAndNumber(maybecx, JSREPORT_ERROR, js_GetErrorMessage, michael@0: nullptr, JSMSG_BAD_SURROGATE_CHAR, buffer); michael@0: } michael@0: return (size_t) -1; michael@0: } michael@0: michael@0: bool michael@0: DeflateStringToUTF8Buffer(JSContext *maybecx, const jschar *src, size_t srclen, michael@0: char *dst, size_t *dstlenp) michael@0: { michael@0: size_t i, utf8Len; michael@0: jschar c, c2; michael@0: uint32_t v; michael@0: uint8_t utf8buf[6]; michael@0: michael@0: size_t dstlen = *dstlenp; michael@0: size_t origDstlen = dstlen; michael@0: michael@0: while (srclen) { michael@0: c = *src++; michael@0: srclen--; michael@0: if (c >= 0xDC00 && c <= 0xDFFF) michael@0: goto badSurrogate; michael@0: if (c < 0xD800 || c > 0xDBFF) { michael@0: v = c; michael@0: } else { michael@0: if (srclen < 1) michael@0: goto badSurrogate; michael@0: c2 = *src; michael@0: if ((c2 < 0xDC00) || (c2 > 0xDFFF)) michael@0: goto badSurrogate; michael@0: src++; michael@0: srclen--; michael@0: v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000; michael@0: } michael@0: if (v < 0x0080) { michael@0: /* no encoding necessary - performance hack */ michael@0: if (dstlen == 0) michael@0: goto bufferTooSmall; michael@0: *dst++ = (char) v; michael@0: utf8Len = 1; michael@0: } else { michael@0: utf8Len = js_OneUcs4ToUtf8Char(utf8buf, v); michael@0: if (utf8Len > dstlen) michael@0: goto bufferTooSmall; michael@0: for (i = 0; i < utf8Len; i++) michael@0: *dst++ = (char) utf8buf[i]; michael@0: } michael@0: dstlen -= utf8Len; michael@0: } michael@0: *dstlenp = (origDstlen - dstlen); michael@0: return true; michael@0: michael@0: badSurrogate: michael@0: *dstlenp = (origDstlen - dstlen); michael@0: /* Delegate error reporting to the measurement function. */ michael@0: if (maybecx) michael@0: GetDeflatedUTF8StringLength(maybecx, src - 1, srclen + 1); michael@0: return false; michael@0: michael@0: bufferTooSmall: michael@0: *dstlenp = (origDstlen - dstlen); michael@0: if (maybecx) { michael@0: JS_ReportErrorNumber(maybecx, js_GetErrorMessage, nullptr, michael@0: JSMSG_BUFFER_TOO_SMALL); michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: /******************************************************************************* michael@0: ** JSAPI function prototypes michael@0: *******************************************************************************/ michael@0: michael@0: // We use an enclosing struct here out of paranoia about the ability of gcc 4.4 michael@0: // (and maybe 4.5) to correctly compile this if it were a template function. michael@0: // See also the comments in dom/workers/Events.cpp (and other adjacent files) by michael@0: // the |struct Property| there. michael@0: template michael@0: struct Property michael@0: { michael@0: static bool michael@0: Fun(JSContext* cx, unsigned argc, JS::Value* vp) michael@0: { michael@0: JS::CallArgs args = JS::CallArgsFromVp(argc, vp); michael@0: return JS::CallNonGenericMethod(cx, args); michael@0: } michael@0: }; michael@0: michael@0: static bool ConstructAbstract(JSContext* cx, unsigned argc, jsval* vp); michael@0: michael@0: namespace CType { michael@0: static bool ConstructData(JSContext* cx, unsigned argc, jsval* vp); michael@0: static bool ConstructBasic(JSContext* cx, HandleObject obj, const CallArgs& args); michael@0: michael@0: static void Trace(JSTracer* trc, JSObject* obj); michael@0: static void Finalize(JSFreeOp *fop, JSObject* obj); michael@0: michael@0: bool IsCType(HandleValue v); michael@0: bool IsCTypeOrProto(HandleValue v); michael@0: michael@0: bool PrototypeGetter(JSContext* cx, JS::CallArgs args); michael@0: bool NameGetter(JSContext* cx, JS::CallArgs args); michael@0: bool SizeGetter(JSContext* cx, JS::CallArgs args); michael@0: bool PtrGetter(JSContext* cx, JS::CallArgs args); michael@0: michael@0: static bool CreateArray(JSContext* cx, unsigned argc, jsval* vp); michael@0: static bool ToString(JSContext* cx, unsigned argc, jsval* vp); michael@0: static bool ToSource(JSContext* cx, unsigned argc, jsval* vp); michael@0: static bool HasInstance(JSContext* cx, HandleObject obj, MutableHandleValue v, bool* bp); michael@0: michael@0: michael@0: /* michael@0: * Get the global "ctypes" object. michael@0: * michael@0: * |obj| must be a CType object. michael@0: * michael@0: * This function never returns nullptr. michael@0: */ michael@0: static JSObject* GetGlobalCTypes(JSContext* cx, JSObject* obj); michael@0: michael@0: } michael@0: michael@0: namespace ABI { michael@0: bool IsABI(JSObject* obj); michael@0: static bool ToSource(JSContext* cx, unsigned argc, jsval* vp); michael@0: } michael@0: michael@0: namespace PointerType { michael@0: static bool Create(JSContext* cx, unsigned argc, jsval* vp); michael@0: static bool ConstructData(JSContext* cx, HandleObject obj, const CallArgs& args); michael@0: michael@0: bool IsPointerType(HandleValue v); michael@0: bool IsPointer(HandleValue v); michael@0: michael@0: bool TargetTypeGetter(JSContext* cx, JS::CallArgs args); michael@0: bool ContentsGetter(JSContext* cx, JS::CallArgs args); michael@0: bool ContentsSetter(JSContext* cx, JS::CallArgs args); michael@0: michael@0: static bool IsNull(JSContext* cx, unsigned argc, jsval* vp); michael@0: static bool Increment(JSContext* cx, unsigned argc, jsval* vp); michael@0: static bool Decrement(JSContext* cx, unsigned argc, jsval* vp); michael@0: // The following is not an instance function, since we don't want to expose arbitrary michael@0: // pointer arithmetic at this moment. michael@0: static bool OffsetBy(JSContext* cx, const CallArgs& args, int offset); michael@0: } michael@0: michael@0: namespace ArrayType { michael@0: bool IsArrayType(HandleValue v); michael@0: bool IsArrayOrArrayType(HandleValue v); michael@0: michael@0: static bool Create(JSContext* cx, unsigned argc, jsval* vp); michael@0: static bool ConstructData(JSContext* cx, HandleObject obj, const CallArgs& args); michael@0: michael@0: bool ElementTypeGetter(JSContext* cx, JS::CallArgs args); michael@0: bool LengthGetter(JSContext* cx, JS::CallArgs args); michael@0: michael@0: static bool Getter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp); michael@0: static bool Setter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, MutableHandleValue vp); michael@0: static bool AddressOfElement(JSContext* cx, unsigned argc, jsval* vp); michael@0: } michael@0: michael@0: namespace StructType { michael@0: bool IsStruct(HandleValue v); michael@0: michael@0: static bool Create(JSContext* cx, unsigned argc, jsval* vp); michael@0: static bool ConstructData(JSContext* cx, HandleObject obj, const CallArgs& args); michael@0: michael@0: bool FieldsArrayGetter(JSContext* cx, JS::CallArgs args); michael@0: michael@0: static bool FieldGetter(JSContext* cx, HandleObject obj, HandleId idval, michael@0: MutableHandleValue vp); michael@0: static bool FieldSetter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, michael@0: MutableHandleValue vp); michael@0: static bool AddressOfField(JSContext* cx, unsigned argc, jsval* vp); michael@0: static bool Define(JSContext* cx, unsigned argc, jsval* vp); michael@0: } michael@0: michael@0: namespace FunctionType { michael@0: static bool Create(JSContext* cx, unsigned argc, jsval* vp); michael@0: static bool ConstructData(JSContext* cx, HandleObject typeObj, michael@0: HandleObject dataObj, HandleObject fnObj, HandleObject thisObj, jsval errVal); michael@0: michael@0: static bool Call(JSContext* cx, unsigned argc, jsval* vp); michael@0: michael@0: bool IsFunctionType(HandleValue v); michael@0: michael@0: bool ArgTypesGetter(JSContext* cx, JS::CallArgs args); michael@0: bool ReturnTypeGetter(JSContext* cx, JS::CallArgs args); michael@0: bool ABIGetter(JSContext* cx, JS::CallArgs args); michael@0: bool IsVariadicGetter(JSContext* cx, JS::CallArgs args); michael@0: } michael@0: michael@0: namespace CClosure { michael@0: static void Trace(JSTracer* trc, JSObject* obj); michael@0: static void Finalize(JSFreeOp *fop, JSObject* obj); michael@0: michael@0: // libffi callback michael@0: static void ClosureStub(ffi_cif* cif, void* result, void** args, michael@0: void* userData); michael@0: } michael@0: michael@0: namespace CData { michael@0: static void Finalize(JSFreeOp *fop, JSObject* obj); michael@0: michael@0: bool ValueGetter(JSContext* cx, JS::CallArgs args); michael@0: bool ValueSetter(JSContext* cx, JS::CallArgs args); michael@0: michael@0: static bool Address(JSContext* cx, unsigned argc, jsval* vp); michael@0: static bool ReadString(JSContext* cx, unsigned argc, jsval* vp); michael@0: static bool ReadStringReplaceMalformed(JSContext* cx, unsigned argc, jsval* vp); michael@0: static bool ToSource(JSContext* cx, unsigned argc, jsval* vp); michael@0: static JSString *GetSourceString(JSContext *cx, HandleObject typeObj, michael@0: void *data); michael@0: michael@0: bool ErrnoGetter(JSContext* cx, JS::CallArgs args); michael@0: michael@0: #if defined(XP_WIN) michael@0: bool LastErrorGetter(JSContext* cx, JS::CallArgs args); michael@0: #endif // defined(XP_WIN) michael@0: } michael@0: michael@0: namespace CDataFinalizer { michael@0: /* michael@0: * Attach a C function as a finalizer to a JS object. michael@0: * michael@0: * This function is available from JS as |ctypes.withFinalizer|. michael@0: * michael@0: * JavaScript signature: michael@0: * function(CData, CData): CDataFinalizer michael@0: * value finalizer finalizable michael@0: * michael@0: * Where |finalizer| is a one-argument function taking a value michael@0: * with the same type as |value|. michael@0: */ michael@0: static bool Construct(JSContext* cx, unsigned argc, jsval *vp); michael@0: michael@0: /* michael@0: * Private data held by |CDataFinalizer|. michael@0: * michael@0: * See also |enum CDataFinalizerSlot| for the slots of michael@0: * |CDataFinalizer|. michael@0: * michael@0: * Note: the private data may be nullptr, if |dispose|, |forget| or the michael@0: * finalizer has already been called. michael@0: */ michael@0: struct Private { michael@0: /* michael@0: * The C data to pass to the code. michael@0: * Finalization/|dispose|/|forget| release this memory. michael@0: */ michael@0: void *cargs; michael@0: michael@0: /* michael@0: * The total size of the buffer pointed by |cargs| michael@0: */ michael@0: size_t cargs_size; michael@0: michael@0: /* michael@0: * Low-level signature information. michael@0: * Finalization/|dispose|/|forget| release this memory. michael@0: */ michael@0: ffi_cif CIF; michael@0: michael@0: /* michael@0: * The C function to invoke during finalization. michael@0: * Do not deallocate this. michael@0: */ michael@0: uintptr_t code; michael@0: michael@0: /* michael@0: * A buffer for holding the return value. michael@0: * Finalization/|dispose|/|forget| release this memory. michael@0: */ michael@0: void *rvalue; michael@0: }; michael@0: michael@0: /* michael@0: * Methods of instances of |CDataFinalizer| michael@0: */ michael@0: namespace Methods { michael@0: static bool Dispose(JSContext* cx, unsigned argc, jsval *vp); michael@0: static bool Forget(JSContext* cx, unsigned argc, jsval *vp); michael@0: static bool ToSource(JSContext* cx, unsigned argc, jsval *vp); michael@0: static bool ToString(JSContext* cx, unsigned argc, jsval *vp); michael@0: } michael@0: michael@0: /* michael@0: * Utility functions michael@0: * michael@0: * @return true if |obj| is a CDataFinalizer, false otherwise. michael@0: */ michael@0: static bool IsCDataFinalizer(JSObject *obj); michael@0: michael@0: /* michael@0: * Clean up the finalization information of a CDataFinalizer. michael@0: * michael@0: * Used by |Finalize|, |Dispose| and |Forget|. michael@0: * michael@0: * @param p The private information of the CDataFinalizer. If nullptr, michael@0: * this function does nothing. michael@0: * @param obj Either nullptr, if the object should not be cleaned up (i.e. michael@0: * during finalization) or a CDataFinalizer JSObject. Always use nullptr michael@0: * if you are calling from a finalizer. michael@0: */ michael@0: static void Cleanup(Private *p, JSObject *obj); michael@0: michael@0: /* michael@0: * Perform the actual call to the finalizer code. michael@0: */ michael@0: static void CallFinalizer(CDataFinalizer::Private *p, michael@0: int* errnoStatus, michael@0: int32_t* lastErrorStatus); michael@0: michael@0: /* michael@0: * Return the CType of a CDataFinalizer object, or nullptr if the object michael@0: * has been cleaned-up already. michael@0: */ michael@0: static JSObject *GetCType(JSContext *cx, JSObject *obj); michael@0: michael@0: /* michael@0: * Perform finalization of a |CDataFinalizer| michael@0: */ michael@0: static void Finalize(JSFreeOp *fop, JSObject *obj); michael@0: michael@0: /* michael@0: * Return the jsval contained by this finalizer. michael@0: * michael@0: * Note that the jsval is actually not recorded, but converted back from C. michael@0: */ michael@0: static bool GetValue(JSContext *cx, JSObject *obj, jsval *result); michael@0: michael@0: static JSObject* GetCData(JSContext *cx, JSObject *obj); michael@0: } michael@0: michael@0: michael@0: // Int64Base provides functions common to Int64 and UInt64. michael@0: namespace Int64Base { michael@0: JSObject* Construct(JSContext* cx, HandleObject proto, uint64_t data, michael@0: bool isUnsigned); michael@0: michael@0: uint64_t GetInt(JSObject* obj); michael@0: michael@0: bool ToString(JSContext* cx, JSObject* obj, const CallArgs& args, michael@0: bool isUnsigned); michael@0: michael@0: bool ToSource(JSContext* cx, JSObject* obj, const CallArgs& args, michael@0: bool isUnsigned); michael@0: michael@0: static void Finalize(JSFreeOp *fop, JSObject* obj); michael@0: } michael@0: michael@0: namespace Int64 { michael@0: static bool Construct(JSContext* cx, unsigned argc, jsval* vp); michael@0: michael@0: static bool ToString(JSContext* cx, unsigned argc, jsval* vp); michael@0: static bool ToSource(JSContext* cx, unsigned argc, jsval* vp); michael@0: michael@0: static bool Compare(JSContext* cx, unsigned argc, jsval* vp); michael@0: static bool Lo(JSContext* cx, unsigned argc, jsval* vp); michael@0: static bool Hi(JSContext* cx, unsigned argc, jsval* vp); michael@0: static bool Join(JSContext* cx, unsigned argc, jsval* vp); michael@0: } michael@0: michael@0: namespace UInt64 { michael@0: static bool Construct(JSContext* cx, unsigned argc, jsval* vp); michael@0: michael@0: static bool ToString(JSContext* cx, unsigned argc, jsval* vp); michael@0: static bool ToSource(JSContext* cx, unsigned argc, jsval* vp); michael@0: michael@0: static bool Compare(JSContext* cx, unsigned argc, jsval* vp); michael@0: static bool Lo(JSContext* cx, unsigned argc, jsval* vp); michael@0: static bool Hi(JSContext* cx, unsigned argc, jsval* vp); michael@0: static bool Join(JSContext* cx, unsigned argc, jsval* vp); michael@0: } michael@0: michael@0: /******************************************************************************* michael@0: ** JSClass definitions and initialization functions michael@0: *******************************************************************************/ michael@0: michael@0: // Class representing the 'ctypes' object itself. This exists to contain the michael@0: // JSCTypesCallbacks set of function pointers. michael@0: static const JSClass sCTypesGlobalClass = { michael@0: "ctypes", michael@0: JSCLASS_HAS_RESERVED_SLOTS(CTYPESGLOBAL_SLOTS), michael@0: JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, michael@0: JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub michael@0: }; michael@0: michael@0: static const JSClass sCABIClass = { michael@0: "CABI", michael@0: JSCLASS_HAS_RESERVED_SLOTS(CABI_SLOTS), michael@0: JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, michael@0: JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub michael@0: }; michael@0: michael@0: // Class representing ctypes.{C,Pointer,Array,Struct,Function}Type.prototype. michael@0: // This exists to give said prototypes a class of "CType", and to provide michael@0: // reserved slots for stashing various other prototype objects. michael@0: static const JSClass sCTypeProtoClass = { michael@0: "CType", michael@0: JSCLASS_HAS_RESERVED_SLOTS(CTYPEPROTO_SLOTS), michael@0: JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, michael@0: JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, nullptr, michael@0: ConstructAbstract, nullptr, ConstructAbstract michael@0: }; michael@0: michael@0: // Class representing ctypes.CData.prototype and the 'prototype' properties michael@0: // of CTypes. This exists to give said prototypes a class of "CData". michael@0: static const JSClass sCDataProtoClass = { michael@0: "CData", michael@0: 0, michael@0: JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, michael@0: JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub michael@0: }; michael@0: michael@0: static const JSClass sCTypeClass = { michael@0: "CType", michael@0: JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS), michael@0: JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, michael@0: JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CType::Finalize, michael@0: CType::ConstructData, CType::HasInstance, CType::ConstructData, michael@0: CType::Trace michael@0: }; michael@0: michael@0: static const JSClass sCDataClass = { michael@0: "CData", michael@0: JSCLASS_HAS_RESERVED_SLOTS(CDATA_SLOTS), michael@0: JS_PropertyStub, JS_DeletePropertyStub, ArrayType::Getter, ArrayType::Setter, michael@0: JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CData::Finalize, michael@0: FunctionType::Call, nullptr, FunctionType::Call michael@0: }; michael@0: michael@0: static const JSClass sCClosureClass = { michael@0: "CClosure", michael@0: JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS), michael@0: JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, michael@0: JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CClosure::Finalize, michael@0: nullptr, nullptr, nullptr, CClosure::Trace michael@0: }; michael@0: michael@0: /* michael@0: * Class representing the prototype of CDataFinalizer. michael@0: */ michael@0: static const JSClass sCDataFinalizerProtoClass = { michael@0: "CDataFinalizer", michael@0: 0, michael@0: JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, michael@0: JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub michael@0: }; michael@0: michael@0: /* michael@0: * Class representing instances of CDataFinalizer. michael@0: * michael@0: * Instances of CDataFinalizer have both private data (with type michael@0: * |CDataFinalizer::Private|) and slots (see |CDataFinalizerSlots|). michael@0: */ michael@0: static const JSClass sCDataFinalizerClass = { michael@0: "CDataFinalizer", michael@0: JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(CDATAFINALIZER_SLOTS), michael@0: JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, michael@0: JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CDataFinalizer::Finalize, michael@0: }; michael@0: michael@0: michael@0: #define CTYPESFN_FLAGS \ michael@0: (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT) michael@0: michael@0: #define CTYPESCTOR_FLAGS \ michael@0: (CTYPESFN_FLAGS | JSFUN_CONSTRUCTOR) michael@0: michael@0: #define CTYPESACC_FLAGS \ michael@0: (JSPROP_ENUMERATE | JSPROP_PERMANENT) michael@0: michael@0: #define CABIFN_FLAGS \ michael@0: (JSPROP_READONLY | JSPROP_PERMANENT) michael@0: michael@0: #define CDATAFN_FLAGS \ michael@0: (JSPROP_READONLY | JSPROP_PERMANENT) michael@0: michael@0: #define CDATAFINALIZERFN_FLAGS \ michael@0: (JSPROP_READONLY | JSPROP_PERMANENT) michael@0: michael@0: static const JSPropertySpec sCTypeProps[] = { michael@0: JS_PSG("name", michael@0: (Property::Fun), michael@0: CTYPESACC_FLAGS), michael@0: JS_PSG("size", michael@0: (Property::Fun), michael@0: CTYPESACC_FLAGS), michael@0: JS_PSG("ptr", michael@0: (Property::Fun), michael@0: CTYPESACC_FLAGS), michael@0: JS_PSG("prototype", michael@0: (Property::Fun), michael@0: CTYPESACC_FLAGS), michael@0: JS_PS_END michael@0: }; michael@0: michael@0: static const JSFunctionSpec sCTypeFunctions[] = { michael@0: JS_FN("array", CType::CreateArray, 0, CTYPESFN_FLAGS), michael@0: JS_FN("toString", CType::ToString, 0, CTYPESFN_FLAGS), michael@0: JS_FN("toSource", CType::ToSource, 0, CTYPESFN_FLAGS), michael@0: JS_FS_END michael@0: }; michael@0: michael@0: static const JSFunctionSpec sCABIFunctions[] = { michael@0: JS_FN("toSource", ABI::ToSource, 0, CABIFN_FLAGS), michael@0: JS_FN("toString", ABI::ToSource, 0, CABIFN_FLAGS), michael@0: JS_FS_END michael@0: }; michael@0: michael@0: static const JSPropertySpec sCDataProps[] = { michael@0: JS_PSGS("value", michael@0: (Property::Fun), michael@0: (Property::Fun), michael@0: JSPROP_PERMANENT), michael@0: JS_PS_END michael@0: }; michael@0: michael@0: static const JSFunctionSpec sCDataFunctions[] = { michael@0: JS_FN("address", CData::Address, 0, CDATAFN_FLAGS), michael@0: JS_FN("readString", CData::ReadString, 0, CDATAFN_FLAGS), michael@0: JS_FN("readStringReplaceMalformed", CData::ReadStringReplaceMalformed, 0, CDATAFN_FLAGS), michael@0: JS_FN("toSource", CData::ToSource, 0, CDATAFN_FLAGS), michael@0: JS_FN("toString", CData::ToSource, 0, CDATAFN_FLAGS), michael@0: JS_FS_END michael@0: }; michael@0: michael@0: static const JSFunctionSpec sCDataFinalizerFunctions[] = { michael@0: JS_FN("dispose", CDataFinalizer::Methods::Dispose, 0, CDATAFINALIZERFN_FLAGS), michael@0: JS_FN("forget", CDataFinalizer::Methods::Forget, 0, CDATAFINALIZERFN_FLAGS), michael@0: JS_FN("readString",CData::ReadString, 0, CDATAFINALIZERFN_FLAGS), michael@0: JS_FN("toString", CDataFinalizer::Methods::ToString, 0, CDATAFINALIZERFN_FLAGS), michael@0: JS_FN("toSource", CDataFinalizer::Methods::ToSource, 0, CDATAFINALIZERFN_FLAGS), michael@0: JS_FS_END michael@0: }; michael@0: michael@0: static const JSFunctionSpec sPointerFunction = michael@0: JS_FN("PointerType", PointerType::Create, 1, CTYPESCTOR_FLAGS); michael@0: michael@0: static const JSPropertySpec sPointerProps[] = { michael@0: JS_PSG("targetType", michael@0: (Property::Fun), michael@0: CTYPESACC_FLAGS), michael@0: JS_PS_END michael@0: }; michael@0: michael@0: static const JSFunctionSpec sPointerInstanceFunctions[] = { michael@0: JS_FN("isNull", PointerType::IsNull, 0, CTYPESFN_FLAGS), michael@0: JS_FN("increment", PointerType::Increment, 0, CTYPESFN_FLAGS), michael@0: JS_FN("decrement", PointerType::Decrement, 0, CTYPESFN_FLAGS), michael@0: JS_FS_END michael@0: }; michael@0: michael@0: static const JSPropertySpec sPointerInstanceProps[] = { michael@0: JS_PSGS("contents", michael@0: (Property::Fun), michael@0: (Property::Fun), michael@0: JSPROP_PERMANENT), michael@0: JS_PS_END michael@0: }; michael@0: michael@0: static const JSFunctionSpec sArrayFunction = michael@0: JS_FN("ArrayType", ArrayType::Create, 1, CTYPESCTOR_FLAGS); michael@0: michael@0: static const JSPropertySpec sArrayProps[] = { michael@0: JS_PSG("elementType", michael@0: (Property::Fun), michael@0: CTYPESACC_FLAGS), michael@0: JS_PSG("length", michael@0: (Property::Fun), michael@0: CTYPESACC_FLAGS), michael@0: JS_PS_END michael@0: }; michael@0: michael@0: static const JSFunctionSpec sArrayInstanceFunctions[] = { michael@0: JS_FN("addressOfElement", ArrayType::AddressOfElement, 1, CDATAFN_FLAGS), michael@0: JS_FS_END michael@0: }; michael@0: michael@0: static const JSPropertySpec sArrayInstanceProps[] = { michael@0: JS_PSG("length", michael@0: (Property::Fun), michael@0: JSPROP_PERMANENT), michael@0: JS_PS_END michael@0: }; michael@0: michael@0: static const JSFunctionSpec sStructFunction = michael@0: JS_FN("StructType", StructType::Create, 2, CTYPESCTOR_FLAGS); michael@0: michael@0: static const JSPropertySpec sStructProps[] = { michael@0: JS_PSG("fields", michael@0: (Property::Fun), michael@0: CTYPESACC_FLAGS), michael@0: JS_PS_END michael@0: }; michael@0: michael@0: static const JSFunctionSpec sStructFunctions[] = { michael@0: JS_FN("define", StructType::Define, 1, CDATAFN_FLAGS), michael@0: JS_FS_END michael@0: }; michael@0: michael@0: static const JSFunctionSpec sStructInstanceFunctions[] = { michael@0: JS_FN("addressOfField", StructType::AddressOfField, 1, CDATAFN_FLAGS), michael@0: JS_FS_END michael@0: }; michael@0: michael@0: static const JSFunctionSpec sFunctionFunction = michael@0: JS_FN("FunctionType", FunctionType::Create, 2, CTYPESCTOR_FLAGS); michael@0: michael@0: static const JSPropertySpec sFunctionProps[] = { michael@0: JS_PSG("argTypes", michael@0: (Property::Fun), michael@0: CTYPESACC_FLAGS), michael@0: JS_PSG("returnType", michael@0: (Property::Fun), michael@0: CTYPESACC_FLAGS), michael@0: JS_PSG("abi", michael@0: (Property::Fun), michael@0: CTYPESACC_FLAGS), michael@0: JS_PSG("isVariadic", michael@0: (Property::Fun), michael@0: CTYPESACC_FLAGS), michael@0: JS_PS_END michael@0: }; michael@0: michael@0: static const JSFunctionSpec sFunctionInstanceFunctions[] = { michael@0: JS_FN("call", js_fun_call, 1, CDATAFN_FLAGS), michael@0: JS_FN("apply", js_fun_apply, 2, CDATAFN_FLAGS), michael@0: JS_FS_END michael@0: }; michael@0: michael@0: static const JSClass sInt64ProtoClass = { michael@0: "Int64", michael@0: 0, michael@0: JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, michael@0: JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub michael@0: }; michael@0: michael@0: static const JSClass sUInt64ProtoClass = { michael@0: "UInt64", michael@0: 0, michael@0: JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, michael@0: JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub michael@0: }; michael@0: michael@0: static const JSClass sInt64Class = { michael@0: "Int64", michael@0: JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS), michael@0: JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, michael@0: JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Int64Base::Finalize michael@0: }; michael@0: michael@0: static const JSClass sUInt64Class = { michael@0: "UInt64", michael@0: JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS), michael@0: JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, michael@0: JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Int64Base::Finalize michael@0: }; michael@0: michael@0: static const JSFunctionSpec sInt64StaticFunctions[] = { michael@0: JS_FN("compare", Int64::Compare, 2, CTYPESFN_FLAGS), michael@0: JS_FN("lo", Int64::Lo, 1, CTYPESFN_FLAGS), michael@0: JS_FN("hi", Int64::Hi, 1, CTYPESFN_FLAGS), michael@0: JS_FN("join", Int64::Join, 2, CTYPESFN_FLAGS), michael@0: JS_FS_END michael@0: }; michael@0: michael@0: static const JSFunctionSpec sUInt64StaticFunctions[] = { michael@0: JS_FN("compare", UInt64::Compare, 2, CTYPESFN_FLAGS), michael@0: JS_FN("lo", UInt64::Lo, 1, CTYPESFN_FLAGS), michael@0: JS_FN("hi", UInt64::Hi, 1, CTYPESFN_FLAGS), michael@0: JS_FN("join", UInt64::Join, 2, CTYPESFN_FLAGS), michael@0: JS_FS_END michael@0: }; michael@0: michael@0: static const JSFunctionSpec sInt64Functions[] = { michael@0: JS_FN("toString", Int64::ToString, 0, CTYPESFN_FLAGS), michael@0: JS_FN("toSource", Int64::ToSource, 0, CTYPESFN_FLAGS), michael@0: JS_FS_END michael@0: }; michael@0: michael@0: static const JSFunctionSpec sUInt64Functions[] = { michael@0: JS_FN("toString", UInt64::ToString, 0, CTYPESFN_FLAGS), michael@0: JS_FN("toSource", UInt64::ToSource, 0, CTYPESFN_FLAGS), michael@0: JS_FS_END michael@0: }; michael@0: michael@0: static const JSPropertySpec sModuleProps[] = { michael@0: JS_PSG("errno", michael@0: (Property::Fun), michael@0: JSPROP_PERMANENT), michael@0: #if defined(XP_WIN) michael@0: JS_PSG("winLastError", michael@0: (Property::Fun), michael@0: JSPROP_PERMANENT), michael@0: #endif // defined(XP_WIN) michael@0: JS_PS_END michael@0: }; michael@0: michael@0: static const JSFunctionSpec sModuleFunctions[] = { michael@0: JS_FN("CDataFinalizer", CDataFinalizer::Construct, 2, CTYPESFN_FLAGS), michael@0: JS_FN("open", Library::Open, 1, CTYPESFN_FLAGS), michael@0: JS_FN("cast", CData::Cast, 2, CTYPESFN_FLAGS), michael@0: JS_FN("getRuntime", CData::GetRuntime, 1, CTYPESFN_FLAGS), michael@0: JS_FN("libraryName", Library::Name, 1, CTYPESFN_FLAGS), michael@0: JS_FS_END michael@0: }; michael@0: michael@0: static MOZ_ALWAYS_INLINE JSString* michael@0: NewUCString(JSContext* cx, const AutoString& from) michael@0: { michael@0: return JS_NewUCStringCopyN(cx, from.begin(), from.length()); michael@0: } michael@0: michael@0: /* michael@0: * Return a size rounded up to a multiple of a power of two. michael@0: * michael@0: * Note: |align| must be a power of 2. michael@0: */ michael@0: static MOZ_ALWAYS_INLINE size_t michael@0: Align(size_t val, size_t align) michael@0: { michael@0: // Ensure that align is a power of two. michael@0: MOZ_ASSERT(align != 0 && (align & (align - 1)) == 0); michael@0: return ((val - 1) | (align - 1)) + 1; michael@0: } michael@0: michael@0: static ABICode michael@0: GetABICode(JSObject* obj) michael@0: { michael@0: // make sure we have an object representing a CABI class, michael@0: // and extract the enumerated class type from the reserved slot. michael@0: if (JS_GetClass(obj) != &sCABIClass) michael@0: return INVALID_ABI; michael@0: michael@0: jsval result = JS_GetReservedSlot(obj, SLOT_ABICODE); michael@0: return ABICode(JSVAL_TO_INT(result)); michael@0: } michael@0: michael@0: static const JSErrorFormatString ErrorFormatString[CTYPESERR_LIMIT] = { michael@0: #define MSG_DEF(name, number, count, exception, format) \ michael@0: { format, count, exception } , michael@0: #include "ctypes/ctypes.msg" michael@0: #undef MSG_DEF michael@0: }; michael@0: michael@0: static const JSErrorFormatString* michael@0: GetErrorMessage(void* userRef, const char* locale, const unsigned errorNumber) michael@0: { michael@0: if (0 < errorNumber && errorNumber < CTYPESERR_LIMIT) michael@0: return &ErrorFormatString[errorNumber]; michael@0: return nullptr; michael@0: } michael@0: michael@0: static bool michael@0: TypeError(JSContext* cx, const char* expected, HandleValue actual) michael@0: { michael@0: JSString* str = JS_ValueToSource(cx, actual); michael@0: JSAutoByteString bytes; michael@0: michael@0: const char* src; michael@0: if (str) { michael@0: src = bytes.encodeLatin1(cx, str); michael@0: if (!src) michael@0: return false; michael@0: } else { michael@0: JS_ClearPendingException(cx); michael@0: src = "<>"; michael@0: } michael@0: JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, michael@0: CTYPESMSG_TYPE_ERROR, expected, src); michael@0: return false; michael@0: } michael@0: michael@0: static JSObject* michael@0: InitCTypeClass(JSContext* cx, HandleObject parent) michael@0: { michael@0: JSFunction *fun = JS_DefineFunction(cx, parent, "CType", ConstructAbstract, 0, michael@0: CTYPESCTOR_FLAGS); michael@0: if (!fun) michael@0: return nullptr; michael@0: michael@0: RootedObject ctor(cx, JS_GetFunctionObject(fun)); michael@0: RootedObject fnproto(cx); michael@0: if (!JS_GetPrototype(cx, ctor, &fnproto)) michael@0: return nullptr; michael@0: JS_ASSERT(ctor); michael@0: JS_ASSERT(fnproto); michael@0: michael@0: // Set up ctypes.CType.prototype. michael@0: RootedObject prototype(cx, JS_NewObject(cx, &sCTypeProtoClass, fnproto, parent)); michael@0: if (!prototype) michael@0: return nullptr; michael@0: michael@0: if (!JS_DefineProperty(cx, ctor, "prototype", prototype, michael@0: JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: return nullptr; michael@0: michael@0: if (!JS_DefineProperty(cx, prototype, "constructor", ctor, michael@0: JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: return nullptr; michael@0: michael@0: // Define properties and functions common to all CTypes. michael@0: if (!JS_DefineProperties(cx, prototype, sCTypeProps) || michael@0: !JS_DefineFunctions(cx, prototype, sCTypeFunctions)) michael@0: return nullptr; michael@0: michael@0: if (!JS_FreezeObject(cx, ctor) || !JS_FreezeObject(cx, prototype)) michael@0: return nullptr; michael@0: michael@0: return prototype; michael@0: } michael@0: michael@0: static JSObject* michael@0: InitABIClass(JSContext* cx, JSObject* parent) michael@0: { michael@0: RootedObject obj(cx, JS_NewObject(cx, nullptr, NullPtr(), NullPtr())); michael@0: michael@0: if (!obj) michael@0: return nullptr; michael@0: michael@0: if (!JS_DefineFunctions(cx, obj, sCABIFunctions)) michael@0: return nullptr; michael@0: michael@0: return obj; michael@0: } michael@0: michael@0: michael@0: static JSObject* michael@0: InitCDataClass(JSContext* cx, HandleObject parent, HandleObject CTypeProto) michael@0: { michael@0: JSFunction* fun = JS_DefineFunction(cx, parent, "CData", ConstructAbstract, 0, michael@0: CTYPESCTOR_FLAGS); michael@0: if (!fun) michael@0: return nullptr; michael@0: michael@0: RootedObject ctor(cx, JS_GetFunctionObject(fun)); michael@0: JS_ASSERT(ctor); michael@0: michael@0: // Set up ctypes.CData.__proto__ === ctypes.CType.prototype. michael@0: // (Note that 'ctypes.CData instanceof Function' is still true, thanks to the michael@0: // prototype chain.) michael@0: if (!JS_SetPrototype(cx, ctor, CTypeProto)) michael@0: return nullptr; michael@0: michael@0: // Set up ctypes.CData.prototype. michael@0: RootedObject prototype(cx, JS_NewObject(cx, &sCDataProtoClass, NullPtr(), parent)); michael@0: if (!prototype) michael@0: return nullptr; michael@0: michael@0: if (!JS_DefineProperty(cx, ctor, "prototype", prototype, michael@0: JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: return nullptr; michael@0: michael@0: if (!JS_DefineProperty(cx, prototype, "constructor", ctor, michael@0: JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: return nullptr; michael@0: michael@0: // Define properties and functions common to all CDatas. michael@0: if (!JS_DefineProperties(cx, prototype, sCDataProps) || michael@0: !JS_DefineFunctions(cx, prototype, sCDataFunctions)) michael@0: return nullptr; michael@0: michael@0: if (//!JS_FreezeObject(cx, prototype) || // XXX fixme - see bug 541212! michael@0: !JS_FreezeObject(cx, ctor)) michael@0: return nullptr; michael@0: michael@0: return prototype; michael@0: } michael@0: michael@0: static bool michael@0: DefineABIConstant(JSContext* cx, michael@0: HandleObject parent, michael@0: const char* name, michael@0: ABICode code, michael@0: HandleObject prototype) michael@0: { michael@0: RootedObject obj(cx, JS_DefineObject(cx, parent, name, &sCABIClass, prototype, michael@0: JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)); michael@0: if (!obj) michael@0: return false; michael@0: JS_SetReservedSlot(obj, SLOT_ABICODE, INT_TO_JSVAL(code)); michael@0: return JS_FreezeObject(cx, obj); michael@0: } michael@0: michael@0: // Set up a single type constructor for michael@0: // ctypes.{Pointer,Array,Struct,Function}Type. michael@0: static bool michael@0: InitTypeConstructor(JSContext* cx, michael@0: HandleObject parent, michael@0: HandleObject CTypeProto, michael@0: HandleObject CDataProto, michael@0: const JSFunctionSpec spec, michael@0: const JSFunctionSpec* fns, michael@0: const JSPropertySpec* props, michael@0: const JSFunctionSpec* instanceFns, michael@0: const JSPropertySpec* instanceProps, michael@0: MutableHandleObject typeProto, michael@0: MutableHandleObject dataProto) michael@0: { michael@0: JSFunction* fun = js::DefineFunctionWithReserved(cx, parent, spec.name, spec.call.op, michael@0: spec.nargs, spec.flags); michael@0: if (!fun) michael@0: return false; michael@0: michael@0: RootedObject obj(cx, JS_GetFunctionObject(fun)); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: // Set up the .prototype and .prototype.constructor properties. michael@0: typeProto.set(JS_NewObject(cx, &sCTypeProtoClass, CTypeProto, parent)); michael@0: if (!typeProto) michael@0: return false; michael@0: michael@0: // Define property before proceeding, for GC safety. michael@0: if (!JS_DefineProperty(cx, obj, "prototype", typeProto, michael@0: JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: return false; michael@0: michael@0: if (fns && !JS_DefineFunctions(cx, typeProto, fns)) michael@0: return false; michael@0: michael@0: if (!JS_DefineProperties(cx, typeProto, props)) michael@0: return false; michael@0: michael@0: if (!JS_DefineProperty(cx, typeProto, "constructor", obj, michael@0: JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: return false; michael@0: michael@0: // Stash ctypes.{Pointer,Array,Struct}Type.prototype on a reserved slot of michael@0: // the type constructor, for faster lookup. michael@0: js::SetFunctionNativeReserved(obj, SLOT_FN_CTORPROTO, OBJECT_TO_JSVAL(typeProto)); michael@0: michael@0: // Create an object to serve as the common ancestor for all CData objects michael@0: // created from the given type constructor. This has ctypes.CData.prototype michael@0: // as its prototype, such that it inherits the properties and functions michael@0: // common to all CDatas. michael@0: dataProto.set(JS_NewObject(cx, &sCDataProtoClass, CDataProto, parent)); michael@0: if (!dataProto) michael@0: return false; michael@0: michael@0: // Define functions and properties on the 'dataProto' object that are common michael@0: // to all CData objects created from this type constructor. (These will michael@0: // become functions and properties on CData objects created from this type.) michael@0: if (instanceFns && !JS_DefineFunctions(cx, dataProto, instanceFns)) michael@0: return false; michael@0: michael@0: if (instanceProps && !JS_DefineProperties(cx, dataProto, instanceProps)) michael@0: return false; michael@0: michael@0: // Link the type prototype to the data prototype. michael@0: JS_SetReservedSlot(typeProto, SLOT_OURDATAPROTO, OBJECT_TO_JSVAL(dataProto)); michael@0: michael@0: if (!JS_FreezeObject(cx, obj) || michael@0: //!JS_FreezeObject(cx, dataProto) || // XXX fixme - see bug 541212! michael@0: !JS_FreezeObject(cx, typeProto)) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static JSObject* michael@0: InitInt64Class(JSContext* cx, michael@0: HandleObject parent, michael@0: const JSClass* clasp, michael@0: JSNative construct, michael@0: const JSFunctionSpec* fs, michael@0: const JSFunctionSpec* static_fs) michael@0: { michael@0: // Init type class and constructor michael@0: RootedObject prototype(cx, JS_InitClass(cx, parent, js::NullPtr(), clasp, construct, michael@0: 0, nullptr, fs, nullptr, static_fs)); michael@0: if (!prototype) michael@0: return nullptr; michael@0: michael@0: RootedObject ctor(cx, JS_GetConstructor(cx, prototype)); michael@0: if (!ctor) michael@0: return nullptr; michael@0: if (!JS_FreezeObject(cx, ctor)) michael@0: return nullptr; michael@0: michael@0: // Redefine the 'join' function as an extended native and stash michael@0: // ctypes.{Int64,UInt64}.prototype in a reserved slot of the new function. michael@0: JS_ASSERT(clasp == &sInt64ProtoClass || clasp == &sUInt64ProtoClass); michael@0: JSNative native = (clasp == &sInt64ProtoClass) ? Int64::Join : UInt64::Join; michael@0: JSFunction* fun = js::DefineFunctionWithReserved(cx, ctor, "join", native, michael@0: 2, CTYPESFN_FLAGS); michael@0: if (!fun) michael@0: return nullptr; michael@0: michael@0: js::SetFunctionNativeReserved(fun, SLOT_FN_INT64PROTO, michael@0: OBJECT_TO_JSVAL(prototype)); michael@0: michael@0: if (!JS_FreezeObject(cx, prototype)) michael@0: return nullptr; michael@0: michael@0: return prototype; michael@0: } michael@0: michael@0: static void michael@0: AttachProtos(JSObject* proto, const AutoObjectVector& protos) michael@0: { michael@0: // For a given 'proto' of [[Class]] "CTypeProto", attach each of the 'protos' michael@0: // to the appropriate CTypeProtoSlot. (SLOT_CTYPES is the last slot michael@0: // of [[Class]] "CTypeProto" that we fill in this automated manner.) michael@0: for (uint32_t i = 0; i <= SLOT_CTYPES; ++i) michael@0: JS_SetReservedSlot(proto, i, OBJECT_TO_JSVAL(protos[i])); michael@0: } michael@0: michael@0: static bool michael@0: InitTypeClasses(JSContext* cx, HandleObject parent) michael@0: { michael@0: // Initialize the ctypes.CType class. This acts as an abstract base class for michael@0: // the various types, and provides the common API functions. It has: michael@0: // * [[Class]] "Function" michael@0: // * __proto__ === Function.prototype michael@0: // * A constructor that throws a TypeError. (You can't construct an michael@0: // abstract type!) michael@0: // * 'prototype' property: michael@0: // * [[Class]] "CTypeProto" michael@0: // * __proto__ === Function.prototype michael@0: // * A constructor that throws a TypeError. (You can't construct an michael@0: // abstract type instance!) michael@0: // * 'constructor' property === ctypes.CType michael@0: // * Provides properties and functions common to all CTypes. michael@0: RootedObject CTypeProto(cx, InitCTypeClass(cx, parent)); michael@0: if (!CTypeProto) michael@0: return false; michael@0: michael@0: // Initialize the ctypes.CData class. This acts as an abstract base class for michael@0: // instances of the various types, and provides the common API functions. michael@0: // It has: michael@0: // * [[Class]] "Function" michael@0: // * __proto__ === Function.prototype michael@0: // * A constructor that throws a TypeError. (You can't construct an michael@0: // abstract type instance!) michael@0: // * 'prototype' property: michael@0: // * [[Class]] "CDataProto" michael@0: // * 'constructor' property === ctypes.CData michael@0: // * Provides properties and functions common to all CDatas. michael@0: RootedObject CDataProto(cx, InitCDataClass(cx, parent, CTypeProto)); michael@0: if (!CDataProto) michael@0: return false; michael@0: michael@0: // Link CTypeProto to CDataProto. michael@0: JS_SetReservedSlot(CTypeProto, SLOT_OURDATAPROTO, OBJECT_TO_JSVAL(CDataProto)); michael@0: michael@0: // Create and attach the special class constructors: ctypes.PointerType, michael@0: // ctypes.ArrayType, ctypes.StructType, and ctypes.FunctionType. michael@0: // Each of these constructors 'c' has, respectively: michael@0: // * [[Class]] "Function" michael@0: // * __proto__ === Function.prototype michael@0: // * A constructor that creates a user-defined type. michael@0: // * 'prototype' property: michael@0: // * [[Class]] "CTypeProto" michael@0: // * __proto__ === ctypes.CType.prototype michael@0: // * 'constructor' property === 'c' michael@0: // We also construct an object 'p' to serve, given a type object 't' michael@0: // constructed from one of these type constructors, as michael@0: // 't.prototype.__proto__'. This object has: michael@0: // * [[Class]] "CDataProto" michael@0: // * __proto__ === ctypes.CData.prototype michael@0: // * Properties and functions common to all CDatas. michael@0: // Therefore an instance 't' of ctypes.{Pointer,Array,Struct,Function}Type michael@0: // will have, resp.: michael@0: // * [[Class]] "CType" michael@0: // * __proto__ === ctypes.{Pointer,Array,Struct,Function}Type.prototype michael@0: // * A constructor which creates and returns a CData object, containing michael@0: // binary data of the given type. michael@0: // * 'prototype' property: michael@0: // * [[Class]] "CDataProto" michael@0: // * __proto__ === 'p', the prototype object from above michael@0: // * 'constructor' property === 't' michael@0: AutoObjectVector protos(cx); michael@0: protos.resize(CTYPEPROTO_SLOTS); michael@0: if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto, michael@0: sPointerFunction, nullptr, sPointerProps, michael@0: sPointerInstanceFunctions, sPointerInstanceProps, michael@0: protos.handleAt(SLOT_POINTERPROTO), protos.handleAt(SLOT_POINTERDATAPROTO))) michael@0: return false; michael@0: michael@0: if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto, michael@0: sArrayFunction, nullptr, sArrayProps, michael@0: sArrayInstanceFunctions, sArrayInstanceProps, michael@0: protos.handleAt(SLOT_ARRAYPROTO), protos.handleAt(SLOT_ARRAYDATAPROTO))) michael@0: return false; michael@0: michael@0: if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto, michael@0: sStructFunction, sStructFunctions, sStructProps, michael@0: sStructInstanceFunctions, nullptr, michael@0: protos.handleAt(SLOT_STRUCTPROTO), protos.handleAt(SLOT_STRUCTDATAPROTO))) michael@0: return false; michael@0: michael@0: if (!InitTypeConstructor(cx, parent, CTypeProto, protos.handleAt(SLOT_POINTERDATAPROTO), michael@0: sFunctionFunction, nullptr, sFunctionProps, sFunctionInstanceFunctions, nullptr, michael@0: protos.handleAt(SLOT_FUNCTIONPROTO), protos.handleAt(SLOT_FUNCTIONDATAPROTO))) michael@0: return false; michael@0: michael@0: protos[SLOT_CDATAPROTO] = CDataProto; michael@0: michael@0: // Create and attach the ctypes.{Int64,UInt64} constructors. michael@0: // Each of these has, respectively: michael@0: // * [[Class]] "Function" michael@0: // * __proto__ === Function.prototype michael@0: // * A constructor that creates a ctypes.{Int64,UInt64} object, respectively. michael@0: // * 'prototype' property: michael@0: // * [[Class]] {"Int64Proto","UInt64Proto"} michael@0: // * 'constructor' property === ctypes.{Int64,UInt64} michael@0: protos[SLOT_INT64PROTO] = InitInt64Class(cx, parent, &sInt64ProtoClass, michael@0: Int64::Construct, sInt64Functions, sInt64StaticFunctions); michael@0: if (!protos[SLOT_INT64PROTO]) michael@0: return false; michael@0: protos[SLOT_UINT64PROTO] = InitInt64Class(cx, parent, &sUInt64ProtoClass, michael@0: UInt64::Construct, sUInt64Functions, sUInt64StaticFunctions); michael@0: if (!protos[SLOT_UINT64PROTO]) michael@0: return false; michael@0: michael@0: // Finally, store a pointer to the global ctypes object. michael@0: // Note that there is no other reliable manner of locating this object. michael@0: protos[SLOT_CTYPES] = parent; michael@0: michael@0: // Attach the prototypes just created to each of ctypes.CType.prototype, michael@0: // and the special type constructors, so we can access them when constructing michael@0: // instances of those types. michael@0: AttachProtos(CTypeProto, protos); michael@0: AttachProtos(protos[SLOT_POINTERPROTO], protos); michael@0: AttachProtos(protos[SLOT_ARRAYPROTO], protos); michael@0: AttachProtos(protos[SLOT_STRUCTPROTO], protos); michael@0: AttachProtos(protos[SLOT_FUNCTIONPROTO], protos); michael@0: michael@0: RootedObject ABIProto(cx, InitABIClass(cx, parent)); michael@0: if (!ABIProto) michael@0: return false; michael@0: michael@0: // Attach objects representing ABI constants. michael@0: if (!DefineABIConstant(cx, parent, "default_abi", ABI_DEFAULT, ABIProto) || michael@0: !DefineABIConstant(cx, parent, "stdcall_abi", ABI_STDCALL, ABIProto) || michael@0: !DefineABIConstant(cx, parent, "winapi_abi", ABI_WINAPI, ABIProto)) michael@0: return false; michael@0: michael@0: // Create objects representing the builtin types, and attach them to the michael@0: // ctypes object. Each type object 't' has: michael@0: // * [[Class]] "CType" michael@0: // * __proto__ === ctypes.CType.prototype michael@0: // * A constructor which creates and returns a CData object, containing michael@0: // binary data of the given type. michael@0: // * 'prototype' property: michael@0: // * [[Class]] "CDataProto" michael@0: // * __proto__ === ctypes.CData.prototype michael@0: // * 'constructor' property === 't' michael@0: #define DEFINE_TYPE(name, type, ffiType) \ michael@0: RootedObject typeObj_##name(cx, \ michael@0: CType::DefineBuiltin(cx, parent, #name, CTypeProto, CDataProto, #name, \ michael@0: TYPE_##name, INT_TO_JSVAL(sizeof(type)), \ michael@0: INT_TO_JSVAL(ffiType.alignment), &ffiType)); \ michael@0: if (!typeObj_##name) \ michael@0: return false; michael@0: #include "ctypes/typedefs.h" michael@0: michael@0: // Alias 'ctypes.unsigned' as 'ctypes.unsigned_int', since they represent michael@0: // the same type in C. michael@0: if (!JS_DefineProperty(cx, parent, "unsigned", typeObj_unsigned_int, michael@0: JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: return false; michael@0: michael@0: // Create objects representing the special types void_t and voidptr_t. michael@0: RootedObject typeObj(cx, michael@0: CType::DefineBuiltin(cx, parent, "void_t", CTypeProto, CDataProto, "void", michael@0: TYPE_void_t, JSVAL_VOID, JSVAL_VOID, &ffi_type_void)); michael@0: if (!typeObj) michael@0: return false; michael@0: michael@0: typeObj = PointerType::CreateInternal(cx, typeObj); michael@0: if (!typeObj) michael@0: return false; michael@0: if (!JS_DefineProperty(cx, parent, "voidptr_t", typeObj, michael@0: JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: IsCTypesGlobal(JSObject* obj) michael@0: { michael@0: return JS_GetClass(obj) == &sCTypesGlobalClass; michael@0: } michael@0: michael@0: bool michael@0: IsCTypesGlobal(HandleValue v) michael@0: { michael@0: return v.isObject() && IsCTypesGlobal(&v.toObject()); michael@0: } michael@0: michael@0: // Get the JSCTypesCallbacks struct from the 'ctypes' object 'obj'. michael@0: JSCTypesCallbacks* michael@0: GetCallbacks(JSObject* obj) michael@0: { michael@0: JS_ASSERT(IsCTypesGlobal(obj)); michael@0: michael@0: jsval result = JS_GetReservedSlot(obj, SLOT_CALLBACKS); michael@0: if (JSVAL_IS_VOID(result)) michael@0: return nullptr; michael@0: michael@0: return static_cast(JSVAL_TO_PRIVATE(result)); michael@0: } michael@0: michael@0: // Utility function to access a property of an object as an object michael@0: // returns false and sets the error if the property does not exist michael@0: // or is not an object michael@0: static bool GetObjectProperty(JSContext *cx, HandleObject obj, michael@0: const char *property, MutableHandleObject result) michael@0: { michael@0: RootedValue val(cx); michael@0: if (!JS_GetProperty(cx, obj, property, &val)) { michael@0: return false; michael@0: } michael@0: michael@0: if (JSVAL_IS_PRIMITIVE(val)) { michael@0: JS_ReportError(cx, "missing or non-object field"); michael@0: return false; michael@0: } michael@0: michael@0: result.set(JSVAL_TO_OBJECT(val)); michael@0: return true; michael@0: } michael@0: michael@0: } /* namespace ctypes */ michael@0: } /* namespace js */ michael@0: michael@0: using namespace js; michael@0: using namespace js::ctypes; michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_InitCTypesClass(JSContext* cx, HandleObject global) michael@0: { michael@0: // attach ctypes property to global object michael@0: RootedObject ctypes(cx, JS_NewObject(cx, &sCTypesGlobalClass, NullPtr(), NullPtr())); michael@0: if (!ctypes) michael@0: return false; michael@0: michael@0: if (!JS_DefineProperty(cx, global, "ctypes", ctypes, JSPROP_READONLY | JSPROP_PERMANENT, michael@0: JS_PropertyStub, JS_StrictPropertyStub)){ michael@0: return false; michael@0: } michael@0: michael@0: if (!InitTypeClasses(cx, ctypes)) michael@0: return false; michael@0: michael@0: // attach API functions and properties michael@0: if (!JS_DefineFunctions(cx, ctypes, sModuleFunctions) || michael@0: !JS_DefineProperties(cx, ctypes, sModuleProps)) michael@0: return false; michael@0: michael@0: // Set up ctypes.CDataFinalizer.prototype. michael@0: RootedObject ctor(cx); michael@0: if (!GetObjectProperty(cx, ctypes, "CDataFinalizer", &ctor)) michael@0: return false; michael@0: michael@0: RootedObject prototype(cx, JS_NewObject(cx, &sCDataFinalizerProtoClass, NullPtr(), ctypes)); michael@0: if (!prototype) michael@0: return false; michael@0: michael@0: if (!JS_DefineFunctions(cx, prototype, sCDataFinalizerFunctions)) michael@0: return false; michael@0: michael@0: if (!JS_DefineProperty(cx, ctor, "prototype", prototype, michael@0: JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: return false; michael@0: michael@0: if (!JS_DefineProperty(cx, prototype, "constructor", ctor, michael@0: JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: return false; michael@0: michael@0: michael@0: // Seal the ctypes object, to prevent modification. michael@0: return JS_FreezeObject(cx, ctypes); michael@0: } michael@0: michael@0: JS_PUBLIC_API(void) michael@0: JS_SetCTypesCallbacks(JSObject *ctypesObj, JSCTypesCallbacks* callbacks) michael@0: { michael@0: JS_ASSERT(callbacks); michael@0: JS_ASSERT(IsCTypesGlobal(ctypesObj)); michael@0: michael@0: // Set the callbacks on a reserved slot. michael@0: JS_SetReservedSlot(ctypesObj, SLOT_CALLBACKS, PRIVATE_TO_JSVAL(callbacks)); michael@0: } michael@0: michael@0: namespace js { michael@0: michael@0: JS_FRIEND_API(size_t) michael@0: SizeOfDataIfCDataObject(mozilla::MallocSizeOf mallocSizeOf, JSObject *obj) michael@0: { michael@0: if (!CData::IsCData(obj)) michael@0: return 0; michael@0: michael@0: size_t n = 0; michael@0: jsval slot = JS_GetReservedSlot(obj, ctypes::SLOT_OWNS); michael@0: if (!JSVAL_IS_VOID(slot)) { michael@0: bool owns = JSVAL_TO_BOOLEAN(slot); michael@0: slot = JS_GetReservedSlot(obj, ctypes::SLOT_DATA); michael@0: if (!JSVAL_IS_VOID(slot)) { michael@0: char** buffer = static_cast(JSVAL_TO_PRIVATE(slot)); michael@0: n += mallocSizeOf(buffer); michael@0: if (owns) michael@0: n += mallocSizeOf(*buffer); michael@0: } michael@0: } michael@0: return n; michael@0: } michael@0: michael@0: namespace ctypes { michael@0: michael@0: /******************************************************************************* michael@0: ** Type conversion functions michael@0: *******************************************************************************/ michael@0: michael@0: // Enforce some sanity checks on type widths and properties. michael@0: // Where the architecture is 64-bit, make sure it's LP64 or LLP64. (ctypes.int michael@0: // autoconverts to a primitive JS number; to support ILP64 architectures, it michael@0: // would need to autoconvert to an Int64 object instead. Therefore we enforce michael@0: // this invariant here.) michael@0: JS_STATIC_ASSERT(sizeof(bool) == 1 || sizeof(bool) == 4); michael@0: JS_STATIC_ASSERT(sizeof(char) == 1); michael@0: JS_STATIC_ASSERT(sizeof(short) == 2); michael@0: JS_STATIC_ASSERT(sizeof(int) == 4); michael@0: JS_STATIC_ASSERT(sizeof(unsigned) == 4); michael@0: JS_STATIC_ASSERT(sizeof(long) == 4 || sizeof(long) == 8); michael@0: JS_STATIC_ASSERT(sizeof(long long) == 8); michael@0: JS_STATIC_ASSERT(sizeof(size_t) == sizeof(uintptr_t)); michael@0: JS_STATIC_ASSERT(sizeof(float) == 4); michael@0: JS_STATIC_ASSERT(sizeof(PRFuncPtr) == sizeof(void*)); michael@0: JS_STATIC_ASSERT(NumericLimits::is_signed); michael@0: michael@0: // Templated helper to convert FromType to TargetType, for the default case michael@0: // where the trivial POD constructor will do. michael@0: template michael@0: struct ConvertImpl { michael@0: static MOZ_ALWAYS_INLINE TargetType Convert(FromType d) { michael@0: return TargetType(d); michael@0: } michael@0: }; michael@0: michael@0: #ifdef _MSC_VER michael@0: // MSVC can't perform double to unsigned __int64 conversion when the michael@0: // double is greater than 2^63 - 1. Help it along a little. michael@0: template<> michael@0: struct ConvertImpl { michael@0: static MOZ_ALWAYS_INLINE uint64_t Convert(double d) { michael@0: return d > 0x7fffffffffffffffui64 ? michael@0: uint64_t(d - 0x8000000000000000ui64) + 0x8000000000000000ui64 : michael@0: uint64_t(d); michael@0: } michael@0: }; michael@0: #endif michael@0: michael@0: // C++ doesn't guarantee that exact values are the only ones that will michael@0: // round-trip. In fact, on some platforms, including SPARC, there are pairs of michael@0: // values, a uint64_t and a double, such that neither value is exactly michael@0: // representable in the other type, but they cast to each other. michael@0: #if defined(SPARC) || defined(__powerpc__) michael@0: // Simulate x86 overflow behavior michael@0: template<> michael@0: struct ConvertImpl { michael@0: static MOZ_ALWAYS_INLINE uint64_t Convert(double d) { michael@0: return d >= 0xffffffffffffffff ? michael@0: 0x8000000000000000 : uint64_t(d); michael@0: } michael@0: }; michael@0: michael@0: template<> michael@0: struct ConvertImpl { michael@0: static MOZ_ALWAYS_INLINE int64_t Convert(double d) { michael@0: return d >= 0x7fffffffffffffff ? michael@0: 0x8000000000000000 : int64_t(d); michael@0: } michael@0: }; michael@0: #endif michael@0: michael@0: template michael@0: static MOZ_ALWAYS_INLINE TargetType Convert(FromType d) michael@0: { michael@0: return ConvertImpl::Convert(d); michael@0: } michael@0: michael@0: template michael@0: static MOZ_ALWAYS_INLINE bool IsAlwaysExact() michael@0: { michael@0: // Return 'true' if TargetType can always exactly represent FromType. michael@0: // This means that: michael@0: // 1) TargetType must be the same or more bits wide as FromType. For integers michael@0: // represented in 'n' bits, unsigned variants will have 'n' digits while michael@0: // signed will have 'n - 1'. For floating point types, 'digits' is the michael@0: // mantissa width. michael@0: // 2) If FromType is signed, TargetType must also be signed. (Floating point michael@0: // types are always signed.) michael@0: // 3) If TargetType is an exact integral type, FromType must be also. michael@0: if (NumericLimits::digits < NumericLimits::digits) michael@0: return false; michael@0: michael@0: if (NumericLimits::is_signed && michael@0: !NumericLimits::is_signed) michael@0: return false; michael@0: michael@0: if (!NumericLimits::is_exact && michael@0: NumericLimits::is_exact) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // Templated helper to determine if FromType 'i' converts losslessly to michael@0: // TargetType 'j'. Default case where both types are the same signedness. michael@0: template michael@0: struct IsExactImpl { michael@0: static MOZ_ALWAYS_INLINE bool Test(FromType i, TargetType j) { michael@0: JS_STATIC_ASSERT(NumericLimits::is_exact); michael@0: return FromType(j) == i; michael@0: } michael@0: }; michael@0: michael@0: // Specialization where TargetType is unsigned, FromType is signed. michael@0: template michael@0: struct IsExactImpl { michael@0: static MOZ_ALWAYS_INLINE bool Test(FromType i, TargetType j) { michael@0: JS_STATIC_ASSERT(NumericLimits::is_exact); michael@0: return i >= 0 && FromType(j) == i; michael@0: } michael@0: }; michael@0: michael@0: // Specialization where TargetType is signed, FromType is unsigned. michael@0: template michael@0: struct IsExactImpl { michael@0: static MOZ_ALWAYS_INLINE bool Test(FromType i, TargetType j) { michael@0: JS_STATIC_ASSERT(NumericLimits::is_exact); michael@0: return TargetType(i) >= 0 && FromType(j) == i; michael@0: } michael@0: }; michael@0: michael@0: // Convert FromType 'i' to TargetType 'result', returning true iff 'result' michael@0: // is an exact representation of 'i'. michael@0: template michael@0: static MOZ_ALWAYS_INLINE bool ConvertExact(FromType i, TargetType* result) michael@0: { michael@0: // Require that TargetType is integral, to simplify conversion. michael@0: JS_STATIC_ASSERT(NumericLimits::is_exact); michael@0: michael@0: *result = Convert(i); michael@0: michael@0: // See if we can avoid a dynamic check. michael@0: if (IsAlwaysExact()) michael@0: return true; michael@0: michael@0: // Return 'true' if 'i' is exactly representable in 'TargetType'. michael@0: return IsExactImpl::is_signed, michael@0: NumericLimits::is_signed>::Test(i, *result); michael@0: } michael@0: michael@0: // Templated helper to determine if Type 'i' is negative. Default case michael@0: // where IntegerType is unsigned. michael@0: template michael@0: struct IsNegativeImpl { michael@0: static MOZ_ALWAYS_INLINE bool Test(Type i) { michael@0: return false; michael@0: } michael@0: }; michael@0: michael@0: // Specialization where Type is signed. michael@0: template michael@0: struct IsNegativeImpl { michael@0: static MOZ_ALWAYS_INLINE bool Test(Type i) { michael@0: return i < 0; michael@0: } michael@0: }; michael@0: michael@0: // Determine whether Type 'i' is negative. michael@0: template michael@0: static MOZ_ALWAYS_INLINE bool IsNegative(Type i) michael@0: { michael@0: return IsNegativeImpl::is_signed>::Test(i); michael@0: } michael@0: michael@0: // Implicitly convert val to bool, allowing bool, int, and double michael@0: // arguments numerically equal to 0 or 1. michael@0: static bool michael@0: jsvalToBool(JSContext* cx, jsval val, bool* result) michael@0: { michael@0: if (JSVAL_IS_BOOLEAN(val)) { michael@0: *result = JSVAL_TO_BOOLEAN(val); michael@0: return true; michael@0: } michael@0: if (JSVAL_IS_INT(val)) { michael@0: int32_t i = JSVAL_TO_INT(val); michael@0: *result = i != 0; michael@0: return i == 0 || i == 1; michael@0: } michael@0: if (JSVAL_IS_DOUBLE(val)) { michael@0: double d = JSVAL_TO_DOUBLE(val); michael@0: *result = d != 0; michael@0: // Allow -0. michael@0: return d == 1 || d == 0; michael@0: } michael@0: // Don't silently convert null to bool. It's probably a mistake. michael@0: return false; michael@0: } michael@0: michael@0: // Implicitly convert val to IntegerType, allowing bool, int, double, michael@0: // Int64, UInt64, and CData integer types 't' where all values of 't' are michael@0: // representable by IntegerType. michael@0: template michael@0: static bool michael@0: jsvalToInteger(JSContext* cx, jsval val, IntegerType* result) michael@0: { michael@0: JS_STATIC_ASSERT(NumericLimits::is_exact); michael@0: michael@0: if (JSVAL_IS_INT(val)) { michael@0: // Make sure the integer fits in the alotted precision, and has the right michael@0: // sign. michael@0: int32_t i = JSVAL_TO_INT(val); michael@0: return ConvertExact(i, result); michael@0: } michael@0: if (JSVAL_IS_DOUBLE(val)) { michael@0: // Don't silently lose bits here -- check that val really is an michael@0: // integer value, and has the right sign. michael@0: double d = JSVAL_TO_DOUBLE(val); michael@0: return ConvertExact(d, result); michael@0: } michael@0: if (!JSVAL_IS_PRIMITIVE(val)) { michael@0: JSObject* obj = JSVAL_TO_OBJECT(val); michael@0: if (CData::IsCData(obj)) { michael@0: JSObject* typeObj = CData::GetCType(obj); michael@0: void* data = CData::GetData(obj); michael@0: michael@0: // Check whether the source type is always representable, with exact michael@0: // precision, by the target type. If it is, convert the value. michael@0: switch (CType::GetTypeCode(typeObj)) { michael@0: #define DEFINE_INT_TYPE(name, fromType, ffiType) \ michael@0: case TYPE_##name: \ michael@0: if (!IsAlwaysExact()) \ michael@0: return false; \ michael@0: *result = IntegerType(*static_cast(data)); \ michael@0: return true; michael@0: #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) michael@0: #include "ctypes/typedefs.h" michael@0: case TYPE_void_t: michael@0: case TYPE_bool: michael@0: case TYPE_float: michael@0: case TYPE_double: michael@0: case TYPE_float32_t: michael@0: case TYPE_float64_t: michael@0: case TYPE_char: michael@0: case TYPE_signed_char: michael@0: case TYPE_unsigned_char: michael@0: case TYPE_jschar: michael@0: case TYPE_pointer: michael@0: case TYPE_function: michael@0: case TYPE_array: michael@0: case TYPE_struct: michael@0: // Not a compatible number type. michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: if (Int64::IsInt64(obj)) { michael@0: // Make sure the integer fits in IntegerType. michael@0: int64_t i = Int64Base::GetInt(obj); michael@0: return ConvertExact(i, result); michael@0: } michael@0: michael@0: if (UInt64::IsUInt64(obj)) { michael@0: // Make sure the integer fits in IntegerType. michael@0: uint64_t i = Int64Base::GetInt(obj); michael@0: return ConvertExact(i, result); michael@0: } michael@0: michael@0: if (CDataFinalizer::IsCDataFinalizer(obj)) { michael@0: RootedValue innerData(cx); michael@0: if (!CDataFinalizer::GetValue(cx, obj, innerData.address())) { michael@0: return false; // Nothing to convert michael@0: } michael@0: return jsvalToInteger(cx, innerData, result); michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: if (JSVAL_IS_BOOLEAN(val)) { michael@0: // Implicitly promote boolean values to 0 or 1, like C. michael@0: *result = JSVAL_TO_BOOLEAN(val); michael@0: JS_ASSERT(*result == 0 || *result == 1); michael@0: return true; michael@0: } michael@0: // Don't silently convert null to an integer. It's probably a mistake. michael@0: return false; michael@0: } michael@0: michael@0: // Implicitly convert val to FloatType, allowing int, double, michael@0: // Int64, UInt64, and CData numeric types 't' where all values of 't' are michael@0: // representable by FloatType. michael@0: template michael@0: static bool michael@0: jsvalToFloat(JSContext *cx, jsval val, FloatType* result) michael@0: { michael@0: JS_STATIC_ASSERT(!NumericLimits::is_exact); michael@0: michael@0: // The following casts may silently throw away some bits, but there's michael@0: // no good way around it. Sternly requiring that the 64-bit double michael@0: // argument be exactly representable as a 32-bit float is michael@0: // unrealistic: it would allow 1/2 to pass but not 1/3. michael@0: if (JSVAL_IS_INT(val)) { michael@0: *result = FloatType(JSVAL_TO_INT(val)); michael@0: return true; michael@0: } michael@0: if (JSVAL_IS_DOUBLE(val)) { michael@0: *result = FloatType(JSVAL_TO_DOUBLE(val)); michael@0: return true; michael@0: } michael@0: if (!JSVAL_IS_PRIMITIVE(val)) { michael@0: JSObject* obj = JSVAL_TO_OBJECT(val); michael@0: if (CData::IsCData(obj)) { michael@0: JSObject* typeObj = CData::GetCType(obj); michael@0: void* data = CData::GetData(obj); michael@0: michael@0: // Check whether the source type is always representable, with exact michael@0: // precision, by the target type. If it is, convert the value. michael@0: switch (CType::GetTypeCode(typeObj)) { michael@0: #define DEFINE_FLOAT_TYPE(name, fromType, ffiType) \ michael@0: case TYPE_##name: \ michael@0: if (!IsAlwaysExact()) \ michael@0: return false; \ michael@0: *result = FloatType(*static_cast(data)); \ michael@0: return true; michael@0: #define DEFINE_INT_TYPE(x, y, z) DEFINE_FLOAT_TYPE(x, y, z) michael@0: #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) michael@0: #include "ctypes/typedefs.h" michael@0: case TYPE_void_t: michael@0: case TYPE_bool: michael@0: case TYPE_char: michael@0: case TYPE_signed_char: michael@0: case TYPE_unsigned_char: michael@0: case TYPE_jschar: michael@0: case TYPE_pointer: michael@0: case TYPE_function: michael@0: case TYPE_array: michael@0: case TYPE_struct: michael@0: // Not a compatible number type. michael@0: return false; michael@0: } michael@0: } michael@0: } michael@0: // Don't silently convert true to 1.0 or false to 0.0, even though C/C++ michael@0: // does it. It's likely to be a mistake. michael@0: return false; michael@0: } michael@0: michael@0: template michael@0: static bool michael@0: StringToInteger(JSContext* cx, JSString* string, IntegerType* result) michael@0: { michael@0: JS_STATIC_ASSERT(NumericLimits::is_exact); michael@0: michael@0: const jschar* cp = string->getChars(nullptr); michael@0: if (!cp) michael@0: return false; michael@0: michael@0: const jschar* end = cp + string->length(); michael@0: if (cp == end) michael@0: return false; michael@0: michael@0: IntegerType sign = 1; michael@0: if (cp[0] == '-') { michael@0: if (!NumericLimits::is_signed) michael@0: return false; michael@0: michael@0: sign = -1; michael@0: ++cp; michael@0: } michael@0: michael@0: // Assume base-10, unless the string begins with '0x' or '0X'. michael@0: IntegerType base = 10; michael@0: if (end - cp > 2 && cp[0] == '0' && (cp[1] == 'x' || cp[1] == 'X')) { michael@0: cp += 2; michael@0: base = 16; michael@0: } michael@0: michael@0: // Scan the string left to right and build the number, michael@0: // checking for valid characters 0 - 9, a - f, A - F and overflow. michael@0: IntegerType i = 0; michael@0: while (cp != end) { michael@0: jschar c = *cp++; michael@0: if (c >= '0' && c <= '9') michael@0: c -= '0'; michael@0: else if (base == 16 && c >= 'a' && c <= 'f') michael@0: c = c - 'a' + 10; michael@0: else if (base == 16 && c >= 'A' && c <= 'F') michael@0: c = c - 'A' + 10; michael@0: else michael@0: return false; michael@0: michael@0: IntegerType ii = i; michael@0: i = ii * base + sign * c; michael@0: if (i / base != ii) // overflow michael@0: return false; michael@0: } michael@0: michael@0: *result = i; michael@0: return true; michael@0: } michael@0: michael@0: // Implicitly convert val to IntegerType, allowing int, double, michael@0: // Int64, UInt64, and optionally a decimal or hexadecimal string argument. michael@0: // (This is common code shared by jsvalToSize and the Int64/UInt64 constructors.) michael@0: template michael@0: static bool michael@0: jsvalToBigInteger(JSContext* cx, michael@0: jsval val, michael@0: bool allowString, michael@0: IntegerType* result) michael@0: { michael@0: JS_STATIC_ASSERT(NumericLimits::is_exact); michael@0: michael@0: if (JSVAL_IS_INT(val)) { michael@0: // Make sure the integer fits in the alotted precision, and has the right michael@0: // sign. michael@0: int32_t i = JSVAL_TO_INT(val); michael@0: return ConvertExact(i, result); michael@0: } michael@0: if (JSVAL_IS_DOUBLE(val)) { michael@0: // Don't silently lose bits here -- check that val really is an michael@0: // integer value, and has the right sign. michael@0: double d = JSVAL_TO_DOUBLE(val); michael@0: return ConvertExact(d, result); michael@0: } michael@0: if (allowString && JSVAL_IS_STRING(val)) { michael@0: // Allow conversion from base-10 or base-16 strings, provided the result michael@0: // fits in IntegerType. (This allows an Int64 or UInt64 object to be passed michael@0: // to the JS array element operator, which will automatically call michael@0: // toString() on the object for us.) michael@0: return StringToInteger(cx, JSVAL_TO_STRING(val), result); michael@0: } michael@0: if (!JSVAL_IS_PRIMITIVE(val)) { michael@0: // Allow conversion from an Int64 or UInt64 object directly. michael@0: JSObject* obj = JSVAL_TO_OBJECT(val); michael@0: michael@0: if (UInt64::IsUInt64(obj)) { michael@0: // Make sure the integer fits in IntegerType. michael@0: uint64_t i = Int64Base::GetInt(obj); michael@0: return ConvertExact(i, result); michael@0: } michael@0: michael@0: if (Int64::IsInt64(obj)) { michael@0: // Make sure the integer fits in IntegerType. michael@0: int64_t i = Int64Base::GetInt(obj); michael@0: return ConvertExact(i, result); michael@0: } michael@0: michael@0: if (CDataFinalizer::IsCDataFinalizer(obj)) { michael@0: RootedValue innerData(cx); michael@0: if (!CDataFinalizer::GetValue(cx, obj, innerData.address())) { michael@0: return false; // Nothing to convert michael@0: } michael@0: return jsvalToBigInteger(cx, innerData, allowString, result); michael@0: } michael@0: michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: // Implicitly convert val to a size value, where the size value is represented michael@0: // by size_t but must also fit in a double. michael@0: static bool michael@0: jsvalToSize(JSContext* cx, jsval val, bool allowString, size_t* result) michael@0: { michael@0: if (!jsvalToBigInteger(cx, val, allowString, result)) michael@0: return false; michael@0: michael@0: // Also check that the result fits in a double. michael@0: return Convert(double(*result)) == *result; michael@0: } michael@0: michael@0: // Implicitly convert val to IntegerType, allowing int, double, michael@0: // Int64, UInt64, and optionally a decimal or hexadecimal string argument. michael@0: // (This is common code shared by jsvalToSize and the Int64/UInt64 constructors.) michael@0: template michael@0: static bool michael@0: jsidToBigInteger(JSContext* cx, michael@0: jsid val, michael@0: bool allowString, michael@0: IntegerType* result) michael@0: { michael@0: JS_STATIC_ASSERT(NumericLimits::is_exact); michael@0: michael@0: if (JSID_IS_INT(val)) { michael@0: // Make sure the integer fits in the alotted precision, and has the right michael@0: // sign. michael@0: int32_t i = JSID_TO_INT(val); michael@0: return ConvertExact(i, result); michael@0: } michael@0: if (allowString && JSID_IS_STRING(val)) { michael@0: // Allow conversion from base-10 or base-16 strings, provided the result michael@0: // fits in IntegerType. (This allows an Int64 or UInt64 object to be passed michael@0: // to the JS array element operator, which will automatically call michael@0: // toString() on the object for us.) michael@0: return StringToInteger(cx, JSID_TO_STRING(val), result); michael@0: } michael@0: if (JSID_IS_OBJECT(val)) { michael@0: // Allow conversion from an Int64 or UInt64 object directly. michael@0: JSObject* obj = JSID_TO_OBJECT(val); michael@0: michael@0: if (UInt64::IsUInt64(obj)) { michael@0: // Make sure the integer fits in IntegerType. michael@0: uint64_t i = Int64Base::GetInt(obj); michael@0: return ConvertExact(i, result); michael@0: } michael@0: michael@0: if (Int64::IsInt64(obj)) { michael@0: // Make sure the integer fits in IntegerType. michael@0: int64_t i = Int64Base::GetInt(obj); michael@0: return ConvertExact(i, result); michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: // Implicitly convert val to a size value, where the size value is represented michael@0: // by size_t but must also fit in a double. michael@0: static bool michael@0: jsidToSize(JSContext* cx, jsid val, bool allowString, size_t* result) michael@0: { michael@0: if (!jsidToBigInteger(cx, val, allowString, result)) michael@0: return false; michael@0: michael@0: // Also check that the result fits in a double. michael@0: return Convert(double(*result)) == *result; michael@0: } michael@0: michael@0: // Implicitly convert a size value to a jsval, ensuring that the size_t value michael@0: // fits in a double. michael@0: static bool michael@0: SizeTojsval(JSContext* cx, size_t size, jsval* result) michael@0: { michael@0: if (Convert(double(size)) != size) { michael@0: JS_ReportError(cx, "size overflow"); michael@0: return false; michael@0: } michael@0: michael@0: *result = JS_NumberValue(double(size)); michael@0: return true; michael@0: } michael@0: michael@0: // Forcefully convert val to IntegerType when explicitly requested. michael@0: template michael@0: static bool michael@0: jsvalToIntegerExplicit(jsval val, IntegerType* result) michael@0: { michael@0: JS_STATIC_ASSERT(NumericLimits::is_exact); michael@0: michael@0: if (JSVAL_IS_DOUBLE(val)) { michael@0: // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast. michael@0: double d = JSVAL_TO_DOUBLE(val); michael@0: *result = mozilla::IsFinite(d) ? IntegerType(d) : 0; michael@0: return true; michael@0: } michael@0: if (!JSVAL_IS_PRIMITIVE(val)) { michael@0: // Convert Int64 and UInt64 values by C-style cast. michael@0: JSObject* obj = JSVAL_TO_OBJECT(val); michael@0: if (Int64::IsInt64(obj)) { michael@0: int64_t i = Int64Base::GetInt(obj); michael@0: *result = IntegerType(i); michael@0: return true; michael@0: } michael@0: if (UInt64::IsUInt64(obj)) { michael@0: uint64_t i = Int64Base::GetInt(obj); michael@0: *result = IntegerType(i); michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: // Forcefully convert val to a pointer value when explicitly requested. michael@0: static bool michael@0: jsvalToPtrExplicit(JSContext* cx, jsval val, uintptr_t* result) michael@0: { michael@0: if (JSVAL_IS_INT(val)) { michael@0: // int32_t always fits in intptr_t. If the integer is negative, cast through michael@0: // an intptr_t intermediate to sign-extend. michael@0: int32_t i = JSVAL_TO_INT(val); michael@0: *result = i < 0 ? uintptr_t(intptr_t(i)) : uintptr_t(i); michael@0: return true; michael@0: } michael@0: if (JSVAL_IS_DOUBLE(val)) { michael@0: double d = JSVAL_TO_DOUBLE(val); michael@0: if (d < 0) { michael@0: // Cast through an intptr_t intermediate to sign-extend. michael@0: intptr_t i = Convert(d); michael@0: if (double(i) != d) michael@0: return false; michael@0: michael@0: *result = uintptr_t(i); michael@0: return true; michael@0: } michael@0: michael@0: // Don't silently lose bits here -- check that val really is an michael@0: // integer value, and has the right sign. michael@0: *result = Convert(d); michael@0: return double(*result) == d; michael@0: } michael@0: if (!JSVAL_IS_PRIMITIVE(val)) { michael@0: JSObject* obj = JSVAL_TO_OBJECT(val); michael@0: if (Int64::IsInt64(obj)) { michael@0: int64_t i = Int64Base::GetInt(obj); michael@0: intptr_t p = intptr_t(i); michael@0: michael@0: // Make sure the integer fits in the alotted precision. michael@0: if (int64_t(p) != i) michael@0: return false; michael@0: *result = uintptr_t(p); michael@0: return true; michael@0: } michael@0: michael@0: if (UInt64::IsUInt64(obj)) { michael@0: uint64_t i = Int64Base::GetInt(obj); michael@0: michael@0: // Make sure the integer fits in the alotted precision. michael@0: *result = uintptr_t(i); michael@0: return uint64_t(*result) == i; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: template michael@0: void michael@0: IntegerToString(IntegerType i, int radix, Vector& result) michael@0: { michael@0: JS_STATIC_ASSERT(NumericLimits::is_exact); michael@0: michael@0: // The buffer must be big enough for all the bits of IntegerType to fit, michael@0: // in base-2, including '-'. michael@0: CharType buffer[sizeof(IntegerType) * 8 + 1]; michael@0: CharType* end = buffer + sizeof(buffer) / sizeof(CharType); michael@0: CharType* cp = end; michael@0: michael@0: // Build the string in reverse. We use multiplication and subtraction michael@0: // instead of modulus because that's much faster. michael@0: const bool isNegative = IsNegative(i); michael@0: size_t sign = isNegative ? -1 : 1; michael@0: do { michael@0: IntegerType ii = i / IntegerType(radix); michael@0: size_t index = sign * size_t(i - ii * IntegerType(radix)); michael@0: *--cp = "0123456789abcdefghijklmnopqrstuvwxyz"[index]; michael@0: i = ii; michael@0: } while (i != 0); michael@0: michael@0: if (isNegative) michael@0: *--cp = '-'; michael@0: michael@0: JS_ASSERT(cp >= buffer); michael@0: result.append(cp, end); michael@0: } michael@0: michael@0: template michael@0: static size_t michael@0: strnlen(const CharType* begin, size_t max) michael@0: { michael@0: for (const CharType* s = begin; s != begin + max; ++s) michael@0: if (*s == 0) michael@0: return s - begin; michael@0: michael@0: return max; michael@0: } michael@0: michael@0: // Convert C binary value 'data' of CType 'typeObj' to a JS primitive, where michael@0: // possible; otherwise, construct and return a CData object. The following michael@0: // semantics apply when constructing a CData object for return: michael@0: // * If 'wantPrimitive' is true, the caller indicates that 'result' must be michael@0: // a JS primitive, and ConvertToJS will fail if 'result' would be a CData michael@0: // object. Otherwise: michael@0: // * If a CData object 'parentObj' is supplied, the new CData object is michael@0: // dependent on the given parent and its buffer refers to a slice of the michael@0: // parent's buffer. michael@0: // * If 'parentObj' is null, the new CData object may or may not own its michael@0: // resulting buffer depending on the 'ownResult' argument. michael@0: static bool michael@0: ConvertToJS(JSContext* cx, michael@0: HandleObject typeObj, michael@0: HandleObject parentObj, michael@0: void* data, michael@0: bool wantPrimitive, michael@0: bool ownResult, michael@0: jsval* result) michael@0: { michael@0: JS_ASSERT(!parentObj || CData::IsCData(parentObj)); michael@0: JS_ASSERT(!parentObj || !ownResult); michael@0: JS_ASSERT(!wantPrimitive || !ownResult); michael@0: michael@0: TypeCode typeCode = CType::GetTypeCode(typeObj); michael@0: michael@0: switch (typeCode) { michael@0: case TYPE_void_t: michael@0: *result = JSVAL_VOID; michael@0: break; michael@0: case TYPE_bool: michael@0: *result = *static_cast(data) ? JSVAL_TRUE : JSVAL_FALSE; michael@0: break; michael@0: #define DEFINE_INT_TYPE(name, type, ffiType) \ michael@0: case TYPE_##name: { \ michael@0: type value = *static_cast(data); \ michael@0: if (sizeof(type) < 4) \ michael@0: *result = INT_TO_JSVAL(int32_t(value)); \ michael@0: else \ michael@0: *result = JS_NumberValue(double(value)); \ michael@0: break; \ michael@0: } michael@0: #define DEFINE_WRAPPED_INT_TYPE(name, type, ffiType) \ michael@0: case TYPE_##name: { \ michael@0: /* Return an Int64 or UInt64 object - do not convert to a JS number. */ \ michael@0: uint64_t value; \ michael@0: RootedObject proto(cx); \ michael@0: if (!NumericLimits::is_signed) { \ michael@0: value = *static_cast(data); \ michael@0: /* Get ctypes.UInt64.prototype from ctypes.CType.prototype. */ \ michael@0: proto = CType::GetProtoFromType(cx, typeObj, SLOT_UINT64PROTO); \ michael@0: if (!proto) \ michael@0: return false; \ michael@0: } else { \ michael@0: value = int64_t(*static_cast(data)); \ michael@0: /* Get ctypes.Int64.prototype from ctypes.CType.prototype. */ \ michael@0: proto = CType::GetProtoFromType(cx, typeObj, SLOT_INT64PROTO); \ michael@0: if (!proto) \ michael@0: return false; \ michael@0: } \ michael@0: \ michael@0: JSObject* obj = Int64Base::Construct(cx, proto, value, \ michael@0: !NumericLimits::is_signed); \ michael@0: if (!obj) \ michael@0: return false; \ michael@0: *result = OBJECT_TO_JSVAL(obj); \ michael@0: break; \ michael@0: } michael@0: #define DEFINE_FLOAT_TYPE(name, type, ffiType) \ michael@0: case TYPE_##name: { \ michael@0: type value = *static_cast(data); \ michael@0: *result = JS_NumberValue(double(value)); \ michael@0: break; \ michael@0: } michael@0: #define DEFINE_CHAR_TYPE(name, type, ffiType) \ michael@0: case TYPE_##name: \ michael@0: /* Convert to an integer. We have no idea what character encoding to */ \ michael@0: /* use, if any. */ \ michael@0: *result = INT_TO_JSVAL(*static_cast(data)); \ michael@0: break; michael@0: #include "ctypes/typedefs.h" michael@0: case TYPE_jschar: { michael@0: // Convert the jschar to a 1-character string. michael@0: JSString* str = JS_NewUCStringCopyN(cx, static_cast(data), 1); michael@0: if (!str) michael@0: return false; michael@0: michael@0: *result = STRING_TO_JSVAL(str); michael@0: break; michael@0: } michael@0: case TYPE_pointer: michael@0: case TYPE_array: michael@0: case TYPE_struct: { michael@0: // We're about to create a new CData object to return. If the caller doesn't michael@0: // want this, return early. michael@0: if (wantPrimitive) { michael@0: JS_ReportError(cx, "cannot convert to primitive value"); michael@0: return false; michael@0: } michael@0: michael@0: JSObject* obj = CData::Create(cx, typeObj, parentObj, data, ownResult); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: *result = OBJECT_TO_JSVAL(obj); michael@0: break; michael@0: } michael@0: case TYPE_function: michael@0: MOZ_ASSUME_UNREACHABLE("cannot return a FunctionType"); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // Determine if the contents of a typed array can be converted without michael@0: // ambiguity to a C type. Elements of a Int8Array are converted to michael@0: // ctypes.int8_t, UInt8Array to ctypes.uint8_t, etc. michael@0: bool CanConvertTypedArrayItemTo(JSObject *baseType, JSObject *valObj, JSContext *cx) { michael@0: TypeCode baseTypeCode = CType::GetTypeCode(baseType); michael@0: if (baseTypeCode == TYPE_void_t) { michael@0: return true; michael@0: } michael@0: TypeCode elementTypeCode; michael@0: switch (JS_GetArrayBufferViewType(valObj)) { michael@0: case ScalarTypeDescr::TYPE_INT8: michael@0: elementTypeCode = TYPE_int8_t; michael@0: break; michael@0: case ScalarTypeDescr::TYPE_UINT8: michael@0: case ScalarTypeDescr::TYPE_UINT8_CLAMPED: michael@0: elementTypeCode = TYPE_uint8_t; michael@0: break; michael@0: case ScalarTypeDescr::TYPE_INT16: michael@0: elementTypeCode = TYPE_int16_t; michael@0: break; michael@0: case ScalarTypeDescr::TYPE_UINT16: michael@0: elementTypeCode = TYPE_uint16_t; michael@0: break; michael@0: case ScalarTypeDescr::TYPE_INT32: michael@0: elementTypeCode = TYPE_int32_t; michael@0: break; michael@0: case ScalarTypeDescr::TYPE_UINT32: michael@0: elementTypeCode = TYPE_uint32_t; michael@0: break; michael@0: case ScalarTypeDescr::TYPE_FLOAT32: michael@0: elementTypeCode = TYPE_float32_t; michael@0: break; michael@0: case ScalarTypeDescr::TYPE_FLOAT64: michael@0: elementTypeCode = TYPE_float64_t; michael@0: break; michael@0: default: michael@0: return false; michael@0: } michael@0: return elementTypeCode == baseTypeCode; michael@0: } michael@0: michael@0: // Implicitly convert jsval 'val' to a C binary representation of CType michael@0: // 'targetType', storing the result in 'buffer'. Adequate space must be michael@0: // provided in 'buffer' by the caller. This function generally does minimal michael@0: // coercion between types. There are two cases in which this function is used: michael@0: // 1) The target buffer is internal to a CData object; we simply write data michael@0: // into it. michael@0: // 2) We are converting an argument for an ffi call, in which case 'isArgument' michael@0: // will be true. This allows us to handle a special case: if necessary, michael@0: // we can autoconvert a JS string primitive to a pointer-to-character type. michael@0: // In this case, ownership of the allocated string is handed off to the michael@0: // caller; 'freePointer' will be set to indicate this. michael@0: static bool michael@0: ImplicitConvert(JSContext* cx, michael@0: HandleValue val, michael@0: JSObject* targetType_, michael@0: void* buffer, michael@0: bool isArgument, michael@0: bool* freePointer) michael@0: { michael@0: RootedObject targetType(cx, targetType_); michael@0: JS_ASSERT(CType::IsSizeDefined(targetType)); michael@0: michael@0: // First, check if val is either a CData object or a CDataFinalizer michael@0: // of type targetType. michael@0: JSObject* sourceData = nullptr; michael@0: JSObject* sourceType = nullptr; michael@0: RootedObject valObj(cx, nullptr); michael@0: if (!JSVAL_IS_PRIMITIVE(val)) { michael@0: valObj = JSVAL_TO_OBJECT(val); michael@0: if (CData::IsCData(valObj)) { michael@0: sourceData = valObj; michael@0: sourceType = CData::GetCType(sourceData); michael@0: michael@0: // If the types are equal, copy the buffer contained within the CData. michael@0: // (Note that the buffers may overlap partially or completely.) michael@0: if (CType::TypesEqual(sourceType, targetType)) { michael@0: size_t size = CType::GetSize(sourceType); michael@0: memmove(buffer, CData::GetData(sourceData), size); michael@0: return true; michael@0: } michael@0: } else if (CDataFinalizer::IsCDataFinalizer(valObj)) { michael@0: sourceData = valObj; michael@0: sourceType = CDataFinalizer::GetCType(cx, sourceData); michael@0: michael@0: CDataFinalizer::Private *p = (CDataFinalizer::Private *) michael@0: JS_GetPrivate(sourceData); michael@0: michael@0: if (!p) { michael@0: // We have called |dispose| or |forget| already. michael@0: JS_ReportError(cx, "Attempting to convert an empty CDataFinalizer"); michael@0: return false; michael@0: } michael@0: michael@0: // If the types are equal, copy the buffer contained within the CData. michael@0: if (CType::TypesEqual(sourceType, targetType)) { michael@0: memmove(buffer, p->cargs, p->cargs_size); michael@0: return true; michael@0: } michael@0: } michael@0: } michael@0: michael@0: TypeCode targetCode = CType::GetTypeCode(targetType); michael@0: michael@0: switch (targetCode) { michael@0: case TYPE_bool: { michael@0: // Do not implicitly lose bits, but allow the values 0, 1, and -0. michael@0: // Programs can convert explicitly, if needed, using `Boolean(v)` or `!!v`. michael@0: bool result; michael@0: if (!jsvalToBool(cx, val, &result)) michael@0: return TypeError(cx, "boolean", val); michael@0: *static_cast(buffer) = result; michael@0: break; michael@0: } michael@0: #define DEFINE_INT_TYPE(name, type, ffiType) \ michael@0: case TYPE_##name: { \ michael@0: /* Do not implicitly lose bits. */ \ michael@0: type result; \ michael@0: if (!jsvalToInteger(cx, val, &result)) \ michael@0: return TypeError(cx, #name, val); \ michael@0: *static_cast(buffer) = result; \ michael@0: break; \ michael@0: } michael@0: #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) michael@0: #define DEFINE_FLOAT_TYPE(name, type, ffiType) \ michael@0: case TYPE_##name: { \ michael@0: type result; \ michael@0: if (!jsvalToFloat(cx, val, &result)) \ michael@0: return TypeError(cx, #name, val); \ michael@0: *static_cast(buffer) = result; \ michael@0: break; \ michael@0: } michael@0: #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) michael@0: #define DEFINE_JSCHAR_TYPE(name, type, ffiType) \ michael@0: case TYPE_##name: { \ michael@0: /* Convert from a 1-character string, regardless of encoding, */ \ michael@0: /* or from an integer, provided the result fits in 'type'. */ \ michael@0: type result; \ michael@0: if (JSVAL_IS_STRING(val)) { \ michael@0: JSString* str = JSVAL_TO_STRING(val); \ michael@0: if (str->length() != 1) \ michael@0: return TypeError(cx, #name, val); \ michael@0: const jschar *chars = str->getChars(cx); \ michael@0: if (!chars) \ michael@0: return false; \ michael@0: result = chars[0]; \ michael@0: } else if (!jsvalToInteger(cx, val, &result)) { \ michael@0: return TypeError(cx, #name, val); \ michael@0: } \ michael@0: *static_cast(buffer) = result; \ michael@0: break; \ michael@0: } michael@0: #include "ctypes/typedefs.h" michael@0: case TYPE_pointer: { michael@0: if (JSVAL_IS_NULL(val)) { michael@0: // Convert to a null pointer. michael@0: *static_cast(buffer) = nullptr; michael@0: break; michael@0: } michael@0: michael@0: JS::Rooted baseType(cx, PointerType::GetBaseType(targetType)); michael@0: if (sourceData) { michael@0: // First, determine if the targetType is ctypes.void_t.ptr. michael@0: TypeCode sourceCode = CType::GetTypeCode(sourceType); michael@0: void* sourceBuffer = CData::GetData(sourceData); michael@0: bool voidptrTarget = CType::GetTypeCode(baseType) == TYPE_void_t; michael@0: michael@0: if (sourceCode == TYPE_pointer && voidptrTarget) { michael@0: // Autoconvert if targetType is ctypes.voidptr_t. michael@0: *static_cast(buffer) = *static_cast(sourceBuffer); michael@0: break; michael@0: } michael@0: if (sourceCode == TYPE_array) { michael@0: // Autoconvert an array to a ctypes.void_t.ptr or to michael@0: // sourceType.elementType.ptr, just like C. michael@0: JSObject* elementType = ArrayType::GetBaseType(sourceType); michael@0: if (voidptrTarget || CType::TypesEqual(baseType, elementType)) { michael@0: *static_cast(buffer) = sourceBuffer; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: } else if (isArgument && JSVAL_IS_STRING(val)) { michael@0: // Convert the string for the ffi call. This requires allocating space michael@0: // which the caller assumes ownership of. michael@0: // TODO: Extend this so we can safely convert strings at other times also. michael@0: JSString* sourceString = JSVAL_TO_STRING(val); michael@0: size_t sourceLength = sourceString->length(); michael@0: const jschar* sourceChars = sourceString->getChars(cx); michael@0: if (!sourceChars) michael@0: return false; michael@0: michael@0: switch (CType::GetTypeCode(baseType)) { michael@0: case TYPE_char: michael@0: case TYPE_signed_char: michael@0: case TYPE_unsigned_char: { michael@0: // Convert from UTF-16 to UTF-8. michael@0: size_t nbytes = michael@0: GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength); michael@0: if (nbytes == (size_t) -1) michael@0: return false; michael@0: michael@0: char** charBuffer = static_cast(buffer); michael@0: *charBuffer = cx->pod_malloc(nbytes + 1); michael@0: if (!*charBuffer) { michael@0: JS_ReportAllocationOverflow(cx); michael@0: return false; michael@0: } michael@0: michael@0: ASSERT_OK(DeflateStringToUTF8Buffer(cx, sourceChars, sourceLength, michael@0: *charBuffer, &nbytes)); michael@0: (*charBuffer)[nbytes] = 0; michael@0: *freePointer = true; michael@0: break; michael@0: } michael@0: case TYPE_jschar: { michael@0: // Copy the jschar string data. (We could provide direct access to the michael@0: // JSString's buffer, but this approach is safer if the caller happens michael@0: // to modify the string.) michael@0: jschar** jscharBuffer = static_cast(buffer); michael@0: *jscharBuffer = cx->pod_malloc(sourceLength + 1); michael@0: if (!*jscharBuffer) { michael@0: JS_ReportAllocationOverflow(cx); michael@0: return false; michael@0: } michael@0: michael@0: *freePointer = true; michael@0: memcpy(*jscharBuffer, sourceChars, sourceLength * sizeof(jschar)); michael@0: (*jscharBuffer)[sourceLength] = 0; michael@0: break; michael@0: } michael@0: default: michael@0: return TypeError(cx, "string pointer", val); michael@0: } michael@0: break; michael@0: } else if (!JSVAL_IS_PRIMITIVE(val) && JS_IsArrayBufferObject(valObj)) { michael@0: // Convert ArrayBuffer to pointer without any copy. michael@0: // Just as with C arrays, we make no effort to michael@0: // keep the ArrayBuffer alive. michael@0: void* p = JS_GetStableArrayBufferData(cx, valObj); michael@0: if (!p) michael@0: return false; michael@0: *static_cast(buffer) = p; michael@0: break; michael@0: } if (!JSVAL_IS_PRIMITIVE(val) && JS_IsTypedArrayObject(valObj)) { michael@0: if(!CanConvertTypedArrayItemTo(baseType, valObj, cx)) { michael@0: return TypeError(cx, "typed array with the appropriate type", val); michael@0: } michael@0: michael@0: // Convert TypedArray to pointer without any copy. michael@0: // Just as with C arrays, we make no effort to michael@0: // keep the TypedArray alive. michael@0: *static_cast(buffer) = JS_GetArrayBufferViewData(valObj); michael@0: break; michael@0: } michael@0: return TypeError(cx, "pointer", val); michael@0: } michael@0: case TYPE_array: { michael@0: RootedObject baseType(cx, ArrayType::GetBaseType(targetType)); michael@0: size_t targetLength = ArrayType::GetLength(targetType); michael@0: michael@0: if (JSVAL_IS_STRING(val)) { michael@0: JSString* sourceString = JSVAL_TO_STRING(val); michael@0: size_t sourceLength = sourceString->length(); michael@0: const jschar* sourceChars = sourceString->getChars(cx); michael@0: if (!sourceChars) michael@0: return false; michael@0: michael@0: switch (CType::GetTypeCode(baseType)) { michael@0: case TYPE_char: michael@0: case TYPE_signed_char: michael@0: case TYPE_unsigned_char: { michael@0: // Convert from UTF-16 to UTF-8. michael@0: size_t nbytes = michael@0: GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength); michael@0: if (nbytes == (size_t) -1) michael@0: return false; michael@0: michael@0: if (targetLength < nbytes) { michael@0: JS_ReportError(cx, "ArrayType has insufficient length"); michael@0: return false; michael@0: } michael@0: michael@0: char* charBuffer = static_cast(buffer); michael@0: ASSERT_OK(DeflateStringToUTF8Buffer(cx, sourceChars, sourceLength, michael@0: charBuffer, &nbytes)); michael@0: michael@0: if (targetLength > nbytes) michael@0: charBuffer[nbytes] = 0; michael@0: michael@0: break; michael@0: } michael@0: case TYPE_jschar: { michael@0: // Copy the string data, jschar for jschar, including the terminator michael@0: // if there's space. michael@0: if (targetLength < sourceLength) { michael@0: JS_ReportError(cx, "ArrayType has insufficient length"); michael@0: return false; michael@0: } michael@0: michael@0: memcpy(buffer, sourceChars, sourceLength * sizeof(jschar)); michael@0: if (targetLength > sourceLength) michael@0: static_cast(buffer)[sourceLength] = 0; michael@0: michael@0: break; michael@0: } michael@0: default: michael@0: return TypeError(cx, "array", val); michael@0: } michael@0: michael@0: } else if (!JSVAL_IS_PRIMITIVE(val) && JS_IsArrayObject(cx, valObj)) { michael@0: // Convert each element of the array by calling ImplicitConvert. michael@0: uint32_t sourceLength; michael@0: if (!JS_GetArrayLength(cx, valObj, &sourceLength) || michael@0: targetLength != size_t(sourceLength)) { michael@0: JS_ReportError(cx, "ArrayType length does not match source array length"); michael@0: return false; michael@0: } michael@0: michael@0: // Convert into an intermediate, in case of failure. michael@0: size_t elementSize = CType::GetSize(baseType); michael@0: size_t arraySize = elementSize * targetLength; michael@0: AutoPtr intermediate(cx->pod_malloc(arraySize)); michael@0: if (!intermediate) { michael@0: JS_ReportAllocationOverflow(cx); michael@0: return false; michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < sourceLength; ++i) { michael@0: RootedValue item(cx); michael@0: if (!JS_GetElement(cx, valObj, i, &item)) michael@0: return false; michael@0: michael@0: char* data = intermediate.get() + elementSize * i; michael@0: if (!ImplicitConvert(cx, item, baseType, data, false, nullptr)) michael@0: return false; michael@0: } michael@0: michael@0: memcpy(buffer, intermediate.get(), arraySize); michael@0: michael@0: } else if (!JSVAL_IS_PRIMITIVE(val) && michael@0: JS_IsArrayBufferObject(valObj)) { michael@0: // Check that array is consistent with type, then michael@0: // copy the array. michael@0: uint32_t sourceLength = JS_GetArrayBufferByteLength(valObj); michael@0: size_t elementSize = CType::GetSize(baseType); michael@0: size_t arraySize = elementSize * targetLength; michael@0: if (arraySize != size_t(sourceLength)) { michael@0: JS_ReportError(cx, "ArrayType length does not match source ArrayBuffer length"); michael@0: return false; michael@0: } michael@0: memcpy(buffer, JS_GetArrayBufferData(valObj), sourceLength); michael@0: break; michael@0: } else if (!JSVAL_IS_PRIMITIVE(val) && michael@0: JS_IsTypedArrayObject(valObj)) { michael@0: // Check that array is consistent with type, then michael@0: // copy the array. michael@0: if(!CanConvertTypedArrayItemTo(baseType, valObj, cx)) { michael@0: return TypeError(cx, "typed array with the appropriate type", val); michael@0: } michael@0: michael@0: uint32_t sourceLength = JS_GetTypedArrayByteLength(valObj); michael@0: size_t elementSize = CType::GetSize(baseType); michael@0: size_t arraySize = elementSize * targetLength; michael@0: if (arraySize != size_t(sourceLength)) { michael@0: JS_ReportError(cx, "typed array length does not match source TypedArray length"); michael@0: return false; michael@0: } michael@0: memcpy(buffer, JS_GetArrayBufferViewData(valObj), sourceLength); michael@0: break; michael@0: } else { michael@0: // Don't implicitly convert to string. Users can implicitly convert michael@0: // with `String(x)` or `""+x`. michael@0: return TypeError(cx, "array", val); michael@0: } michael@0: break; michael@0: } michael@0: case TYPE_struct: { michael@0: if (!JSVAL_IS_PRIMITIVE(val) && !sourceData) { michael@0: // Enumerate the properties of the object; if they match the struct michael@0: // specification, convert the fields. michael@0: RootedObject iter(cx, JS_NewPropertyIterator(cx, valObj)); michael@0: if (!iter) michael@0: return false; michael@0: michael@0: // Convert into an intermediate, in case of failure. michael@0: size_t structSize = CType::GetSize(targetType); michael@0: AutoPtr intermediate(cx->pod_malloc(structSize)); michael@0: if (!intermediate) { michael@0: JS_ReportAllocationOverflow(cx); michael@0: return false; michael@0: } michael@0: michael@0: RootedId id(cx); michael@0: size_t i = 0; michael@0: while (1) { michael@0: if (!JS_NextProperty(cx, iter, id.address())) michael@0: return false; michael@0: if (JSID_IS_VOID(id)) michael@0: break; michael@0: michael@0: if (!JSID_IS_STRING(id)) { michael@0: JS_ReportError(cx, "property name is not a string"); michael@0: return false; michael@0: } michael@0: michael@0: JSFlatString *name = JSID_TO_FLAT_STRING(id); michael@0: const FieldInfo* field = StructType::LookupField(cx, targetType, name); michael@0: if (!field) michael@0: return false; michael@0: michael@0: RootedValue prop(cx); michael@0: if (!JS_GetPropertyById(cx, valObj, id, &prop)) michael@0: return false; michael@0: michael@0: // Convert the field via ImplicitConvert(). michael@0: char* fieldData = intermediate.get() + field->mOffset; michael@0: if (!ImplicitConvert(cx, prop, field->mType, fieldData, false, nullptr)) michael@0: return false; michael@0: michael@0: ++i; michael@0: } michael@0: michael@0: const FieldInfoHash* fields = StructType::GetFieldInfo(targetType); michael@0: if (i != fields->count()) { michael@0: JS_ReportError(cx, "missing fields"); michael@0: return false; michael@0: } michael@0: michael@0: memcpy(buffer, intermediate.get(), structSize); michael@0: break; michael@0: } michael@0: michael@0: return TypeError(cx, "struct", val); michael@0: } michael@0: case TYPE_void_t: michael@0: case TYPE_function: michael@0: MOZ_ASSUME_UNREACHABLE("invalid type"); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // Convert jsval 'val' to a C binary representation of CType 'targetType', michael@0: // storing the result in 'buffer'. This function is more forceful than michael@0: // ImplicitConvert. michael@0: static bool michael@0: ExplicitConvert(JSContext* cx, HandleValue val, HandleObject targetType, void* buffer) michael@0: { michael@0: // If ImplicitConvert succeeds, use that result. michael@0: if (ImplicitConvert(cx, val, targetType, buffer, false, nullptr)) michael@0: return true; michael@0: michael@0: // If ImplicitConvert failed, and there is no pending exception, then assume michael@0: // hard failure (out of memory, or some other similarly serious condition). michael@0: // We store any pending exception in case we need to re-throw it. michael@0: RootedValue ex(cx); michael@0: if (!JS_GetPendingException(cx, &ex)) michael@0: return false; michael@0: michael@0: // Otherwise, assume soft failure. Clear the pending exception so that we michael@0: // can throw a different one as required. michael@0: JS_ClearPendingException(cx); michael@0: michael@0: TypeCode type = CType::GetTypeCode(targetType); michael@0: michael@0: switch (type) { michael@0: case TYPE_bool: { michael@0: *static_cast(buffer) = ToBoolean(val); michael@0: break; michael@0: } michael@0: #define DEFINE_INT_TYPE(name, type, ffiType) \ michael@0: case TYPE_##name: { \ michael@0: /* Convert numeric values with a C-style cast, and */ \ michael@0: /* allow conversion from a base-10 or base-16 string. */ \ michael@0: type result; \ michael@0: if (!jsvalToIntegerExplicit(val, &result) && \ michael@0: (!JSVAL_IS_STRING(val) || \ michael@0: !StringToInteger(cx, JSVAL_TO_STRING(val), &result))) \ michael@0: return TypeError(cx, #name, val); \ michael@0: *static_cast(buffer) = result; \ michael@0: break; \ michael@0: } michael@0: #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) michael@0: #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) michael@0: #define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_CHAR_TYPE(x, y, z) michael@0: #include "ctypes/typedefs.h" michael@0: case TYPE_pointer: { michael@0: // Convert a number, Int64 object, or UInt64 object to a pointer. michael@0: uintptr_t result; michael@0: if (!jsvalToPtrExplicit(cx, val, &result)) michael@0: return TypeError(cx, "pointer", val); michael@0: *static_cast(buffer) = result; michael@0: break; michael@0: } michael@0: case TYPE_float32_t: michael@0: case TYPE_float64_t: michael@0: case TYPE_float: michael@0: case TYPE_double: michael@0: case TYPE_array: michael@0: case TYPE_struct: michael@0: // ImplicitConvert is sufficient. Re-throw the exception it generated. michael@0: JS_SetPendingException(cx, ex); michael@0: return false; michael@0: case TYPE_void_t: michael@0: case TYPE_function: michael@0: MOZ_ASSUME_UNREACHABLE("invalid type"); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: // Given a CType 'typeObj', generate a string describing the C type declaration michael@0: // corresponding to 'typeObj'. For instance, the CType constructed from michael@0: // 'ctypes.int32_t.ptr.array(4).ptr.ptr' will result in the type string michael@0: // 'int32_t*(**)[4]'. michael@0: static JSString* michael@0: BuildTypeName(JSContext* cx, JSObject* typeObj_) michael@0: { michael@0: AutoString result; michael@0: RootedObject typeObj(cx, typeObj_); michael@0: michael@0: // Walk the hierarchy of types, outermost to innermost, building up the type michael@0: // string. This consists of the base type, which goes on the left. michael@0: // Derived type modifiers (* and []) build from the inside outward, with michael@0: // pointers on the left and arrays on the right. An excellent description michael@0: // of the rules for building C type declarations can be found at: michael@0: // http://unixwiz.net/techtips/reading-cdecl.html michael@0: TypeCode prevGrouping = CType::GetTypeCode(typeObj), currentGrouping; michael@0: while (1) { michael@0: currentGrouping = CType::GetTypeCode(typeObj); michael@0: switch (currentGrouping) { michael@0: case TYPE_pointer: { michael@0: // Pointer types go on the left. michael@0: PrependString(result, "*"); michael@0: michael@0: typeObj = PointerType::GetBaseType(typeObj); michael@0: prevGrouping = currentGrouping; michael@0: continue; michael@0: } michael@0: case TYPE_array: { michael@0: if (prevGrouping == TYPE_pointer) { michael@0: // Outer type is pointer, inner type is array. Grouping is required. michael@0: PrependString(result, "("); michael@0: AppendString(result, ")"); michael@0: } michael@0: michael@0: // Array types go on the right. michael@0: AppendString(result, "["); michael@0: size_t length; michael@0: if (ArrayType::GetSafeLength(typeObj, &length)) michael@0: IntegerToString(length, 10, result); michael@0: michael@0: AppendString(result, "]"); michael@0: michael@0: typeObj = ArrayType::GetBaseType(typeObj); michael@0: prevGrouping = currentGrouping; michael@0: continue; michael@0: } michael@0: case TYPE_function: { michael@0: FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj); michael@0: michael@0: // Add in the calling convention, if it's not cdecl. michael@0: // There's no trailing or leading space needed here, as none of the michael@0: // modifiers can produce a string beginning with an identifier --- michael@0: // except for TYPE_function itself, which is fine because functions michael@0: // can't return functions. michael@0: ABICode abi = GetABICode(fninfo->mABI); michael@0: if (abi == ABI_STDCALL) michael@0: PrependString(result, "__stdcall"); michael@0: else if (abi == ABI_WINAPI) michael@0: PrependString(result, "WINAPI"); michael@0: michael@0: // Function application binds more tightly than dereferencing, so michael@0: // wrap pointer types in parens. Functions can't return functions michael@0: // (only pointers to them), and arrays can't hold functions michael@0: // (similarly), so we don't need to address those cases. michael@0: if (prevGrouping == TYPE_pointer) { michael@0: PrependString(result, "("); michael@0: AppendString(result, ")"); michael@0: } michael@0: michael@0: // Argument list goes on the right. michael@0: AppendString(result, "("); michael@0: for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) { michael@0: RootedObject argType(cx, fninfo->mArgTypes[i]); michael@0: JSString* argName = CType::GetName(cx, argType); michael@0: AppendString(result, argName); michael@0: if (i != fninfo->mArgTypes.length() - 1 || michael@0: fninfo->mIsVariadic) michael@0: AppendString(result, ", "); michael@0: } michael@0: if (fninfo->mIsVariadic) michael@0: AppendString(result, "..."); michael@0: AppendString(result, ")"); michael@0: michael@0: // Set 'typeObj' to the return type, and let the loop process it. michael@0: // 'prevGrouping' doesn't matter here, because functions cannot return michael@0: // arrays -- thus the parenthetical rules don't get tickled. michael@0: typeObj = fninfo->mReturnType; michael@0: continue; michael@0: } michael@0: default: michael@0: // Either a basic or struct type. Use the type's name as the base type. michael@0: break; michael@0: } michael@0: break; michael@0: } michael@0: michael@0: // If prepending the base type name directly would splice two michael@0: // identifiers, insert a space. michael@0: if (('a' <= result[0] && result[0] <= 'z') || michael@0: ('A' <= result[0] && result[0] <= 'Z') || michael@0: (result[0] == '_')) michael@0: PrependString(result, " "); michael@0: michael@0: // Stick the base type and derived type parts together. michael@0: JSString* baseName = CType::GetName(cx, typeObj); michael@0: PrependString(result, baseName); michael@0: return NewUCString(cx, result); michael@0: } michael@0: michael@0: // Given a CType 'typeObj', generate a string 'result' such that 'eval(result)' michael@0: // would construct the same CType. If 'makeShort' is true, assume that any michael@0: // StructType 't' is bound to an in-scope variable of name 't.name', and use michael@0: // that variable in place of generating a string to construct the type 't'. michael@0: // (This means the type comparison function CType::TypesEqual will return true michael@0: // when comparing the input and output of BuildTypeSource, since struct michael@0: // equality is determined by strict JSObject pointer equality.) michael@0: static void michael@0: BuildTypeSource(JSContext* cx, michael@0: JSObject* typeObj_, michael@0: bool makeShort, michael@0: AutoString& result) michael@0: { michael@0: RootedObject typeObj(cx, typeObj_); michael@0: michael@0: // Walk the types, building up the toSource() string. michael@0: switch (CType::GetTypeCode(typeObj)) { michael@0: case TYPE_void_t: michael@0: #define DEFINE_TYPE(name, type, ffiType) \ michael@0: case TYPE_##name: michael@0: #include "ctypes/typedefs.h" michael@0: { michael@0: AppendString(result, "ctypes."); michael@0: JSString* nameStr = CType::GetName(cx, typeObj); michael@0: AppendString(result, nameStr); michael@0: break; michael@0: } michael@0: case TYPE_pointer: { michael@0: RootedObject baseType(cx, PointerType::GetBaseType(typeObj)); michael@0: michael@0: // Specialcase ctypes.voidptr_t. michael@0: if (CType::GetTypeCode(baseType) == TYPE_void_t) { michael@0: AppendString(result, "ctypes.voidptr_t"); michael@0: break; michael@0: } michael@0: michael@0: // Recursively build the source string, and append '.ptr'. michael@0: BuildTypeSource(cx, baseType, makeShort, result); michael@0: AppendString(result, ".ptr"); michael@0: break; michael@0: } michael@0: case TYPE_function: { michael@0: FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj); michael@0: michael@0: AppendString(result, "ctypes.FunctionType("); michael@0: michael@0: switch (GetABICode(fninfo->mABI)) { michael@0: case ABI_DEFAULT: michael@0: AppendString(result, "ctypes.default_abi, "); michael@0: break; michael@0: case ABI_STDCALL: michael@0: AppendString(result, "ctypes.stdcall_abi, "); michael@0: break; michael@0: case ABI_WINAPI: michael@0: AppendString(result, "ctypes.winapi_abi, "); michael@0: break; michael@0: case INVALID_ABI: michael@0: MOZ_ASSUME_UNREACHABLE("invalid abi"); michael@0: } michael@0: michael@0: // Recursively build the source string describing the function return and michael@0: // argument types. michael@0: BuildTypeSource(cx, fninfo->mReturnType, true, result); michael@0: michael@0: if (fninfo->mArgTypes.length() > 0) { michael@0: AppendString(result, ", ["); michael@0: for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) { michael@0: BuildTypeSource(cx, fninfo->mArgTypes[i], true, result); michael@0: if (i != fninfo->mArgTypes.length() - 1 || michael@0: fninfo->mIsVariadic) michael@0: AppendString(result, ", "); michael@0: } michael@0: if (fninfo->mIsVariadic) michael@0: AppendString(result, "\"...\""); michael@0: AppendString(result, "]"); michael@0: } michael@0: michael@0: AppendString(result, ")"); michael@0: break; michael@0: } michael@0: case TYPE_array: { michael@0: // Recursively build the source string, and append '.array(n)', michael@0: // where n is the array length, or the empty string if the array length michael@0: // is undefined. michael@0: JSObject* baseType = ArrayType::GetBaseType(typeObj); michael@0: BuildTypeSource(cx, baseType, makeShort, result); michael@0: AppendString(result, ".array("); michael@0: michael@0: size_t length; michael@0: if (ArrayType::GetSafeLength(typeObj, &length)) michael@0: IntegerToString(length, 10, result); michael@0: michael@0: AppendString(result, ")"); michael@0: break; michael@0: } michael@0: case TYPE_struct: { michael@0: JSString* name = CType::GetName(cx, typeObj); michael@0: michael@0: if (makeShort) { michael@0: // Shorten the type declaration by assuming that StructType 't' is bound michael@0: // to an in-scope variable of name 't.name'. michael@0: AppendString(result, name); michael@0: break; michael@0: } michael@0: michael@0: // Write the full struct declaration. michael@0: AppendString(result, "ctypes.StructType(\""); michael@0: AppendString(result, name); michael@0: AppendString(result, "\""); michael@0: michael@0: // If it's an opaque struct, we're done. michael@0: if (!CType::IsSizeDefined(typeObj)) { michael@0: AppendString(result, ")"); michael@0: break; michael@0: } michael@0: michael@0: AppendString(result, ", ["); michael@0: michael@0: const FieldInfoHash* fields = StructType::GetFieldInfo(typeObj); michael@0: size_t length = fields->count(); michael@0: Array fieldsArray; michael@0: if (!fieldsArray.resize(length)) michael@0: break; michael@0: michael@0: for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) michael@0: fieldsArray[r.front().value().mIndex] = &r.front(); michael@0: michael@0: for (size_t i = 0; i < length; ++i) { michael@0: const FieldInfoHash::Entry* entry = fieldsArray[i]; michael@0: AppendString(result, "{ \""); michael@0: AppendString(result, entry->key()); michael@0: AppendString(result, "\": "); michael@0: BuildTypeSource(cx, entry->value().mType, true, result); michael@0: AppendString(result, " }"); michael@0: if (i != length - 1) michael@0: AppendString(result, ", "); michael@0: } michael@0: michael@0: AppendString(result, "])"); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Given a CData object of CType 'typeObj' with binary value 'data', generate a michael@0: // string 'result' such that 'eval(result)' would construct a CData object with michael@0: // the same CType and containing the same binary value. This assumes that any michael@0: // StructType 't' is bound to an in-scope variable of name 't.name'. (This means michael@0: // the type comparison function CType::TypesEqual will return true when michael@0: // comparing the types, since struct equality is determined by strict JSObject michael@0: // pointer equality.) Further, if 'isImplicit' is true, ensure that the michael@0: // resulting string can ImplicitConvert successfully if passed to another data michael@0: // constructor. (This is important when called recursively, since fields of michael@0: // structs and arrays are converted with ImplicitConvert.) michael@0: static bool michael@0: BuildDataSource(JSContext* cx, michael@0: HandleObject typeObj, michael@0: void* data, michael@0: bool isImplicit, michael@0: AutoString& result) michael@0: { michael@0: TypeCode type = CType::GetTypeCode(typeObj); michael@0: switch (type) { michael@0: case TYPE_bool: michael@0: if (*static_cast(data)) michael@0: AppendString(result, "true"); michael@0: else michael@0: AppendString(result, "false"); michael@0: break; michael@0: #define DEFINE_INT_TYPE(name, type, ffiType) \ michael@0: case TYPE_##name: \ michael@0: /* Serialize as a primitive decimal integer. */ \ michael@0: IntegerToString(*static_cast(data), 10, result); \ michael@0: break; michael@0: #define DEFINE_WRAPPED_INT_TYPE(name, type, ffiType) \ michael@0: case TYPE_##name: \ michael@0: /* Serialize as a wrapped decimal integer. */ \ michael@0: if (!NumericLimits::is_signed) \ michael@0: AppendString(result, "ctypes.UInt64(\""); \ michael@0: else \ michael@0: AppendString(result, "ctypes.Int64(\""); \ michael@0: \ michael@0: IntegerToString(*static_cast(data), 10, result); \ michael@0: AppendString(result, "\")"); \ michael@0: break; michael@0: #define DEFINE_FLOAT_TYPE(name, type, ffiType) \ michael@0: case TYPE_##name: { \ michael@0: /* Serialize as a primitive double. */ \ michael@0: double fp = *static_cast(data); \ michael@0: ToCStringBuf cbuf; \ michael@0: char* str = NumberToCString(cx, &cbuf, fp); \ michael@0: if (!str) { \ michael@0: JS_ReportOutOfMemory(cx); \ michael@0: return false; \ michael@0: } \ michael@0: \ michael@0: result.append(str, strlen(str)); \ michael@0: break; \ michael@0: } michael@0: #define DEFINE_CHAR_TYPE(name, type, ffiType) \ michael@0: case TYPE_##name: \ michael@0: /* Serialize as an integer. */ \ michael@0: IntegerToString(*static_cast(data), 10, result); \ michael@0: break; michael@0: #include "ctypes/typedefs.h" michael@0: case TYPE_jschar: { michael@0: // Serialize as a 1-character JS string. michael@0: JSString* str = JS_NewUCStringCopyN(cx, static_cast(data), 1); michael@0: if (!str) michael@0: return false; michael@0: michael@0: // Escape characters, and quote as necessary. michael@0: RootedValue valStr(cx, StringValue(str)); michael@0: JSString* src = JS_ValueToSource(cx, valStr); michael@0: if (!src) michael@0: return false; michael@0: michael@0: AppendString(result, src); michael@0: break; michael@0: } michael@0: case TYPE_pointer: michael@0: case TYPE_function: { michael@0: if (isImplicit) { michael@0: // The result must be able to ImplicitConvert successfully. michael@0: // Wrap in a type constructor, then serialize for ExplicitConvert. michael@0: BuildTypeSource(cx, typeObj, true, result); michael@0: AppendString(result, "("); michael@0: } michael@0: michael@0: // Serialize the pointer value as a wrapped hexadecimal integer. michael@0: uintptr_t ptr = *static_cast(data); michael@0: AppendString(result, "ctypes.UInt64(\"0x"); michael@0: IntegerToString(ptr, 16, result); michael@0: AppendString(result, "\")"); michael@0: michael@0: if (isImplicit) michael@0: AppendString(result, ")"); michael@0: michael@0: break; michael@0: } michael@0: case TYPE_array: { michael@0: // Serialize each element of the array recursively. Each element must michael@0: // be able to ImplicitConvert successfully. michael@0: RootedObject baseType(cx, ArrayType::GetBaseType(typeObj)); michael@0: AppendString(result, "["); michael@0: michael@0: size_t length = ArrayType::GetLength(typeObj); michael@0: size_t elementSize = CType::GetSize(baseType); michael@0: for (size_t i = 0; i < length; ++i) { michael@0: char* element = static_cast(data) + elementSize * i; michael@0: if (!BuildDataSource(cx, baseType, element, true, result)) michael@0: return false; michael@0: michael@0: if (i + 1 < length) michael@0: AppendString(result, ", "); michael@0: } michael@0: AppendString(result, "]"); michael@0: break; michael@0: } michael@0: case TYPE_struct: { michael@0: if (isImplicit) { michael@0: // The result must be able to ImplicitConvert successfully. michael@0: // Serialize the data as an object with properties, rather than michael@0: // a sequence of arguments to the StructType constructor. michael@0: AppendString(result, "{"); michael@0: } michael@0: michael@0: // Serialize each field of the struct recursively. Each field must michael@0: // be able to ImplicitConvert successfully. michael@0: const FieldInfoHash* fields = StructType::GetFieldInfo(typeObj); michael@0: size_t length = fields->count(); michael@0: Array fieldsArray; michael@0: if (!fieldsArray.resize(length)) michael@0: return false; michael@0: michael@0: for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) michael@0: fieldsArray[r.front().value().mIndex] = &r.front(); michael@0: michael@0: for (size_t i = 0; i < length; ++i) { michael@0: const FieldInfoHash::Entry* entry = fieldsArray[i]; michael@0: michael@0: if (isImplicit) { michael@0: AppendString(result, "\""); michael@0: AppendString(result, entry->key()); michael@0: AppendString(result, "\": "); michael@0: } michael@0: michael@0: char* fieldData = static_cast(data) + entry->value().mOffset; michael@0: RootedObject entryType(cx, entry->value().mType); michael@0: if (!BuildDataSource(cx, entryType, fieldData, true, result)) michael@0: return false; michael@0: michael@0: if (i + 1 != length) michael@0: AppendString(result, ", "); michael@0: } michael@0: michael@0: if (isImplicit) michael@0: AppendString(result, "}"); michael@0: michael@0: break; michael@0: } michael@0: case TYPE_void_t: michael@0: MOZ_ASSUME_UNREACHABLE("invalid type"); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /******************************************************************************* michael@0: ** JSAPI callback function implementations michael@0: *******************************************************************************/ michael@0: michael@0: bool michael@0: ConstructAbstract(JSContext* cx, michael@0: unsigned argc, michael@0: jsval* vp) michael@0: { michael@0: // Calling an abstract base class constructor is disallowed. michael@0: JS_ReportError(cx, "cannot construct from abstract type"); michael@0: return false; michael@0: } michael@0: michael@0: /******************************************************************************* michael@0: ** CType implementation michael@0: *******************************************************************************/ michael@0: michael@0: bool michael@0: CType::ConstructData(JSContext* cx, michael@0: unsigned argc, michael@0: jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: // get the callee object... michael@0: RootedObject obj(cx, &args.callee()); michael@0: if (!CType::IsCType(obj)) { michael@0: JS_ReportError(cx, "not a CType"); michael@0: return false; michael@0: } michael@0: michael@0: // How we construct the CData object depends on what type we represent. michael@0: // An instance 'd' of a CData object of type 't' has: michael@0: // * [[Class]] "CData" michael@0: // * __proto__ === t.prototype michael@0: switch (GetTypeCode(obj)) { michael@0: case TYPE_void_t: michael@0: JS_ReportError(cx, "cannot construct from void_t"); michael@0: return false; michael@0: case TYPE_function: michael@0: JS_ReportError(cx, "cannot construct from FunctionType; use FunctionType.ptr instead"); michael@0: return false; michael@0: case TYPE_pointer: michael@0: return PointerType::ConstructData(cx, obj, args); michael@0: case TYPE_array: michael@0: return ArrayType::ConstructData(cx, obj, args); michael@0: case TYPE_struct: michael@0: return StructType::ConstructData(cx, obj, args); michael@0: default: michael@0: return ConstructBasic(cx, obj, args); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: CType::ConstructBasic(JSContext* cx, michael@0: HandleObject obj, michael@0: const CallArgs& args) michael@0: { michael@0: if (args.length() > 1) { michael@0: JS_ReportError(cx, "CType constructor takes zero or one argument"); michael@0: return false; michael@0: } michael@0: michael@0: // construct a CData object michael@0: RootedObject result(cx, CData::Create(cx, obj, NullPtr(), nullptr, true)); michael@0: if (!result) michael@0: return false; michael@0: michael@0: if (args.length() == 1) { michael@0: if (!ExplicitConvert(cx, args[0], obj, CData::GetData(result))) michael@0: return false; michael@0: } michael@0: michael@0: args.rval().setObject(*result); michael@0: return true; michael@0: } michael@0: michael@0: JSObject* michael@0: CType::Create(JSContext* cx, michael@0: HandleObject typeProto, michael@0: HandleObject dataProto, michael@0: TypeCode type, michael@0: JSString* name_, michael@0: jsval size_, michael@0: jsval align_, michael@0: ffi_type* ffiType) michael@0: { michael@0: RootedString name(cx, name_); michael@0: RootedValue size(cx, size_); michael@0: RootedValue align(cx, align_); michael@0: RootedObject parent(cx, JS_GetParent(typeProto)); michael@0: JS_ASSERT(parent); michael@0: michael@0: // Create a CType object with the properties and slots common to all CTypes. michael@0: // Each type object 't' has: michael@0: // * [[Class]] "CType" michael@0: // * __proto__ === 'typeProto'; one of ctypes.{CType,PointerType,ArrayType, michael@0: // StructType}.prototype michael@0: // * A constructor which creates and returns a CData object, containing michael@0: // binary data of the given type. michael@0: // * 'prototype' property: michael@0: // * [[Class]] "CDataProto" michael@0: // * __proto__ === 'dataProto'; an object containing properties and michael@0: // functions common to all CData objects of types derived from michael@0: // 'typeProto'. (For instance, this could be ctypes.CData.prototype michael@0: // for simple types, or something representing structs for StructTypes.) michael@0: // * 'constructor' property === 't' michael@0: // * Additional properties specified by 'ps', as appropriate for the michael@0: // specific type instance 't'. michael@0: RootedObject typeObj(cx, JS_NewObject(cx, &sCTypeClass, typeProto, parent)); michael@0: if (!typeObj) michael@0: return nullptr; michael@0: michael@0: // Set up the reserved slots. michael@0: JS_SetReservedSlot(typeObj, SLOT_TYPECODE, INT_TO_JSVAL(type)); michael@0: if (ffiType) michael@0: JS_SetReservedSlot(typeObj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(ffiType)); michael@0: if (name) michael@0: JS_SetReservedSlot(typeObj, SLOT_NAME, STRING_TO_JSVAL(name)); michael@0: JS_SetReservedSlot(typeObj, SLOT_SIZE, size); michael@0: JS_SetReservedSlot(typeObj, SLOT_ALIGN, align); michael@0: michael@0: if (dataProto) { michael@0: // Set up the 'prototype' and 'prototype.constructor' properties. michael@0: RootedObject prototype(cx, JS_NewObject(cx, &sCDataProtoClass, dataProto, parent)); michael@0: if (!prototype) michael@0: return nullptr; michael@0: michael@0: if (!JS_DefineProperty(cx, prototype, "constructor", typeObj, michael@0: JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: return nullptr; michael@0: michael@0: // Set the 'prototype' object. michael@0: //if (!JS_FreezeObject(cx, prototype)) // XXX fixme - see bug 541212! michael@0: // return nullptr; michael@0: JS_SetReservedSlot(typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype)); michael@0: } michael@0: michael@0: if (!JS_FreezeObject(cx, typeObj)) michael@0: return nullptr; michael@0: michael@0: // Assert a sanity check on size and alignment: size % alignment should always michael@0: // be zero. michael@0: JS_ASSERT_IF(IsSizeDefined(typeObj), michael@0: GetSize(typeObj) % GetAlignment(typeObj) == 0); michael@0: michael@0: return typeObj; michael@0: } michael@0: michael@0: JSObject* michael@0: CType::DefineBuiltin(JSContext* cx, michael@0: JSObject* parent_, michael@0: const char* propName, michael@0: JSObject* typeProto_, michael@0: JSObject* dataProto_, michael@0: const char* name, michael@0: TypeCode type, michael@0: jsval size_, michael@0: jsval align_, michael@0: ffi_type* ffiType) michael@0: { michael@0: RootedObject parent(cx, parent_); michael@0: RootedObject typeProto(cx, typeProto_); michael@0: RootedObject dataProto(cx, dataProto_); michael@0: RootedValue size(cx, size_); michael@0: RootedValue align(cx, align_); michael@0: michael@0: RootedString nameStr(cx, JS_NewStringCopyZ(cx, name)); michael@0: if (!nameStr) michael@0: return nullptr; michael@0: michael@0: // Create a new CType object with the common properties and slots. michael@0: RootedObject typeObj(cx, Create(cx, typeProto, dataProto, type, nameStr, size, align, ffiType)); michael@0: if (!typeObj) michael@0: return nullptr; michael@0: michael@0: // Define the CType as a 'propName' property on 'parent'. michael@0: if (!JS_DefineProperty(cx, parent, propName, typeObj, michael@0: JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: return nullptr; michael@0: michael@0: return typeObj; michael@0: } michael@0: michael@0: void michael@0: CType::Finalize(JSFreeOp *fop, JSObject* obj) michael@0: { michael@0: // Make sure our TypeCode slot is legit. If it's not, bail. michael@0: jsval slot = JS_GetReservedSlot(obj, SLOT_TYPECODE); michael@0: if (JSVAL_IS_VOID(slot)) michael@0: return; michael@0: michael@0: // The contents of our slots depends on what kind of type we are. michael@0: switch (TypeCode(JSVAL_TO_INT(slot))) { michael@0: case TYPE_function: { michael@0: // Free the FunctionInfo. michael@0: slot = JS_GetReservedSlot(obj, SLOT_FNINFO); michael@0: if (!JSVAL_IS_VOID(slot)) michael@0: FreeOp::get(fop)->delete_(static_cast(JSVAL_TO_PRIVATE(slot))); michael@0: break; michael@0: } michael@0: michael@0: case TYPE_struct: { michael@0: // Free the FieldInfoHash table. michael@0: slot = JS_GetReservedSlot(obj, SLOT_FIELDINFO); michael@0: if (!JSVAL_IS_VOID(slot)) { michael@0: void* info = JSVAL_TO_PRIVATE(slot); michael@0: FreeOp::get(fop)->delete_(static_cast(info)); michael@0: } michael@0: } michael@0: michael@0: // Fall through. michael@0: case TYPE_array: { michael@0: // Free the ffi_type info. michael@0: slot = JS_GetReservedSlot(obj, SLOT_FFITYPE); michael@0: if (!JSVAL_IS_VOID(slot)) { michael@0: ffi_type* ffiType = static_cast(JSVAL_TO_PRIVATE(slot)); michael@0: FreeOp::get(fop)->free_(ffiType->elements); michael@0: FreeOp::get(fop)->delete_(ffiType); michael@0: } michael@0: michael@0: break; michael@0: } michael@0: default: michael@0: // Nothing to do here. michael@0: break; michael@0: } michael@0: } michael@0: michael@0: void michael@0: CType::Trace(JSTracer* trc, JSObject* obj) michael@0: { michael@0: // Make sure our TypeCode slot is legit. If it's not, bail. michael@0: jsval slot = obj->getSlot(SLOT_TYPECODE); michael@0: if (JSVAL_IS_VOID(slot)) michael@0: return; michael@0: michael@0: // The contents of our slots depends on what kind of type we are. michael@0: switch (TypeCode(JSVAL_TO_INT(slot))) { michael@0: case TYPE_struct: { michael@0: slot = obj->getReservedSlot(SLOT_FIELDINFO); michael@0: if (JSVAL_IS_VOID(slot)) michael@0: return; michael@0: michael@0: FieldInfoHash* fields = michael@0: static_cast(JSVAL_TO_PRIVATE(slot)); michael@0: for (FieldInfoHash::Enum e(*fields); !e.empty(); e.popFront()) { michael@0: JSString *key = e.front().key(); michael@0: JS_CallStringTracer(trc, &key, "fieldName"); michael@0: if (key != e.front().key()) michael@0: e.rekeyFront(JS_ASSERT_STRING_IS_FLAT(key)); michael@0: JS_CallHeapObjectTracer(trc, &e.front().value().mType, "fieldType"); michael@0: } michael@0: michael@0: break; michael@0: } michael@0: case TYPE_function: { michael@0: // Check if we have a FunctionInfo. michael@0: slot = obj->getReservedSlot(SLOT_FNINFO); michael@0: if (JSVAL_IS_VOID(slot)) michael@0: return; michael@0: michael@0: FunctionInfo* fninfo = static_cast(JSVAL_TO_PRIVATE(slot)); michael@0: JS_ASSERT(fninfo); michael@0: michael@0: // Identify our objects to the tracer. michael@0: JS_CallHeapObjectTracer(trc, &fninfo->mABI, "abi"); michael@0: JS_CallHeapObjectTracer(trc, &fninfo->mReturnType, "returnType"); michael@0: for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) michael@0: JS_CallHeapObjectTracer(trc, &fninfo->mArgTypes[i], "argType"); michael@0: michael@0: break; michael@0: } michael@0: default: michael@0: // Nothing to do here. michael@0: break; michael@0: } michael@0: } michael@0: michael@0: bool michael@0: CType::IsCType(JSObject* obj) michael@0: { michael@0: return JS_GetClass(obj) == &sCTypeClass; michael@0: } michael@0: michael@0: bool michael@0: CType::IsCTypeProto(JSObject* obj) michael@0: { michael@0: return JS_GetClass(obj) == &sCTypeProtoClass; michael@0: } michael@0: michael@0: TypeCode michael@0: CType::GetTypeCode(JSObject* typeObj) michael@0: { michael@0: JS_ASSERT(IsCType(typeObj)); michael@0: michael@0: jsval result = JS_GetReservedSlot(typeObj, SLOT_TYPECODE); michael@0: return TypeCode(JSVAL_TO_INT(result)); michael@0: } michael@0: michael@0: bool michael@0: CType::TypesEqual(JSObject* t1, JSObject* t2) michael@0: { michael@0: JS_ASSERT(IsCType(t1) && IsCType(t2)); michael@0: michael@0: // Fast path: check for object equality. michael@0: if (t1 == t2) michael@0: return true; michael@0: michael@0: // First, perform shallow comparison. michael@0: TypeCode c1 = GetTypeCode(t1); michael@0: TypeCode c2 = GetTypeCode(t2); michael@0: if (c1 != c2) michael@0: return false; michael@0: michael@0: // Determine whether the types require shallow or deep comparison. michael@0: switch (c1) { michael@0: case TYPE_pointer: { michael@0: // Compare base types. michael@0: JSObject* b1 = PointerType::GetBaseType(t1); michael@0: JSObject* b2 = PointerType::GetBaseType(t2); michael@0: return TypesEqual(b1, b2); michael@0: } michael@0: case TYPE_function: { michael@0: FunctionInfo* f1 = FunctionType::GetFunctionInfo(t1); michael@0: FunctionInfo* f2 = FunctionType::GetFunctionInfo(t2); michael@0: michael@0: // Compare abi, return type, and argument types. michael@0: if (f1->mABI != f2->mABI) michael@0: return false; michael@0: michael@0: if (!TypesEqual(f1->mReturnType, f2->mReturnType)) michael@0: return false; michael@0: michael@0: if (f1->mArgTypes.length() != f2->mArgTypes.length()) michael@0: return false; michael@0: michael@0: if (f1->mIsVariadic != f2->mIsVariadic) michael@0: return false; michael@0: michael@0: for (size_t i = 0; i < f1->mArgTypes.length(); ++i) { michael@0: if (!TypesEqual(f1->mArgTypes[i], f2->mArgTypes[i])) michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: case TYPE_array: { michael@0: // Compare length, then base types. michael@0: // An undefined length array matches other undefined length arrays. michael@0: size_t s1 = 0, s2 = 0; michael@0: bool d1 = ArrayType::GetSafeLength(t1, &s1); michael@0: bool d2 = ArrayType::GetSafeLength(t2, &s2); michael@0: if (d1 != d2 || (d1 && s1 != s2)) michael@0: return false; michael@0: michael@0: JSObject* b1 = ArrayType::GetBaseType(t1); michael@0: JSObject* b2 = ArrayType::GetBaseType(t2); michael@0: return TypesEqual(b1, b2); michael@0: } michael@0: case TYPE_struct: michael@0: // Require exact type object equality. michael@0: return false; michael@0: default: michael@0: // Shallow comparison is sufficient. michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: bool michael@0: CType::GetSafeSize(JSObject* obj, size_t* result) michael@0: { michael@0: JS_ASSERT(CType::IsCType(obj)); michael@0: michael@0: jsval size = JS_GetReservedSlot(obj, SLOT_SIZE); michael@0: michael@0: // The "size" property can be an int, a double, or JSVAL_VOID michael@0: // (for arrays of undefined length), and must always fit in a size_t. michael@0: if (JSVAL_IS_INT(size)) { michael@0: *result = JSVAL_TO_INT(size); michael@0: return true; michael@0: } michael@0: if (JSVAL_IS_DOUBLE(size)) { michael@0: *result = Convert(JSVAL_TO_DOUBLE(size)); michael@0: return true; michael@0: } michael@0: michael@0: JS_ASSERT(JSVAL_IS_VOID(size)); michael@0: return false; michael@0: } michael@0: michael@0: size_t michael@0: CType::GetSize(JSObject* obj) michael@0: { michael@0: JS_ASSERT(CType::IsCType(obj)); michael@0: michael@0: jsval size = JS_GetReservedSlot(obj, SLOT_SIZE); michael@0: michael@0: JS_ASSERT(!JSVAL_IS_VOID(size)); michael@0: michael@0: // The "size" property can be an int, a double, or JSVAL_VOID michael@0: // (for arrays of undefined length), and must always fit in a size_t. michael@0: // For callers who know it can never be JSVAL_VOID, return a size_t directly. michael@0: if (JSVAL_IS_INT(size)) michael@0: return JSVAL_TO_INT(size); michael@0: return Convert(JSVAL_TO_DOUBLE(size)); michael@0: } michael@0: michael@0: bool michael@0: CType::IsSizeDefined(JSObject* obj) michael@0: { michael@0: JS_ASSERT(CType::IsCType(obj)); michael@0: michael@0: jsval size = JS_GetReservedSlot(obj, SLOT_SIZE); michael@0: michael@0: // The "size" property can be an int, a double, or JSVAL_VOID michael@0: // (for arrays of undefined length), and must always fit in a size_t. michael@0: JS_ASSERT(JSVAL_IS_INT(size) || JSVAL_IS_DOUBLE(size) || JSVAL_IS_VOID(size)); michael@0: return !JSVAL_IS_VOID(size); michael@0: } michael@0: michael@0: size_t michael@0: CType::GetAlignment(JSObject* obj) michael@0: { michael@0: JS_ASSERT(CType::IsCType(obj)); michael@0: michael@0: jsval slot = JS_GetReservedSlot(obj, SLOT_ALIGN); michael@0: return static_cast(JSVAL_TO_INT(slot)); michael@0: } michael@0: michael@0: ffi_type* michael@0: CType::GetFFIType(JSContext* cx, JSObject* obj) michael@0: { michael@0: JS_ASSERT(CType::IsCType(obj)); michael@0: michael@0: jsval slot = JS_GetReservedSlot(obj, SLOT_FFITYPE); michael@0: michael@0: if (!JSVAL_IS_VOID(slot)) { michael@0: return static_cast(JSVAL_TO_PRIVATE(slot)); michael@0: } michael@0: michael@0: AutoPtr result; michael@0: switch (CType::GetTypeCode(obj)) { michael@0: case TYPE_array: michael@0: result = ArrayType::BuildFFIType(cx, obj); michael@0: break; michael@0: michael@0: case TYPE_struct: michael@0: result = StructType::BuildFFIType(cx, obj); michael@0: break; michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("simple types must have an ffi_type"); michael@0: } michael@0: michael@0: if (!result) michael@0: return nullptr; michael@0: JS_SetReservedSlot(obj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(result.get())); michael@0: return result.forget(); michael@0: } michael@0: michael@0: JSString* michael@0: CType::GetName(JSContext* cx, HandleObject obj) michael@0: { michael@0: JS_ASSERT(CType::IsCType(obj)); michael@0: michael@0: jsval string = JS_GetReservedSlot(obj, SLOT_NAME); michael@0: if (!JSVAL_IS_VOID(string)) michael@0: return JSVAL_TO_STRING(string); michael@0: michael@0: // Build the type name lazily. michael@0: JSString* name = BuildTypeName(cx, obj); michael@0: if (!name) michael@0: return nullptr; michael@0: JS_SetReservedSlot(obj, SLOT_NAME, STRING_TO_JSVAL(name)); michael@0: return name; michael@0: } michael@0: michael@0: JSObject* michael@0: CType::GetProtoFromCtor(JSObject* obj, CTypeProtoSlot slot) michael@0: { michael@0: // Get ctypes.{Pointer,Array,Struct}Type.prototype from a reserved slot michael@0: // on the type constructor. michael@0: jsval protoslot = js::GetFunctionNativeReserved(obj, SLOT_FN_CTORPROTO); michael@0: JSObject* proto = &protoslot.toObject(); michael@0: JS_ASSERT(proto); michael@0: JS_ASSERT(CType::IsCTypeProto(proto)); michael@0: michael@0: // Get the desired prototype. michael@0: jsval result = JS_GetReservedSlot(proto, slot); michael@0: return &result.toObject(); michael@0: } michael@0: michael@0: JSObject* michael@0: CType::GetProtoFromType(JSContext* cx, JSObject* objArg, CTypeProtoSlot slot) michael@0: { michael@0: JS_ASSERT(IsCType(objArg)); michael@0: RootedObject obj(cx, objArg); michael@0: michael@0: // Get the prototype of the type object. michael@0: RootedObject proto(cx); michael@0: if (!JS_GetPrototype(cx, obj, &proto)) michael@0: return nullptr; michael@0: JS_ASSERT(proto); michael@0: JS_ASSERT(CType::IsCTypeProto(proto)); michael@0: michael@0: // Get the requested ctypes.{Pointer,Array,Struct,Function}Type.prototype. michael@0: jsval result = JS_GetReservedSlot(proto, slot); michael@0: JS_ASSERT(!JSVAL_IS_PRIMITIVE(result)); michael@0: return JSVAL_TO_OBJECT(result); michael@0: } michael@0: michael@0: bool michael@0: CType::IsCTypeOrProto(HandleValue v) michael@0: { michael@0: if (!v.isObject()) michael@0: return false; michael@0: JSObject* obj = &v.toObject(); michael@0: return CType::IsCType(obj) || CType::IsCTypeProto(obj); michael@0: } michael@0: michael@0: bool michael@0: CType::PrototypeGetter(JSContext* cx, JS::CallArgs args) michael@0: { michael@0: RootedObject obj(cx, &args.thisv().toObject()); michael@0: unsigned slot = CType::IsCTypeProto(obj) ? (unsigned) SLOT_OURDATAPROTO michael@0: : (unsigned) SLOT_PROTO; michael@0: args.rval().set(JS_GetReservedSlot(obj, slot)); michael@0: MOZ_ASSERT(args.rval().isObject() || args.rval().isUndefined()); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CType::IsCType(HandleValue v) michael@0: { michael@0: return v.isObject() && CType::IsCType(&v.toObject()); michael@0: } michael@0: michael@0: bool michael@0: CType::NameGetter(JSContext* cx, JS::CallArgs args) michael@0: { michael@0: RootedObject obj(cx, &args.thisv().toObject()); michael@0: JSString* name = CType::GetName(cx, obj); michael@0: if (!name) michael@0: return false; michael@0: michael@0: args.rval().setString(name); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CType::SizeGetter(JSContext* cx, JS::CallArgs args) michael@0: { michael@0: RootedObject obj(cx, &args.thisv().toObject()); michael@0: args.rval().set(JS_GetReservedSlot(obj, SLOT_SIZE)); michael@0: MOZ_ASSERT(args.rval().isNumber() || args.rval().isUndefined()); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CType::PtrGetter(JSContext* cx, JS::CallArgs args) michael@0: { michael@0: RootedObject obj(cx, &args.thisv().toObject()); michael@0: JSObject* pointerType = PointerType::CreateInternal(cx, obj); michael@0: if (!pointerType) michael@0: return false; michael@0: michael@0: args.rval().setObject(*pointerType); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CType::CreateArray(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: RootedObject baseType(cx, JS_THIS_OBJECT(cx, vp)); michael@0: if (!baseType) michael@0: return false; michael@0: if (!CType::IsCType(baseType)) { michael@0: JS_ReportError(cx, "not a CType"); michael@0: return false; michael@0: } michael@0: michael@0: // Construct and return a new ArrayType object. michael@0: if (args.length() > 1) { michael@0: JS_ReportError(cx, "array takes zero or one argument"); michael@0: return false; michael@0: } michael@0: michael@0: // Convert the length argument to a size_t. michael@0: size_t length = 0; michael@0: if (args.length() == 1 && !jsvalToSize(cx, args[0], false, &length)) { michael@0: JS_ReportError(cx, "argument must be a nonnegative integer"); michael@0: return false; michael@0: } michael@0: michael@0: JSObject* result = ArrayType::CreateInternal(cx, baseType, length, args.length() == 1); michael@0: if (!result) michael@0: return false; michael@0: michael@0: args.rval().setObject(*result); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CType::ToString(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: RootedObject obj(cx, JS_THIS_OBJECT(cx, vp)); michael@0: if (!obj) michael@0: return false; michael@0: if (!CType::IsCType(obj) && !CType::IsCTypeProto(obj)) { michael@0: JS_ReportError(cx, "not a CType"); michael@0: return false; michael@0: } michael@0: michael@0: // Create the appropriate string depending on whether we're sCTypeClass or michael@0: // sCTypeProtoClass. michael@0: JSString* result; michael@0: if (CType::IsCType(obj)) { michael@0: AutoString type; michael@0: AppendString(type, "type "); michael@0: AppendString(type, GetName(cx, obj)); michael@0: result = NewUCString(cx, type); michael@0: } michael@0: else { michael@0: result = JS_NewStringCopyZ(cx, "[CType proto object]"); michael@0: } michael@0: if (!result) michael@0: return false; michael@0: michael@0: args.rval().setString(result); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CType::ToSource(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JSObject* obj = JS_THIS_OBJECT(cx, vp); michael@0: if (!obj) michael@0: return false; michael@0: if (!CType::IsCType(obj) && !CType::IsCTypeProto(obj)) michael@0: { michael@0: JS_ReportError(cx, "not a CType"); michael@0: return false; michael@0: } michael@0: michael@0: // Create the appropriate string depending on whether we're sCTypeClass or michael@0: // sCTypeProtoClass. michael@0: JSString* result; michael@0: if (CType::IsCType(obj)) { michael@0: AutoString source; michael@0: BuildTypeSource(cx, obj, false, source); michael@0: result = NewUCString(cx, source); michael@0: } else { michael@0: result = JS_NewStringCopyZ(cx, "[CType proto object]"); michael@0: } michael@0: if (!result) michael@0: return false; michael@0: michael@0: args.rval().setString(result); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CType::HasInstance(JSContext* cx, HandleObject obj, MutableHandleValue v, bool* bp) michael@0: { michael@0: JS_ASSERT(CType::IsCType(obj)); michael@0: michael@0: jsval slot = JS_GetReservedSlot(obj, SLOT_PROTO); michael@0: JS::Rooted prototype(cx, &slot.toObject()); michael@0: JS_ASSERT(prototype); michael@0: JS_ASSERT(CData::IsCDataProto(prototype)); michael@0: michael@0: *bp = false; michael@0: if (JSVAL_IS_PRIMITIVE(v)) michael@0: return true; michael@0: michael@0: RootedObject proto(cx, &v.toObject()); michael@0: for (;;) { michael@0: if (!JS_GetPrototype(cx, proto, &proto)) michael@0: return false; michael@0: if (!proto) michael@0: break; michael@0: if (proto == prototype) { michael@0: *bp = true; michael@0: break; michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: static JSObject* michael@0: CType::GetGlobalCTypes(JSContext* cx, JSObject* objArg) michael@0: { michael@0: JS_ASSERT(CType::IsCType(objArg)); michael@0: michael@0: RootedObject obj(cx, objArg); michael@0: RootedObject objTypeProto(cx); michael@0: if (!JS_GetPrototype(cx, obj, &objTypeProto)) michael@0: return nullptr; michael@0: JS_ASSERT(objTypeProto); michael@0: JS_ASSERT(CType::IsCTypeProto(objTypeProto)); michael@0: michael@0: jsval valCTypes = JS_GetReservedSlot(objTypeProto, SLOT_CTYPES); michael@0: JS_ASSERT(!JSVAL_IS_PRIMITIVE(valCTypes)); michael@0: michael@0: JS_ASSERT(!JSVAL_IS_PRIMITIVE(valCTypes)); michael@0: return &valCTypes.toObject(); michael@0: } michael@0: michael@0: /******************************************************************************* michael@0: ** ABI implementation michael@0: *******************************************************************************/ michael@0: michael@0: bool michael@0: ABI::IsABI(JSObject* obj) michael@0: { michael@0: return JS_GetClass(obj) == &sCABIClass; michael@0: } michael@0: michael@0: bool michael@0: ABI::ToSource(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 0) { michael@0: JS_ReportError(cx, "toSource takes zero arguments"); michael@0: return false; michael@0: } michael@0: michael@0: JSObject* obj = JS_THIS_OBJECT(cx, vp); michael@0: if (!obj) michael@0: return false; michael@0: if (!ABI::IsABI(obj)) { michael@0: JS_ReportError(cx, "not an ABI"); michael@0: return false; michael@0: } michael@0: michael@0: JSString* result; michael@0: switch (GetABICode(obj)) { michael@0: case ABI_DEFAULT: michael@0: result = JS_NewStringCopyZ(cx, "ctypes.default_abi"); michael@0: break; michael@0: case ABI_STDCALL: michael@0: result = JS_NewStringCopyZ(cx, "ctypes.stdcall_abi"); michael@0: break; michael@0: case ABI_WINAPI: michael@0: result = JS_NewStringCopyZ(cx, "ctypes.winapi_abi"); michael@0: break; michael@0: default: michael@0: JS_ReportError(cx, "not a valid ABICode"); michael@0: return false; michael@0: } michael@0: if (!result) michael@0: return false; michael@0: michael@0: args.rval().setString(result); michael@0: return true; michael@0: } michael@0: michael@0: michael@0: /******************************************************************************* michael@0: ** PointerType implementation michael@0: *******************************************************************************/ michael@0: michael@0: bool michael@0: PointerType::Create(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: // Construct and return a new PointerType object. michael@0: if (args.length() != 1) { michael@0: JS_ReportError(cx, "PointerType takes one argument"); michael@0: return false; michael@0: } michael@0: michael@0: jsval arg = args[0]; michael@0: RootedObject obj(cx); michael@0: if (JSVAL_IS_PRIMITIVE(arg) || !CType::IsCType(obj = &arg.toObject())) { michael@0: JS_ReportError(cx, "first argument must be a CType"); michael@0: return false; michael@0: } michael@0: michael@0: JSObject* result = CreateInternal(cx, obj); michael@0: if (!result) michael@0: return false; michael@0: michael@0: args.rval().setObject(*result); michael@0: return true; michael@0: } michael@0: michael@0: JSObject* michael@0: PointerType::CreateInternal(JSContext* cx, HandleObject baseType) michael@0: { michael@0: // check if we have a cached PointerType on our base CType. michael@0: jsval slot = JS_GetReservedSlot(baseType, SLOT_PTR); michael@0: if (!slot.isUndefined()) michael@0: return &slot.toObject(); michael@0: michael@0: // Get ctypes.PointerType.prototype and the common prototype for CData objects michael@0: // of this type, or ctypes.FunctionType.prototype for function pointers. michael@0: CTypeProtoSlot slotId = CType::GetTypeCode(baseType) == TYPE_function ? michael@0: SLOT_FUNCTIONDATAPROTO : SLOT_POINTERDATAPROTO; michael@0: RootedObject dataProto(cx, CType::GetProtoFromType(cx, baseType, slotId)); michael@0: if (!dataProto) michael@0: return nullptr; michael@0: RootedObject typeProto(cx, CType::GetProtoFromType(cx, baseType, SLOT_POINTERPROTO)); michael@0: if (!typeProto) michael@0: return nullptr; michael@0: michael@0: // Create a new CType object with the common properties and slots. michael@0: JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_pointer, michael@0: nullptr, INT_TO_JSVAL(sizeof(void*)), michael@0: INT_TO_JSVAL(ffi_type_pointer.alignment), michael@0: &ffi_type_pointer); michael@0: if (!typeObj) michael@0: return nullptr; michael@0: michael@0: // Set the target type. (This will be 'null' for an opaque pointer type.) michael@0: JS_SetReservedSlot(typeObj, SLOT_TARGET_T, OBJECT_TO_JSVAL(baseType)); michael@0: michael@0: // Finally, cache our newly-created PointerType on our pointed-to CType. michael@0: JS_SetReservedSlot(baseType, SLOT_PTR, OBJECT_TO_JSVAL(typeObj)); michael@0: michael@0: return typeObj; michael@0: } michael@0: michael@0: bool michael@0: PointerType::ConstructData(JSContext* cx, michael@0: HandleObject obj, michael@0: const CallArgs& args) michael@0: { michael@0: if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_pointer) { michael@0: JS_ReportError(cx, "not a PointerType"); michael@0: return false; michael@0: } michael@0: michael@0: if (args.length() > 3) { michael@0: JS_ReportError(cx, "constructor takes 0, 1, 2, or 3 arguments"); michael@0: return false; michael@0: } michael@0: michael@0: RootedObject result(cx, CData::Create(cx, obj, NullPtr(), nullptr, true)); michael@0: if (!result) michael@0: return false; michael@0: michael@0: // Set return value early, must not observe *vp after michael@0: args.rval().setObject(*result); michael@0: michael@0: // There are 3 things that we might be creating here: michael@0: // 1 - A null pointer (no arguments) michael@0: // 2 - An initialized pointer (1 argument) michael@0: // 3 - A closure (1-3 arguments) michael@0: // michael@0: // The API doesn't give us a perfect way to distinguish 2 and 3, but the michael@0: // heuristics we use should be fine. michael@0: michael@0: // michael@0: // Case 1 - Null pointer michael@0: // michael@0: if (args.length() == 0) michael@0: return true; michael@0: michael@0: // Analyze the arguments a bit to decide what to do next. michael@0: RootedObject baseObj(cx, PointerType::GetBaseType(obj)); michael@0: bool looksLikeClosure = CType::GetTypeCode(baseObj) == TYPE_function && michael@0: args[0].isObject() && michael@0: JS_ObjectIsCallable(cx, &args[0].toObject()); michael@0: michael@0: // michael@0: // Case 2 - Initialized pointer michael@0: // michael@0: if (!looksLikeClosure) { michael@0: if (args.length() != 1) { michael@0: JS_ReportError(cx, "first argument must be a function"); michael@0: return false; michael@0: } michael@0: return ExplicitConvert(cx, args[0], obj, CData::GetData(result)); michael@0: } michael@0: michael@0: // michael@0: // Case 3 - Closure michael@0: // michael@0: michael@0: // The second argument is an optional 'this' parameter with which to invoke michael@0: // the given js function. Callers may leave this blank, or pass null if they michael@0: // wish to pass the third argument. michael@0: RootedObject thisObj(cx, nullptr); michael@0: if (args.length() >= 2) { michael@0: if (args[1].isNull()) { michael@0: thisObj = nullptr; michael@0: } else if (!JSVAL_IS_PRIMITIVE(args[1])) { michael@0: thisObj = &args[1].toObject(); michael@0: } else if (!JS_ValueToObject(cx, args[1], &thisObj)) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: // The third argument is an optional error sentinel that js-ctypes will return michael@0: // if an exception is raised while executing the closure. The type must match michael@0: // the return type of the callback. michael@0: jsval errVal = JSVAL_VOID; michael@0: if (args.length() == 3) michael@0: errVal = args[2]; michael@0: michael@0: RootedObject fnObj(cx, &args[0].toObject()); michael@0: return FunctionType::ConstructData(cx, baseObj, result, fnObj, thisObj, errVal); michael@0: } michael@0: michael@0: JSObject* michael@0: PointerType::GetBaseType(JSObject* obj) michael@0: { michael@0: JS_ASSERT(CType::GetTypeCode(obj) == TYPE_pointer); michael@0: michael@0: jsval type = JS_GetReservedSlot(obj, SLOT_TARGET_T); michael@0: JS_ASSERT(!type.isNull()); michael@0: return &type.toObject(); michael@0: } michael@0: michael@0: bool michael@0: PointerType::IsPointerType(HandleValue v) michael@0: { michael@0: if (!v.isObject()) michael@0: return false; michael@0: JSObject* obj = &v.toObject(); michael@0: return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_pointer; michael@0: } michael@0: michael@0: bool michael@0: PointerType::IsPointer(HandleValue v) michael@0: { michael@0: if (!v.isObject()) michael@0: return false; michael@0: JSObject* obj = &v.toObject(); michael@0: return CData::IsCData(obj) && CType::GetTypeCode(CData::GetCType(obj)) == TYPE_pointer; michael@0: } michael@0: michael@0: bool michael@0: PointerType::TargetTypeGetter(JSContext* cx, JS::CallArgs args) michael@0: { michael@0: RootedObject obj(cx, &args.thisv().toObject()); michael@0: args.rval().set(JS_GetReservedSlot(obj, SLOT_TARGET_T)); michael@0: MOZ_ASSERT(args.rval().isObject()); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: PointerType::IsNull(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JSObject* obj = JS_THIS_OBJECT(cx, vp); michael@0: if (!obj) michael@0: return false; michael@0: if (!CData::IsCData(obj)) { michael@0: JS_ReportError(cx, "not a CData"); michael@0: return false; michael@0: } michael@0: michael@0: // Get pointer type and base type. michael@0: JSObject* typeObj = CData::GetCType(obj); michael@0: if (CType::GetTypeCode(typeObj) != TYPE_pointer) { michael@0: JS_ReportError(cx, "not a PointerType"); michael@0: return false; michael@0: } michael@0: michael@0: void* data = *static_cast(CData::GetData(obj)); michael@0: args.rval().setBoolean(data == nullptr); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: PointerType::OffsetBy(JSContext* cx, const CallArgs& args, int offset) michael@0: { michael@0: JSObject* obj = JS_THIS_OBJECT(cx, args.base()); michael@0: if (!obj) michael@0: return false; michael@0: if (!CData::IsCData(obj)) { michael@0: JS_ReportError(cx, "not a CData"); michael@0: return false; michael@0: } michael@0: michael@0: RootedObject typeObj(cx, CData::GetCType(obj)); michael@0: if (CType::GetTypeCode(typeObj) != TYPE_pointer) { michael@0: JS_ReportError(cx, "not a PointerType"); michael@0: return false; michael@0: } michael@0: michael@0: RootedObject baseType(cx, PointerType::GetBaseType(typeObj)); michael@0: if (!CType::IsSizeDefined(baseType)) { michael@0: JS_ReportError(cx, "cannot modify pointer of undefined size"); michael@0: return false; michael@0: } michael@0: michael@0: size_t elementSize = CType::GetSize(baseType); michael@0: char* data = static_cast(*static_cast(CData::GetData(obj))); michael@0: void* address = data + offset * elementSize; michael@0: michael@0: // Create a PointerType CData object containing the new address. michael@0: JSObject* result = CData::Create(cx, typeObj, NullPtr(), &address, true); michael@0: if (!result) michael@0: return false; michael@0: michael@0: args.rval().setObject(*result); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: PointerType::Increment(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return OffsetBy(cx, args, 1); michael@0: } michael@0: michael@0: bool michael@0: PointerType::Decrement(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return OffsetBy(cx, args, -1); michael@0: } michael@0: michael@0: bool michael@0: PointerType::ContentsGetter(JSContext* cx, JS::CallArgs args) michael@0: { michael@0: RootedObject obj(cx, &args.thisv().toObject()); michael@0: RootedObject baseType(cx, GetBaseType(CData::GetCType(obj))); michael@0: if (!CType::IsSizeDefined(baseType)) { michael@0: JS_ReportError(cx, "cannot get contents of undefined size"); michael@0: return false; michael@0: } michael@0: michael@0: void* data = *static_cast(CData::GetData(obj)); michael@0: if (data == nullptr) { michael@0: JS_ReportError(cx, "cannot read contents of null pointer"); michael@0: return false; michael@0: } michael@0: michael@0: RootedValue result(cx); michael@0: if (!ConvertToJS(cx, baseType, NullPtr(), data, false, false, result.address())) michael@0: return false; michael@0: michael@0: args.rval().set(result); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: PointerType::ContentsSetter(JSContext* cx, JS::CallArgs args) michael@0: { michael@0: RootedObject obj(cx, &args.thisv().toObject()); michael@0: RootedObject baseType(cx, GetBaseType(CData::GetCType(obj))); michael@0: if (!CType::IsSizeDefined(baseType)) { michael@0: JS_ReportError(cx, "cannot set contents of undefined size"); michael@0: return false; michael@0: } michael@0: michael@0: void* data = *static_cast(CData::GetData(obj)); michael@0: if (data == nullptr) { michael@0: JS_ReportError(cx, "cannot write contents to null pointer"); michael@0: return false; michael@0: } michael@0: michael@0: args.rval().setUndefined(); michael@0: return ImplicitConvert(cx, args.get(0), baseType, data, false, nullptr); michael@0: } michael@0: michael@0: /******************************************************************************* michael@0: ** ArrayType implementation michael@0: *******************************************************************************/ michael@0: michael@0: bool michael@0: ArrayType::Create(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: // Construct and return a new ArrayType object. michael@0: if (args.length() < 1 || args.length() > 2) { michael@0: JS_ReportError(cx, "ArrayType takes one or two arguments"); michael@0: return false; michael@0: } michael@0: michael@0: if (JSVAL_IS_PRIMITIVE(args[0]) || michael@0: !CType::IsCType(&args[0].toObject())) { michael@0: JS_ReportError(cx, "first argument must be a CType"); michael@0: return false; michael@0: } michael@0: michael@0: // Convert the length argument to a size_t. michael@0: size_t length = 0; michael@0: if (args.length() == 2 && !jsvalToSize(cx, args[1], false, &length)) { michael@0: JS_ReportError(cx, "second argument must be a nonnegative integer"); michael@0: return false; michael@0: } michael@0: michael@0: RootedObject baseType(cx, &args[0].toObject()); michael@0: JSObject* result = CreateInternal(cx, baseType, length, args.length() == 2); michael@0: if (!result) michael@0: return false; michael@0: michael@0: args.rval().setObject(*result); michael@0: return true; michael@0: } michael@0: michael@0: JSObject* michael@0: ArrayType::CreateInternal(JSContext* cx, michael@0: HandleObject baseType, michael@0: size_t length, michael@0: bool lengthDefined) michael@0: { michael@0: // Get ctypes.ArrayType.prototype and the common prototype for CData objects michael@0: // of this type, from ctypes.CType.prototype. michael@0: RootedObject typeProto(cx, CType::GetProtoFromType(cx, baseType, SLOT_ARRAYPROTO)); michael@0: if (!typeProto) michael@0: return nullptr; michael@0: RootedObject dataProto(cx, CType::GetProtoFromType(cx, baseType, SLOT_ARRAYDATAPROTO)); michael@0: if (!dataProto) michael@0: return nullptr; michael@0: michael@0: // Determine the size of the array from the base type, if possible. michael@0: // The size of the base type must be defined. michael@0: // If our length is undefined, both our size and length will be undefined. michael@0: size_t baseSize; michael@0: if (!CType::GetSafeSize(baseType, &baseSize)) { michael@0: JS_ReportError(cx, "base size must be defined"); michael@0: return nullptr; michael@0: } michael@0: michael@0: RootedValue sizeVal(cx, JSVAL_VOID); michael@0: RootedValue lengthVal(cx, JSVAL_VOID); michael@0: if (lengthDefined) { michael@0: // Check for overflow, and convert to an int or double as required. michael@0: size_t size = length * baseSize; michael@0: if (length > 0 && size / length != baseSize) { michael@0: JS_ReportError(cx, "size overflow"); michael@0: return nullptr; michael@0: } michael@0: if (!SizeTojsval(cx, size, sizeVal.address()) || michael@0: !SizeTojsval(cx, length, lengthVal.address())) michael@0: return nullptr; michael@0: } michael@0: michael@0: size_t align = CType::GetAlignment(baseType); michael@0: michael@0: // Create a new CType object with the common properties and slots. michael@0: JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_array, nullptr, michael@0: sizeVal, INT_TO_JSVAL(align), nullptr); michael@0: if (!typeObj) michael@0: return nullptr; michael@0: michael@0: // Set the element type. michael@0: JS_SetReservedSlot(typeObj, SLOT_ELEMENT_T, OBJECT_TO_JSVAL(baseType)); michael@0: michael@0: // Set the length. michael@0: JS_SetReservedSlot(typeObj, SLOT_LENGTH, lengthVal); michael@0: michael@0: return typeObj; michael@0: } michael@0: michael@0: bool michael@0: ArrayType::ConstructData(JSContext* cx, michael@0: HandleObject obj_, michael@0: const CallArgs& args) michael@0: { michael@0: RootedObject obj(cx, obj_); // Make a mutable version michael@0: michael@0: if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_array) { michael@0: JS_ReportError(cx, "not an ArrayType"); michael@0: return false; michael@0: } michael@0: michael@0: // Decide whether we have an object to initialize from. We'll override this michael@0: // if we get a length argument instead. michael@0: bool convertObject = args.length() == 1; michael@0: michael@0: // Check if we're an array of undefined length. If we are, allow construction michael@0: // with a length argument, or with an actual JS array. michael@0: if (CType::IsSizeDefined(obj)) { michael@0: if (args.length() > 1) { michael@0: JS_ReportError(cx, "constructor takes zero or one argument"); michael@0: return false; michael@0: } michael@0: michael@0: } else { michael@0: if (args.length() != 1) { michael@0: JS_ReportError(cx, "constructor takes one argument"); michael@0: return false; michael@0: } michael@0: michael@0: RootedObject baseType(cx, GetBaseType(obj)); michael@0: michael@0: size_t length; michael@0: if (jsvalToSize(cx, args[0], false, &length)) { michael@0: // Have a length, rather than an object to initialize from. michael@0: convertObject = false; michael@0: michael@0: } else if (!JSVAL_IS_PRIMITIVE(args[0])) { michael@0: // We were given an object with a .length property. michael@0: // This could be a JS array, or a CData array. michael@0: RootedObject arg(cx, &args[0].toObject()); michael@0: RootedValue lengthVal(cx); michael@0: if (!JS_GetProperty(cx, arg, "length", &lengthVal) || michael@0: !jsvalToSize(cx, lengthVal, false, &length)) { michael@0: JS_ReportError(cx, "argument must be an array object or length"); michael@0: return false; michael@0: } michael@0: michael@0: } else if (args[0].isString()) { michael@0: // We were given a string. Size the array to the appropriate length, michael@0: // including space for the terminator. michael@0: JSString* sourceString = args[0].toString(); michael@0: size_t sourceLength = sourceString->length(); michael@0: const jschar* sourceChars = sourceString->getChars(cx); michael@0: if (!sourceChars) michael@0: return false; michael@0: michael@0: switch (CType::GetTypeCode(baseType)) { michael@0: case TYPE_char: michael@0: case TYPE_signed_char: michael@0: case TYPE_unsigned_char: { michael@0: // Determine the UTF-8 length. michael@0: length = GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength); michael@0: if (length == (size_t) -1) michael@0: return false; michael@0: michael@0: ++length; michael@0: break; michael@0: } michael@0: case TYPE_jschar: michael@0: length = sourceLength + 1; michael@0: break; michael@0: default: michael@0: return TypeError(cx, "array", args[0]); michael@0: } michael@0: michael@0: } else { michael@0: JS_ReportError(cx, "argument must be an array object or length"); michael@0: return false; michael@0: } michael@0: michael@0: // Construct a new ArrayType of defined length, for the new CData object. michael@0: obj = CreateInternal(cx, baseType, length, true); michael@0: if (!obj) michael@0: return false; michael@0: } michael@0: michael@0: JSObject* result = CData::Create(cx, obj, NullPtr(), nullptr, true); michael@0: if (!result) michael@0: return false; michael@0: michael@0: args.rval().setObject(*result); michael@0: michael@0: if (convertObject) { michael@0: if (!ExplicitConvert(cx, args[0], obj, CData::GetData(result))) michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: JSObject* michael@0: ArrayType::GetBaseType(JSObject* obj) michael@0: { michael@0: JS_ASSERT(CType::IsCType(obj)); michael@0: JS_ASSERT(CType::GetTypeCode(obj) == TYPE_array); michael@0: michael@0: jsval type = JS_GetReservedSlot(obj, SLOT_ELEMENT_T); michael@0: JS_ASSERT(!JSVAL_IS_NULL(type)); michael@0: return &type.toObject(); michael@0: } michael@0: michael@0: bool michael@0: ArrayType::GetSafeLength(JSObject* obj, size_t* result) michael@0: { michael@0: JS_ASSERT(CType::IsCType(obj)); michael@0: JS_ASSERT(CType::GetTypeCode(obj) == TYPE_array); michael@0: michael@0: jsval length = JS_GetReservedSlot(obj, SLOT_LENGTH); michael@0: michael@0: // The "length" property can be an int, a double, or JSVAL_VOID michael@0: // (for arrays of undefined length), and must always fit in a size_t. michael@0: if (length.isInt32()) { michael@0: *result = length.toInt32();; michael@0: return true; michael@0: } michael@0: if (length.isDouble()) { michael@0: *result = Convert(length.toDouble()); michael@0: return true; michael@0: } michael@0: michael@0: JS_ASSERT(length.isUndefined()); michael@0: return false; michael@0: } michael@0: michael@0: size_t michael@0: ArrayType::GetLength(JSObject* obj) michael@0: { michael@0: JS_ASSERT(CType::IsCType(obj)); michael@0: JS_ASSERT(CType::GetTypeCode(obj) == TYPE_array); michael@0: michael@0: jsval length = JS_GetReservedSlot(obj, SLOT_LENGTH); michael@0: michael@0: JS_ASSERT(!length.isUndefined()); michael@0: michael@0: // The "length" property can be an int, a double, or JSVAL_VOID michael@0: // (for arrays of undefined length), and must always fit in a size_t. michael@0: // For callers who know it can never be JSVAL_VOID, return a size_t directly. michael@0: if (length.isInt32()) michael@0: return length.toInt32();; michael@0: return Convert(length.toDouble()); michael@0: } michael@0: michael@0: ffi_type* michael@0: ArrayType::BuildFFIType(JSContext* cx, JSObject* obj) michael@0: { michael@0: JS_ASSERT(CType::IsCType(obj)); michael@0: JS_ASSERT(CType::GetTypeCode(obj) == TYPE_array); michael@0: JS_ASSERT(CType::IsSizeDefined(obj)); michael@0: michael@0: JSObject* baseType = ArrayType::GetBaseType(obj); michael@0: ffi_type* ffiBaseType = CType::GetFFIType(cx, baseType); michael@0: if (!ffiBaseType) michael@0: return nullptr; michael@0: michael@0: size_t length = ArrayType::GetLength(obj); michael@0: michael@0: // Create an ffi_type to represent the array. This is necessary for the case michael@0: // where the array is part of a struct. Since libffi has no intrinsic michael@0: // support for array types, we approximate it by creating a struct type michael@0: // with elements of type 'baseType' and with appropriate size and alignment michael@0: // values. It would be nice to not do all the work of setting up 'elements', michael@0: // but some libffi platforms currently require that it be meaningful. I'm michael@0: // looking at you, x86_64. michael@0: AutoPtr ffiType(cx->new_()); michael@0: if (!ffiType) { michael@0: JS_ReportOutOfMemory(cx); michael@0: return nullptr; michael@0: } michael@0: michael@0: ffiType->type = FFI_TYPE_STRUCT; michael@0: ffiType->size = CType::GetSize(obj); michael@0: ffiType->alignment = CType::GetAlignment(obj); michael@0: ffiType->elements = cx->pod_malloc(length + 1); michael@0: if (!ffiType->elements) { michael@0: JS_ReportAllocationOverflow(cx); michael@0: return nullptr; michael@0: } michael@0: michael@0: for (size_t i = 0; i < length; ++i) michael@0: ffiType->elements[i] = ffiBaseType; michael@0: ffiType->elements[length] = nullptr; michael@0: michael@0: return ffiType.forget(); michael@0: } michael@0: michael@0: bool michael@0: ArrayType::IsArrayType(HandleValue v) michael@0: { michael@0: if (!v.isObject()) michael@0: return false; michael@0: JSObject* obj = &v.toObject(); michael@0: return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_array; michael@0: } michael@0: michael@0: bool michael@0: ArrayType::IsArrayOrArrayType(HandleValue v) michael@0: { michael@0: if (!v.isObject()) michael@0: return false; michael@0: JSObject* obj = &v.toObject(); michael@0: michael@0: // Allow both CTypes and CDatas of the ArrayType persuasion by extracting the michael@0: // CType if we're dealing with a CData. michael@0: if (CData::IsCData(obj)) { michael@0: obj = CData::GetCType(obj); michael@0: } michael@0: return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_array; michael@0: } michael@0: michael@0: bool michael@0: ArrayType::ElementTypeGetter(JSContext* cx, JS::CallArgs args) michael@0: { michael@0: RootedObject obj(cx, &args.thisv().toObject()); michael@0: args.rval().set(JS_GetReservedSlot(obj, SLOT_ELEMENT_T)); michael@0: MOZ_ASSERT(args.rval().isObject()); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ArrayType::LengthGetter(JSContext* cx, JS::CallArgs args) michael@0: { michael@0: JSObject *obj = &args.thisv().toObject(); michael@0: michael@0: // This getter exists for both CTypes and CDatas of the ArrayType persuasion. michael@0: // If we're dealing with a CData, get the CType from it. michael@0: if (CData::IsCData(obj)) michael@0: obj = CData::GetCType(obj); michael@0: michael@0: args.rval().set(JS_GetReservedSlot(obj, SLOT_LENGTH)); michael@0: JS_ASSERT(args.rval().isNumber() || args.rval().isUndefined()); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ArrayType::Getter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp) michael@0: { michael@0: // This should never happen, but we'll check to be safe. michael@0: if (!CData::IsCData(obj)) { michael@0: JS_ReportError(cx, "not a CData"); michael@0: return false; michael@0: } michael@0: michael@0: // Bail early if we're not an ArrayType. (This setter is present for all michael@0: // CData, regardless of CType.) michael@0: JSObject* typeObj = CData::GetCType(obj); michael@0: if (CType::GetTypeCode(typeObj) != TYPE_array) michael@0: return true; michael@0: michael@0: // Convert the index to a size_t and bounds-check it. michael@0: size_t index; michael@0: size_t length = GetLength(typeObj); michael@0: bool ok = jsidToSize(cx, idval, true, &index); michael@0: int32_t dummy; michael@0: if (!ok && JSID_IS_STRING(idval) && !StringToInteger(cx, JSID_TO_STRING(idval), &dummy)) { michael@0: // String either isn't a number, or doesn't fit in size_t. michael@0: // Chances are it's a regular property lookup, so return. michael@0: return true; michael@0: } michael@0: if (!ok || index >= length) { michael@0: JS_ReportError(cx, "invalid index"); michael@0: return false; michael@0: } michael@0: michael@0: RootedObject baseType(cx, GetBaseType(typeObj)); michael@0: size_t elementSize = CType::GetSize(baseType); michael@0: char* data = static_cast(CData::GetData(obj)) + elementSize * index; michael@0: return ConvertToJS(cx, baseType, obj, data, false, false, vp.address()); michael@0: } michael@0: michael@0: bool michael@0: ArrayType::Setter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, MutableHandleValue vp) michael@0: { michael@0: // This should never happen, but we'll check to be safe. michael@0: if (!CData::IsCData(obj)) { michael@0: JS_ReportError(cx, "not a CData"); michael@0: return false; michael@0: } michael@0: michael@0: // Bail early if we're not an ArrayType. (This setter is present for all michael@0: // CData, regardless of CType.) michael@0: JSObject* typeObj = CData::GetCType(obj); michael@0: if (CType::GetTypeCode(typeObj) != TYPE_array) michael@0: return true; michael@0: michael@0: // Convert the index to a size_t and bounds-check it. michael@0: size_t index; michael@0: size_t length = GetLength(typeObj); michael@0: bool ok = jsidToSize(cx, idval, true, &index); michael@0: int32_t dummy; michael@0: if (!ok && JSID_IS_STRING(idval) && !StringToInteger(cx, JSID_TO_STRING(idval), &dummy)) { michael@0: // String either isn't a number, or doesn't fit in size_t. michael@0: // Chances are it's a regular property lookup, so return. michael@0: return true; michael@0: } michael@0: if (!ok || index >= length) { michael@0: JS_ReportError(cx, "invalid index"); michael@0: return false; michael@0: } michael@0: michael@0: JSObject* baseType = GetBaseType(typeObj); michael@0: size_t elementSize = CType::GetSize(baseType); michael@0: char* data = static_cast(CData::GetData(obj)) + elementSize * index; michael@0: return ImplicitConvert(cx, vp, baseType, data, false, nullptr); michael@0: } michael@0: michael@0: bool michael@0: ArrayType::AddressOfElement(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: RootedObject obj(cx, JS_THIS_OBJECT(cx, vp)); michael@0: if (!obj) michael@0: return false; michael@0: if (!CData::IsCData(obj)) { michael@0: JS_ReportError(cx, "not a CData"); michael@0: return false; michael@0: } michael@0: michael@0: RootedObject typeObj(cx, CData::GetCType(obj)); michael@0: if (CType::GetTypeCode(typeObj) != TYPE_array) { michael@0: JS_ReportError(cx, "not an ArrayType"); michael@0: return false; michael@0: } michael@0: michael@0: if (args.length() != 1) { michael@0: JS_ReportError(cx, "addressOfElement takes one argument"); michael@0: return false; michael@0: } michael@0: michael@0: RootedObject baseType(cx, GetBaseType(typeObj)); michael@0: RootedObject pointerType(cx, PointerType::CreateInternal(cx, baseType)); michael@0: if (!pointerType) michael@0: return false; michael@0: michael@0: // Create a PointerType CData object containing null. michael@0: RootedObject result(cx, CData::Create(cx, pointerType, NullPtr(), nullptr, true)); michael@0: if (!result) michael@0: return false; michael@0: michael@0: args.rval().setObject(*result); michael@0: michael@0: // Convert the index to a size_t and bounds-check it. michael@0: size_t index; michael@0: size_t length = GetLength(typeObj); michael@0: if (!jsvalToSize(cx, args[0], false, &index) || michael@0: index >= length) { michael@0: JS_ReportError(cx, "invalid index"); michael@0: return false; michael@0: } michael@0: michael@0: // Manually set the pointer inside the object, so we skip the conversion step. michael@0: void** data = static_cast(CData::GetData(result)); michael@0: size_t elementSize = CType::GetSize(baseType); michael@0: *data = static_cast(CData::GetData(obj)) + elementSize * index; michael@0: return true; michael@0: } michael@0: michael@0: /******************************************************************************* michael@0: ** StructType implementation michael@0: *******************************************************************************/ michael@0: michael@0: // For a struct field descriptor 'val' of the form { name : type }, extract michael@0: // 'name' and 'type'. michael@0: static JSFlatString* michael@0: ExtractStructField(JSContext* cx, jsval val, JSObject** typeObj) michael@0: { michael@0: if (JSVAL_IS_PRIMITIVE(val)) { michael@0: JS_ReportError(cx, "struct field descriptors require a valid name and type"); michael@0: return nullptr; michael@0: } michael@0: michael@0: RootedObject obj(cx, JSVAL_TO_OBJECT(val)); michael@0: RootedObject iter(cx, JS_NewPropertyIterator(cx, obj)); michael@0: if (!iter) michael@0: return nullptr; michael@0: michael@0: RootedId nameid(cx); michael@0: if (!JS_NextProperty(cx, iter, nameid.address())) michael@0: return nullptr; michael@0: if (JSID_IS_VOID(nameid)) { michael@0: JS_ReportError(cx, "struct field descriptors require a valid name and type"); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!JSID_IS_STRING(nameid)) { michael@0: JS_ReportError(cx, "struct field descriptors require a valid name and type"); michael@0: return nullptr; michael@0: } michael@0: michael@0: // make sure we have one, and only one, property michael@0: jsid id; michael@0: if (!JS_NextProperty(cx, iter, &id)) michael@0: return nullptr; michael@0: if (!JSID_IS_VOID(id)) { michael@0: JS_ReportError(cx, "struct field descriptors must contain one property"); michael@0: return nullptr; michael@0: } michael@0: michael@0: RootedValue propVal(cx); michael@0: if (!JS_GetPropertyById(cx, obj, nameid, &propVal)) michael@0: return nullptr; michael@0: michael@0: if (propVal.isPrimitive() || !CType::IsCType(&propVal.toObject())) { michael@0: JS_ReportError(cx, "struct field descriptors require a valid name and type"); michael@0: return nullptr; michael@0: } michael@0: michael@0: // Undefined size or zero size struct members are illegal. michael@0: // (Zero-size arrays are legal as struct members in C++, but libffi will michael@0: // choke on a zero-size struct, so we disallow them.) michael@0: *typeObj = &propVal.toObject(); michael@0: size_t size; michael@0: if (!CType::GetSafeSize(*typeObj, &size) || size == 0) { michael@0: JS_ReportError(cx, "struct field types must have defined and nonzero size"); michael@0: return nullptr; michael@0: } michael@0: michael@0: return JSID_TO_FLAT_STRING(nameid); michael@0: } michael@0: michael@0: // For a struct field with 'name' and 'type', add an element of the form michael@0: // { name : type }. michael@0: static bool michael@0: AddFieldToArray(JSContext* cx, michael@0: jsval* element, michael@0: JSFlatString* name_, michael@0: JSObject* typeObj_) michael@0: { michael@0: RootedObject typeObj(cx, typeObj_); michael@0: Rooted name(cx, name_); michael@0: RootedObject fieldObj(cx, JS_NewObject(cx, nullptr, NullPtr(), NullPtr())); michael@0: if (!fieldObj) michael@0: return false; michael@0: michael@0: *element = OBJECT_TO_JSVAL(fieldObj); michael@0: michael@0: if (!JS_DefineUCProperty(cx, fieldObj, michael@0: name->chars(), name->length(), michael@0: OBJECT_TO_JSVAL(typeObj), nullptr, nullptr, michael@0: JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: return false; michael@0: michael@0: return JS_FreezeObject(cx, fieldObj); michael@0: } michael@0: michael@0: bool michael@0: StructType::Create(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: // Construct and return a new StructType object. michael@0: if (args.length() < 1 || args.length() > 2) { michael@0: JS_ReportError(cx, "StructType takes one or two arguments"); michael@0: return false; michael@0: } michael@0: michael@0: jsval name = args[0]; michael@0: if (!name.isString()) { michael@0: JS_ReportError(cx, "first argument must be a string"); michael@0: return false; michael@0: } michael@0: michael@0: // Get ctypes.StructType.prototype from the ctypes.StructType constructor. michael@0: RootedObject typeProto(cx, CType::GetProtoFromCtor(&args.callee(), SLOT_STRUCTPROTO)); michael@0: michael@0: // Create a simple StructType with no defined fields. The result will be michael@0: // non-instantiable as CData, will have no 'prototype' property, and will michael@0: // have undefined size and alignment and no ffi_type. michael@0: RootedObject result(cx, CType::Create(cx, typeProto, NullPtr(), TYPE_struct, michael@0: JSVAL_TO_STRING(name), JSVAL_VOID, JSVAL_VOID, nullptr)); michael@0: if (!result) michael@0: return false; michael@0: michael@0: if (args.length() == 2) { michael@0: RootedObject arr(cx, JSVAL_IS_PRIMITIVE(args[1]) ? nullptr : &args[1].toObject()); michael@0: if (!arr || !JS_IsArrayObject(cx, arr)) { michael@0: JS_ReportError(cx, "second argument must be an array"); michael@0: return false; michael@0: } michael@0: michael@0: // Define the struct fields. michael@0: if (!DefineInternal(cx, result, arr)) michael@0: return false; michael@0: } michael@0: michael@0: args.rval().setObject(*result); michael@0: return true; michael@0: } michael@0: michael@0: static void michael@0: PostBarrierCallback(JSTracer *trc, JSString *key, void *data) michael@0: { michael@0: typedef HashMap UnbarrieredFieldInfoHash; michael@0: michael@0: UnbarrieredFieldInfoHash *table = reinterpret_cast(data); michael@0: JSString *prior = key; michael@0: JS_CallStringTracer(trc, &key, "CType fieldName"); michael@0: table->rekeyIfMoved(JS_ASSERT_STRING_IS_FLAT(prior), JS_ASSERT_STRING_IS_FLAT(key)); michael@0: } michael@0: michael@0: bool michael@0: StructType::DefineInternal(JSContext* cx, JSObject* typeObj_, JSObject* fieldsObj_) michael@0: { michael@0: RootedObject typeObj(cx, typeObj_); michael@0: RootedObject fieldsObj(cx, fieldsObj_); michael@0: michael@0: uint32_t len; michael@0: ASSERT_OK(JS_GetArrayLength(cx, fieldsObj, &len)); michael@0: michael@0: // Get the common prototype for CData objects of this type from michael@0: // ctypes.CType.prototype. michael@0: RootedObject dataProto(cx, CType::GetProtoFromType(cx, typeObj, SLOT_STRUCTDATAPROTO)); michael@0: if (!dataProto) michael@0: return false; michael@0: michael@0: // Set up the 'prototype' and 'prototype.constructor' properties. michael@0: // The prototype will reflect the struct fields as properties on CData objects michael@0: // created from this type. michael@0: RootedObject prototype(cx, JS_NewObject(cx, &sCDataProtoClass, dataProto, NullPtr())); michael@0: if (!prototype) michael@0: return false; michael@0: michael@0: if (!JS_DefineProperty(cx, prototype, "constructor", typeObj, michael@0: JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: return false; michael@0: michael@0: // Create a FieldInfoHash to stash on the type object, and an array to root michael@0: // its constituents. (We cannot simply stash the hash in a reserved slot now michael@0: // to get GC safety for free, since if anything in this function fails we michael@0: // do not want to mutate 'typeObj'.) michael@0: AutoPtr fields(cx->new_()); michael@0: if (!fields || !fields->init(len)) { michael@0: JS_ReportOutOfMemory(cx); michael@0: return false; michael@0: } michael@0: JS::AutoValueVector fieldRoots(cx); michael@0: if (!fieldRoots.resize(len)) { michael@0: JS_ReportOutOfMemory(cx); michael@0: return false; michael@0: } michael@0: michael@0: // Process the field types. michael@0: size_t structSize, structAlign; michael@0: if (len != 0) { michael@0: structSize = 0; michael@0: structAlign = 0; michael@0: michael@0: for (uint32_t i = 0; i < len; ++i) { michael@0: RootedValue item(cx); michael@0: if (!JS_GetElement(cx, fieldsObj, i, &item)) michael@0: return false; michael@0: michael@0: RootedObject fieldType(cx, nullptr); michael@0: Rooted name(cx, ExtractStructField(cx, item, fieldType.address())); michael@0: if (!name) michael@0: return false; michael@0: fieldRoots[i] = JS::ObjectValue(*fieldType); michael@0: michael@0: // Make sure each field name is unique michael@0: FieldInfoHash::AddPtr entryPtr = fields->lookupForAdd(name); michael@0: if (entryPtr) { michael@0: JS_ReportError(cx, "struct fields must have unique names"); michael@0: return false; michael@0: } michael@0: michael@0: // Add the field to the StructType's 'prototype' property. michael@0: if (!JS_DefineUCProperty(cx, prototype, michael@0: name->chars(), name->length(), JSVAL_VOID, michael@0: StructType::FieldGetter, StructType::FieldSetter, michael@0: JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_PERMANENT)) michael@0: return false; michael@0: michael@0: size_t fieldSize = CType::GetSize(fieldType); michael@0: size_t fieldAlign = CType::GetAlignment(fieldType); michael@0: size_t fieldOffset = Align(structSize, fieldAlign); michael@0: // Check for overflow. Since we hold invariant that fieldSize % fieldAlign michael@0: // be zero, we can safely check fieldOffset + fieldSize without first michael@0: // checking fieldOffset for overflow. michael@0: if (fieldOffset + fieldSize < structSize) { michael@0: JS_ReportError(cx, "size overflow"); michael@0: return false; michael@0: } michael@0: michael@0: // Add field name to the hash michael@0: FieldInfo info; michael@0: info.mType = fieldType; michael@0: info.mIndex = i; michael@0: info.mOffset = fieldOffset; michael@0: ASSERT_OK(fields->add(entryPtr, name, info)); michael@0: JS_StoreStringPostBarrierCallback(cx, PostBarrierCallback, name, fields.get()); michael@0: michael@0: structSize = fieldOffset + fieldSize; michael@0: michael@0: if (fieldAlign > structAlign) michael@0: structAlign = fieldAlign; michael@0: } michael@0: michael@0: // Pad the struct tail according to struct alignment. michael@0: size_t structTail = Align(structSize, structAlign); michael@0: if (structTail < structSize) { michael@0: JS_ReportError(cx, "size overflow"); michael@0: return false; michael@0: } michael@0: structSize = structTail; michael@0: michael@0: } else { michael@0: // Empty structs are illegal in C, but are legal and have a size of michael@0: // 1 byte in C++. We're going to allow them, and trick libffi into michael@0: // believing this by adding a char member. The resulting struct will have michael@0: // no getters or setters, and will be initialized to zero. michael@0: structSize = 1; michael@0: structAlign = 1; michael@0: } michael@0: michael@0: RootedValue sizeVal(cx); michael@0: if (!SizeTojsval(cx, structSize, sizeVal.address())) michael@0: return false; michael@0: michael@0: JS_SetReservedSlot(typeObj, SLOT_FIELDINFO, PRIVATE_TO_JSVAL(fields.forget())); michael@0: michael@0: JS_SetReservedSlot(typeObj, SLOT_SIZE, sizeVal); michael@0: JS_SetReservedSlot(typeObj, SLOT_ALIGN, INT_TO_JSVAL(structAlign)); michael@0: //if (!JS_FreezeObject(cx, prototype)0 // XXX fixme - see bug 541212! michael@0: // return false; michael@0: JS_SetReservedSlot(typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype)); michael@0: return true; michael@0: } michael@0: michael@0: ffi_type* michael@0: StructType::BuildFFIType(JSContext* cx, JSObject* obj) michael@0: { michael@0: JS_ASSERT(CType::IsCType(obj)); michael@0: JS_ASSERT(CType::GetTypeCode(obj) == TYPE_struct); michael@0: JS_ASSERT(CType::IsSizeDefined(obj)); michael@0: michael@0: const FieldInfoHash* fields = GetFieldInfo(obj); michael@0: size_t len = fields->count(); michael@0: michael@0: size_t structSize = CType::GetSize(obj); michael@0: size_t structAlign = CType::GetAlignment(obj); michael@0: michael@0: AutoPtr ffiType(cx->new_()); michael@0: if (!ffiType) { michael@0: JS_ReportOutOfMemory(cx); michael@0: return nullptr; michael@0: } michael@0: ffiType->type = FFI_TYPE_STRUCT; michael@0: michael@0: AutoPtr elements; michael@0: if (len != 0) { michael@0: elements = cx->pod_malloc(len + 1); michael@0: if (!elements) { michael@0: JS_ReportOutOfMemory(cx); michael@0: return nullptr; michael@0: } michael@0: elements[len] = nullptr; michael@0: michael@0: for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) { michael@0: const FieldInfoHash::Entry& entry = r.front(); michael@0: ffi_type* fieldType = CType::GetFFIType(cx, entry.value().mType); michael@0: if (!fieldType) michael@0: return nullptr; michael@0: elements[entry.value().mIndex] = fieldType; michael@0: } michael@0: michael@0: } else { michael@0: // Represent an empty struct as having a size of 1 byte, just like C++. michael@0: JS_ASSERT(structSize == 1); michael@0: JS_ASSERT(structAlign == 1); michael@0: elements = cx->pod_malloc(2); michael@0: if (!elements) { michael@0: JS_ReportOutOfMemory(cx); michael@0: return nullptr; michael@0: } michael@0: elements[0] = &ffi_type_uint8; michael@0: elements[1] = nullptr; michael@0: } michael@0: michael@0: ffiType->elements = elements.get(); michael@0: michael@0: #ifdef DEBUG michael@0: // Perform a sanity check: the result of our struct size and alignment michael@0: // calculations should match libffi's. We force it to do this calculation michael@0: // by calling ffi_prep_cif. michael@0: ffi_cif cif; michael@0: ffiType->size = 0; michael@0: ffiType->alignment = 0; michael@0: ffi_status status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 0, ffiType.get(), nullptr); michael@0: JS_ASSERT(status == FFI_OK); michael@0: JS_ASSERT(structSize == ffiType->size); michael@0: JS_ASSERT(structAlign == ffiType->alignment); michael@0: #else michael@0: // Fill in the ffi_type's size and align fields. This makes libffi treat the michael@0: // type as initialized; it will not recompute the values. (We assume michael@0: // everything agrees; if it doesn't, we really want to know about it, which michael@0: // is the purpose of the above debug-only check.) michael@0: ffiType->size = structSize; michael@0: ffiType->alignment = structAlign; michael@0: #endif michael@0: michael@0: elements.forget(); michael@0: return ffiType.forget(); michael@0: } michael@0: michael@0: bool michael@0: StructType::Define(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: RootedObject obj(cx, JS_THIS_OBJECT(cx, vp)); michael@0: if (!obj) michael@0: return false; michael@0: if (!CType::IsCType(obj) || michael@0: CType::GetTypeCode(obj) != TYPE_struct) { michael@0: JS_ReportError(cx, "not a StructType"); michael@0: return false; michael@0: } michael@0: michael@0: if (CType::IsSizeDefined(obj)) { michael@0: JS_ReportError(cx, "StructType has already been defined"); michael@0: return false; michael@0: } michael@0: michael@0: if (args.length() != 1) { michael@0: JS_ReportError(cx, "define takes one argument"); michael@0: return false; michael@0: } michael@0: michael@0: jsval arg = args[0]; michael@0: if (JSVAL_IS_PRIMITIVE(arg)) { michael@0: JS_ReportError(cx, "argument must be an array"); michael@0: return false; michael@0: } michael@0: RootedObject arr(cx, JSVAL_TO_OBJECT(arg)); michael@0: if (!JS_IsArrayObject(cx, arr)) { michael@0: JS_ReportError(cx, "argument must be an array"); michael@0: return false; michael@0: } michael@0: michael@0: return DefineInternal(cx, obj, arr); michael@0: } michael@0: michael@0: bool michael@0: StructType::ConstructData(JSContext* cx, michael@0: HandleObject obj, michael@0: const CallArgs& args) michael@0: { michael@0: if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_struct) { michael@0: JS_ReportError(cx, "not a StructType"); michael@0: return false; michael@0: } michael@0: michael@0: if (!CType::IsSizeDefined(obj)) { michael@0: JS_ReportError(cx, "cannot construct an opaque StructType"); michael@0: return false; michael@0: } michael@0: michael@0: JSObject* result = CData::Create(cx, obj, NullPtr(), nullptr, true); michael@0: if (!result) michael@0: return false; michael@0: michael@0: args.rval().setObject(*result); michael@0: michael@0: if (args.length() == 0) michael@0: return true; michael@0: michael@0: char* buffer = static_cast(CData::GetData(result)); michael@0: const FieldInfoHash* fields = GetFieldInfo(obj); michael@0: michael@0: if (args.length() == 1) { michael@0: // There are two possible interpretations of the argument: michael@0: // 1) It may be an object '{ ... }' with properties representing the michael@0: // struct fields intended to ExplicitConvert wholesale to our StructType. michael@0: // 2) If the struct contains one field, the arg may be intended to michael@0: // ImplicitConvert directly to that arg's CType. michael@0: // Thankfully, the conditions for these two possibilities to succeed michael@0: // are mutually exclusive, so we can pick the right one. michael@0: michael@0: // Try option 1) first. michael@0: if (ExplicitConvert(cx, args[0], obj, buffer)) michael@0: return true; michael@0: michael@0: if (fields->count() != 1) michael@0: return false; michael@0: michael@0: // If ExplicitConvert failed, and there is no pending exception, then assume michael@0: // hard failure (out of memory, or some other similarly serious condition). michael@0: if (!JS_IsExceptionPending(cx)) michael@0: return false; michael@0: michael@0: // Otherwise, assume soft failure, and clear the pending exception so that we michael@0: // can throw a different one as required. michael@0: JS_ClearPendingException(cx); michael@0: michael@0: // Fall through to try option 2). michael@0: } michael@0: michael@0: // We have a type constructor of the form 'ctypes.StructType(a, b, c, ...)'. michael@0: // ImplicitConvert each field. michael@0: if (args.length() == fields->count()) { michael@0: for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) { michael@0: const FieldInfo& field = r.front().value(); michael@0: STATIC_ASSUME(field.mIndex < fields->count()); /* Quantified invariant */ michael@0: if (!ImplicitConvert(cx, args[field.mIndex], field.mType, michael@0: buffer + field.mOffset, michael@0: false, nullptr)) michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: JS_ReportError(cx, "constructor takes 0, 1, or %u arguments", michael@0: fields->count()); michael@0: return false; michael@0: } michael@0: michael@0: const FieldInfoHash* michael@0: StructType::GetFieldInfo(JSObject* obj) michael@0: { michael@0: JS_ASSERT(CType::IsCType(obj)); michael@0: JS_ASSERT(CType::GetTypeCode(obj) == TYPE_struct); michael@0: michael@0: jsval slot = JS_GetReservedSlot(obj, SLOT_FIELDINFO); michael@0: JS_ASSERT(!JSVAL_IS_VOID(slot) && JSVAL_TO_PRIVATE(slot)); michael@0: michael@0: return static_cast(JSVAL_TO_PRIVATE(slot)); michael@0: } michael@0: michael@0: const FieldInfo* michael@0: StructType::LookupField(JSContext* cx, JSObject* obj, JSFlatString *name) michael@0: { michael@0: JS_ASSERT(CType::IsCType(obj)); michael@0: JS_ASSERT(CType::GetTypeCode(obj) == TYPE_struct); michael@0: michael@0: FieldInfoHash::Ptr ptr = GetFieldInfo(obj)->lookup(name); michael@0: if (ptr) michael@0: return &ptr->value(); michael@0: michael@0: JSAutoByteString bytes(cx, name); michael@0: if (!bytes) michael@0: return nullptr; michael@0: michael@0: JS_ReportError(cx, "%s does not name a field", bytes.ptr()); michael@0: return nullptr; michael@0: } michael@0: michael@0: JSObject* michael@0: StructType::BuildFieldsArray(JSContext* cx, JSObject* obj) michael@0: { michael@0: JS_ASSERT(CType::IsCType(obj)); michael@0: JS_ASSERT(CType::GetTypeCode(obj) == TYPE_struct); michael@0: JS_ASSERT(CType::IsSizeDefined(obj)); michael@0: michael@0: const FieldInfoHash* fields = GetFieldInfo(obj); michael@0: size_t len = fields->count(); michael@0: michael@0: // Prepare a new array for the 'fields' property of the StructType. michael@0: JS::AutoValueVector fieldsVec(cx); michael@0: if (!fieldsVec.resize(len)) michael@0: return nullptr; michael@0: michael@0: for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) { michael@0: const FieldInfoHash::Entry& entry = r.front(); michael@0: // Add the field descriptor to the array. michael@0: if (!AddFieldToArray(cx, &fieldsVec[entry.value().mIndex], michael@0: entry.key(), entry.value().mType)) michael@0: return nullptr; michael@0: } michael@0: michael@0: RootedObject fieldsProp(cx, JS_NewArrayObject(cx, fieldsVec)); michael@0: if (!fieldsProp) michael@0: return nullptr; michael@0: michael@0: // Seal the fields array. michael@0: if (!JS_FreezeObject(cx, fieldsProp)) michael@0: return nullptr; michael@0: michael@0: return fieldsProp; michael@0: } michael@0: michael@0: /* static */ bool michael@0: StructType::IsStruct(HandleValue v) michael@0: { michael@0: if (!v.isObject()) michael@0: return false; michael@0: JSObject* obj = &v.toObject(); michael@0: return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_struct; michael@0: } michael@0: michael@0: bool michael@0: StructType::FieldsArrayGetter(JSContext* cx, JS::CallArgs args) michael@0: { michael@0: RootedObject obj(cx, &args.thisv().toObject()); michael@0: michael@0: args.rval().set(JS_GetReservedSlot(obj, SLOT_FIELDS)); michael@0: michael@0: if (!CType::IsSizeDefined(obj)) { michael@0: MOZ_ASSERT(args.rval().isUndefined()); michael@0: return true; michael@0: } michael@0: michael@0: if (args.rval().isUndefined()) { michael@0: // Build the 'fields' array lazily. michael@0: JSObject* fields = BuildFieldsArray(cx, obj); michael@0: if (!fields) michael@0: return false; michael@0: JS_SetReservedSlot(obj, SLOT_FIELDS, OBJECT_TO_JSVAL(fields)); michael@0: michael@0: args.rval().setObject(*fields); michael@0: } michael@0: michael@0: MOZ_ASSERT(args.rval().isObject()); michael@0: MOZ_ASSERT(JS_IsArrayObject(cx, args.rval())); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: StructType::FieldGetter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp) michael@0: { michael@0: if (!CData::IsCData(obj)) { michael@0: JS_ReportError(cx, "not a CData"); michael@0: return false; michael@0: } michael@0: michael@0: JSObject* typeObj = CData::GetCType(obj); michael@0: if (CType::GetTypeCode(typeObj) != TYPE_struct) { michael@0: JS_ReportError(cx, "not a StructType"); michael@0: return false; michael@0: } michael@0: michael@0: const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_FLAT_STRING(idval)); michael@0: if (!field) michael@0: return false; michael@0: michael@0: char* data = static_cast(CData::GetData(obj)) + field->mOffset; michael@0: RootedObject fieldType(cx, field->mType); michael@0: return ConvertToJS(cx, fieldType, obj, data, false, false, vp.address()); michael@0: } michael@0: michael@0: bool michael@0: StructType::FieldSetter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, MutableHandleValue vp) michael@0: { michael@0: if (!CData::IsCData(obj)) { michael@0: JS_ReportError(cx, "not a CData"); michael@0: return false; michael@0: } michael@0: michael@0: JSObject* typeObj = CData::GetCType(obj); michael@0: if (CType::GetTypeCode(typeObj) != TYPE_struct) { michael@0: JS_ReportError(cx, "not a StructType"); michael@0: return false; michael@0: } michael@0: michael@0: const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_FLAT_STRING(idval)); michael@0: if (!field) michael@0: return false; michael@0: michael@0: char* data = static_cast(CData::GetData(obj)) + field->mOffset; michael@0: return ImplicitConvert(cx, vp, field->mType, data, false, nullptr); michael@0: } michael@0: michael@0: bool michael@0: StructType::AddressOfField(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: RootedObject obj(cx, JS_THIS_OBJECT(cx, vp)); michael@0: if (!obj) michael@0: return false; michael@0: if (!CData::IsCData(obj)) { michael@0: JS_ReportError(cx, "not a CData"); michael@0: return false; michael@0: } michael@0: michael@0: JSObject* typeObj = CData::GetCType(obj); michael@0: if (CType::GetTypeCode(typeObj) != TYPE_struct) { michael@0: JS_ReportError(cx, "not a StructType"); michael@0: return false; michael@0: } michael@0: michael@0: if (args.length() != 1) { michael@0: JS_ReportError(cx, "addressOfField takes one argument"); michael@0: return false; michael@0: } michael@0: michael@0: JSFlatString *str = JS_FlattenString(cx, args[0].toString()); michael@0: if (!str) michael@0: return false; michael@0: michael@0: const FieldInfo* field = LookupField(cx, typeObj, str); michael@0: if (!field) michael@0: return false; michael@0: michael@0: RootedObject baseType(cx, field->mType); michael@0: RootedObject pointerType(cx, PointerType::CreateInternal(cx, baseType)); michael@0: if (!pointerType) michael@0: return false; michael@0: michael@0: // Create a PointerType CData object containing null. michael@0: JSObject* result = CData::Create(cx, pointerType, NullPtr(), nullptr, true); michael@0: if (!result) michael@0: return false; michael@0: michael@0: args.rval().setObject(*result); michael@0: michael@0: // Manually set the pointer inside the object, so we skip the conversion step. michael@0: void** data = static_cast(CData::GetData(result)); michael@0: *data = static_cast(CData::GetData(obj)) + field->mOffset; michael@0: return true; michael@0: } michael@0: michael@0: /******************************************************************************* michael@0: ** FunctionType implementation michael@0: *******************************************************************************/ michael@0: michael@0: // Helper class for handling allocation of function arguments. michael@0: struct AutoValue michael@0: { michael@0: AutoValue() : mData(nullptr) { } michael@0: michael@0: ~AutoValue() michael@0: { michael@0: js_free(mData); michael@0: } michael@0: michael@0: bool SizeToType(JSContext* cx, JSObject* type) michael@0: { michael@0: // Allocate a minimum of sizeof(ffi_arg) to handle small integers. michael@0: size_t size = Align(CType::GetSize(type), sizeof(ffi_arg)); michael@0: mData = js_malloc(size); michael@0: if (mData) michael@0: memset(mData, 0, size); michael@0: return mData != nullptr; michael@0: } michael@0: michael@0: void* mData; michael@0: }; michael@0: michael@0: static bool michael@0: GetABI(JSContext* cx, jsval abiType, ffi_abi* result) michael@0: { michael@0: if (JSVAL_IS_PRIMITIVE(abiType)) michael@0: return false; michael@0: michael@0: ABICode abi = GetABICode(JSVAL_TO_OBJECT(abiType)); michael@0: michael@0: // determine the ABI from the subset of those available on the michael@0: // given platform. ABI_DEFAULT specifies the default michael@0: // C calling convention (cdecl) on each platform. michael@0: switch (abi) { michael@0: case ABI_DEFAULT: michael@0: *result = FFI_DEFAULT_ABI; michael@0: return true; michael@0: case ABI_STDCALL: michael@0: case ABI_WINAPI: michael@0: #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2) michael@0: *result = FFI_STDCALL; michael@0: return true; michael@0: #elif (defined(_WIN64)) michael@0: // We'd like the same code to work across Win32 and Win64, so stdcall_api michael@0: // and winapi_abi become aliases to the lone Win64 ABI. michael@0: *result = FFI_WIN64; michael@0: return true; michael@0: #endif michael@0: case INVALID_ABI: michael@0: break; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: static JSObject* michael@0: PrepareType(JSContext* cx, jsval type) michael@0: { michael@0: if (JSVAL_IS_PRIMITIVE(type) || michael@0: !CType::IsCType(JSVAL_TO_OBJECT(type))) { michael@0: JS_ReportError(cx, "not a ctypes type"); michael@0: return nullptr; michael@0: } michael@0: michael@0: JSObject* result = JSVAL_TO_OBJECT(type); michael@0: TypeCode typeCode = CType::GetTypeCode(result); michael@0: michael@0: if (typeCode == TYPE_array) { michael@0: // convert array argument types to pointers, just like C. michael@0: // ImplicitConvert will do the same, when passing an array as data. michael@0: RootedObject baseType(cx, ArrayType::GetBaseType(result)); michael@0: result = PointerType::CreateInternal(cx, baseType); michael@0: if (!result) michael@0: return nullptr; michael@0: michael@0: } else if (typeCode == TYPE_void_t || typeCode == TYPE_function) { michael@0: // disallow void or function argument types michael@0: JS_ReportError(cx, "Cannot have void or function argument type"); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!CType::IsSizeDefined(result)) { michael@0: JS_ReportError(cx, "Argument type must have defined size"); michael@0: return nullptr; michael@0: } michael@0: michael@0: // libffi cannot pass types of zero size by value. michael@0: JS_ASSERT(CType::GetSize(result) != 0); michael@0: michael@0: return result; michael@0: } michael@0: michael@0: static JSObject* michael@0: PrepareReturnType(JSContext* cx, jsval type) michael@0: { michael@0: if (JSVAL_IS_PRIMITIVE(type) || michael@0: !CType::IsCType(JSVAL_TO_OBJECT(type))) { michael@0: JS_ReportError(cx, "not a ctypes type"); michael@0: return nullptr; michael@0: } michael@0: michael@0: JSObject* result = JSVAL_TO_OBJECT(type); michael@0: TypeCode typeCode = CType::GetTypeCode(result); michael@0: michael@0: // Arrays and functions can never be return types. michael@0: if (typeCode == TYPE_array || typeCode == TYPE_function) { michael@0: JS_ReportError(cx, "Return type cannot be an array or function"); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (typeCode != TYPE_void_t && !CType::IsSizeDefined(result)) { michael@0: JS_ReportError(cx, "Return type must have defined size"); michael@0: return nullptr; michael@0: } michael@0: michael@0: // libffi cannot pass types of zero size by value. michael@0: JS_ASSERT(typeCode == TYPE_void_t || CType::GetSize(result) != 0); michael@0: michael@0: return result; michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE bool michael@0: IsEllipsis(JSContext* cx, jsval v, bool* isEllipsis) michael@0: { michael@0: *isEllipsis = false; michael@0: if (!JSVAL_IS_STRING(v)) michael@0: return true; michael@0: JSString* str = JSVAL_TO_STRING(v); michael@0: if (str->length() != 3) michael@0: return true; michael@0: const jschar* chars = str->getChars(cx); michael@0: if (!chars) michael@0: return false; michael@0: jschar dot = '.'; michael@0: *isEllipsis = (chars[0] == dot && michael@0: chars[1] == dot && michael@0: chars[2] == dot); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: PrepareCIF(JSContext* cx, michael@0: FunctionInfo* fninfo) michael@0: { michael@0: ffi_abi abi; michael@0: if (!GetABI(cx, OBJECT_TO_JSVAL(fninfo->mABI), &abi)) { michael@0: JS_ReportError(cx, "Invalid ABI specification"); michael@0: return false; michael@0: } michael@0: michael@0: ffi_type* rtype = CType::GetFFIType(cx, fninfo->mReturnType); michael@0: if (!rtype) michael@0: return false; michael@0: michael@0: ffi_status status = michael@0: ffi_prep_cif(&fninfo->mCIF, michael@0: abi, michael@0: fninfo->mFFITypes.length(), michael@0: rtype, michael@0: fninfo->mFFITypes.begin()); michael@0: michael@0: switch (status) { michael@0: case FFI_OK: michael@0: return true; michael@0: case FFI_BAD_ABI: michael@0: JS_ReportError(cx, "Invalid ABI specification"); michael@0: return false; michael@0: case FFI_BAD_TYPEDEF: michael@0: JS_ReportError(cx, "Invalid type specification"); michael@0: return false; michael@0: default: michael@0: JS_ReportError(cx, "Unknown libffi error"); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: void michael@0: FunctionType::BuildSymbolName(JSString* name, michael@0: JSObject* typeObj, michael@0: AutoCString& result) michael@0: { michael@0: FunctionInfo* fninfo = GetFunctionInfo(typeObj); michael@0: michael@0: switch (GetABICode(fninfo->mABI)) { michael@0: case ABI_DEFAULT: michael@0: case ABI_WINAPI: michael@0: // For cdecl or WINAPI functions, no mangling is necessary. michael@0: AppendString(result, name); michael@0: break; michael@0: michael@0: case ABI_STDCALL: { michael@0: #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2) michael@0: // On WIN32, stdcall functions look like: michael@0: // _foo@40 michael@0: // where 'foo' is the function name, and '40' is the aligned size of the michael@0: // arguments. michael@0: AppendString(result, "_"); michael@0: AppendString(result, name); michael@0: AppendString(result, "@"); michael@0: michael@0: // Compute the suffix by aligning each argument to sizeof(ffi_arg). michael@0: size_t size = 0; michael@0: for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) { michael@0: JSObject* argType = fninfo->mArgTypes[i]; michael@0: size += Align(CType::GetSize(argType), sizeof(ffi_arg)); michael@0: } michael@0: michael@0: IntegerToString(size, 10, result); michael@0: #elif defined(_WIN64) michael@0: // On Win64, stdcall is an alias to the default ABI for compatibility, so no michael@0: // mangling is done. michael@0: AppendString(result, name); michael@0: #endif michael@0: break; michael@0: } michael@0: michael@0: case INVALID_ABI: michael@0: MOZ_ASSUME_UNREACHABLE("invalid abi"); michael@0: } michael@0: } michael@0: michael@0: static FunctionInfo* michael@0: NewFunctionInfo(JSContext* cx, michael@0: jsval abiType, michael@0: jsval returnType, michael@0: jsval* argTypes, michael@0: unsigned argLength) michael@0: { michael@0: AutoPtr fninfo(cx->new_()); michael@0: if (!fninfo) { michael@0: JS_ReportOutOfMemory(cx); michael@0: return nullptr; michael@0: } michael@0: michael@0: ffi_abi abi; michael@0: if (!GetABI(cx, abiType, &abi)) { michael@0: JS_ReportError(cx, "Invalid ABI specification"); michael@0: return nullptr; michael@0: } michael@0: fninfo->mABI = JSVAL_TO_OBJECT(abiType); michael@0: michael@0: // prepare the result type michael@0: fninfo->mReturnType = PrepareReturnType(cx, returnType); michael@0: if (!fninfo->mReturnType) michael@0: return nullptr; michael@0: michael@0: // prepare the argument types michael@0: if (!fninfo->mArgTypes.reserve(argLength) || michael@0: !fninfo->mFFITypes.reserve(argLength)) { michael@0: JS_ReportOutOfMemory(cx); michael@0: return nullptr; michael@0: } michael@0: michael@0: fninfo->mIsVariadic = false; michael@0: michael@0: for (uint32_t i = 0; i < argLength; ++i) { michael@0: bool isEllipsis; michael@0: if (!IsEllipsis(cx, argTypes[i], &isEllipsis)) michael@0: return nullptr; michael@0: if (isEllipsis) { michael@0: fninfo->mIsVariadic = true; michael@0: if (i < 1) { michael@0: JS_ReportError(cx, "\"...\" may not be the first and only parameter " michael@0: "type of a variadic function declaration"); michael@0: return nullptr; michael@0: } michael@0: if (i < argLength - 1) { michael@0: JS_ReportError(cx, "\"...\" must be the last parameter type of a " michael@0: "variadic function declaration"); michael@0: return nullptr; michael@0: } michael@0: if (GetABICode(fninfo->mABI) != ABI_DEFAULT) { michael@0: JS_ReportError(cx, "Variadic functions must use the __cdecl calling " michael@0: "convention"); michael@0: return nullptr; michael@0: } michael@0: break; michael@0: } michael@0: michael@0: JSObject* argType = PrepareType(cx, argTypes[i]); michael@0: if (!argType) michael@0: return nullptr; michael@0: michael@0: ffi_type* ffiType = CType::GetFFIType(cx, argType); michael@0: if (!ffiType) michael@0: return nullptr; michael@0: michael@0: fninfo->mArgTypes.infallibleAppend(argType); michael@0: fninfo->mFFITypes.infallibleAppend(ffiType); michael@0: } michael@0: michael@0: if (fninfo->mIsVariadic) michael@0: // wait to PrepareCIF until function is called michael@0: return fninfo.forget(); michael@0: michael@0: if (!PrepareCIF(cx, fninfo.get())) michael@0: return nullptr; michael@0: michael@0: return fninfo.forget(); michael@0: } michael@0: michael@0: bool michael@0: FunctionType::Create(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: // Construct and return a new FunctionType object. michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() < 2 || args.length() > 3) { michael@0: JS_ReportError(cx, "FunctionType takes two or three arguments"); michael@0: return false; michael@0: } michael@0: michael@0: AutoValueVector argTypes(cx); michael@0: RootedObject arrayObj(cx, nullptr); michael@0: michael@0: if (args.length() == 3) { michael@0: // Prepare an array of jsvals for the arguments. michael@0: if (!JSVAL_IS_PRIMITIVE(args[2])) michael@0: arrayObj = &args[2].toObject(); michael@0: if (!arrayObj || !JS_IsArrayObject(cx, arrayObj)) { michael@0: JS_ReportError(cx, "third argument must be an array"); michael@0: return false; michael@0: } michael@0: michael@0: uint32_t len; michael@0: ASSERT_OK(JS_GetArrayLength(cx, arrayObj, &len)); michael@0: michael@0: if (!argTypes.resize(len)) { michael@0: JS_ReportOutOfMemory(cx); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: // Pull out the argument types from the array, if any. michael@0: JS_ASSERT_IF(argTypes.length(), arrayObj); michael@0: for (uint32_t i = 0; i < argTypes.length(); ++i) { michael@0: if (!JS_GetElement(cx, arrayObj, i, argTypes.handleAt(i))) michael@0: return false; michael@0: } michael@0: michael@0: JSObject* result = CreateInternal(cx, args[0], args[1], michael@0: argTypes.begin(), argTypes.length()); michael@0: if (!result) michael@0: return false; michael@0: michael@0: args.rval().setObject(*result); michael@0: return true; michael@0: } michael@0: michael@0: JSObject* michael@0: FunctionType::CreateInternal(JSContext* cx, michael@0: jsval abi, michael@0: jsval rtype, michael@0: jsval* argtypes, michael@0: unsigned arglen) michael@0: { michael@0: // Determine and check the types, and prepare the function CIF. michael@0: AutoPtr fninfo(NewFunctionInfo(cx, abi, rtype, argtypes, arglen)); michael@0: if (!fninfo) michael@0: return nullptr; michael@0: michael@0: // Get ctypes.FunctionType.prototype and the common prototype for CData objects michael@0: // of this type, from ctypes.CType.prototype. michael@0: RootedObject typeProto(cx, CType::GetProtoFromType(cx, fninfo->mReturnType, michael@0: SLOT_FUNCTIONPROTO)); michael@0: if (!typeProto) michael@0: return nullptr; michael@0: RootedObject dataProto(cx, CType::GetProtoFromType(cx, fninfo->mReturnType, michael@0: SLOT_FUNCTIONDATAPROTO)); michael@0: if (!dataProto) michael@0: return nullptr; michael@0: michael@0: // Create a new CType object with the common properties and slots. michael@0: JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_function, michael@0: nullptr, JSVAL_VOID, JSVAL_VOID, nullptr); michael@0: if (!typeObj) michael@0: return nullptr; michael@0: michael@0: // Stash the FunctionInfo in a reserved slot. michael@0: JS_SetReservedSlot(typeObj, SLOT_FNINFO, PRIVATE_TO_JSVAL(fninfo.forget())); michael@0: michael@0: return typeObj; michael@0: } michael@0: michael@0: // Construct a function pointer to a JS function (see CClosure::Create()). michael@0: // Regular function pointers are constructed directly in michael@0: // PointerType::ConstructData(). michael@0: bool michael@0: FunctionType::ConstructData(JSContext* cx, michael@0: HandleObject typeObj, michael@0: HandleObject dataObj, michael@0: HandleObject fnObj, michael@0: HandleObject thisObj, michael@0: jsval errVal) michael@0: { michael@0: JS_ASSERT(CType::GetTypeCode(typeObj) == TYPE_function); michael@0: michael@0: PRFuncPtr* data = static_cast(CData::GetData(dataObj)); michael@0: michael@0: FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj); michael@0: if (fninfo->mIsVariadic) { michael@0: JS_ReportError(cx, "Can't declare a variadic callback function"); michael@0: return false; michael@0: } michael@0: if (GetABICode(fninfo->mABI) == ABI_WINAPI) { michael@0: JS_ReportError(cx, "Can't declare a ctypes.winapi_abi callback function, " michael@0: "use ctypes.stdcall_abi instead"); michael@0: return false; michael@0: } michael@0: michael@0: RootedObject closureObj(cx, CClosure::Create(cx, typeObj, fnObj, thisObj, errVal, data)); michael@0: if (!closureObj) michael@0: return false; michael@0: michael@0: // Set the closure object as the referent of the new CData object. michael@0: JS_SetReservedSlot(dataObj, SLOT_REFERENT, OBJECT_TO_JSVAL(closureObj)); michael@0: michael@0: // Seal the CData object, to prevent modification of the function pointer. michael@0: // This permanently associates this object with the closure, and avoids michael@0: // having to do things like reset SLOT_REFERENT when someone tries to michael@0: // change the pointer value. michael@0: // XXX This will need to change when bug 541212 is fixed -- CData::ValueSetter michael@0: // could be called on a frozen object. michael@0: return JS_FreezeObject(cx, dataObj); michael@0: } michael@0: michael@0: typedef Array AutoValueAutoArray; michael@0: michael@0: static bool michael@0: ConvertArgument(JSContext* cx, michael@0: HandleValue arg, michael@0: JSObject* type, michael@0: AutoValue* value, michael@0: AutoValueAutoArray* strings) michael@0: { michael@0: if (!value->SizeToType(cx, type)) { michael@0: JS_ReportAllocationOverflow(cx); michael@0: return false; michael@0: } michael@0: michael@0: bool freePointer = false; michael@0: if (!ImplicitConvert(cx, arg, type, value->mData, true, &freePointer)) michael@0: return false; michael@0: michael@0: if (freePointer) { michael@0: // ImplicitConvert converted a string for us, which we have to free. michael@0: // Keep track of it. michael@0: if (!strings->growBy(1)) { michael@0: JS_ReportOutOfMemory(cx); michael@0: return false; michael@0: } michael@0: strings->back().mData = *static_cast(value->mData); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: FunctionType::Call(JSContext* cx, michael@0: unsigned argc, michael@0: jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: // get the callee object... michael@0: RootedObject obj(cx, &args.callee()); michael@0: if (!CData::IsCData(obj)) { michael@0: JS_ReportError(cx, "not a CData"); michael@0: return false; michael@0: } michael@0: michael@0: RootedObject typeObj(cx, CData::GetCType(obj)); michael@0: if (CType::GetTypeCode(typeObj) != TYPE_pointer) { michael@0: JS_ReportError(cx, "not a FunctionType.ptr"); michael@0: return false; michael@0: } michael@0: michael@0: typeObj = PointerType::GetBaseType(typeObj); michael@0: if (CType::GetTypeCode(typeObj) != TYPE_function) { michael@0: JS_ReportError(cx, "not a FunctionType.ptr"); michael@0: return false; michael@0: } michael@0: michael@0: FunctionInfo* fninfo = GetFunctionInfo(typeObj); michael@0: uint32_t argcFixed = fninfo->mArgTypes.length(); michael@0: michael@0: if ((!fninfo->mIsVariadic && args.length() != argcFixed) || michael@0: (fninfo->mIsVariadic && args.length() < argcFixed)) { michael@0: JS_ReportError(cx, "Number of arguments does not match declaration"); michael@0: return false; michael@0: } michael@0: michael@0: // Check if we have a Library object. If we do, make sure it's open. michael@0: jsval slot = JS_GetReservedSlot(obj, SLOT_REFERENT); michael@0: if (!slot.isUndefined() && Library::IsLibrary(&slot.toObject())) { michael@0: PRLibrary* library = Library::GetLibrary(&slot.toObject()); michael@0: if (!library) { michael@0: JS_ReportError(cx, "library is not open"); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: // prepare the values for each argument michael@0: AutoValueAutoArray values; michael@0: AutoValueAutoArray strings; michael@0: if (!values.resize(args.length())) { michael@0: JS_ReportOutOfMemory(cx); michael@0: return false; michael@0: } michael@0: michael@0: for (unsigned i = 0; i < argcFixed; ++i) michael@0: if (!ConvertArgument(cx, args[i], fninfo->mArgTypes[i], &values[i], &strings)) michael@0: return false; michael@0: michael@0: if (fninfo->mIsVariadic) { michael@0: if (!fninfo->mFFITypes.resize(args.length())) { michael@0: JS_ReportOutOfMemory(cx); michael@0: return false; michael@0: } michael@0: michael@0: RootedObject obj(cx); // Could reuse obj instead of declaring a second michael@0: RootedObject type(cx); // RootedObject, but readability would suffer. michael@0: michael@0: for (uint32_t i = argcFixed; i < args.length(); ++i) { michael@0: if (JSVAL_IS_PRIMITIVE(args[i]) || michael@0: !CData::IsCData(obj = &args[i].toObject())) { michael@0: // Since we know nothing about the CTypes of the ... arguments, michael@0: // they absolutely must be CData objects already. michael@0: JS_ReportError(cx, "argument %d of type %s is not a CData object", michael@0: i, JS_GetTypeName(cx, JS_TypeOfValue(cx, args[i]))); michael@0: return false; michael@0: } michael@0: if (!(type = CData::GetCType(obj)) || michael@0: !(type = PrepareType(cx, OBJECT_TO_JSVAL(type))) || michael@0: // Relying on ImplicitConvert only for the limited purpose of michael@0: // converting one CType to another (e.g., T[] to T*). michael@0: !ConvertArgument(cx, args[i], type, &values[i], &strings) || michael@0: !(fninfo->mFFITypes[i] = CType::GetFFIType(cx, type))) { michael@0: // These functions report their own errors. michael@0: return false; michael@0: } michael@0: } michael@0: if (!PrepareCIF(cx, fninfo)) michael@0: return false; michael@0: } michael@0: michael@0: // initialize a pointer to an appropriate location, for storing the result michael@0: AutoValue returnValue; michael@0: TypeCode typeCode = CType::GetTypeCode(fninfo->mReturnType); michael@0: if (typeCode != TYPE_void_t && michael@0: !returnValue.SizeToType(cx, fninfo->mReturnType)) { michael@0: JS_ReportAllocationOverflow(cx); michael@0: return false; michael@0: } michael@0: michael@0: // Let the runtime callback know that we are about to call into C. michael@0: js::AutoCTypesActivityCallback autoCallback(cx, js::CTYPES_CALL_BEGIN, js::CTYPES_CALL_END); michael@0: michael@0: uintptr_t fn = *reinterpret_cast(CData::GetData(obj)); michael@0: michael@0: #if defined(XP_WIN) michael@0: int32_t lastErrorStatus; // The status as defined by |GetLastError| michael@0: int32_t savedLastError = GetLastError(); michael@0: SetLastError(0); michael@0: #endif //defined(XP_WIN) michael@0: int errnoStatus; // The status as defined by |errno| michael@0: int savedErrno = errno; michael@0: errno = 0; michael@0: michael@0: ffi_call(&fninfo->mCIF, FFI_FN(fn), returnValue.mData, michael@0: reinterpret_cast(values.begin())); michael@0: michael@0: // Save error value. michael@0: // We need to save it before leaving the scope of |suspend| as destructing michael@0: // |suspend| has the side-effect of clearing |GetLastError| michael@0: // (see bug 684017). michael@0: michael@0: errnoStatus = errno; michael@0: #if defined(XP_WIN) michael@0: lastErrorStatus = GetLastError(); michael@0: SetLastError(savedLastError); michael@0: #endif // defined(XP_WIN) michael@0: michael@0: errno = savedErrno; michael@0: michael@0: // We're no longer calling into C. michael@0: autoCallback.DoEndCallback(); michael@0: michael@0: // Store the error value for later consultation with |ctypes.getStatus| michael@0: JSObject *objCTypes = CType::GetGlobalCTypes(cx, typeObj); michael@0: if (!objCTypes) michael@0: return false; michael@0: michael@0: JS_SetReservedSlot(objCTypes, SLOT_ERRNO, INT_TO_JSVAL(errnoStatus)); michael@0: #if defined(XP_WIN) michael@0: JS_SetReservedSlot(objCTypes, SLOT_LASTERROR, INT_TO_JSVAL(lastErrorStatus)); michael@0: #endif // defined(XP_WIN) michael@0: michael@0: // Small integer types get returned as a word-sized ffi_arg. Coerce it back michael@0: // into the correct size for ConvertToJS. michael@0: switch (typeCode) { michael@0: #define DEFINE_INT_TYPE(name, type, ffiType) \ michael@0: case TYPE_##name: \ michael@0: if (sizeof(type) < sizeof(ffi_arg)) { \ michael@0: ffi_arg data = *static_cast(returnValue.mData); \ michael@0: *static_cast(returnValue.mData) = static_cast(data); \ michael@0: } \ michael@0: break; michael@0: #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) michael@0: #define DEFINE_BOOL_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) michael@0: #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) michael@0: #define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) michael@0: #include "ctypes/typedefs.h" michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: // prepare a JS object from the result michael@0: RootedObject returnType(cx, fninfo->mReturnType); michael@0: return ConvertToJS(cx, returnType, NullPtr(), returnValue.mData, false, true, vp); michael@0: } michael@0: michael@0: FunctionInfo* michael@0: FunctionType::GetFunctionInfo(JSObject* obj) michael@0: { michael@0: JS_ASSERT(CType::IsCType(obj)); michael@0: JS_ASSERT(CType::GetTypeCode(obj) == TYPE_function); michael@0: michael@0: jsval slot = JS_GetReservedSlot(obj, SLOT_FNINFO); michael@0: JS_ASSERT(!JSVAL_IS_VOID(slot) && JSVAL_TO_PRIVATE(slot)); michael@0: michael@0: return static_cast(JSVAL_TO_PRIVATE(slot)); michael@0: } michael@0: michael@0: bool michael@0: FunctionType::IsFunctionType(HandleValue v) michael@0: { michael@0: if (!v.isObject()) michael@0: return false; michael@0: JSObject* obj = &v.toObject(); michael@0: return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_function; michael@0: } michael@0: michael@0: bool michael@0: FunctionType::ArgTypesGetter(JSContext* cx, JS::CallArgs args) michael@0: { michael@0: JS::Rooted obj(cx, &args.thisv().toObject()); michael@0: michael@0: args.rval().set(JS_GetReservedSlot(obj, SLOT_ARGS_T)); michael@0: if (!args.rval().isUndefined()) michael@0: return true; michael@0: michael@0: FunctionInfo* fninfo = GetFunctionInfo(obj); michael@0: size_t len = fninfo->mArgTypes.length(); michael@0: michael@0: // Prepare a new array. michael@0: JS::Rooted argTypes(cx); michael@0: { michael@0: JS::AutoValueVector vec(cx); michael@0: if (!vec.resize(len)) michael@0: return false; michael@0: michael@0: for (size_t i = 0; i < len; ++i) michael@0: vec[i] = JS::ObjectValue(*fninfo->mArgTypes[i]); michael@0: michael@0: argTypes = JS_NewArrayObject(cx, vec); michael@0: if (!argTypes) michael@0: return false; michael@0: } michael@0: michael@0: // Seal and cache it. michael@0: if (!JS_FreezeObject(cx, argTypes)) michael@0: return false; michael@0: JS_SetReservedSlot(obj, SLOT_ARGS_T, JS::ObjectValue(*argTypes)); michael@0: michael@0: args.rval().setObject(*argTypes); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: FunctionType::ReturnTypeGetter(JSContext* cx, JS::CallArgs args) michael@0: { michael@0: // Get the returnType object from the FunctionInfo. michael@0: args.rval().setObject(*GetFunctionInfo(&args.thisv().toObject())->mReturnType); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: FunctionType::ABIGetter(JSContext* cx, JS::CallArgs args) michael@0: { michael@0: // Get the abi object from the FunctionInfo. michael@0: args.rval().setObject(*GetFunctionInfo(&args.thisv().toObject())->mABI); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: FunctionType::IsVariadicGetter(JSContext* cx, JS::CallArgs args) michael@0: { michael@0: args.rval().setBoolean(GetFunctionInfo(&args.thisv().toObject())->mIsVariadic); michael@0: return true; michael@0: } michael@0: michael@0: /******************************************************************************* michael@0: ** CClosure implementation michael@0: *******************************************************************************/ michael@0: michael@0: JSObject* michael@0: CClosure::Create(JSContext* cx, michael@0: HandleObject typeObj, michael@0: HandleObject fnObj, michael@0: HandleObject thisObj, michael@0: jsval errVal_, michael@0: PRFuncPtr* fnptr) michael@0: { michael@0: RootedValue errVal(cx, errVal_); michael@0: JS_ASSERT(fnObj); michael@0: michael@0: RootedObject result(cx, JS_NewObject(cx, &sCClosureClass, NullPtr(), NullPtr())); michael@0: if (!result) michael@0: return nullptr; michael@0: michael@0: // Get the FunctionInfo from the FunctionType. michael@0: FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj); michael@0: JS_ASSERT(!fninfo->mIsVariadic); michael@0: JS_ASSERT(GetABICode(fninfo->mABI) != ABI_WINAPI); michael@0: michael@0: AutoPtr cinfo(cx->new_(JS_GetRuntime(cx))); michael@0: if (!cinfo) { michael@0: JS_ReportOutOfMemory(cx); michael@0: return nullptr; michael@0: } michael@0: michael@0: // Get the prototype of the FunctionType object, of class CTypeProto, michael@0: // which stores our JSContext for use with the closure. michael@0: RootedObject proto(cx); michael@0: if (!JS_GetPrototype(cx, typeObj, &proto)) michael@0: return nullptr; michael@0: JS_ASSERT(proto); michael@0: JS_ASSERT(CType::IsCTypeProto(proto)); michael@0: michael@0: // Get a JSContext for use with the closure. michael@0: cinfo->cx = js::DefaultJSContext(JS_GetRuntime(cx)); michael@0: michael@0: // Prepare the error sentinel value. It's important to do this now, because michael@0: // we might be unable to convert the value to the proper type. If so, we want michael@0: // the caller to know about it _now_, rather than some uncertain time in the michael@0: // future when the error sentinel is actually needed. michael@0: if (!JSVAL_IS_VOID(errVal)) { michael@0: michael@0: // Make sure the callback returns something. michael@0: if (CType::GetTypeCode(fninfo->mReturnType) == TYPE_void_t) { michael@0: JS_ReportError(cx, "A void callback can't pass an error sentinel"); michael@0: return nullptr; michael@0: } michael@0: michael@0: // With the exception of void, the FunctionType constructor ensures that michael@0: // the return type has a defined size. michael@0: JS_ASSERT(CType::IsSizeDefined(fninfo->mReturnType)); michael@0: michael@0: // Allocate a buffer for the return value. michael@0: size_t rvSize = CType::GetSize(fninfo->mReturnType); michael@0: cinfo->errResult = cx->malloc_(rvSize); michael@0: if (!cinfo->errResult) michael@0: return nullptr; michael@0: michael@0: // Do the value conversion. This might fail, in which case we throw. michael@0: if (!ImplicitConvert(cx, errVal, fninfo->mReturnType, cinfo->errResult, michael@0: false, nullptr)) michael@0: return nullptr; michael@0: } else { michael@0: cinfo->errResult = nullptr; michael@0: } michael@0: michael@0: // Copy the important bits of context into cinfo. michael@0: cinfo->closureObj = result; michael@0: cinfo->typeObj = typeObj; michael@0: cinfo->thisObj = thisObj; michael@0: cinfo->jsfnObj = fnObj; michael@0: michael@0: // Create an ffi_closure object and initialize it. michael@0: void* code; michael@0: cinfo->closure = michael@0: static_cast(ffi_closure_alloc(sizeof(ffi_closure), &code)); michael@0: if (!cinfo->closure || !code) { michael@0: JS_ReportError(cx, "couldn't create closure - libffi error"); michael@0: return nullptr; michael@0: } michael@0: michael@0: ffi_status status = ffi_prep_closure_loc(cinfo->closure, &fninfo->mCIF, michael@0: CClosure::ClosureStub, cinfo.get(), code); michael@0: if (status != FFI_OK) { michael@0: JS_ReportError(cx, "couldn't create closure - libffi error"); michael@0: return nullptr; michael@0: } michael@0: michael@0: // Stash the ClosureInfo struct on our new object. michael@0: JS_SetReservedSlot(result, SLOT_CLOSUREINFO, PRIVATE_TO_JSVAL(cinfo.forget())); michael@0: michael@0: // Casting between void* and a function pointer is forbidden in C and C++. michael@0: // Do it via an integral type. michael@0: *fnptr = reinterpret_cast(reinterpret_cast(code)); michael@0: return result; michael@0: } michael@0: michael@0: void michael@0: CClosure::Trace(JSTracer* trc, JSObject* obj) michael@0: { michael@0: // Make sure our ClosureInfo slot is legit. If it's not, bail. michael@0: jsval slot = JS_GetReservedSlot(obj, SLOT_CLOSUREINFO); michael@0: if (JSVAL_IS_VOID(slot)) michael@0: return; michael@0: michael@0: ClosureInfo* cinfo = static_cast(JSVAL_TO_PRIVATE(slot)); michael@0: michael@0: // Identify our objects to the tracer. (There's no need to identify michael@0: // 'closureObj', since that's us.) michael@0: JS_CallHeapObjectTracer(trc, &cinfo->typeObj, "typeObj"); michael@0: JS_CallHeapObjectTracer(trc, &cinfo->jsfnObj, "jsfnObj"); michael@0: if (cinfo->thisObj) michael@0: JS_CallHeapObjectTracer(trc, &cinfo->thisObj, "thisObj"); michael@0: } michael@0: michael@0: void michael@0: CClosure::Finalize(JSFreeOp *fop, JSObject* obj) michael@0: { michael@0: // Make sure our ClosureInfo slot is legit. If it's not, bail. michael@0: jsval slot = JS_GetReservedSlot(obj, SLOT_CLOSUREINFO); michael@0: if (JSVAL_IS_VOID(slot)) michael@0: return; michael@0: michael@0: ClosureInfo* cinfo = static_cast(JSVAL_TO_PRIVATE(slot)); michael@0: FreeOp::get(fop)->delete_(cinfo); michael@0: } michael@0: michael@0: void michael@0: CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData) michael@0: { michael@0: JS_ASSERT(cif); michael@0: JS_ASSERT(result); michael@0: JS_ASSERT(args); michael@0: JS_ASSERT(userData); michael@0: michael@0: // Retrieve the essentials from our closure object. michael@0: ClosureInfo* cinfo = static_cast(userData); michael@0: JSContext* cx = cinfo->cx; michael@0: michael@0: // Let the runtime callback know that we are about to call into JS again. The end callback will michael@0: // fire automatically when we exit this function. michael@0: js::AutoCTypesActivityCallback autoCallback(cx, js::CTYPES_CALLBACK_BEGIN, michael@0: js::CTYPES_CALLBACK_END); michael@0: michael@0: RootedObject typeObj(cx, cinfo->typeObj); michael@0: RootedObject thisObj(cx, cinfo->thisObj); michael@0: RootedValue jsfnVal(cx, ObjectValue(*cinfo->jsfnObj)); michael@0: michael@0: JS_AbortIfWrongThread(JS_GetRuntime(cx)); michael@0: michael@0: JSAutoRequest ar(cx); michael@0: JSAutoCompartment ac(cx, cinfo->jsfnObj); michael@0: michael@0: // Assert that our CIFs agree. michael@0: FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj); michael@0: JS_ASSERT(cif == &fninfo->mCIF); michael@0: michael@0: TypeCode typeCode = CType::GetTypeCode(fninfo->mReturnType); michael@0: michael@0: // Initialize the result to zero, in case something fails. Small integer types michael@0: // are promoted to a word-sized ffi_arg, so we must be careful to zero the michael@0: // whole word. michael@0: size_t rvSize = 0; michael@0: if (cif->rtype != &ffi_type_void) { michael@0: rvSize = cif->rtype->size; michael@0: switch (typeCode) { michael@0: #define DEFINE_INT_TYPE(name, type, ffiType) \ michael@0: case TYPE_##name: michael@0: #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) michael@0: #define DEFINE_BOOL_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) michael@0: #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) michael@0: #define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) michael@0: #include "ctypes/typedefs.h" michael@0: rvSize = Align(rvSize, sizeof(ffi_arg)); michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: memset(result, 0, rvSize); michael@0: } michael@0: michael@0: // Set up an array for converted arguments. michael@0: JS::AutoValueVector argv(cx); michael@0: if (!argv.resize(cif->nargs)) { michael@0: JS_ReportOutOfMemory(cx); michael@0: return; michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < cif->nargs; ++i) { michael@0: // Convert each argument, and have any CData objects created depend on michael@0: // the existing buffers. michael@0: RootedObject argType(cx, fninfo->mArgTypes[i]); michael@0: if (!ConvertToJS(cx, argType, NullPtr(), args[i], false, false, &argv[i])) michael@0: return; michael@0: } michael@0: michael@0: // Call the JS function. 'thisObj' may be nullptr, in which case the JS michael@0: // engine will find an appropriate object to use. michael@0: RootedValue rval(cx); michael@0: bool success = JS_CallFunctionValue(cx, thisObj, jsfnVal, argv, &rval); michael@0: michael@0: // Convert the result. Note that we pass 'isArgument = false', such that michael@0: // ImplicitConvert will *not* autoconvert a JS string into a pointer-to-char michael@0: // type, which would require an allocation that we can't track. The JS michael@0: // function must perform this conversion itself and return a PointerType michael@0: // CData; thusly, the burden of freeing the data is left to the user. michael@0: if (success && cif->rtype != &ffi_type_void) michael@0: success = ImplicitConvert(cx, rval, fninfo->mReturnType, result, false, michael@0: nullptr); michael@0: michael@0: if (!success) { michael@0: // Something failed. The callee may have thrown, or it may not have michael@0: // returned a value that ImplicitConvert() was happy with. Depending on how michael@0: // prudent the consumer has been, we may or may not have a recovery plan. michael@0: michael@0: // In any case, a JS exception cannot be passed to C code, so report the michael@0: // exception if any and clear it from the cx. michael@0: if (JS_IsExceptionPending(cx)) michael@0: JS_ReportPendingException(cx); michael@0: michael@0: if (cinfo->errResult) { michael@0: // Good case: we have a sentinel that we can return. Copy it in place of michael@0: // the actual return value, and then proceed. michael@0: michael@0: // The buffer we're returning might be larger than the size of the return michael@0: // type, due to libffi alignment issues (see above). But it should never michael@0: // be smaller. michael@0: size_t copySize = CType::GetSize(fninfo->mReturnType); michael@0: JS_ASSERT(copySize <= rvSize); michael@0: memcpy(result, cinfo->errResult, copySize); michael@0: } else { michael@0: // Bad case: not much we can do here. The rv is already zeroed out, so we michael@0: // just report (another) error and hope for the best. JS_ReportError will michael@0: // actually throw an exception here, so then we have to report it. Again. michael@0: // Ugh. michael@0: JS_ReportError(cx, "JavaScript callback failed, and an error sentinel " michael@0: "was not specified."); michael@0: if (JS_IsExceptionPending(cx)) michael@0: JS_ReportPendingException(cx); michael@0: michael@0: return; michael@0: } michael@0: } michael@0: michael@0: // Small integer types must be returned as a word-sized ffi_arg. Coerce it michael@0: // back into the size libffi expects. michael@0: switch (typeCode) { michael@0: #define DEFINE_INT_TYPE(name, type, ffiType) \ michael@0: case TYPE_##name: \ michael@0: if (sizeof(type) < sizeof(ffi_arg)) { \ michael@0: ffi_arg data = *static_cast(result); \ michael@0: *static_cast(result) = data; \ michael@0: } \ michael@0: break; michael@0: #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) michael@0: #define DEFINE_BOOL_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) michael@0: #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) michael@0: #define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) michael@0: #include "ctypes/typedefs.h" michael@0: default: michael@0: break; michael@0: } michael@0: } michael@0: michael@0: /******************************************************************************* michael@0: ** CData implementation michael@0: *******************************************************************************/ michael@0: michael@0: // Create a new CData object of type 'typeObj' containing binary data supplied michael@0: // in 'source', optionally with a referent object 'refObj'. michael@0: // michael@0: // * 'typeObj' must be a CType of defined (but possibly zero) size. michael@0: // michael@0: // * If an object 'refObj' is supplied, the new CData object stores the michael@0: // referent object in a reserved slot for GC safety, such that 'refObj' will michael@0: // be held alive by the resulting CData object. 'refObj' may or may not be michael@0: // a CData object; merely an object we want to keep alive. michael@0: // * If 'refObj' is a CData object, 'ownResult' must be false. michael@0: // * Otherwise, 'refObj' is a Library or CClosure object, and 'ownResult' michael@0: // may be true or false. michael@0: // * Otherwise 'refObj' is nullptr. In this case, 'ownResult' may be true or michael@0: // false. michael@0: // michael@0: // * If 'ownResult' is true, the CData object will allocate an appropriately michael@0: // sized buffer, and free it upon finalization. If 'source' data is michael@0: // supplied, the data will be copied from 'source' into the buffer; michael@0: // otherwise, the entirety of the new buffer will be initialized to zero. michael@0: // * If 'ownResult' is false, the new CData's buffer refers to a slice of michael@0: // another buffer kept alive by 'refObj'. 'source' data must be provided, michael@0: // and the new CData's buffer will refer to 'source'. michael@0: JSObject* michael@0: CData::Create(JSContext* cx, michael@0: HandleObject typeObj, michael@0: HandleObject refObj, michael@0: void* source, michael@0: bool ownResult) michael@0: { michael@0: JS_ASSERT(typeObj); michael@0: JS_ASSERT(CType::IsCType(typeObj)); michael@0: JS_ASSERT(CType::IsSizeDefined(typeObj)); michael@0: JS_ASSERT(ownResult || source); michael@0: JS_ASSERT_IF(refObj && CData::IsCData(refObj), !ownResult); michael@0: michael@0: // Get the 'prototype' property from the type. michael@0: jsval slot = JS_GetReservedSlot(typeObj, SLOT_PROTO); michael@0: JS_ASSERT(!JSVAL_IS_PRIMITIVE(slot)); michael@0: michael@0: RootedObject proto(cx, JSVAL_TO_OBJECT(slot)); michael@0: RootedObject parent(cx, JS_GetParent(typeObj)); michael@0: JS_ASSERT(parent); michael@0: michael@0: RootedObject dataObj(cx, JS_NewObject(cx, &sCDataClass, proto, parent)); michael@0: if (!dataObj) michael@0: return nullptr; michael@0: michael@0: // set the CData's associated type michael@0: JS_SetReservedSlot(dataObj, SLOT_CTYPE, OBJECT_TO_JSVAL(typeObj)); michael@0: michael@0: // Stash the referent object, if any, for GC safety. michael@0: if (refObj) michael@0: JS_SetReservedSlot(dataObj, SLOT_REFERENT, OBJECT_TO_JSVAL(refObj)); michael@0: michael@0: // Set our ownership flag. michael@0: JS_SetReservedSlot(dataObj, SLOT_OWNS, BOOLEAN_TO_JSVAL(ownResult)); michael@0: michael@0: // attach the buffer. since it might not be 2-byte aligned, we need to michael@0: // allocate an aligned space for it and store it there. :( michael@0: char** buffer = cx->new_(); michael@0: if (!buffer) { michael@0: JS_ReportOutOfMemory(cx); michael@0: return nullptr; michael@0: } michael@0: michael@0: char* data; michael@0: if (!ownResult) { michael@0: data = static_cast(source); michael@0: } else { michael@0: // Initialize our own buffer. michael@0: size_t size = CType::GetSize(typeObj); michael@0: data = (char*)cx->malloc_(size); michael@0: if (!data) { michael@0: // Report a catchable allocation error. michael@0: JS_ReportAllocationOverflow(cx); michael@0: js_free(buffer); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!source) michael@0: memset(data, 0, size); michael@0: else michael@0: memcpy(data, source, size); michael@0: } michael@0: michael@0: *buffer = data; michael@0: JS_SetReservedSlot(dataObj, SLOT_DATA, PRIVATE_TO_JSVAL(buffer)); michael@0: michael@0: return dataObj; michael@0: } michael@0: michael@0: void michael@0: CData::Finalize(JSFreeOp *fop, JSObject* obj) michael@0: { michael@0: // Delete our buffer, and the data it contains if we own it. michael@0: jsval slot = JS_GetReservedSlot(obj, SLOT_OWNS); michael@0: if (JSVAL_IS_VOID(slot)) michael@0: return; michael@0: michael@0: bool owns = JSVAL_TO_BOOLEAN(slot); michael@0: michael@0: slot = JS_GetReservedSlot(obj, SLOT_DATA); michael@0: if (JSVAL_IS_VOID(slot)) michael@0: return; michael@0: char** buffer = static_cast(JSVAL_TO_PRIVATE(slot)); michael@0: michael@0: if (owns) michael@0: FreeOp::get(fop)->free_(*buffer); michael@0: FreeOp::get(fop)->delete_(buffer); michael@0: } michael@0: michael@0: JSObject* michael@0: CData::GetCType(JSObject* dataObj) michael@0: { michael@0: JS_ASSERT(CData::IsCData(dataObj)); michael@0: michael@0: jsval slot = JS_GetReservedSlot(dataObj, SLOT_CTYPE); michael@0: JSObject* typeObj = JSVAL_TO_OBJECT(slot); michael@0: JS_ASSERT(CType::IsCType(typeObj)); michael@0: return typeObj; michael@0: } michael@0: michael@0: void* michael@0: CData::GetData(JSObject* dataObj) michael@0: { michael@0: JS_ASSERT(CData::IsCData(dataObj)); michael@0: michael@0: jsval slot = JS_GetReservedSlot(dataObj, SLOT_DATA); michael@0: michael@0: void** buffer = static_cast(JSVAL_TO_PRIVATE(slot)); michael@0: JS_ASSERT(buffer); michael@0: JS_ASSERT(*buffer); michael@0: return *buffer; michael@0: } michael@0: michael@0: bool michael@0: CData::IsCData(JSObject* obj) michael@0: { michael@0: return JS_GetClass(obj) == &sCDataClass; michael@0: } michael@0: michael@0: bool michael@0: CData::IsCData(HandleValue v) michael@0: { michael@0: return v.isObject() && CData::IsCData(&v.toObject()); michael@0: } michael@0: michael@0: bool michael@0: CData::IsCDataProto(JSObject* obj) michael@0: { michael@0: return JS_GetClass(obj) == &sCDataProtoClass; michael@0: } michael@0: michael@0: bool michael@0: CData::ValueGetter(JSContext* cx, JS::CallArgs args) michael@0: { michael@0: RootedObject obj(cx, &args.thisv().toObject()); michael@0: michael@0: // Convert the value to a primitive; do not create a new CData object. michael@0: RootedObject ctype(cx, GetCType(obj)); michael@0: return ConvertToJS(cx, ctype, NullPtr(), GetData(obj), true, false, args.rval().address()); michael@0: } michael@0: michael@0: bool michael@0: CData::ValueSetter(JSContext* cx, JS::CallArgs args) michael@0: { michael@0: RootedObject obj(cx, &args.thisv().toObject()); michael@0: args.rval().setUndefined(); michael@0: return ImplicitConvert(cx, args.get(0), GetCType(obj), GetData(obj), false, nullptr); michael@0: } michael@0: michael@0: bool michael@0: CData::Address(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 0) { michael@0: JS_ReportError(cx, "address takes zero arguments"); michael@0: return false; michael@0: } michael@0: michael@0: RootedObject obj(cx, JS_THIS_OBJECT(cx, vp)); michael@0: if (!obj) michael@0: return false; michael@0: if (!IsCData(obj)) { michael@0: JS_ReportError(cx, "not a CData"); michael@0: return false; michael@0: } michael@0: michael@0: RootedObject typeObj(cx, CData::GetCType(obj)); michael@0: RootedObject pointerType(cx, PointerType::CreateInternal(cx, typeObj)); michael@0: if (!pointerType) michael@0: return false; michael@0: michael@0: // Create a PointerType CData object containing null. michael@0: JSObject* result = CData::Create(cx, pointerType, NullPtr(), nullptr, true); michael@0: if (!result) michael@0: return false; michael@0: michael@0: args.rval().setObject(*result); michael@0: michael@0: // Manually set the pointer inside the object, so we skip the conversion step. michael@0: void** data = static_cast(GetData(result)); michael@0: *data = GetData(obj); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CData::Cast(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 2) { michael@0: JS_ReportError(cx, "cast takes two arguments"); michael@0: return false; michael@0: } michael@0: michael@0: if (JSVAL_IS_PRIMITIVE(args[0]) || michael@0: !CData::IsCData(&args[0].toObject())) { michael@0: JS_ReportError(cx, "first argument must be a CData"); michael@0: return false; michael@0: } michael@0: RootedObject sourceData(cx, &args[0].toObject()); michael@0: JSObject* sourceType = CData::GetCType(sourceData); michael@0: michael@0: if (JSVAL_IS_PRIMITIVE(args[1]) || michael@0: !CType::IsCType(&args[1].toObject())) { michael@0: JS_ReportError(cx, "second argument must be a CType"); michael@0: return false; michael@0: } michael@0: michael@0: RootedObject targetType(cx, &args[1].toObject()); michael@0: size_t targetSize; michael@0: if (!CType::GetSafeSize(targetType, &targetSize) || michael@0: targetSize > CType::GetSize(sourceType)) { michael@0: JS_ReportError(cx, michael@0: "target CType has undefined or larger size than source CType"); michael@0: return false; michael@0: } michael@0: michael@0: // Construct a new CData object with a type of 'targetType' and a referent michael@0: // of 'sourceData'. michael@0: void* data = CData::GetData(sourceData); michael@0: JSObject* result = CData::Create(cx, targetType, sourceData, data, false); michael@0: if (!result) michael@0: return false; michael@0: michael@0: args.rval().setObject(*result); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CData::GetRuntime(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 1) { michael@0: JS_ReportError(cx, "getRuntime takes one argument"); michael@0: return false; michael@0: } michael@0: michael@0: if (JSVAL_IS_PRIMITIVE(args[0]) || michael@0: !CType::IsCType(&args[0].toObject())) { michael@0: JS_ReportError(cx, "first argument must be a CType"); michael@0: return false; michael@0: } michael@0: michael@0: RootedObject targetType(cx, &args[0].toObject()); michael@0: size_t targetSize; michael@0: if (!CType::GetSafeSize(targetType, &targetSize) || michael@0: targetSize != sizeof(void*)) { michael@0: JS_ReportError(cx, "target CType has non-pointer size"); michael@0: return false; michael@0: } michael@0: michael@0: void* data = static_cast(cx->runtime()); michael@0: JSObject* result = CData::Create(cx, targetType, NullPtr(), &data, true); michael@0: if (!result) michael@0: return false; michael@0: michael@0: args.rval().setObject(*result); michael@0: return true; michael@0: } michael@0: michael@0: typedef JS::TwoByteCharsZ (*InflateUTF8Method)(JSContext *, const JS::UTF8Chars, size_t *); michael@0: michael@0: static bool michael@0: ReadStringCommon(JSContext* cx, InflateUTF8Method inflateUTF8, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 0) { michael@0: JS_ReportError(cx, "readString takes zero arguments"); michael@0: return false; michael@0: } michael@0: michael@0: JSObject* obj = CDataFinalizer::GetCData(cx, JS_THIS_OBJECT(cx, vp)); michael@0: if (!obj || !CData::IsCData(obj)) { michael@0: JS_ReportError(cx, "not a CData"); michael@0: return false; michael@0: } michael@0: michael@0: // Make sure we are a pointer to, or an array of, an 8-bit or 16-bit michael@0: // character or integer type. michael@0: JSObject* baseType; michael@0: JSObject* typeObj = CData::GetCType(obj); michael@0: TypeCode typeCode = CType::GetTypeCode(typeObj); michael@0: void* data; michael@0: size_t maxLength = -1; michael@0: switch (typeCode) { michael@0: case TYPE_pointer: michael@0: baseType = PointerType::GetBaseType(typeObj); michael@0: data = *static_cast(CData::GetData(obj)); michael@0: if (data == nullptr) { michael@0: JS_ReportError(cx, "cannot read contents of null pointer"); michael@0: return false; michael@0: } michael@0: break; michael@0: case TYPE_array: michael@0: baseType = ArrayType::GetBaseType(typeObj); michael@0: data = CData::GetData(obj); michael@0: maxLength = ArrayType::GetLength(typeObj); michael@0: break; michael@0: default: michael@0: JS_ReportError(cx, "not a PointerType or ArrayType"); michael@0: return false; michael@0: } michael@0: michael@0: // Convert the string buffer, taking care to determine the correct string michael@0: // length in the case of arrays (which may contain embedded nulls). michael@0: JSString* result; michael@0: switch (CType::GetTypeCode(baseType)) { michael@0: case TYPE_int8_t: michael@0: case TYPE_uint8_t: michael@0: case TYPE_char: michael@0: case TYPE_signed_char: michael@0: case TYPE_unsigned_char: { michael@0: char* bytes = static_cast(data); michael@0: size_t length = strnlen(bytes, maxLength); michael@0: michael@0: // Determine the length. michael@0: jschar *dst = inflateUTF8(cx, JS::UTF8Chars(bytes, length), &length).get(); michael@0: if (!dst) michael@0: return false; michael@0: michael@0: result = JS_NewUCString(cx, dst, length); michael@0: break; michael@0: } michael@0: case TYPE_int16_t: michael@0: case TYPE_uint16_t: michael@0: case TYPE_short: michael@0: case TYPE_unsigned_short: michael@0: case TYPE_jschar: { michael@0: jschar* chars = static_cast(data); michael@0: size_t length = strnlen(chars, maxLength); michael@0: result = JS_NewUCStringCopyN(cx, chars, length); michael@0: break; michael@0: } michael@0: default: michael@0: JS_ReportError(cx, michael@0: "base type is not an 8-bit or 16-bit integer or character type"); michael@0: return false; michael@0: } michael@0: michael@0: if (!result) michael@0: return false; michael@0: michael@0: args.rval().setString(result); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CData::ReadString(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: return ReadStringCommon(cx, JS::UTF8CharsToNewTwoByteCharsZ, argc, vp); michael@0: } michael@0: michael@0: bool michael@0: CData::ReadStringReplaceMalformed(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: return ReadStringCommon(cx, JS::LossyUTF8CharsToNewTwoByteCharsZ, argc, vp); michael@0: } michael@0: michael@0: JSString * michael@0: CData::GetSourceString(JSContext *cx, HandleObject typeObj, void *data) michael@0: { michael@0: // Walk the types, building up the toSource() string. michael@0: // First, we build up the type expression: michael@0: // 't.ptr' for pointers; michael@0: // 't.array([n])' for arrays; michael@0: // 'n' for structs, where n = t.name, the struct's name. (We assume this is michael@0: // bound to a variable in the current scope.) michael@0: AutoString source; michael@0: BuildTypeSource(cx, typeObj, true, source); michael@0: AppendString(source, "("); michael@0: if (!BuildDataSource(cx, typeObj, data, false, source)) michael@0: return nullptr; michael@0: michael@0: AppendString(source, ")"); michael@0: michael@0: return NewUCString(cx, source); michael@0: } michael@0: michael@0: bool michael@0: CData::ToSource(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 0) { michael@0: JS_ReportError(cx, "toSource takes zero arguments"); michael@0: return false; michael@0: } michael@0: michael@0: JSObject* obj = JS_THIS_OBJECT(cx, vp); michael@0: if (!obj) michael@0: return false; michael@0: if (!CData::IsCData(obj) && !CData::IsCDataProto(obj)) { michael@0: JS_ReportError(cx, "not a CData"); michael@0: return false; michael@0: } michael@0: michael@0: JSString* result; michael@0: if (CData::IsCData(obj)) { michael@0: RootedObject typeObj(cx, CData::GetCType(obj)); michael@0: void* data = CData::GetData(obj); michael@0: michael@0: result = CData::GetSourceString(cx, typeObj, data); michael@0: } else { michael@0: result = JS_NewStringCopyZ(cx, "[CData proto object]"); michael@0: } michael@0: michael@0: if (!result) michael@0: return false; michael@0: michael@0: args.rval().setString(result); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CData::ErrnoGetter(JSContext* cx, JS::CallArgs args) michael@0: { michael@0: args.rval().set(JS_GetReservedSlot(&args.thisv().toObject(), SLOT_ERRNO)); michael@0: return true; michael@0: } michael@0: michael@0: #if defined(XP_WIN) michael@0: bool michael@0: CData::LastErrorGetter(JSContext* cx, JS::CallArgs args) michael@0: { michael@0: args.rval().set(JS_GetReservedSlot(&args.thisv().toObject(), SLOT_LASTERROR)); michael@0: return true; michael@0: } michael@0: #endif // defined(XP_WIN) michael@0: michael@0: bool michael@0: CDataFinalizer::Methods::ToSource(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: RootedObject objThis(cx, JS_THIS_OBJECT(cx, vp)); michael@0: if (!objThis) michael@0: return false; michael@0: if (!CDataFinalizer::IsCDataFinalizer(objThis)) { michael@0: JS_ReportError(cx, "not a CDataFinalizer"); michael@0: return false; michael@0: } michael@0: michael@0: CDataFinalizer::Private *p = (CDataFinalizer::Private *) michael@0: JS_GetPrivate(objThis); michael@0: michael@0: JSString *strMessage; michael@0: if (!p) { michael@0: strMessage = JS_NewStringCopyZ(cx, "ctypes.CDataFinalizer()"); michael@0: } else { michael@0: RootedObject objType(cx, CDataFinalizer::GetCType(cx, objThis)); michael@0: if (!objType) { michael@0: JS_ReportError(cx, "CDataFinalizer has no type"); michael@0: return false; michael@0: } michael@0: michael@0: AutoString source; michael@0: AppendString(source, "ctypes.CDataFinalizer("); michael@0: JSString *srcValue = CData::GetSourceString(cx, objType, p->cargs); michael@0: if (!srcValue) { michael@0: return false; michael@0: } michael@0: AppendString(source, srcValue); michael@0: AppendString(source, ", "); michael@0: jsval valCodePtrType = JS_GetReservedSlot(objThis, michael@0: SLOT_DATAFINALIZER_CODETYPE); michael@0: if (JSVAL_IS_PRIMITIVE(valCodePtrType)) { michael@0: return false; michael@0: } michael@0: michael@0: RootedObject typeObj(cx, JSVAL_TO_OBJECT(valCodePtrType)); michael@0: JSString *srcDispose = CData::GetSourceString(cx, typeObj, &(p->code)); michael@0: if (!srcDispose) { michael@0: return false; michael@0: } michael@0: michael@0: AppendString(source, srcDispose); michael@0: AppendString(source, ")"); michael@0: strMessage = NewUCString(cx, source); michael@0: } michael@0: michael@0: if (!strMessage) { michael@0: // This is a memory issue, no error message michael@0: return false; michael@0: } michael@0: michael@0: args.rval().setString(strMessage); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CDataFinalizer::Methods::ToString(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JSObject* objThis = JS_THIS_OBJECT(cx, vp); michael@0: if (!objThis) michael@0: return false; michael@0: if (!CDataFinalizer::IsCDataFinalizer(objThis)) { michael@0: JS_ReportError(cx, "not a CDataFinalizer"); michael@0: return false; michael@0: } michael@0: michael@0: JSString *strMessage; michael@0: RootedValue value(cx); michael@0: if (!JS_GetPrivate(objThis)) { michael@0: // Pre-check whether CDataFinalizer::GetValue can fail michael@0: // to avoid reporting an error when not appropriate. michael@0: strMessage = JS_NewStringCopyZ(cx, "[CDataFinalizer - empty]"); michael@0: if (!strMessage) { michael@0: return false; michael@0: } michael@0: } else if (!CDataFinalizer::GetValue(cx, objThis, value.address())) { michael@0: MOZ_ASSUME_UNREACHABLE("Could not convert an empty CDataFinalizer"); michael@0: } else { michael@0: strMessage = ToString(cx, value); michael@0: if (!strMessage) { michael@0: return false; michael@0: } michael@0: } michael@0: args.rval().setString(strMessage); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CDataFinalizer::IsCDataFinalizer(JSObject *obj) michael@0: { michael@0: return JS_GetClass(obj) == &sCDataFinalizerClass; michael@0: } michael@0: michael@0: michael@0: JSObject * michael@0: CDataFinalizer::GetCType(JSContext *cx, JSObject *obj) michael@0: { michael@0: MOZ_ASSERT(IsCDataFinalizer(obj)); michael@0: michael@0: jsval valData = JS_GetReservedSlot(obj, michael@0: SLOT_DATAFINALIZER_VALTYPE); michael@0: if (JSVAL_IS_VOID(valData)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return JSVAL_TO_OBJECT(valData); michael@0: } michael@0: michael@0: JSObject* michael@0: CDataFinalizer::GetCData(JSContext *cx, JSObject *obj) michael@0: { michael@0: if (!obj) { michael@0: JS_ReportError(cx, "No C data"); michael@0: return nullptr; michael@0: } michael@0: if (CData::IsCData(obj)) { michael@0: return obj; michael@0: } michael@0: if (!CDataFinalizer::IsCDataFinalizer(obj)) { michael@0: JS_ReportError(cx, "Not C data"); michael@0: return nullptr; michael@0: } michael@0: RootedValue val(cx); michael@0: if (!CDataFinalizer::GetValue(cx, obj, val.address()) || JSVAL_IS_PRIMITIVE(val)) { michael@0: JS_ReportError(cx, "Empty CDataFinalizer"); michael@0: return nullptr; michael@0: } michael@0: return JSVAL_TO_OBJECT(val); michael@0: } michael@0: michael@0: bool michael@0: CDataFinalizer::GetValue(JSContext *cx, JSObject *obj, jsval *aResult) michael@0: { michael@0: MOZ_ASSERT(IsCDataFinalizer(obj)); michael@0: michael@0: CDataFinalizer::Private *p = (CDataFinalizer::Private *) michael@0: JS_GetPrivate(obj); michael@0: michael@0: if (!p) { michael@0: JS_ReportError(cx, "Attempting to get the value of an empty CDataFinalizer"); michael@0: return false; // We have called |dispose| or |forget| already. michael@0: } michael@0: michael@0: RootedObject ctype(cx, GetCType(cx, obj)); michael@0: return ConvertToJS(cx, ctype, /*parent*/NullPtr(), p -> cargs, false, true, aResult); michael@0: } michael@0: michael@0: /* michael@0: * Attach a C function as a finalizer to a JS object. michael@0: * michael@0: * Pseudo-JS signature: michael@0: * function(CData, CData U>): CDataFinalizer michael@0: * value, finalizer michael@0: * michael@0: * This function attaches strong references to the following values: michael@0: * - the CType of |value| michael@0: * michael@0: * Note: This function takes advantage of the fact that non-variadic michael@0: * CData functions are initialized during creation. michael@0: */ michael@0: bool michael@0: CDataFinalizer::Construct(JSContext* cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: RootedObject objSelf(cx, &args.callee()); michael@0: RootedObject objProto(cx); michael@0: if (!GetObjectProperty(cx, objSelf, "prototype", &objProto)) { michael@0: JS_ReportError(cx, "CDataFinalizer.prototype does not exist"); michael@0: return false; michael@0: } michael@0: michael@0: // Get arguments michael@0: if (args.length() == 0) { // Special case: the empty (already finalized) object michael@0: JSObject *objResult = JS_NewObject(cx, &sCDataFinalizerClass, objProto, NullPtr()); michael@0: args.rval().setObject(*objResult); michael@0: return true; michael@0: } michael@0: michael@0: if (args.length() != 2) { michael@0: JS_ReportError(cx, "CDataFinalizer takes 2 arguments"); michael@0: return false; michael@0: } michael@0: michael@0: JS::HandleValue valCodePtr = args[1]; michael@0: if (!valCodePtr.isObject()) { michael@0: return TypeError(cx, "_a CData object_ of a function pointer type", michael@0: valCodePtr); michael@0: } michael@0: JSObject *objCodePtr = &valCodePtr.toObject(); michael@0: michael@0: //Note: Using a custom argument formatter here would be awkward (requires michael@0: //a destructor just to uninstall the formatter). michael@0: michael@0: // 2. Extract argument type of |objCodePtr| michael@0: if (!CData::IsCData(objCodePtr)) { michael@0: return TypeError(cx, "a _CData_ object of a function pointer type", michael@0: valCodePtr); michael@0: } michael@0: RootedObject objCodePtrType(cx, CData::GetCType(objCodePtr)); michael@0: RootedValue valCodePtrType(cx, ObjectValue(*objCodePtrType)); michael@0: MOZ_ASSERT(objCodePtrType); michael@0: michael@0: TypeCode typCodePtr = CType::GetTypeCode(objCodePtrType); michael@0: if (typCodePtr != TYPE_pointer) { michael@0: return TypeError(cx, "a CData object of a function _pointer_ type", michael@0: valCodePtrType); michael@0: } michael@0: michael@0: JSObject *objCodeType = PointerType::GetBaseType(objCodePtrType); michael@0: MOZ_ASSERT(objCodeType); michael@0: michael@0: TypeCode typCode = CType::GetTypeCode(objCodeType); michael@0: if (typCode != TYPE_function) { michael@0: return TypeError(cx, "a CData object of a _function_ pointer type", michael@0: valCodePtrType); michael@0: } michael@0: uintptr_t code = *reinterpret_cast(CData::GetData(objCodePtr)); michael@0: if (!code) { michael@0: return TypeError(cx, "a CData object of a _non-NULL_ function pointer type", michael@0: valCodePtrType); michael@0: } michael@0: michael@0: FunctionInfo* funInfoFinalizer = michael@0: FunctionType::GetFunctionInfo(objCodeType); michael@0: MOZ_ASSERT(funInfoFinalizer); michael@0: michael@0: if ((funInfoFinalizer->mArgTypes.length() != 1) michael@0: || (funInfoFinalizer->mIsVariadic)) { michael@0: RootedValue valCodeType(cx, ObjectValue(*objCodeType)); michael@0: return TypeError(cx, "a function accepting exactly one argument", michael@0: valCodeType); michael@0: } michael@0: RootedObject objArgType(cx, funInfoFinalizer->mArgTypes[0]); michael@0: RootedObject returnType(cx, funInfoFinalizer->mReturnType); michael@0: michael@0: // Invariant: At this stage, we know that funInfoFinalizer->mIsVariadic michael@0: // is |false|. Therefore, funInfoFinalizer->mCIF has already been initialized. michael@0: michael@0: bool freePointer = false; michael@0: michael@0: // 3. Perform dynamic cast of |args[0]| into |objType|, store it in |cargs| michael@0: michael@0: size_t sizeArg; michael@0: RootedValue valData(cx, args[0]); michael@0: if (!CType::GetSafeSize(objArgType, &sizeArg)) { michael@0: return TypeError(cx, "(an object with known size)", valData); michael@0: } michael@0: michael@0: ScopedJSFreePtr cargs(malloc(sizeArg)); michael@0: michael@0: if (!ImplicitConvert(cx, valData, objArgType, cargs.get(), michael@0: false, &freePointer)) { michael@0: RootedValue valArgType(cx, ObjectValue(*objArgType)); michael@0: return TypeError(cx, "(an object that can be converted to the following type)", michael@0: valArgType); michael@0: } michael@0: if (freePointer) { michael@0: // Note: We could handle that case, if necessary. michael@0: JS_ReportError(cx, "Internal Error during CDataFinalizer. Object cannot be represented"); michael@0: return false; michael@0: } michael@0: michael@0: // 4. Prepare buffer for holding return value michael@0: michael@0: ScopedJSFreePtr rvalue; michael@0: if (CType::GetTypeCode(returnType) != TYPE_void_t) { michael@0: rvalue = malloc(Align(CType::GetSize(returnType), michael@0: sizeof(ffi_arg))); michael@0: } //Otherwise, simply do not allocate michael@0: michael@0: // 5. Create |objResult| michael@0: michael@0: JSObject *objResult = JS_NewObject(cx, &sCDataFinalizerClass, objProto, NullPtr()); michael@0: if (!objResult) { michael@0: return false; michael@0: } michael@0: michael@0: // If our argument is a CData, it holds a type. michael@0: // This is the type that we should capture, not that michael@0: // of the function, which may be less precise. michael@0: JSObject *objBestArgType = objArgType; michael@0: if (!JSVAL_IS_PRIMITIVE(valData)) { michael@0: JSObject *objData = &valData.toObject(); michael@0: if (CData::IsCData(objData)) { michael@0: objBestArgType = CData::GetCType(objData); michael@0: size_t sizeBestArg; michael@0: if (!CType::GetSafeSize(objBestArgType, &sizeBestArg)) { michael@0: MOZ_ASSUME_UNREACHABLE("object with unknown size"); michael@0: } michael@0: if (sizeBestArg != sizeArg) { michael@0: return TypeError(cx, "(an object with the same size as that expected by the C finalization function)", valData); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Used by GetCType michael@0: JS_SetReservedSlot(objResult, michael@0: SLOT_DATAFINALIZER_VALTYPE, michael@0: OBJECT_TO_JSVAL(objBestArgType)); michael@0: michael@0: // Used by ToSource michael@0: JS_SetReservedSlot(objResult, michael@0: SLOT_DATAFINALIZER_CODETYPE, michael@0: OBJECT_TO_JSVAL(objCodePtrType)); michael@0: michael@0: ffi_abi abi; michael@0: if (!GetABI(cx, OBJECT_TO_JSVAL(funInfoFinalizer->mABI), &abi)) { michael@0: JS_ReportError(cx, "Internal Error: " michael@0: "Invalid ABI specification in CDataFinalizer"); michael@0: return false; michael@0: } michael@0: michael@0: ffi_type* rtype = CType::GetFFIType(cx, funInfoFinalizer->mReturnType); michael@0: if (!rtype) { michael@0: JS_ReportError(cx, "Internal Error: " michael@0: "Could not access ffi type of CDataFinalizer"); michael@0: return false; michael@0: } michael@0: michael@0: // 7. Store C information as private michael@0: ScopedJSFreePtr michael@0: p((CDataFinalizer::Private*)malloc(sizeof(CDataFinalizer::Private))); michael@0: michael@0: memmove(&p->CIF, &funInfoFinalizer->mCIF, sizeof(ffi_cif)); michael@0: michael@0: p->cargs = cargs.forget(); michael@0: p->rvalue = rvalue.forget(); michael@0: p->cargs_size = sizeArg; michael@0: p->code = code; michael@0: michael@0: michael@0: JS_SetPrivate(objResult, p.forget()); michael@0: args.rval().setObject(*objResult); michael@0: return true; michael@0: } michael@0: michael@0: michael@0: /* michael@0: * Actually call the finalizer. Does not perform any cleanup on the object. michael@0: * michael@0: * Preconditions: |this| must be a |CDataFinalizer|, |p| must be non-null. michael@0: * The function fails if |this| has gone through |Forget|/|Dispose| michael@0: * or |Finalize|. michael@0: * michael@0: * This function does not alter the value of |errno|/|GetLastError|. michael@0: * michael@0: * If argument |errnoStatus| is non-nullptr, it receives the value of |errno| michael@0: * immediately after the call. Under Windows, if argument |lastErrorStatus| michael@0: * is non-nullptr, it receives the value of |GetLastError| immediately after michael@0: * the call. On other platforms, |lastErrorStatus| is ignored. michael@0: */ michael@0: void michael@0: CDataFinalizer::CallFinalizer(CDataFinalizer::Private *p, michael@0: int* errnoStatus, michael@0: int32_t* lastErrorStatus) michael@0: { michael@0: int savedErrno = errno; michael@0: errno = 0; michael@0: #if defined(XP_WIN) michael@0: int32_t savedLastError = GetLastError(); michael@0: SetLastError(0); michael@0: #endif // defined(XP_WIN) michael@0: michael@0: ffi_call(&p->CIF, FFI_FN(p->code), p->rvalue, &p->cargs); michael@0: michael@0: if (errnoStatus) { michael@0: *errnoStatus = errno; michael@0: } michael@0: errno = savedErrno; michael@0: #if defined(XP_WIN) michael@0: if (lastErrorStatus) { michael@0: *lastErrorStatus = GetLastError(); michael@0: } michael@0: SetLastError(savedLastError); michael@0: #endif // defined(XP_WIN) michael@0: } michael@0: michael@0: /* michael@0: * Forget the value. michael@0: * michael@0: * Preconditions: |this| must be a |CDataFinalizer|. michael@0: * The function fails if |this| has gone through |Forget|/|Dispose| michael@0: * or |Finalize|. michael@0: * michael@0: * Does not call the finalizer. Cleans up the Private memory and releases all michael@0: * strong references. michael@0: */ michael@0: bool michael@0: CDataFinalizer::Methods::Forget(JSContext* cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 0) { michael@0: JS_ReportError(cx, "CDataFinalizer.prototype.forget takes no arguments"); michael@0: return false; michael@0: } michael@0: michael@0: JS::Rooted obj(cx, args.thisv().toObjectOrNull()); michael@0: if (!obj) michael@0: return false; michael@0: if (!CDataFinalizer::IsCDataFinalizer(obj)) { michael@0: RootedValue val(cx, ObjectValue(*obj)); michael@0: return TypeError(cx, "a CDataFinalizer", val); michael@0: } michael@0: michael@0: CDataFinalizer::Private *p = (CDataFinalizer::Private *) michael@0: JS_GetPrivate(obj); michael@0: michael@0: if (!p) { michael@0: JS_ReportError(cx, "forget called on an empty CDataFinalizer"); michael@0: return false; michael@0: } michael@0: michael@0: RootedValue valJSData(cx); michael@0: RootedObject ctype(cx, GetCType(cx, obj)); michael@0: if (!ConvertToJS(cx, ctype, NullPtr(), p->cargs, false, true, valJSData.address())) { michael@0: JS_ReportError(cx, "CDataFinalizer value cannot be represented"); michael@0: return false; michael@0: } michael@0: michael@0: CDataFinalizer::Cleanup(p, obj); michael@0: michael@0: args.rval().set(valJSData); michael@0: return true; michael@0: } michael@0: michael@0: /* michael@0: * Clean up the value. michael@0: * michael@0: * Preconditions: |this| must be a |CDataFinalizer|. michael@0: * The function fails if |this| has gone through |Forget|/|Dispose| michael@0: * or |Finalize|. michael@0: * michael@0: * Calls the finalizer, cleans up the Private memory and releases all michael@0: * strong references. michael@0: */ michael@0: bool michael@0: CDataFinalizer::Methods::Dispose(JSContext* cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 0) { michael@0: JS_ReportError(cx, "CDataFinalizer.prototype.dispose takes no arguments"); michael@0: return false; michael@0: } michael@0: michael@0: RootedObject obj(cx, JS_THIS_OBJECT(cx, vp)); michael@0: if (!obj) michael@0: return false; michael@0: if (!CDataFinalizer::IsCDataFinalizer(obj)) { michael@0: RootedValue val(cx, ObjectValue(*obj)); michael@0: return TypeError(cx, "a CDataFinalizer", val); michael@0: } michael@0: michael@0: CDataFinalizer::Private *p = (CDataFinalizer::Private *) michael@0: JS_GetPrivate(obj); michael@0: michael@0: if (!p) { michael@0: JS_ReportError(cx, "dispose called on an empty CDataFinalizer."); michael@0: return false; michael@0: } michael@0: michael@0: jsval valType = JS_GetReservedSlot(obj, SLOT_DATAFINALIZER_VALTYPE); michael@0: JS_ASSERT(!JSVAL_IS_PRIMITIVE(valType)); michael@0: michael@0: JSObject *objCTypes = CType::GetGlobalCTypes(cx, &valType.toObject()); michael@0: if (!objCTypes) michael@0: return false; michael@0: michael@0: jsval valCodePtrType = JS_GetReservedSlot(obj, SLOT_DATAFINALIZER_CODETYPE); michael@0: JS_ASSERT(!JSVAL_IS_PRIMITIVE(valCodePtrType)); michael@0: JSObject *objCodePtrType = &valCodePtrType.toObject(); michael@0: michael@0: JSObject *objCodeType = PointerType::GetBaseType(objCodePtrType); michael@0: JS_ASSERT(objCodeType); michael@0: JS_ASSERT(CType::GetTypeCode(objCodeType) == TYPE_function); michael@0: michael@0: RootedObject resultType(cx, FunctionType::GetFunctionInfo(objCodeType)->mReturnType); michael@0: RootedValue result(cx, JSVAL_VOID); michael@0: michael@0: int errnoStatus; michael@0: #if defined(XP_WIN) michael@0: int32_t lastErrorStatus; michael@0: CDataFinalizer::CallFinalizer(p, &errnoStatus, &lastErrorStatus); michael@0: #else michael@0: CDataFinalizer::CallFinalizer(p, &errnoStatus, nullptr); michael@0: #endif // defined(XP_WIN) michael@0: michael@0: JS_SetReservedSlot(objCTypes, SLOT_ERRNO, INT_TO_JSVAL(errnoStatus)); michael@0: #if defined(XP_WIN) michael@0: JS_SetReservedSlot(objCTypes, SLOT_LASTERROR, INT_TO_JSVAL(lastErrorStatus)); michael@0: #endif // defined(XP_WIN) michael@0: michael@0: if (ConvertToJS(cx, resultType, NullPtr(), p->rvalue, false, true, result.address())) { michael@0: CDataFinalizer::Cleanup(p, obj); michael@0: args.rval().set(result); michael@0: return true; michael@0: } michael@0: CDataFinalizer::Cleanup(p, obj); michael@0: return false; michael@0: } michael@0: michael@0: /* michael@0: * Perform finalization. michael@0: * michael@0: * Preconditions: |this| must be the result of |CDataFinalizer|. michael@0: * It may have gone through |Forget|/|Dispose|. michael@0: * michael@0: * If |this| has not gone through |Forget|/|Dispose|, calls the michael@0: * finalizer, cleans up the Private memory and releases all michael@0: * strong references. michael@0: */ michael@0: void michael@0: CDataFinalizer::Finalize(JSFreeOp* fop, JSObject* obj) michael@0: { michael@0: CDataFinalizer::Private *p = (CDataFinalizer::Private *) michael@0: JS_GetPrivate(obj); michael@0: michael@0: if (!p) { michael@0: return; michael@0: } michael@0: michael@0: CDataFinalizer::CallFinalizer(p, nullptr, nullptr); michael@0: CDataFinalizer::Cleanup(p, nullptr); michael@0: } michael@0: michael@0: /* michael@0: * Perform cleanup of a CDataFinalizer michael@0: * michael@0: * Release strong references, cleanup |Private|. michael@0: * michael@0: * Argument |p| contains the private information of the CDataFinalizer. If michael@0: * nullptr, this function does nothing. michael@0: * Argument |obj| should contain |nullptr| during finalization (or in any michael@0: * context in which the object itself should not be cleaned up), or a michael@0: * CDataFinalizer object otherwise. michael@0: */ michael@0: void michael@0: CDataFinalizer::Cleanup(CDataFinalizer::Private *p, JSObject *obj) michael@0: { michael@0: if (!p) { michael@0: return; // We have already cleaned up michael@0: } michael@0: michael@0: free(p->cargs); michael@0: free(p->rvalue); michael@0: free(p); michael@0: michael@0: if (!obj) { michael@0: return; // No slots to clean up michael@0: } michael@0: michael@0: JS_ASSERT(CDataFinalizer::IsCDataFinalizer(obj)); michael@0: michael@0: JS_SetPrivate(obj, nullptr); michael@0: for (int i = 0; i < CDATAFINALIZER_SLOTS; ++i) { michael@0: JS_SetReservedSlot(obj, i, JSVAL_NULL); michael@0: } michael@0: } michael@0: michael@0: michael@0: /******************************************************************************* michael@0: ** Int64 and UInt64 implementation michael@0: *******************************************************************************/ michael@0: michael@0: JSObject* michael@0: Int64Base::Construct(JSContext* cx, michael@0: HandleObject proto, michael@0: uint64_t data, michael@0: bool isUnsigned) michael@0: { michael@0: const JSClass* clasp = isUnsigned ? &sUInt64Class : &sInt64Class; michael@0: RootedObject parent(cx, JS_GetParent(proto)); michael@0: RootedObject result(cx, JS_NewObject(cx, clasp, proto, parent)); michael@0: if (!result) michael@0: return nullptr; michael@0: michael@0: // attach the Int64's data michael@0: uint64_t* buffer = cx->new_(data); michael@0: if (!buffer) { michael@0: JS_ReportOutOfMemory(cx); michael@0: return nullptr; michael@0: } michael@0: michael@0: JS_SetReservedSlot(result, SLOT_INT64, PRIVATE_TO_JSVAL(buffer)); michael@0: michael@0: if (!JS_FreezeObject(cx, result)) michael@0: return nullptr; michael@0: michael@0: return result; michael@0: } michael@0: michael@0: void michael@0: Int64Base::Finalize(JSFreeOp *fop, JSObject* obj) michael@0: { michael@0: jsval slot = JS_GetReservedSlot(obj, SLOT_INT64); michael@0: if (JSVAL_IS_VOID(slot)) michael@0: return; michael@0: michael@0: FreeOp::get(fop)->delete_(static_cast(JSVAL_TO_PRIVATE(slot))); michael@0: } michael@0: michael@0: uint64_t michael@0: Int64Base::GetInt(JSObject* obj) { michael@0: JS_ASSERT(Int64::IsInt64(obj) || UInt64::IsUInt64(obj)); michael@0: michael@0: jsval slot = JS_GetReservedSlot(obj, SLOT_INT64); michael@0: return *static_cast(JSVAL_TO_PRIVATE(slot)); michael@0: } michael@0: michael@0: bool michael@0: Int64Base::ToString(JSContext* cx, michael@0: JSObject* obj, michael@0: const CallArgs& args, michael@0: bool isUnsigned) michael@0: { michael@0: if (args.length() > 1) { michael@0: JS_ReportError(cx, "toString takes zero or one argument"); michael@0: return false; michael@0: } michael@0: michael@0: int radix = 10; michael@0: if (args.length() == 1) { michael@0: jsval arg = args[0]; michael@0: if (arg.isInt32()) michael@0: radix = arg.toInt32(); michael@0: if (!arg.isInt32() || radix < 2 || radix > 36) { michael@0: JS_ReportError(cx, "radix argument must be an integer between 2 and 36"); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: AutoString intString; michael@0: if (isUnsigned) { michael@0: IntegerToString(GetInt(obj), radix, intString); michael@0: } else { michael@0: IntegerToString(static_cast(GetInt(obj)), radix, intString); michael@0: } michael@0: michael@0: JSString *result = NewUCString(cx, intString); michael@0: if (!result) michael@0: return false; michael@0: michael@0: args.rval().setString(result); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: Int64Base::ToSource(JSContext* cx, michael@0: JSObject* obj, michael@0: const CallArgs& args, michael@0: bool isUnsigned) michael@0: { michael@0: if (args.length() != 0) { michael@0: JS_ReportError(cx, "toSource takes zero arguments"); michael@0: return false; michael@0: } michael@0: michael@0: // Return a decimal string suitable for constructing the number. michael@0: AutoString source; michael@0: if (isUnsigned) { michael@0: AppendString(source, "ctypes.UInt64(\""); michael@0: IntegerToString(GetInt(obj), 10, source); michael@0: } else { michael@0: AppendString(source, "ctypes.Int64(\""); michael@0: IntegerToString(static_cast(GetInt(obj)), 10, source); michael@0: } michael@0: AppendString(source, "\")"); michael@0: michael@0: JSString *result = NewUCString(cx, source); michael@0: if (!result) michael@0: return false; michael@0: michael@0: args.rval().setString(result); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: Int64::Construct(JSContext* cx, michael@0: unsigned argc, michael@0: jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: // Construct and return a new Int64 object. michael@0: if (args.length() != 1) { michael@0: JS_ReportError(cx, "Int64 takes one argument"); michael@0: return false; michael@0: } michael@0: michael@0: int64_t i = 0; michael@0: if (!jsvalToBigInteger(cx, args[0], true, &i)) michael@0: return TypeError(cx, "int64", args[0]); michael@0: michael@0: // Get ctypes.Int64.prototype from the 'prototype' property of the ctor. michael@0: RootedValue slot(cx); michael@0: RootedObject callee(cx, &args.callee()); michael@0: ASSERT_OK(JS_GetProperty(cx, callee, "prototype", &slot)); michael@0: RootedObject proto(cx, JSVAL_TO_OBJECT(slot)); michael@0: JS_ASSERT(JS_GetClass(proto) == &sInt64ProtoClass); michael@0: michael@0: JSObject* result = Int64Base::Construct(cx, proto, i, false); michael@0: if (!result) michael@0: return false; michael@0: michael@0: args.rval().setObject(*result); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: Int64::IsInt64(JSObject* obj) michael@0: { michael@0: return JS_GetClass(obj) == &sInt64Class; michael@0: } michael@0: michael@0: bool michael@0: Int64::ToString(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JSObject* obj = JS_THIS_OBJECT(cx, vp); michael@0: if (!obj) michael@0: return false; michael@0: if (!Int64::IsInt64(obj)) { michael@0: JS_ReportError(cx, "not an Int64"); michael@0: return false; michael@0: } michael@0: michael@0: return Int64Base::ToString(cx, obj, args, false); michael@0: } michael@0: michael@0: bool michael@0: Int64::ToSource(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JSObject* obj = JS_THIS_OBJECT(cx, vp); michael@0: if (!obj) michael@0: return false; michael@0: if (!Int64::IsInt64(obj)) { michael@0: JS_ReportError(cx, "not an Int64"); michael@0: return false; michael@0: } michael@0: michael@0: return Int64Base::ToSource(cx, obj, args, false); michael@0: } michael@0: michael@0: bool michael@0: Int64::Compare(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 2 || michael@0: JSVAL_IS_PRIMITIVE(args[0]) || michael@0: JSVAL_IS_PRIMITIVE(args[1]) || michael@0: !Int64::IsInt64(&args[0].toObject()) || michael@0: !Int64::IsInt64(&args[1].toObject())) { michael@0: JS_ReportError(cx, "compare takes two Int64 arguments"); michael@0: return false; michael@0: } michael@0: michael@0: JSObject* obj1 = &args[0].toObject(); michael@0: JSObject* obj2 = &args[1].toObject(); michael@0: michael@0: int64_t i1 = Int64Base::GetInt(obj1); michael@0: int64_t i2 = Int64Base::GetInt(obj2); michael@0: michael@0: if (i1 == i2) michael@0: args.rval().setInt32(0); michael@0: else if (i1 < i2) michael@0: args.rval().setInt32(-1); michael@0: else michael@0: args.rval().setInt32(1); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: #define LO_MASK ((uint64_t(1) << 32) - 1) michael@0: #define INT64_LO(i) ((i) & LO_MASK) michael@0: #define INT64_HI(i) ((i) >> 32) michael@0: michael@0: bool michael@0: Int64::Lo(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 1 || JSVAL_IS_PRIMITIVE(args[0]) || michael@0: !Int64::IsInt64(&args[0].toObject())) { michael@0: JS_ReportError(cx, "lo takes one Int64 argument"); michael@0: return false; michael@0: } michael@0: michael@0: JSObject* obj = &args[0].toObject(); michael@0: int64_t u = Int64Base::GetInt(obj); michael@0: double d = uint32_t(INT64_LO(u)); michael@0: michael@0: args.rval().setNumber(d); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: Int64::Hi(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 1 || JSVAL_IS_PRIMITIVE(args[0]) || michael@0: !Int64::IsInt64(&args[0].toObject())) { michael@0: JS_ReportError(cx, "hi takes one Int64 argument"); michael@0: return false; michael@0: } michael@0: michael@0: JSObject* obj = &args[0].toObject(); michael@0: int64_t u = Int64Base::GetInt(obj); michael@0: double d = int32_t(INT64_HI(u)); michael@0: michael@0: args.rval().setDouble(d); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: Int64::Join(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 2) { michael@0: JS_ReportError(cx, "join takes two arguments"); michael@0: return false; michael@0: } michael@0: michael@0: int32_t hi; michael@0: uint32_t lo; michael@0: if (!jsvalToInteger(cx, args[0], &hi)) michael@0: return TypeError(cx, "int32", args[0]); michael@0: if (!jsvalToInteger(cx, args[1], &lo)) michael@0: return TypeError(cx, "uint32", args[1]); michael@0: michael@0: int64_t i = (int64_t(hi) << 32) + int64_t(lo); michael@0: michael@0: // Get Int64.prototype from the function's reserved slot. michael@0: JSObject* callee = &args.callee(); michael@0: michael@0: jsval slot = js::GetFunctionNativeReserved(callee, SLOT_FN_INT64PROTO); michael@0: RootedObject proto(cx, &slot.toObject()); michael@0: JS_ASSERT(JS_GetClass(proto) == &sInt64ProtoClass); michael@0: michael@0: JSObject* result = Int64Base::Construct(cx, proto, i, false); michael@0: if (!result) michael@0: return false; michael@0: michael@0: args.rval().setObject(*result); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: UInt64::Construct(JSContext* cx, michael@0: unsigned argc, michael@0: jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: // Construct and return a new UInt64 object. michael@0: if (args.length() != 1) { michael@0: JS_ReportError(cx, "UInt64 takes one argument"); michael@0: return false; michael@0: } michael@0: michael@0: uint64_t u = 0; michael@0: if (!jsvalToBigInteger(cx, args[0], true, &u)) michael@0: return TypeError(cx, "uint64", args[0]); michael@0: michael@0: // Get ctypes.UInt64.prototype from the 'prototype' property of the ctor. michael@0: RootedValue slot(cx); michael@0: RootedObject callee(cx, &args.callee()); michael@0: ASSERT_OK(JS_GetProperty(cx, callee, "prototype", &slot)); michael@0: RootedObject proto(cx, &slot.toObject()); michael@0: JS_ASSERT(JS_GetClass(proto) == &sUInt64ProtoClass); michael@0: michael@0: JSObject* result = Int64Base::Construct(cx, proto, u, true); michael@0: if (!result) michael@0: return false; michael@0: michael@0: args.rval().setObject(*result); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: UInt64::IsUInt64(JSObject* obj) michael@0: { michael@0: return JS_GetClass(obj) == &sUInt64Class; michael@0: } michael@0: michael@0: bool michael@0: UInt64::ToString(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JSObject* obj = JS_THIS_OBJECT(cx, vp); michael@0: if (!obj) michael@0: return false; michael@0: if (!UInt64::IsUInt64(obj)) { michael@0: JS_ReportError(cx, "not a UInt64"); michael@0: return false; michael@0: } michael@0: michael@0: return Int64Base::ToString(cx, obj, args, true); michael@0: } michael@0: michael@0: bool michael@0: UInt64::ToSource(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JSObject* obj = JS_THIS_OBJECT(cx, vp); michael@0: if (!obj) michael@0: return false; michael@0: if (!UInt64::IsUInt64(obj)) { michael@0: JS_ReportError(cx, "not a UInt64"); michael@0: return false; michael@0: } michael@0: michael@0: return Int64Base::ToSource(cx, obj, args, true); michael@0: } michael@0: michael@0: bool michael@0: UInt64::Compare(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 2 || michael@0: JSVAL_IS_PRIMITIVE(args[0]) || michael@0: JSVAL_IS_PRIMITIVE(args[1]) || michael@0: !UInt64::IsUInt64(&args[0].toObject()) || michael@0: !UInt64::IsUInt64(&args[1].toObject())) { michael@0: JS_ReportError(cx, "compare takes two UInt64 arguments"); michael@0: return false; michael@0: } michael@0: michael@0: JSObject* obj1 = &args[0].toObject(); michael@0: JSObject* obj2 = &args[1].toObject(); michael@0: michael@0: uint64_t u1 = Int64Base::GetInt(obj1); michael@0: uint64_t u2 = Int64Base::GetInt(obj2); michael@0: michael@0: if (u1 == u2) michael@0: args.rval().setInt32(0); michael@0: else if (u1 < u2) michael@0: args.rval().setInt32(-1); michael@0: else michael@0: args.rval().setInt32(1); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: UInt64::Lo(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 1 || JSVAL_IS_PRIMITIVE(args[0]) || michael@0: !UInt64::IsUInt64(&args[0].toObject())) { michael@0: JS_ReportError(cx, "lo takes one UInt64 argument"); michael@0: return false; michael@0: } michael@0: michael@0: JSObject* obj = &args[0].toObject(); michael@0: uint64_t u = Int64Base::GetInt(obj); michael@0: double d = uint32_t(INT64_LO(u)); michael@0: michael@0: args.rval().setDouble(d); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: UInt64::Hi(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 1 || JSVAL_IS_PRIMITIVE(args[0]) || michael@0: !UInt64::IsUInt64(&args[0].toObject())) { michael@0: JS_ReportError(cx, "hi takes one UInt64 argument"); michael@0: return false; michael@0: } michael@0: michael@0: JSObject* obj = &args[0].toObject(); michael@0: uint64_t u = Int64Base::GetInt(obj); michael@0: double d = uint32_t(INT64_HI(u)); michael@0: michael@0: args.rval().setDouble(d); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: UInt64::Join(JSContext* cx, unsigned argc, jsval* vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 2) { michael@0: JS_ReportError(cx, "join takes two arguments"); michael@0: return false; michael@0: } michael@0: michael@0: uint32_t hi; michael@0: uint32_t lo; michael@0: if (!jsvalToInteger(cx, args[0], &hi)) michael@0: return TypeError(cx, "uint32_t", args[0]); michael@0: if (!jsvalToInteger(cx, args[1], &lo)) michael@0: return TypeError(cx, "uint32_t", args[1]); michael@0: michael@0: uint64_t u = (uint64_t(hi) << 32) + uint64_t(lo); michael@0: michael@0: // Get UInt64.prototype from the function's reserved slot. michael@0: JSObject* callee = &args.callee(); michael@0: michael@0: jsval slot = js::GetFunctionNativeReserved(callee, SLOT_FN_INT64PROTO); michael@0: RootedObject proto(cx, &slot.toObject()); michael@0: JS_ASSERT(JS_GetClass(proto) == &sUInt64ProtoClass); michael@0: michael@0: JSObject* result = Int64Base::Construct(cx, proto, u, true); michael@0: if (!result) michael@0: return false; michael@0: michael@0: args.rval().setObject(*result); michael@0: return true; michael@0: } michael@0: michael@0: } michael@0: }