| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| * vim: set ts=8 sts=4 et sw=4 tw=99: |
| * This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| #include "ctypes/CTypes.h" |
| |
| #include "mozilla/FloatingPoint.h" |
| #include "mozilla/StandardInteger.h" |
| |
| #include <limits> |
| #include <math.h> |
| |
| #if defined(XP_WIN) || defined(XP_OS2) |
| #include <float.h> |
| #endif |
| |
| #if defined(SOLARIS) |
| #include <ieeefp.h> |
| #endif |
| |
| #ifdef HAVE_SSIZE_T |
| #include <sys/types.h> |
| #endif |
| |
| #if defined(XP_UNIX) |
| #include <errno.h> |
| #elif defined(XP_WIN) |
| #include <windows.h> |
| #endif |
| |
| #include "jscompartment.h" |
| #include "jsnum.h" |
| #include "jsprf.h" |
| #include "jstypedarray.h" |
| |
| #include "ctypes/Library.h" |
| |
| using namespace std; |
| |
| namespace js { |
| namespace ctypes { |
| |
| size_t |
| GetDeflatedUTF8StringLength(JSContext *maybecx, const jschar *chars, |
| size_t nchars) |
| { |
| size_t nbytes; |
| const jschar *end; |
| unsigned c, c2; |
| char buffer[10]; |
| |
| nbytes = nchars; |
| for (end = chars + nchars; chars != end; chars++) { |
| c = *chars; |
| if (c < 0x80) |
| continue; |
| if (0xD800 <= c && c <= 0xDFFF) { |
| /* Surrogate pair. */ |
| chars++; |
| |
| /* nbytes sets 1 length since this is surrogate pair. */ |
| nbytes--; |
| if (c >= 0xDC00 || chars == end) |
| goto bad_surrogate; |
| c2 = *chars; |
| if (c2 < 0xDC00 || c2 > 0xDFFF) |
| goto bad_surrogate; |
| c = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000; |
| } |
| c >>= 11; |
| nbytes++; |
| while (c) { |
| c >>= 5; |
| nbytes++; |
| } |
| } |
| return nbytes; |
| |
| bad_surrogate: |
| if (maybecx) { |
| JS_snprintf(buffer, 10, "0x%x", c); |
| JS_ReportErrorFlagsAndNumber(maybecx, JSREPORT_ERROR, js_GetErrorMessage, |
| NULL, JSMSG_BAD_SURROGATE_CHAR, buffer); |
| } |
| return (size_t) -1; |
| } |
| |
| bool |
| DeflateStringToUTF8Buffer(JSContext *maybecx, const jschar *src, size_t srclen, |
| char *dst, size_t *dstlenp) |
| { |
| size_t i, utf8Len; |
| jschar c, c2; |
| uint32_t v; |
| uint8_t utf8buf[6]; |
| |
| size_t dstlen = *dstlenp; |
| size_t origDstlen = dstlen; |
| |
| while (srclen) { |
| c = *src++; |
| srclen--; |
| if (c >= 0xDC00 && c <= 0xDFFF) |
| goto badSurrogate; |
| if (c < 0xD800 || c > 0xDBFF) { |
| v = c; |
| } else { |
| if (srclen < 1) |
| goto badSurrogate; |
| c2 = *src; |
| if ((c2 < 0xDC00) || (c2 > 0xDFFF)) |
| goto badSurrogate; |
| src++; |
| srclen--; |
| v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000; |
| } |
| if (v < 0x0080) { |
| /* no encoding necessary - performance hack */ |
| if (dstlen == 0) |
| goto bufferTooSmall; |
| *dst++ = (char) v; |
| utf8Len = 1; |
| } else { |
| utf8Len = js_OneUcs4ToUtf8Char(utf8buf, v); |
| if (utf8Len > dstlen) |
| goto bufferTooSmall; |
| for (i = 0; i < utf8Len; i++) |
| *dst++ = (char) utf8buf[i]; |
| } |
| dstlen -= utf8Len; |
| } |
| *dstlenp = (origDstlen - dstlen); |
| return JS_TRUE; |
| |
| badSurrogate: |
| *dstlenp = (origDstlen - dstlen); |
| /* Delegate error reporting to the measurement function. */ |
| if (maybecx) |
| GetDeflatedUTF8StringLength(maybecx, src - 1, srclen + 1); |
| return JS_FALSE; |
| |
| bufferTooSmall: |
| *dstlenp = (origDstlen - dstlen); |
| if (maybecx) { |
| JS_ReportErrorNumber(maybecx, js_GetErrorMessage, NULL, |
| JSMSG_BUFFER_TOO_SMALL); |
| } |
| return JS_FALSE; |
| } |
| |
| /******************************************************************************* |
| ** JSAPI function prototypes |
| *******************************************************************************/ |
| |
| static JSBool ConstructAbstract(JSContext* cx, unsigned argc, jsval* vp); |
| |
| namespace CType { |
| static JSBool ConstructData(JSContext* cx, unsigned argc, jsval* vp); |
| static JSBool ConstructBasic(JSContext* cx, HandleObject obj, const CallArgs& args); |
| |
| static void Trace(JSTracer* trc, JSObject* obj); |
| static void Finalize(JSFreeOp *fop, JSObject* obj); |
| static void FinalizeProtoClass(JSFreeOp *fop, JSObject* obj); |
| |
| static JSBool PrototypeGetter(JSContext* cx, HandleObject obj, HandleId idval, |
| MutableHandleValue vp); |
| static JSBool NameGetter(JSContext* cx, HandleObject obj, HandleId idval, |
| MutableHandleValue vp); |
| static JSBool SizeGetter(JSContext* cx, HandleObject obj, HandleId idval, |
| MutableHandleValue vp); |
| static JSBool PtrGetter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp); |
| static JSBool CreateArray(JSContext* cx, unsigned argc, jsval* vp); |
| static JSBool ToString(JSContext* cx, unsigned argc, jsval* vp); |
| static JSBool ToSource(JSContext* cx, unsigned argc, jsval* vp); |
| static JSBool HasInstance(JSContext* cx, HandleObject obj, MutableHandleValue v, JSBool* bp); |
| |
| |
| /* |
| * Get the global "ctypes" object. |
| * |
| * |obj| must be a CType object. |
| * |
| * This function never returns NULL. |
| */ |
| static JSObject* GetGlobalCTypes(JSContext* cx, JSObject* obj); |
| |
| } |
| |
| namespace ABI { |
| bool IsABI(JSObject* obj); |
| static JSBool ToSource(JSContext* cx, unsigned argc, jsval* vp); |
| } |
| |
| namespace PointerType { |
| static JSBool Create(JSContext* cx, unsigned argc, jsval* vp); |
| static JSBool ConstructData(JSContext* cx, HandleObject obj, const CallArgs& args); |
| |
| static JSBool TargetTypeGetter(JSContext* cx, HandleObject obj, HandleId idval, |
| MutableHandleValue vp); |
| static JSBool ContentsGetter(JSContext* cx, HandleObject obj, HandleId idval, |
| MutableHandleValue vp); |
| static JSBool ContentsSetter(JSContext* cx, HandleObject obj, HandleId idval, JSBool strict, |
| MutableHandleValue vp); |
| static JSBool IsNull(JSContext* cx, unsigned argc, jsval* vp); |
| static JSBool Increment(JSContext* cx, unsigned argc, jsval* vp); |
| static JSBool Decrement(JSContext* cx, unsigned argc, jsval* vp); |
| // The following is not an instance function, since we don't want to expose arbitrary |
| // pointer arithmetic at this moment. |
| static JSBool OffsetBy(JSContext* cx, const CallArgs& args, int offset); |
| } |
| |
| namespace ArrayType { |
| static JSBool Create(JSContext* cx, unsigned argc, jsval* vp); |
| static JSBool ConstructData(JSContext* cx, HandleObject obj, const CallArgs& args); |
| |
| static JSBool ElementTypeGetter(JSContext* cx, HandleObject obj, HandleId idval, |
| MutableHandleValue vp); |
| static JSBool LengthGetter(JSContext* cx, HandleObject obj, HandleId idval, |
| MutableHandleValue vp); |
| static JSBool Getter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp); |
| static JSBool Setter(JSContext* cx, HandleObject obj, HandleId idval, JSBool strict, MutableHandleValue vp); |
| static JSBool AddressOfElement(JSContext* cx, unsigned argc, jsval* vp); |
| } |
| |
| namespace StructType { |
| static JSBool Create(JSContext* cx, unsigned argc, jsval* vp); |
| static JSBool ConstructData(JSContext* cx, HandleObject obj, const CallArgs& args); |
| |
| static JSBool FieldsArrayGetter(JSContext* cx, HandleObject obj, HandleId idval, |
| MutableHandleValue vp); |
| static JSBool FieldGetter(JSContext* cx, HandleObject obj, HandleId idval, |
| MutableHandleValue vp); |
| static JSBool FieldSetter(JSContext* cx, HandleObject obj, HandleId idval, JSBool strict, |
| MutableHandleValue vp); |
| static JSBool AddressOfField(JSContext* cx, unsigned argc, jsval* vp); |
| static JSBool Define(JSContext* cx, unsigned argc, jsval* vp); |
| } |
| |
| namespace FunctionType { |
| static JSBool Create(JSContext* cx, unsigned argc, jsval* vp); |
| static JSBool ConstructData(JSContext* cx, HandleObject typeObj, |
| HandleObject dataObj, HandleObject fnObj, HandleObject thisObj, jsval errVal); |
| |
| static JSBool Call(JSContext* cx, unsigned argc, jsval* vp); |
| |
| static JSBool ArgTypesGetter(JSContext* cx, HandleObject obj, HandleId idval, |
| MutableHandleValue vp); |
| static JSBool ReturnTypeGetter(JSContext* cx, HandleObject obj, HandleId idval, |
| MutableHandleValue vp); |
| static JSBool ABIGetter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp); |
| static JSBool IsVariadicGetter(JSContext* cx, HandleObject obj, HandleId idval, |
| MutableHandleValue vp); |
| } |
| |
| namespace CClosure { |
| static void Trace(JSTracer* trc, JSObject* obj); |
| static void Finalize(JSFreeOp *fop, JSObject* obj); |
| |
| // libffi callback |
| static void ClosureStub(ffi_cif* cif, void* result, void** args, |
| void* userData); |
| } |
| |
| namespace CData { |
| static void Finalize(JSFreeOp *fop, JSObject* obj); |
| |
| static JSBool ValueGetter(JSContext* cx, HandleObject obj, HandleId idval, |
| MutableHandleValue vp); |
| static JSBool ValueSetter(JSContext* cx, HandleObject obj, HandleId idval, |
| JSBool strict, MutableHandleValue vp); |
| static JSBool Address(JSContext* cx, unsigned argc, jsval* vp); |
| static JSBool ReadString(JSContext* cx, unsigned argc, jsval* vp); |
| static JSBool ReadStringReplaceMalformed(JSContext* cx, unsigned argc, jsval* vp); |
| static JSBool ToSource(JSContext* cx, unsigned argc, jsval* vp); |
| static JSString *GetSourceString(JSContext *cx, HandleObject typeObj, |
| void *data); |
| static JSBool ErrnoGetter(JSContext* cx, HandleObject obj, HandleId idval, |
| MutableHandleValue vp); |
| |
| #if defined(XP_WIN) |
| static JSBool LastErrorGetter(JSContext* cx, HandleObject obj, HandleId idval, |
| MutableHandleValue vp); |
| #endif // defined(XP_WIN) |
| } |
| |
| namespace CDataFinalizer { |
| /* |
| * Attach a C function as a finalizer to a JS object. |
| * |
| * This function is available from JS as |ctypes.withFinalizer|. |
| * |
| * JavaScript signature: |
| * function(CData, CData): CDataFinalizer |
| * value finalizer finalizable |
| * |
| * Where |finalizer| is a one-argument function taking a value |
| * with the same type as |value|. |
| */ |
| static JSBool Construct(JSContext* cx, unsigned argc, jsval *vp); |
| |
| /* |
| * Private data held by |CDataFinalizer|. |
| * |
| * See also |enum CDataFinalizerSlot| for the slots of |
| * |CDataFinalizer|. |
| * |
| * Note: the private data may be NULL, if |dispose|, |forget| or the |
| * finalizer has already been called. |
| */ |
| struct Private { |
| /* |
| * The C data to pass to the code. |
| * Finalization/|dispose|/|forget| release this memory. |
| */ |
| void *cargs; |
| |
| /* |
| * The total size of the buffer pointed by |cargs| |
| */ |
| size_t cargs_size; |
| |
| /* |
| * Low-level signature information. |
| * Finalization/|dispose|/|forget| release this memory. |
| */ |
| ffi_cif CIF; |
| |
| /* |
| * The C function to invoke during finalization. |
| * Do not deallocate this. |
| */ |
| uintptr_t code; |
| |
| /* |
| * A buffer for holding the return value. |
| * Finalization/|dispose|/|forget| release this memory. |
| */ |
| void *rvalue; |
| }; |
| |
| /* |
| * Methods of instances of |CDataFinalizer| |
| */ |
| namespace Methods { |
| static JSBool Dispose(JSContext* cx, unsigned argc, jsval *vp); |
| static JSBool Forget(JSContext* cx, unsigned argc, jsval *vp); |
| static JSBool ToSource(JSContext* cx, unsigned argc, jsval *vp); |
| static JSBool ToString(JSContext* cx, unsigned argc, jsval *vp); |
| } |
| |
| /* |
| * Utility functions |
| * |
| * @return true if |obj| is a CDataFinalizer, false otherwise. |
| */ |
| static bool IsCDataFinalizer(JSObject *obj); |
| |
| /* |
| * Clean up the finalization information of a CDataFinalizer. |
| * |
| * Used by |Finalize|, |Dispose| and |Forget|. |
| * |
| * @param p The private information of the CDataFinalizer. If NULL, |
| * this function does nothing. |
| * @param obj Either NULL, if the object should not be cleaned up (i.e. |
| * during finalization) or a CDataFinalizer JSObject. Always use NULL |
| * if you are calling from a finalizer. |
| */ |
| static void Cleanup(Private *p, JSObject *obj); |
| |
| /* |
| * Perform the actual call to the finalizer code. |
| */ |
| static void CallFinalizer(CDataFinalizer::Private *p, |
| int* errnoStatus, |
| int32_t* lastErrorStatus); |
| |
| /* |
| * Return the CType of a CDataFinalizer object, or NULL if the object |
| * has been cleaned-up already. |
| */ |
| static JSObject *GetCType(JSContext *cx, JSObject *obj); |
| |
| /* |
| * Perform finalization of a |CDataFinalizer| |
| */ |
| static void Finalize(JSFreeOp *fop, JSObject *obj); |
| |
| /* |
| * Return the jsval contained by this finalizer. |
| * |
| * Note that the jsval is actually not recorded, but converted back from C. |
| */ |
| static bool GetValue(JSContext *cx, JSObject *obj, jsval *result); |
| |
| static JSObject* GetCData(JSContext *cx, JSObject *obj); |
| } |
| |
| |
| // Int64Base provides functions common to Int64 and UInt64. |
| namespace Int64Base { |
| JSObject* Construct(JSContext* cx, HandleObject proto, uint64_t data, |
| bool isUnsigned); |
| |
| uint64_t GetInt(JSObject* obj); |
| |
| JSBool ToString(JSContext* cx, JSObject* obj, const CallArgs& args, |
| bool isUnsigned); |
| |
| JSBool ToSource(JSContext* cx, JSObject* obj, const CallArgs& args, |
| bool isUnsigned); |
| |
| static void Finalize(JSFreeOp *fop, JSObject* obj); |
| } |
| |
| namespace Int64 { |
| static JSBool Construct(JSContext* cx, unsigned argc, jsval* vp); |
| |
| static JSBool ToString(JSContext* cx, unsigned argc, jsval* vp); |
| static JSBool ToSource(JSContext* cx, unsigned argc, jsval* vp); |
| |
| static JSBool Compare(JSContext* cx, unsigned argc, jsval* vp); |
| static JSBool Lo(JSContext* cx, unsigned argc, jsval* vp); |
| static JSBool Hi(JSContext* cx, unsigned argc, jsval* vp); |
| static JSBool Join(JSContext* cx, unsigned argc, jsval* vp); |
| } |
| |
| namespace UInt64 { |
| static JSBool Construct(JSContext* cx, unsigned argc, jsval* vp); |
| |
| static JSBool ToString(JSContext* cx, unsigned argc, jsval* vp); |
| static JSBool ToSource(JSContext* cx, unsigned argc, jsval* vp); |
| |
| static JSBool Compare(JSContext* cx, unsigned argc, jsval* vp); |
| static JSBool Lo(JSContext* cx, unsigned argc, jsval* vp); |
| static JSBool Hi(JSContext* cx, unsigned argc, jsval* vp); |
| static JSBool Join(JSContext* cx, unsigned argc, jsval* vp); |
| } |
| |
| /******************************************************************************* |
| ** JSClass definitions and initialization functions |
| *******************************************************************************/ |
| |
| // Class representing the 'ctypes' object itself. This exists to contain the |
| // JSCTypesCallbacks set of function pointers. |
| static JSClass sCTypesGlobalClass = { |
| "ctypes", |
| JSCLASS_HAS_RESERVED_SLOTS(CTYPESGLOBAL_SLOTS), |
| JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
| JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub |
| }; |
| |
| static JSClass sCABIClass = { |
| "CABI", |
| JSCLASS_HAS_RESERVED_SLOTS(CABI_SLOTS), |
| JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
| JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub |
| }; |
| |
| // Class representing ctypes.{C,Pointer,Array,Struct,Function}Type.prototype. |
| // This exists to give said prototypes a class of "CType", and to provide |
| // reserved slots for stashing various other prototype objects. |
| static JSClass sCTypeProtoClass = { |
| "CType", |
| JSCLASS_HAS_RESERVED_SLOTS(CTYPEPROTO_SLOTS), |
| JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
| JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CType::FinalizeProtoClass, |
| NULL, ConstructAbstract, NULL, ConstructAbstract |
| }; |
| |
| // Class representing ctypes.CData.prototype and the 'prototype' properties |
| // of CTypes. This exists to give said prototypes a class of "CData". |
| static JSClass sCDataProtoClass = { |
| "CData", |
| 0, |
| JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
| JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub |
| }; |
| |
| static JSClass sCTypeClass = { |
| "CType", |
| JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS), |
| JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
| JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CType::Finalize, |
| NULL, CType::ConstructData, CType::HasInstance, CType::ConstructData, |
| CType::Trace |
| }; |
| |
| static JSClass sCDataClass = { |
| "CData", |
| JSCLASS_HAS_RESERVED_SLOTS(CDATA_SLOTS), |
| JS_PropertyStub, JS_DeletePropertyStub, ArrayType::Getter, ArrayType::Setter, |
| JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CData::Finalize, |
| NULL, FunctionType::Call, NULL, FunctionType::Call |
| }; |
| |
| static JSClass sCClosureClass = { |
| "CClosure", |
| JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS), |
| JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
| JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CClosure::Finalize, |
| NULL, NULL, NULL, NULL, CClosure::Trace |
| }; |
| |
| /* |
| * Class representing the prototype of CDataFinalizer. |
| */ |
| static JSClass sCDataFinalizerProtoClass = { |
| "CDataFinalizer", |
| 0, |
| JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
| JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub |
| }; |
| |
| /* |
| * Class representing instances of CDataFinalizer. |
| * |
| * Instances of CDataFinalizer have both private data (with type |
| * |CDataFinalizer::Private|) and slots (see |CDataFinalizerSlots|). |
| */ |
| static JSClass sCDataFinalizerClass = { |
| "CDataFinalizer", |
| JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(CDATAFINALIZER_SLOTS), |
| JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
| JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CDataFinalizer::Finalize, |
| }; |
| |
| |
| #define CTYPESFN_FLAGS \ |
| (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT) |
| |
| #define CTYPESCTOR_FLAGS \ |
| (CTYPESFN_FLAGS | JSFUN_CONSTRUCTOR) |
| |
| #define CTYPESPROP_FLAGS \ |
| (JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT) |
| |
| #define CABIFN_FLAGS \ |
| (JSPROP_READONLY | JSPROP_PERMANENT) |
| |
| #define CDATAFN_FLAGS \ |
| (JSPROP_READONLY | JSPROP_PERMANENT) |
| |
| #define CDATAFINALIZERFN_FLAGS \ |
| (JSPROP_READONLY | JSPROP_PERMANENT) |
| |
| static const JSPropertySpec sCTypeProps[] = { |
| { "name", 0, CTYPESPROP_FLAGS, JSOP_WRAPPER(CType::NameGetter), JSOP_NULLWRAPPER }, |
| { "size", 0, CTYPESPROP_FLAGS, JSOP_WRAPPER(CType::SizeGetter), JSOP_NULLWRAPPER }, |
| { "ptr", 0, CTYPESPROP_FLAGS, JSOP_WRAPPER(CType::PtrGetter), JSOP_NULLWRAPPER }, |
| { "prototype", 0, CTYPESPROP_FLAGS, JSOP_WRAPPER(CType::PrototypeGetter), JSOP_NULLWRAPPER }, |
| { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER } |
| }; |
| |
| static const JSFunctionSpec sCTypeFunctions[] = { |
| JS_FN("array", CType::CreateArray, 0, CTYPESFN_FLAGS), |
| JS_FN("toString", CType::ToString, 0, CTYPESFN_FLAGS), |
| JS_FN("toSource", CType::ToSource, 0, CTYPESFN_FLAGS), |
| JS_FS_END |
| }; |
| |
| static const JSFunctionSpec sCABIFunctions[] = { |
| JS_FN("toSource", ABI::ToSource, 0, CABIFN_FLAGS), |
| JS_FN("toString", ABI::ToSource, 0, CABIFN_FLAGS), |
| JS_FS_END |
| }; |
| |
| static const JSPropertySpec sCDataProps[] = { |
| { "value", 0, JSPROP_SHARED | JSPROP_PERMANENT, |
| JSOP_WRAPPER(CData::ValueGetter), JSOP_WRAPPER(CData::ValueSetter) }, |
| { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER } |
| }; |
| |
| static const JSFunctionSpec sCDataFunctions[] = { |
| JS_FN("address", CData::Address, 0, CDATAFN_FLAGS), |
| JS_FN("readString", CData::ReadString, 0, CDATAFN_FLAGS), |
| JS_FN("readStringReplaceMalformed", CData::ReadStringReplaceMalformed, 0, CDATAFN_FLAGS), |
| JS_FN("toSource", CData::ToSource, 0, CDATAFN_FLAGS), |
| JS_FN("toString", CData::ToSource, 0, CDATAFN_FLAGS), |
| JS_FS_END |
| }; |
| |
| static const JSPropertySpec sCDataFinalizerProps[] = { |
| { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER } |
| }; |
| |
| static const JSFunctionSpec sCDataFinalizerFunctions[] = { |
| JS_FN("dispose", CDataFinalizer::Methods::Dispose, 0, CDATAFINALIZERFN_FLAGS), |
| JS_FN("forget", CDataFinalizer::Methods::Forget, 0, CDATAFINALIZERFN_FLAGS), |
| JS_FN("readString",CData::ReadString, 0, CDATAFINALIZERFN_FLAGS), |
| JS_FN("toString", CDataFinalizer::Methods::ToString, 0, CDATAFINALIZERFN_FLAGS), |
| JS_FN("toSource", CDataFinalizer::Methods::ToSource, 0, CDATAFINALIZERFN_FLAGS), |
| JS_FS_END |
| }; |
| |
| static const JSFunctionSpec sPointerFunction = |
| JS_FN("PointerType", PointerType::Create, 1, CTYPESCTOR_FLAGS); |
| |
| static const JSPropertySpec sPointerProps[] = { |
| { "targetType", 0, CTYPESPROP_FLAGS, |
| JSOP_WRAPPER(PointerType::TargetTypeGetter), JSOP_NULLWRAPPER }, |
| { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER } |
| }; |
| |
| static const JSFunctionSpec sPointerInstanceFunctions[] = { |
| JS_FN("isNull", PointerType::IsNull, 0, CTYPESFN_FLAGS), |
| JS_FN("increment", PointerType::Increment, 0, CTYPESFN_FLAGS), |
| JS_FN("decrement", PointerType::Decrement, 0, CTYPESFN_FLAGS), |
| JS_FS_END |
| }; |
| |
| static const JSPropertySpec sPointerInstanceProps[] = { |
| { "contents", 0, JSPROP_SHARED | JSPROP_PERMANENT, |
| JSOP_WRAPPER(PointerType::ContentsGetter), |
| JSOP_WRAPPER(PointerType::ContentsSetter) }, |
| { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER } |
| }; |
| |
| static const JSFunctionSpec sArrayFunction = |
| JS_FN("ArrayType", ArrayType::Create, 1, CTYPESCTOR_FLAGS); |
| |
| static const JSPropertySpec sArrayProps[] = { |
| { "elementType", 0, CTYPESPROP_FLAGS, |
| JSOP_WRAPPER(ArrayType::ElementTypeGetter), JSOP_NULLWRAPPER }, |
| { "length", 0, CTYPESPROP_FLAGS, |
| JSOP_WRAPPER(ArrayType::LengthGetter), JSOP_NULLWRAPPER }, |
| { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER } |
| }; |
| |
| static const JSFunctionSpec sArrayInstanceFunctions[] = { |
| JS_FN("addressOfElement", ArrayType::AddressOfElement, 1, CDATAFN_FLAGS), |
| JS_FS_END |
| }; |
| |
| static const JSPropertySpec sArrayInstanceProps[] = { |
| { "length", 0, JSPROP_SHARED | JSPROP_READONLY | JSPROP_PERMANENT, |
| JSOP_WRAPPER(ArrayType::LengthGetter), JSOP_NULLWRAPPER }, |
| { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER } |
| }; |
| |
| static const JSFunctionSpec sStructFunction = |
| JS_FN("StructType", StructType::Create, 2, CTYPESCTOR_FLAGS); |
| |
| static const JSPropertySpec sStructProps[] = { |
| { "fields", 0, CTYPESPROP_FLAGS, |
| JSOP_WRAPPER(StructType::FieldsArrayGetter), JSOP_NULLWRAPPER }, |
| { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER } |
| }; |
| |
| static const JSFunctionSpec sStructFunctions[] = { |
| JS_FN("define", StructType::Define, 1, CDATAFN_FLAGS), |
| JS_FS_END |
| }; |
| |
| static const JSFunctionSpec sStructInstanceFunctions[] = { |
| JS_FN("addressOfField", StructType::AddressOfField, 1, CDATAFN_FLAGS), |
| JS_FS_END |
| }; |
| |
| static const JSFunctionSpec sFunctionFunction = |
| JS_FN("FunctionType", FunctionType::Create, 2, CTYPESCTOR_FLAGS); |
| |
| static const JSPropertySpec sFunctionProps[] = { |
| { "argTypes", 0, CTYPESPROP_FLAGS, |
| JSOP_WRAPPER(FunctionType::ArgTypesGetter), JSOP_NULLWRAPPER }, |
| { "returnType", 0, CTYPESPROP_FLAGS, |
| JSOP_WRAPPER(FunctionType::ReturnTypeGetter), JSOP_NULLWRAPPER }, |
| { "abi", 0, CTYPESPROP_FLAGS, |
| JSOP_WRAPPER(FunctionType::ABIGetter), JSOP_NULLWRAPPER }, |
| { "isVariadic", 0, CTYPESPROP_FLAGS, |
| JSOP_WRAPPER(FunctionType::IsVariadicGetter), JSOP_NULLWRAPPER }, |
| { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER } |
| }; |
| |
| static const JSFunctionSpec sFunctionInstanceFunctions[] = { |
| JS_FN("call", js_fun_call, 1, CDATAFN_FLAGS), |
| JS_FN("apply", js_fun_apply, 2, CDATAFN_FLAGS), |
| JS_FS_END |
| }; |
| |
| static JSClass sInt64ProtoClass = { |
| "Int64", |
| 0, |
| JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
| JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub |
| }; |
| |
| static JSClass sUInt64ProtoClass = { |
| "UInt64", |
| 0, |
| JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
| JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub |
| }; |
| |
| static JSClass sInt64Class = { |
| "Int64", |
| JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS), |
| JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
| JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Int64Base::Finalize |
| }; |
| |
| static JSClass sUInt64Class = { |
| "UInt64", |
| JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS), |
| JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
| JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Int64Base::Finalize |
| }; |
| |
| static const JSFunctionSpec sInt64StaticFunctions[] = { |
| JS_FN("compare", Int64::Compare, 2, CTYPESFN_FLAGS), |
| JS_FN("lo", Int64::Lo, 1, CTYPESFN_FLAGS), |
| JS_FN("hi", Int64::Hi, 1, CTYPESFN_FLAGS), |
| JS_FN("join", Int64::Join, 2, CTYPESFN_FLAGS), |
| JS_FS_END |
| }; |
| |
| static const JSFunctionSpec sUInt64StaticFunctions[] = { |
| JS_FN("compare", UInt64::Compare, 2, CTYPESFN_FLAGS), |
| JS_FN("lo", UInt64::Lo, 1, CTYPESFN_FLAGS), |
| JS_FN("hi", UInt64::Hi, 1, CTYPESFN_FLAGS), |
| JS_FN("join", UInt64::Join, 2, CTYPESFN_FLAGS), |
| JS_FS_END |
| }; |
| |
| static const JSFunctionSpec sInt64Functions[] = { |
| JS_FN("toString", Int64::ToString, 0, CTYPESFN_FLAGS), |
| JS_FN("toSource", Int64::ToSource, 0, CTYPESFN_FLAGS), |
| JS_FS_END |
| }; |
| |
| static const JSFunctionSpec sUInt64Functions[] = { |
| JS_FN("toString", UInt64::ToString, 0, CTYPESFN_FLAGS), |
| JS_FN("toSource", UInt64::ToSource, 0, CTYPESFN_FLAGS), |
| JS_FS_END |
| }; |
| |
| static const JSPropertySpec sModuleProps[] = { |
| { "errno", 0, JSPROP_SHARED | JSPROP_PERMANENT, |
| JSOP_WRAPPER(CData::ErrnoGetter), JSOP_NULLWRAPPER }, |
| #if defined(XP_WIN) |
| { "winLastError", 0, JSPROP_SHARED | JSPROP_PERMANENT, |
| JSOP_WRAPPER(CData::LastErrorGetter), JSOP_NULLWRAPPER }, |
| #endif // defined(XP_WIN) |
| { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER } |
| }; |
| |
| static const JSFunctionSpec sModuleFunctions[] = { |
| JS_FN("CDataFinalizer", CDataFinalizer::Construct, 2, CTYPESFN_FLAGS), |
| JS_FN("open", Library::Open, 1, CTYPESFN_FLAGS), |
| JS_FN("cast", CData::Cast, 2, CTYPESFN_FLAGS), |
| JS_FN("getRuntime", CData::GetRuntime, 1, CTYPESFN_FLAGS), |
| JS_FN("libraryName", Library::Name, 1, CTYPESFN_FLAGS), |
| JS_FS_END |
| }; |
| |
| JS_ALWAYS_INLINE JSString* |
| NewUCString(JSContext* cx, const AutoString& from) |
| { |
| return JS_NewUCStringCopyN(cx, from.begin(), from.length()); |
| } |
| |
| /* |
| * Return a size rounded up to a multiple of a power of two. |
| * |
| * Note: |align| must be a power of 2. |
| */ |
| JS_ALWAYS_INLINE size_t |
| Align(size_t val, size_t align) |
| { |
| // Ensure that align is a power of two. |
| MOZ_ASSERT(align != 0 && (align & (align - 1)) == 0); |
| return ((val - 1) | (align - 1)) + 1; |
| } |
| |
| static ABICode |
| GetABICode(JSObject* obj) |
| { |
| // make sure we have an object representing a CABI class, |
| // and extract the enumerated class type from the reserved slot. |
| if (JS_GetClass(obj) != &sCABIClass) |
| return INVALID_ABI; |
| |
| jsval result = JS_GetReservedSlot(obj, SLOT_ABICODE); |
| return ABICode(JSVAL_TO_INT(result)); |
| } |
| |
| JSErrorFormatString ErrorFormatString[CTYPESERR_LIMIT] = { |
| #define MSG_DEF(name, number, count, exception, format) \ |
| { format, count, exception } , |
| #include "ctypes.msg" |
| #undef MSG_DEF |
| }; |
| |
| const JSErrorFormatString* |
| GetErrorMessage(void* userRef, const char* locale, const unsigned errorNumber) |
| { |
| if (0 < errorNumber && errorNumber < CTYPESERR_LIMIT) |
| return &ErrorFormatString[errorNumber]; |
| return NULL; |
| } |
| |
| JSBool |
| TypeError(JSContext* cx, const char* expected, jsval actual) |
| { |
| JSString* str = JS_ValueToSource(cx, actual); |
| JSAutoByteString bytes; |
| |
| const char* src; |
| if (str) { |
| src = bytes.encodeLatin1(cx, str); |
| if (!src) |
| return false; |
| } else { |
| JS_ClearPendingException(cx); |
| src = "<<error converting value to string>>"; |
| } |
| JS_ReportErrorNumber(cx, GetErrorMessage, NULL, |
| CTYPESMSG_TYPE_ERROR, expected, src); |
| return false; |
| } |
| |
| static JSObject* |
| InitCTypeClass(JSContext* cx, HandleObject parent) |
| { |
| JSFunction *fun = JS_DefineFunction(cx, parent, "CType", ConstructAbstract, 0, |
| CTYPESCTOR_FLAGS); |
| if (!fun) |
| return NULL; |
| |
| RootedObject ctor(cx, JS_GetFunctionObject(fun)); |
| RootedObject fnproto(cx); |
| if (!JS_GetPrototype(cx, ctor, fnproto.address())) |
| return NULL; |
| JS_ASSERT(ctor); |
| JS_ASSERT(fnproto); |
| |
| // Set up ctypes.CType.prototype. |
| RootedObject prototype(cx, JS_NewObject(cx, &sCTypeProtoClass, fnproto, parent)); |
| if (!prototype) |
| return NULL; |
| |
| if (!JS_DefineProperty(cx, ctor, "prototype", OBJECT_TO_JSVAL(prototype), |
| NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) |
| return NULL; |
| |
| if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(ctor), |
| NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) |
| return NULL; |
| |
| // Define properties and functions common to all CTypes. |
| if (!JS_DefineProperties(cx, prototype, sCTypeProps) || |
| !JS_DefineFunctions(cx, prototype, sCTypeFunctions)) |
| return NULL; |
| |
| if (!JS_FreezeObject(cx, ctor) || !JS_FreezeObject(cx, prototype)) |
| return NULL; |
| |
| return prototype; |
| } |
| |
| static JSObject* |
| InitABIClass(JSContext* cx, JSObject* parent) |
| { |
| RootedObject obj(cx, JS_NewObject(cx, NULL, NULL, NULL)); |
| |
| if (!obj) |
| return NULL; |
| |
| if (!JS_DefineFunctions(cx, obj, sCABIFunctions)) |
| return NULL; |
| |
| return obj; |
| } |
| |
| |
| static JSObject* |
| InitCDataClass(JSContext* cx, HandleObject parent, HandleObject CTypeProto) |
| { |
| JSFunction* fun = JS_DefineFunction(cx, parent, "CData", ConstructAbstract, 0, |
| CTYPESCTOR_FLAGS); |
| if (!fun) |
| return NULL; |
| |
| RootedObject ctor(cx, JS_GetFunctionObject(fun)); |
| JS_ASSERT(ctor); |
| |
| // Set up ctypes.CData.__proto__ === ctypes.CType.prototype. |
| // (Note that 'ctypes.CData instanceof Function' is still true, thanks to the |
| // prototype chain.) |
| if (!JS_SetPrototype(cx, ctor, CTypeProto)) |
| return NULL; |
| |
| // Set up ctypes.CData.prototype. |
| RootedObject prototype(cx, JS_NewObject(cx, &sCDataProtoClass, NULL, parent)); |
| if (!prototype) |
| return NULL; |
| |
| if (!JS_DefineProperty(cx, ctor, "prototype", OBJECT_TO_JSVAL(prototype), |
| NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) |
| return NULL; |
| |
| if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(ctor), |
| NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) |
| return NULL; |
| |
| // Define properties and functions common to all CDatas. |
| if (!JS_DefineProperties(cx, prototype, sCDataProps) || |
| !JS_DefineFunctions(cx, prototype, sCDataFunctions)) |
| return NULL; |
| |
| if (//!JS_FreezeObject(cx, prototype) || // XXX fixme - see bug 541212! |
| !JS_FreezeObject(cx, ctor)) |
| return NULL; |
| |
| return prototype; |
| } |
| |
| static JSBool |
| DefineABIConstant(JSContext* cx, |
| HandleObject parent, |
| const char* name, |
| ABICode code, |
| HandleObject prototype) |
| { |
| RootedObject obj(cx, JS_DefineObject(cx, parent, name, &sCABIClass, prototype, |
| JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)); |
| if (!obj) |
| return false; |
| JS_SetReservedSlot(obj, SLOT_ABICODE, INT_TO_JSVAL(code)); |
| return JS_FreezeObject(cx, obj); |
| } |
| |
| // Set up a single type constructor for |
| // ctypes.{Pointer,Array,Struct,Function}Type. |
| static JSBool |
| InitTypeConstructor(JSContext* cx, |
| HandleObject parent, |
| HandleObject CTypeProto, |
| HandleObject CDataProto, |
| const JSFunctionSpec spec, |
| const JSFunctionSpec* fns, |
| const JSPropertySpec* props, |
| const JSFunctionSpec* instanceFns, |
| const JSPropertySpec* instanceProps, |
| MutableHandleObject typeProto, |
| MutableHandleObject dataProto) |
| { |
| JSFunction* fun = js::DefineFunctionWithReserved(cx, parent, spec.name, spec.call.op, |
| spec.nargs, spec.flags); |
| if (!fun) |
| return false; |
| |
| RootedObject obj(cx, JS_GetFunctionObject(fun)); |
| if (!obj) |
| return false; |
| |
| // Set up the .prototype and .prototype.constructor properties. |
| typeProto.set(JS_NewObject(cx, &sCTypeProtoClass, CTypeProto, parent)); |
| if (!typeProto) |
| return false; |
| |
| // Define property before proceeding, for GC safety. |
| if (!JS_DefineProperty(cx, obj, "prototype", OBJECT_TO_JSVAL(typeProto), |
| NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) |
| return false; |
| |
| if (fns && !JS_DefineFunctions(cx, typeProto, fns)) |
| return false; |
| |
| if (!JS_DefineProperties(cx, typeProto, props)) |
| return false; |
| |
| if (!JS_DefineProperty(cx, typeProto, "constructor", OBJECT_TO_JSVAL(obj), |
| NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) |
| return false; |
| |
| // Stash ctypes.{Pointer,Array,Struct}Type.prototype on a reserved slot of |
| // the type constructor, for faster lookup. |
| js::SetFunctionNativeReserved(obj, SLOT_FN_CTORPROTO, OBJECT_TO_JSVAL(typeProto)); |
| |
| // Create an object to serve as the common ancestor for all CData objects |
| // created from the given type constructor. This has ctypes.CData.prototype |
| // as its prototype, such that it inherits the properties and functions |
| // common to all CDatas. |
| dataProto.set(JS_NewObject(cx, &sCDataProtoClass, CDataProto, parent)); |
| if (!dataProto) |
| return false; |
| |
| // Define functions and properties on the 'dataProto' object that are common |
| // to all CData objects created from this type constructor. (These will |
| // become functions and properties on CData objects created from this type.) |
| if (instanceFns && !JS_DefineFunctions(cx, dataProto, instanceFns)) |
| return false; |
| |
| if (instanceProps && !JS_DefineProperties(cx, dataProto, instanceProps)) |
| return false; |
| |
| // Link the type prototype to the data prototype. |
| JS_SetReservedSlot(typeProto, SLOT_OURDATAPROTO, OBJECT_TO_JSVAL(dataProto)); |
| |
| if (!JS_FreezeObject(cx, obj) || |
| //!JS_FreezeObject(cx, dataProto) || // XXX fixme - see bug 541212! |
| !JS_FreezeObject(cx, typeProto)) |
| return false; |
| |
| return true; |
| } |
| |
| JSObject* |
| InitInt64Class(JSContext* cx, |
| HandleObject parent, |
| JSClass* clasp, |
| JSNative construct, |
| const JSFunctionSpec* fs, |
| const JSFunctionSpec* static_fs) |
| { |
| // Init type class and constructor |
| RootedObject prototype(cx, JS_InitClass(cx, parent, NULL, clasp, construct, |
| 0, NULL, fs, NULL, static_fs)); |
| if (!prototype) |
| return NULL; |
| |
| RootedObject ctor(cx, JS_GetConstructor(cx, prototype)); |
| if (!ctor) |
| return NULL; |
| if (!JS_FreezeObject(cx, ctor)) |
| return NULL; |
| |
| // Redefine the 'join' function as an extended native and stash |
| // ctypes.{Int64,UInt64}.prototype in a reserved slot of the new function. |
| JS_ASSERT(clasp == &sInt64ProtoClass || clasp == &sUInt64ProtoClass); |
| JSNative native = (clasp == &sInt64ProtoClass) ? Int64::Join : UInt64::Join; |
| JSFunction* fun = js::DefineFunctionWithReserved(cx, ctor, "join", native, |
| 2, CTYPESFN_FLAGS); |
| if (!fun) |
| return NULL; |
| |
| js::SetFunctionNativeReserved(fun, SLOT_FN_INT64PROTO, |
| OBJECT_TO_JSVAL(prototype)); |
| |
| if (!JS_FreezeObject(cx, prototype)) |
| return NULL; |
| |
| return prototype; |
| } |
| |
| static void |
| AttachProtos(JSObject* proto, const AutoObjectVector& protos) |
| { |
| // For a given 'proto' of [[Class]] "CTypeProto", attach each of the 'protos' |
| // to the appropriate CTypeProtoSlot. (SLOT_CTYPES is the last slot |
| // of [[Class]] "CTypeProto" that we fill in this automated manner.) |
| for (uint32_t i = 0; i <= SLOT_CTYPES; ++i) |
| JS_SetReservedSlot(proto, i, OBJECT_TO_JSVAL(protos[i])); |
| } |
| |
| JSBool |
| InitTypeClasses(JSContext* cx, HandleObject parent) |
| { |
| // Initialize the ctypes.CType class. This acts as an abstract base class for |
| // the various types, and provides the common API functions. It has: |
| // * [[Class]] "Function" |
| // * __proto__ === Function.prototype |
| // * A constructor that throws a TypeError. (You can't construct an |
| // abstract type!) |
| // * 'prototype' property: |
| // * [[Class]] "CTypeProto" |
| // * __proto__ === Function.prototype |
| // * A constructor that throws a TypeError. (You can't construct an |
| // abstract type instance!) |
| // * 'constructor' property === ctypes.CType |
| // * Provides properties and functions common to all CTypes. |
| RootedObject CTypeProto(cx, InitCTypeClass(cx, parent)); |
| if (!CTypeProto) |
| return false; |
| |
| // Initialize the ctypes.CData class. This acts as an abstract base class for |
| // instances of the various types, and provides the common API functions. |
| // It has: |
| // * [[Class]] "Function" |
| // * __proto__ === Function.prototype |
| // * A constructor that throws a TypeError. (You can't construct an |
| // abstract type instance!) |
| // * 'prototype' property: |
| // * [[Class]] "CDataProto" |
| // * 'constructor' property === ctypes.CData |
| // * Provides properties and functions common to all CDatas. |
| RootedObject CDataProto(cx, InitCDataClass(cx, parent, CTypeProto)); |
| if (!CDataProto) |
| return false; |
| |
| // Link CTypeProto to CDataProto. |
| JS_SetReservedSlot(CTypeProto, SLOT_OURDATAPROTO, OBJECT_TO_JSVAL(CDataProto)); |
| |
| // Create and attach the special class constructors: ctypes.PointerType, |
| // ctypes.ArrayType, ctypes.StructType, and ctypes.FunctionType. |
| // Each of these constructors 'c' has, respectively: |
| // * [[Class]] "Function" |
| // * __proto__ === Function.prototype |
| // * A constructor that creates a user-defined type. |
| // * 'prototype' property: |
| // * [[Class]] "CTypeProto" |
| // * __proto__ === ctypes.CType.prototype |
| // * 'constructor' property === 'c' |
| // We also construct an object 'p' to serve, given a type object 't' |
| // constructed from one of these type constructors, as |
| // 't.prototype.__proto__'. This object has: |
| // * [[Class]] "CDataProto" |
| // * __proto__ === ctypes.CData.prototype |
| // * Properties and functions common to all CDatas. |
| // Therefore an instance 't' of ctypes.{Pointer,Array,Struct,Function}Type |
| // will have, resp.: |
| // * [[Class]] "CType" |
| // * __proto__ === ctypes.{Pointer,Array,Struct,Function}Type.prototype |
| // * A constructor which creates and returns a CData object, containing |
| // binary data of the given type. |
| // * 'prototype' property: |
| // * [[Class]] "CDataProto" |
| // * __proto__ === 'p', the prototype object from above |
| // * 'constructor' property === 't' |
| AutoObjectVector protos(cx); |
| protos.resize(CTYPEPROTO_SLOTS); |
| if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto, |
| sPointerFunction, NULL, sPointerProps, |
| sPointerInstanceFunctions, sPointerInstanceProps, |
| protos.handleAt(SLOT_POINTERPROTO), protos.handleAt(SLOT_POINTERDATAPROTO))) |
| return false; |
| |
| if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto, |
| sArrayFunction, NULL, sArrayProps, |
| sArrayInstanceFunctions, sArrayInstanceProps, |
| protos.handleAt(SLOT_ARRAYPROTO), protos.handleAt(SLOT_ARRAYDATAPROTO))) |
| return false; |
| |
| if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto, |
| sStructFunction, sStructFunctions, sStructProps, |
| sStructInstanceFunctions, NULL, |
| protos.handleAt(SLOT_STRUCTPROTO), protos.handleAt(SLOT_STRUCTDATAPROTO))) |
| return false; |
| |
| if (!InitTypeConstructor(cx, parent, CTypeProto, protos.handleAt(SLOT_POINTERDATAPROTO), |
| sFunctionFunction, NULL, sFunctionProps, sFunctionInstanceFunctions, NULL, |
| protos.handleAt(SLOT_FUNCTIONPROTO), protos.handleAt(SLOT_FUNCTIONDATAPROTO))) |
| return false; |
| |
| protos[SLOT_CDATAPROTO] = CDataProto; |
| |
| // Create and attach the ctypes.{Int64,UInt64} constructors. |
| // Each of these has, respectively: |
| // * [[Class]] "Function" |
| // * __proto__ === Function.prototype |
| // * A constructor that creates a ctypes.{Int64,UInt64} object, respectively. |
| // * 'prototype' property: |
| // * [[Class]] {"Int64Proto","UInt64Proto"} |
| // * 'constructor' property === ctypes.{Int64,UInt64} |
| protos[SLOT_INT64PROTO] = InitInt64Class(cx, parent, &sInt64ProtoClass, |
| Int64::Construct, sInt64Functions, sInt64StaticFunctions); |
| if (!protos[SLOT_INT64PROTO]) |
| return false; |
| protos[SLOT_UINT64PROTO] = InitInt64Class(cx, parent, &sUInt64ProtoClass, |
| UInt64::Construct, sUInt64Functions, sUInt64StaticFunctions); |
| if (!protos[SLOT_UINT64PROTO]) |
| return false; |
| |
| // Finally, store a pointer to the global ctypes object. |
| // Note that there is no other reliable manner of locating this object. |
| protos[SLOT_CTYPES] = parent; |
| |
| // Attach the prototypes just created to each of ctypes.CType.prototype, |
| // and the special type constructors, so we can access them when constructing |
| // instances of those types. |
| AttachProtos(CTypeProto, protos); |
| AttachProtos(protos[SLOT_POINTERPROTO], protos); |
| AttachProtos(protos[SLOT_ARRAYPROTO], protos); |
| AttachProtos(protos[SLOT_STRUCTPROTO], protos); |
| AttachProtos(protos[SLOT_FUNCTIONPROTO], protos); |
| |
| RootedObject ABIProto(cx, InitABIClass(cx, parent)); |
| if (!ABIProto) |
| return false; |
| |
| // Attach objects representing ABI constants. |
| if (!DefineABIConstant(cx, parent, "default_abi", ABI_DEFAULT, ABIProto) || |
| !DefineABIConstant(cx, parent, "stdcall_abi", ABI_STDCALL, ABIProto) || |
| !DefineABIConstant(cx, parent, "winapi_abi", ABI_WINAPI, ABIProto)) |
| return false; |
| |
| // Create objects representing the builtin types, and attach them to the |
| // ctypes object. Each type object 't' has: |
| // * [[Class]] "CType" |
| // * __proto__ === ctypes.CType.prototype |
| // * A constructor which creates and returns a CData object, containing |
| // binary data of the given type. |
| // * 'prototype' property: |
| // * [[Class]] "CDataProto" |
| // * __proto__ === ctypes.CData.prototype |
| // * 'constructor' property === 't' |
| #define DEFINE_TYPE(name, type, ffiType) \ |
| RootedObject typeObj_##name(cx, \ |
| CType::DefineBuiltin(cx, parent, #name, CTypeProto, CDataProto, #name, \ |
| TYPE_##name, INT_TO_JSVAL(sizeof(type)), \ |
| INT_TO_JSVAL(ffiType.alignment), &ffiType)); \ |
| if (!typeObj_##name) \ |
| return false; |
| #include "typedefs.h" |
| |
| // Alias 'ctypes.unsigned' as 'ctypes.unsigned_int', since they represent |
| // the same type in C. |
| if (!JS_DefineProperty(cx, parent, "unsigned", |
| OBJECT_TO_JSVAL(typeObj_unsigned_int), NULL, NULL, |
| JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) |
| return false; |
| |
| // Create objects representing the special types void_t and voidptr_t. |
| RootedObject typeObj(cx, |
| CType::DefineBuiltin(cx, parent, "void_t", CTypeProto, CDataProto, "void", |
| TYPE_void_t, JSVAL_VOID, JSVAL_VOID, &ffi_type_void)); |
| if (!typeObj) |
| return false; |
| |
| typeObj = PointerType::CreateInternal(cx, typeObj); |
| if (!typeObj) |
| return false; |
| if (!JS_DefineProperty(cx, parent, "voidptr_t", OBJECT_TO_JSVAL(typeObj), |
| NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) |
| return false; |
| |
| return true; |
| } |
| |
| bool |
| IsCTypesGlobal(JSObject* obj) |
| { |
| return JS_GetClass(obj) == &sCTypesGlobalClass; |
| } |
| |
| // Get the JSCTypesCallbacks struct from the 'ctypes' object 'obj'. |
| JSCTypesCallbacks* |
| GetCallbacks(JSObject* obj) |
| { |
| JS_ASSERT(IsCTypesGlobal(obj)); |
| |
| jsval result = JS_GetReservedSlot(obj, SLOT_CALLBACKS); |
| if (JSVAL_IS_VOID(result)) |
| return NULL; |
| |
| return static_cast<JSCTypesCallbacks*>(JSVAL_TO_PRIVATE(result)); |
| } |
| |
| // Utility function to access a property of an object as an object |
| // returns false and sets the error if the property does not exist |
| // or is not an object |
| bool GetObjectProperty(JSContext *cx, HandleObject obj, |
| const char *property, MutableHandleObject result) |
| { |
| RootedValue val(cx); |
| if (!JS_GetProperty(cx, obj, property, val.address())) { |
| return false; |
| } |
| |
| if (JSVAL_IS_PRIMITIVE(val)) { |
| JS_ReportError(cx, "missing or non-object field"); |
| return false; |
| } |
| |
| result.set(JSVAL_TO_OBJECT(val)); |
| return true; |
| } |
| |
| } /* namespace ctypes */ |
| } /* namespace js */ |
| |
| using namespace js; |
| using namespace js::ctypes; |
| |
| JS_PUBLIC_API(JSBool) |
| JS_InitCTypesClass(JSContext* cx, JSObject *globalArg) |
| { |
| RootedObject global(cx, globalArg); |
| |
| // attach ctypes property to global object |
| RootedObject ctypes(cx, JS_NewObject(cx, &sCTypesGlobalClass, NULL, NULL)); |
| if (!ctypes) |
| return false; |
| |
| if (!JS_DefineProperty(cx, global, "ctypes", OBJECT_TO_JSVAL(ctypes), |
| JS_PropertyStub, JS_StrictPropertyStub, JSPROP_READONLY | JSPROP_PERMANENT)){ |
| return false; |
| } |
| |
| if (!InitTypeClasses(cx, ctypes)) |
| return false; |
| |
| // attach API functions and properties |
| if (!JS_DefineFunctions(cx, ctypes, sModuleFunctions) || |
| !JS_DefineProperties(cx, ctypes, sModuleProps)) |
| return false; |
| |
| // Set up ctypes.CDataFinalizer.prototype. |
| RootedObject ctor(cx); |
| if (!GetObjectProperty(cx, ctypes, "CDataFinalizer", &ctor)) |
| return false; |
| |
| RootedObject prototype(cx, JS_NewObject(cx, &sCDataFinalizerProtoClass, NULL, ctypes)); |
| if (!prototype) |
| return false; |
| |
| if (!JS_DefineProperties(cx, prototype, sCDataFinalizerProps) || |
| !JS_DefineFunctions(cx, prototype, sCDataFinalizerFunctions)) |
| return false; |
| |
| if (!JS_DefineProperty(cx, ctor, "prototype", OBJECT_TO_JSVAL(prototype), |
| NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) |
| return false; |
| |
| if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(ctor), |
| NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) |
| return false; |
| |
| |
| // Seal the ctypes object, to prevent modification. |
| return JS_FreezeObject(cx, ctypes); |
| } |
| |
| JS_PUBLIC_API(void) |
| JS_SetCTypesCallbacks(JSObject *ctypesObj, JSCTypesCallbacks* callbacks) |
| { |
| JS_ASSERT(callbacks); |
| JS_ASSERT(IsCTypesGlobal(ctypesObj)); |
| |
| // Set the callbacks on a reserved slot. |
| JS_SetReservedSlot(ctypesObj, SLOT_CALLBACKS, PRIVATE_TO_JSVAL(callbacks)); |
| } |
| |
| namespace js { |
| |
| JS_FRIEND_API(size_t) |
| SizeOfDataIfCDataObject(JSMallocSizeOfFun mallocSizeOf, JSObject *obj) |
| { |
| if (!CData::IsCData(obj)) |
| return 0; |
| |
| size_t n = 0; |
| jsval slot = JS_GetReservedSlot(obj, ctypes::SLOT_OWNS); |
| if (!JSVAL_IS_VOID(slot)) { |
| JSBool owns = JSVAL_TO_BOOLEAN(slot); |
| slot = JS_GetReservedSlot(obj, ctypes::SLOT_DATA); |
| if (!JSVAL_IS_VOID(slot)) { |
| char** buffer = static_cast<char**>(JSVAL_TO_PRIVATE(slot)); |
| n += mallocSizeOf(buffer); |
| if (owns) |
| n += mallocSizeOf(*buffer); |
| } |
| } |
| return n; |
| } |
| |
| namespace ctypes { |
| |
| /******************************************************************************* |
| ** Type conversion functions |
| *******************************************************************************/ |
| |
| // Enforce some sanity checks on type widths and properties. |
| // Where the architecture is 64-bit, make sure it's LP64 or LLP64. (ctypes.int |
| // autoconverts to a primitive JS number; to support ILP64 architectures, it |
| // would need to autoconvert to an Int64 object instead. Therefore we enforce |
| // this invariant here.) |
| JS_STATIC_ASSERT(sizeof(bool) == 1 || sizeof(bool) == 4); |
| JS_STATIC_ASSERT(sizeof(char) == 1); |
| JS_STATIC_ASSERT(sizeof(short) == 2); |
| JS_STATIC_ASSERT(sizeof(int) == 4); |
| JS_STATIC_ASSERT(sizeof(unsigned) == 4); |
| JS_STATIC_ASSERT(sizeof(long) == 4 || sizeof(long) == 8); |
| JS_STATIC_ASSERT(sizeof(long long) == 8); |
| JS_STATIC_ASSERT(sizeof(size_t) == sizeof(uintptr_t)); |
| JS_STATIC_ASSERT(sizeof(float) == 4); |
| JS_STATIC_ASSERT(sizeof(PRFuncPtr) == sizeof(void*)); |
| JS_STATIC_ASSERT(numeric_limits<double>::is_signed); |
| |
| // Templated helper to convert FromType to TargetType, for the default case |
| // where the trivial POD constructor will do. |
| template<class TargetType, class FromType> |
| struct ConvertImpl { |
| static JS_ALWAYS_INLINE TargetType Convert(FromType d) { |
| return TargetType(d); |
| } |
| }; |
| |
| #ifdef _MSC_VER |
| // MSVC can't perform double to unsigned __int64 conversion when the |
| // double is greater than 2^63 - 1. Help it along a little. |
| template<> |
| struct ConvertImpl<uint64_t, double> { |
| static JS_ALWAYS_INLINE uint64_t Convert(double d) { |
| return d > 0x7fffffffffffffffui64 ? |
| uint64_t(d - 0x8000000000000000ui64) + 0x8000000000000000ui64 : |
| uint64_t(d); |
| } |
| }; |
| #endif |
| |
| // C++ doesn't guarantee that exact values are the only ones that will |
| // round-trip. In fact, on some platforms, including SPARC, there are pairs of |
| // values, a uint64_t and a double, such that neither value is exactly |
| // representable in the other type, but they cast to each other. |
| #ifdef SPARC |
| // Simulate x86 overflow behavior |
| template<> |
| struct ConvertImpl<uint64_t, double> { |
| static JS_ALWAYS_INLINE uint64_t Convert(double d) { |
| return d >= 0xffffffffffffffff ? |
| 0x8000000000000000 : uint64_t(d); |
| } |
| }; |
| |
| template<> |
| struct ConvertImpl<int64_t, double> { |
| static JS_ALWAYS_INLINE int64_t Convert(double d) { |
| return d >= 0x7fffffffffffffff ? |
| 0x8000000000000000 : int64_t(d); |
| } |
| }; |
| #endif |
| |
| template<class TargetType, class FromType> |
| static JS_ALWAYS_INLINE TargetType Convert(FromType d) |
| { |
| return ConvertImpl<TargetType, FromType>::Convert(d); |
| } |
| |
| template<class TargetType, class FromType> |
| static JS_ALWAYS_INLINE bool IsAlwaysExact() |
| { |
| // Return 'true' if TargetType can always exactly represent FromType. |
| // This means that: |
| // 1) TargetType must be the same or more bits wide as FromType. For integers |
| // represented in 'n' bits, unsigned variants will have 'n' digits while |
| // signed will have 'n - 1'. For floating point types, 'digits' is the |
| // mantissa width. |
| // 2) If FromType is signed, TargetType must also be signed. (Floating point |
| // types are always signed.) |
| // 3) If TargetType is an exact integral type, FromType must be also. |
| if (numeric_limits<TargetType>::digits < numeric_limits<FromType>::digits) |
| return false; |
| |
| if (numeric_limits<FromType>::is_signed && |
| !numeric_limits<TargetType>::is_signed) |
| return false; |
| |
| if (!numeric_limits<FromType>::is_exact && |
| numeric_limits<TargetType>::is_exact) |
| return false; |
| |
| return true; |
| } |
| |
| // Templated helper to determine if FromType 'i' converts losslessly to |
| // TargetType 'j'. Default case where both types are the same signedness. |
| template<class TargetType, class FromType, bool TargetSigned, bool FromSigned> |
| struct IsExactImpl { |
| static JS_ALWAYS_INLINE bool Test(FromType i, TargetType j) { |
| JS_STATIC_ASSERT(numeric_limits<TargetType>::is_exact); |
| return FromType(j) == i; |
| } |
| }; |
| |
| // Specialization where TargetType is unsigned, FromType is signed. |
| template<class TargetType, class FromType> |
| struct IsExactImpl<TargetType, FromType, false, true> { |
| static JS_ALWAYS_INLINE bool Test(FromType i, TargetType j) { |
| JS_STATIC_ASSERT(numeric_limits<TargetType>::is_exact); |
| return i >= 0 && FromType(j) == i; |
| } |
| }; |
| |
| // Specialization where TargetType is signed, FromType is unsigned. |
| template<class TargetType, class FromType> |
| struct IsExactImpl<TargetType, FromType, true, false> { |
| static JS_ALWAYS_INLINE bool Test(FromType i, TargetType j) { |
| JS_STATIC_ASSERT(numeric_limits<TargetType>::is_exact); |
| return TargetType(i) >= 0 && FromType(j) == i; |
| } |
| }; |
| |
| // Convert FromType 'i' to TargetType 'result', returning true iff 'result' |
| // is an exact representation of 'i'. |
| template<class TargetType, class FromType> |
| static JS_ALWAYS_INLINE bool ConvertExact(FromType i, TargetType* result) |
| { |
| // Require that TargetType is integral, to simplify conversion. |
| JS_STATIC_ASSERT(numeric_limits<TargetType>::is_exact); |
| |
| *result = Convert<TargetType>(i); |
| |
| // See if we can avoid a dynamic check. |
| if (IsAlwaysExact<TargetType, FromType>()) |
| return true; |
| |
| // Return 'true' if 'i' is exactly representable in 'TargetType'. |
| return IsExactImpl<TargetType, |
| FromType, |
| numeric_limits<TargetType>::is_signed, |
| numeric_limits<FromType>::is_signed>::Test(i, *result); |
| } |
| |
| // Templated helper to determine if Type 'i' is negative. Default case |
| // where IntegerType is unsigned. |
| template<class Type, bool IsSigned> |
| struct IsNegativeImpl { |
| static JS_ALWAYS_INLINE bool Test(Type i) { |
| return false; |
| } |
| }; |
| |
| // Specialization where Type is signed. |
| template<class Type> |
| struct IsNegativeImpl<Type, true> { |
| static JS_ALWAYS_INLINE bool Test(Type i) { |
| return i < 0; |
| } |
| }; |
| |
| // Determine whether Type 'i' is negative. |
| template<class Type> |
| static JS_ALWAYS_INLINE bool IsNegative(Type i) |
| { |
| return IsNegativeImpl<Type, numeric_limits<Type>::is_signed>::Test(i); |
| } |
| |
| // Implicitly convert val to bool, allowing JSBool, int, and double |
| // arguments numerically equal to 0 or 1. |
| static bool |
| jsvalToBool(JSContext* cx, jsval val, bool* result) |
| { |
| if (JSVAL_IS_BOOLEAN(val)) { |
| *result = JSVAL_TO_BOOLEAN(val) != JS_FALSE; |
| return true; |
| } |
| if (JSVAL_IS_INT(val)) { |
| int32_t i = JSVAL_TO_INT(val); |
| *result = i != 0; |
| return i == 0 || i == 1; |
| } |
| if (JSVAL_IS_DOUBLE(val)) { |
| double d = JSVAL_TO_DOUBLE(val); |
| *result = d != 0; |
| // Allow -0. |
| return d == 1 || d == 0; |
| } |
| // Don't silently convert null to bool. It's probably a mistake. |
| return false; |
| } |
| |
| // Implicitly convert val to IntegerType, allowing JSBool, int, double, |
| // Int64, UInt64, and CData integer types 't' where all values of 't' are |
| // representable by IntegerType. |
| template<class IntegerType> |
| static bool |
| jsvalToInteger(JSContext* cx, jsval val, IntegerType* result) |
| { |
| JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact); |
| |
| if (JSVAL_IS_INT(val)) { |
| // Make sure the integer fits in the alotted precision, and has the right |
| // sign. |
| int32_t i = JSVAL_TO_INT(val); |
| return ConvertExact(i, result); |
| } |
| if (JSVAL_IS_DOUBLE(val)) { |
| // Don't silently lose bits here -- check that val really is an |
| // integer value, and has the right sign. |
| double d = JSVAL_TO_DOUBLE(val); |
| return ConvertExact(d, result); |
| } |
| if (!JSVAL_IS_PRIMITIVE(val)) { |
| JSObject* obj = JSVAL_TO_OBJECT(val); |
| if (CData::IsCData(obj)) { |
| JSObject* typeObj = CData::GetCType(obj); |
| void* data = CData::GetData(obj); |
| |
| // Check whether the source type is always representable, with exact |
| // precision, by the target type. If it is, convert the value. |
| switch (CType::GetTypeCode(typeObj)) { |
| #define DEFINE_INT_TYPE(name, fromType, ffiType) \ |
| case TYPE_##name: \ |
| if (!IsAlwaysExact<IntegerType, fromType>()) \ |
| return false; \ |
| *result = IntegerType(*static_cast<fromType*>(data)); \ |
| return true; |
| #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) |
| #include "typedefs.h" |
| case TYPE_void_t: |
| case TYPE_bool: |
| case TYPE_float: |
| case TYPE_double: |
| case TYPE_float32_t: |
| case TYPE_float64_t: |
| case TYPE_char: |
| case TYPE_signed_char: |
| case TYPE_unsigned_char: |
| case TYPE_jschar: |
| case TYPE_pointer: |
| case TYPE_function: |
| case TYPE_array: |
| case TYPE_struct: |
| // Not a compatible number type. |
| return false; |
| } |
| } |
| |
| if (Int64::IsInt64(obj)) { |
| // Make sure the integer fits in IntegerType. |
| int64_t i = Int64Base::GetInt(obj); |
| return ConvertExact(i, result); |
| } |
| |
| if (UInt64::IsUInt64(obj)) { |
| // Make sure the integer fits in IntegerType. |
| uint64_t i = Int64Base::GetInt(obj); |
| return ConvertExact(i, result); |
| } |
| |
| if (CDataFinalizer::IsCDataFinalizer(obj)) { |
| RootedValue innerData(cx); |
| if (!CDataFinalizer::GetValue(cx, obj, innerData.address())) { |
| return false; // Nothing to convert |
| } |
| return jsvalToInteger(cx, innerData, result); |
| } |
| |
| return false; |
| } |
| if (JSVAL_IS_BOOLEAN(val)) { |
| // Implicitly promote boolean values to 0 or 1, like C. |
| *result = JSVAL_TO_BOOLEAN(val); |
| JS_ASSERT(*result == 0 || *result == 1); |
| return true; |
| } |
| // Don't silently convert null to an integer. It's probably a mistake. |
| return false; |
| } |
| |
| // Implicitly convert val to FloatType, allowing int, double, |
| // Int64, UInt64, and CData numeric types 't' where all values of 't' are |
| // representable by FloatType. |
| template<class FloatType> |
| static bool |
| jsvalToFloat(JSContext *cx, jsval val, FloatType* result) |
| { |
| JS_STATIC_ASSERT(!numeric_limits<FloatType>::is_exact); |
| |
| // The following casts may silently throw away some bits, but there's |
| // no good way around it. Sternly requiring that the 64-bit double |
| // argument be exactly representable as a 32-bit float is |
| // unrealistic: it would allow 1/2 to pass but not 1/3. |
| if (JSVAL_IS_INT(val)) { |
| *result = FloatType(JSVAL_TO_INT(val)); |
| return true; |
| } |
| if (JSVAL_IS_DOUBLE(val)) { |
| *result = FloatType(JSVAL_TO_DOUBLE(val)); |
| return true; |
| } |
| if (!JSVAL_IS_PRIMITIVE(val)) { |
| JSObject* obj = JSVAL_TO_OBJECT(val); |
| if (CData::IsCData(obj)) { |
| JSObject* typeObj = CData::GetCType(obj); |
| void* data = CData::GetData(obj); |
| |
| // Check whether the source type is always representable, with exact |
| // precision, by the target type. If it is, convert the value. |
| switch (CType::GetTypeCode(typeObj)) { |
| #define DEFINE_FLOAT_TYPE(name, fromType, ffiType) \ |
| case TYPE_##name: \ |
| if (!IsAlwaysExact<FloatType, fromType>()) \ |
| return false; \ |
| *result = FloatType(*static_cast<fromType*>(data)); \ |
| return true; |
| #define DEFINE_INT_TYPE(x, y, z) DEFINE_FLOAT_TYPE(x, y, z) |
| #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) |
| #include "typedefs.h" |
| case TYPE_void_t: |
| case TYPE_bool: |
| case TYPE_char: |
| case TYPE_signed_char: |
| case TYPE_unsigned_char: |
| case TYPE_jschar: |
| case TYPE_pointer: |
| case TYPE_function: |
| case TYPE_array: |
| case TYPE_struct: |
| // Not a compatible number type. |
| return false; |
| } |
| } |
| } |
| // Don't silently convert true to 1.0 or false to 0.0, even though C/C++ |
| // does it. It's likely to be a mistake. |
| return false; |
| } |
| |
| template<class IntegerType> |
| static bool |
| StringToInteger(JSContext* cx, JSString* string, IntegerType* result) |
| { |
| JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact); |
| |
| const jschar* cp = string->getChars(NULL); |
| if (!cp) |
| return false; |
| |
| const jschar* end = cp + string->length(); |
| if (cp == end) |
| return false; |
| |
| IntegerType sign = 1; |
| if (cp[0] == '-') { |
| if (!numeric_limits<IntegerType>::is_signed) |
| return false; |
| |
| sign = -1; |
| ++cp; |
| } |
| |
| // Assume base-10, unless the string begins with '0x' or '0X'. |
| IntegerType base = 10; |
| if (end - cp > 2 && cp[0] == '0' && (cp[1] == 'x' || cp[1] == 'X')) { |
| cp += 2; |
| base = 16; |
| } |
| |
| // Scan the string left to right and build the number, |
| // checking for valid characters 0 - 9, a - f, A - F and overflow. |
| IntegerType i = 0; |
| while (cp != end) { |
| jschar c = *cp++; |
| if (c >= '0' && c <= '9') |
| c -= '0'; |
| else if (base == 16 && c >= 'a' && c <= 'f') |
| c = c - 'a' + 10; |
| else if (base == 16 && c >= 'A' && c <= 'F') |
| c = c - 'A' + 10; |
| else |
| return false; |
| |
| IntegerType ii = i; |
| i = ii * base + sign * c; |
| if (i / base != ii) // overflow |
| return false; |
| } |
| |
| *result = i; |
| return true; |
| } |
| |
| // Implicitly convert val to IntegerType, allowing int, double, |
| // Int64, UInt64, and optionally a decimal or hexadecimal string argument. |
| // (This is common code shared by jsvalToSize and the Int64/UInt64 constructors.) |
| template<class IntegerType> |
| static bool |
| jsvalToBigInteger(JSContext* cx, |
| jsval val, |
| bool allowString, |
| IntegerType* result) |
| { |
| JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact); |
| |
| if (JSVAL_IS_INT(val)) { |
| // Make sure the integer fits in the alotted precision, and has the right |
| // sign. |
| int32_t i = JSVAL_TO_INT(val); |
| return ConvertExact(i, result); |
| } |
| if (JSVAL_IS_DOUBLE(val)) { |
| // Don't silently lose bits here -- check that val really is an |
| // integer value, and has the right sign. |
| double d = JSVAL_TO_DOUBLE(val); |
| return ConvertExact(d, result); |
| } |
| if (allowString && JSVAL_IS_STRING(val)) { |
| // Allow conversion from base-10 or base-16 strings, provided the result |
| // fits in IntegerType. (This allows an Int64 or UInt64 object to be passed |
| // to the JS array element operator, which will automatically call |
| // toString() on the object for us.) |
| return StringToInteger(cx, JSVAL_TO_STRING(val), result); |
| } |
| if (!JSVAL_IS_PRIMITIVE(val)) { |
| // Allow conversion from an Int64 or UInt64 object directly. |
| JSObject* obj = JSVAL_TO_OBJECT(val); |
| |
| if (UInt64::IsUInt64(obj)) { |
| // Make sure the integer fits in IntegerType. |
| uint64_t i = Int64Base::GetInt(obj); |
| return ConvertExact(i, result); |
| } |
| |
| if (Int64::IsInt64(obj)) { |
| // Make sure the integer fits in IntegerType. |
| int64_t i = Int64Base::GetInt(obj); |
| return ConvertExact(i, result); |
| } |
| |
| if (CDataFinalizer::IsCDataFinalizer(obj)) { |
| RootedValue innerData(cx); |
| if (!CDataFinalizer::GetValue(cx, obj, innerData.address())) { |
| return false; // Nothing to convert |
| } |
| return jsvalToBigInteger(cx, innerData, allowString, result); |
| } |
| |
| } |
| return false; |
| } |
| |
| // Implicitly convert val to a size value, where the size value is represented |
| // by size_t but must also fit in a double. |
| static bool |
| jsvalToSize(JSContext* cx, jsval val, bool allowString, size_t* result) |
| { |
| if (!jsvalToBigInteger(cx, val, allowString, result)) |
| return false; |
| |
| // Also check that the result fits in a double. |
| return Convert<size_t>(double(*result)) == *result; |
| } |
| |
| // Implicitly convert val to IntegerType, allowing int, double, |
| // Int64, UInt64, and optionally a decimal or hexadecimal string argument. |
| // (This is common code shared by jsvalToSize and the Int64/UInt64 constructors.) |
| template<class IntegerType> |
| static bool |
| jsidToBigInteger(JSContext* cx, |
| jsid val, |
| bool allowString, |
| IntegerType* result) |
| { |
| JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact); |
| |
| if (JSID_IS_INT(val)) { |
| // Make sure the integer fits in the alotted precision, and has the right |
| // sign. |
| int32_t i = JSID_TO_INT(val); |
| return ConvertExact(i, result); |
| } |
| if (allowString && JSID_IS_STRING(val)) { |
| // Allow conversion from base-10 or base-16 strings, provided the result |
| // fits in IntegerType. (This allows an Int64 or UInt64 object to be passed |
| // to the JS array element operator, which will automatically call |
| // toString() on the object for us.) |
| return StringToInteger(cx, JSID_TO_STRING(val), result); |
| } |
| if (JSID_IS_OBJECT(val)) { |
| // Allow conversion from an Int64 or UInt64 object directly. |
| JSObject* obj = JSID_TO_OBJECT(val); |
| |
| if (UInt64::IsUInt64(obj)) { |
| // Make sure the integer fits in IntegerType. |
| uint64_t i = Int64Base::GetInt(obj); |
| return ConvertExact(i, result); |
| } |
| |
| if (Int64::IsInt64(obj)) { |
| // Make sure the integer fits in IntegerType. |
| int64_t i = Int64Base::GetInt(obj); |
| return ConvertExact(i, result); |
| } |
| } |
| return false; |
| } |
| |
| // Implicitly convert val to a size value, where the size value is represented |
| // by size_t but must also fit in a double. |
| static bool |
| jsidToSize(JSContext* cx, jsid val, bool allowString, size_t* result) |
| { |
| if (!jsidToBigInteger(cx, val, allowString, result)) |
| return false; |
| |
| // Also check that the result fits in a double. |
| return Convert<size_t>(double(*result)) == *result; |
| } |
| |
| // Implicitly convert a size value to a jsval, ensuring that the size_t value |
| // fits in a double. |
| static JSBool |
| SizeTojsval(JSContext* cx, size_t size, jsval* result) |
| { |
| if (Convert<size_t>(double(size)) != size) { |
| JS_ReportError(cx, "size overflow"); |
| return false; |
| } |
| |
| *result = JS_NumberValue(double(size)); |
| return true; |
| } |
| |
| // Forcefully convert val to IntegerType when explicitly requested. |
| template<class IntegerType> |
| static bool |
| jsvalToIntegerExplicit(jsval val, IntegerType* result) |
| { |
| JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact); |
| |
| if (JSVAL_IS_DOUBLE(val)) { |
| // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast. |
| double d = JSVAL_TO_DOUBLE(val); |
| *result = mozilla::IsFinite(d) ? IntegerType(d) : 0; |
| return true; |
| } |
| if (!JSVAL_IS_PRIMITIVE(val)) { |
| // Convert Int64 and UInt64 values by C-style cast. |
| JSObject* obj = JSVAL_TO_OBJECT(val); |
| if (Int64::IsInt64(obj)) { |
| int64_t i = Int64Base::GetInt(obj); |
| *result = IntegerType(i); |
| return true; |
| } |
| if (UInt64::IsUInt64(obj)) { |
| uint64_t i = Int64Base::GetInt(obj); |
| *result = IntegerType(i); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| // Forcefully convert val to a pointer value when explicitly requested. |
| static bool |
| jsvalToPtrExplicit(JSContext* cx, jsval val, uintptr_t* result) |
| { |
| if (JSVAL_IS_INT(val)) { |
| // int32_t always fits in intptr_t. If the integer is negative, cast through |
| // an intptr_t intermediate to sign-extend. |
| int32_t i = JSVAL_TO_INT(val); |
| *result = i < 0 ? uintptr_t(intptr_t(i)) : uintptr_t(i); |
| return true; |
| } |
| if (JSVAL_IS_DOUBLE(val)) { |
| double d = JSVAL_TO_DOUBLE(val); |
| if (d < 0) { |
| // Cast through an intptr_t intermediate to sign-extend. |
| intptr_t i = Convert<intptr_t>(d); |
| if (double(i) != d) |
| return false; |
| |
| *result = uintptr_t(i); |
| return true; |
| } |
| |
| // Don't silently lose bits here -- check that val really is an |
| // integer value, and has the right sign. |
| *result = Convert<uintptr_t>(d); |
| return double(*result) == d; |
| } |
| if (!JSVAL_IS_PRIMITIVE(val)) { |
| JSObject* obj = JSVAL_TO_OBJECT(val); |
| if (Int64::IsInt64(obj)) { |
| int64_t i = Int64Base::GetInt(obj); |
| intptr_t p = intptr_t(i); |
| |
| // Make sure the integer fits in the alotted precision. |
| if (int64_t(p) != i) |
| return false; |
| *result = uintptr_t(p); |
| return true; |
| } |
| |
| if (UInt64::IsUInt64(obj)) { |
| uint64_t i = Int64Base::GetInt(obj); |
| |
| // Make sure the integer fits in the alotted precision. |
| *result = uintptr_t(i); |
| return uint64_t(*result) == i; |
| } |
| } |
| return false; |
| } |
| |
| template<class IntegerType, class CharType, size_t N, class AP> |
| void |
| IntegerToString(IntegerType i, int radix, Vector<CharType, N, AP>& result) |
| { |
| JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact); |
| |
| // The buffer must be big enough for all the bits of IntegerType to fit, |
| // in base-2, including '-'. |
| CharType buffer[sizeof(IntegerType) * 8 + 1]; |
| CharType* end = buffer + sizeof(buffer) / sizeof(CharType); |
| CharType* cp = end; |
| |
| // Build the string in reverse. We use multiplication and subtraction |
| // instead of modulus because that's much faster. |
| const bool isNegative = IsNegative(i); |
| size_t sign = isNegative ? -1 : 1; |
| do { |
| IntegerType ii = i / IntegerType(radix); |
| size_t index = sign * size_t(i - ii * IntegerType(radix)); |
| *--cp = "0123456789abcdefghijklmnopqrstuvwxyz"[index]; |
| i = ii; |
| } while (i != 0); |
| |
| if (isNegative) |
| *--cp = '-'; |
| |
| JS_ASSERT(cp >= buffer); |
| result.append(cp, end); |
| } |
| |
| template<class CharType> |
| static size_t |
| strnlen(const CharType* begin, size_t max) |
| { |
| for (const CharType* s = begin; s != begin + max; ++s) |
| if (*s == 0) |
| return s - begin; |
| |
| return max; |
| } |
| |
| // Convert C binary value 'data' of CType 'typeObj' to a JS primitive, where |
| // possible; otherwise, construct and return a CData object. The following |
| // semantics apply when constructing a CData object for return: |
| // * If 'wantPrimitive' is true, the caller indicates that 'result' must be |
| // a JS primitive, and ConvertToJS will fail if 'result' would be a CData |
| // object. Otherwise: |
| // * If a CData object 'parentObj' is supplied, the new CData object is |
| // dependent on the given parent and its buffer refers to a slice of the |
| // parent's buffer. |
| // * If 'parentObj' is null, the new CData object may or may not own its |
| // resulting buffer depending on the 'ownResult' argument. |
| JSBool |
| ConvertToJS(JSContext* cx, |
| HandleObject typeObj, |
| HandleObject parentObj, |
| void* data, |
| bool wantPrimitive, |
| bool ownResult, |
| jsval* result) |
| { |
| JS_ASSERT(!parentObj || CData::IsCData(parentObj)); |
| JS_ASSERT(!parentObj || !ownResult); |
| JS_ASSERT(!wantPrimitive || !ownResult); |
| |
| TypeCode typeCode = CType::GetTypeCode(typeObj); |
| |
| switch (typeCode) { |
| case TYPE_void_t: |
| *result = JSVAL_VOID; |
| break; |
| case TYPE_bool: |
| *result = *static_cast<bool*>(data) ? JSVAL_TRUE : JSVAL_FALSE; |
| break; |
| #define DEFINE_INT_TYPE(name, type, ffiType) \ |
| case TYPE_##name: { \ |
| type value = *static_cast<type*>(data); \ |
| if (sizeof(type) < 4) \ |
| *result = INT_TO_JSVAL(int32_t(value)); \ |
| else \ |
| *result = JS_NumberValue(double(value)); \ |
| break; \ |
| } |
| #define DEFINE_WRAPPED_INT_TYPE(name, type, ffiType) \ |
| case TYPE_##name: { \ |
| /* Return an Int64 or UInt64 object - do not convert to a JS number. */ \ |
| uint64_t value; \ |
| RootedObject proto(cx); \ |
| if (!numeric_limits<type>::is_signed) { \ |
| value = *static_cast<type*>(data); \ |
| /* Get ctypes.UInt64.prototype from ctypes.CType.prototype. */ \ |
| proto = CType::GetProtoFromType(cx, typeObj, SLOT_UINT64PROTO); \ |
| if (!proto) \ |
| return false; \ |
| } else { \ |
| value = int64_t(*static_cast<type*>(data)); \ |
| /* Get ctypes.Int64.prototype from ctypes.CType.prototype. */ \ |
| proto = CType::GetProtoFromType(cx, typeObj, SLOT_INT64PROTO); \ |
| if (!proto) \ |
| return false; \ |
| } \ |
| \ |
| JSObject* obj = Int64Base::Construct(cx, proto, value, \ |
| !numeric_limits<type>::is_signed); \ |
| if (!obj) \ |
| return false; \ |
| *result = OBJECT_TO_JSVAL(obj); \ |
| break; \ |
| } |
| #define DEFINE_FLOAT_TYPE(name, type, ffiType) \ |
| case TYPE_##name: { \ |
| type value = *static_cast<type*>(data); \ |
| *result = JS_NumberValue(double(value)); \ |
| break; \ |
| } |
| #define DEFINE_CHAR_TYPE(name, type, ffiType) \ |
| case TYPE_##name: \ |
| /* Convert to an integer. We have no idea what character encoding to */ \ |
| /* use, if any. */ \ |
| *result = INT_TO_JSVAL(*static_cast<type*>(data)); \ |
| break; |
| #include "typedefs.h" |
| case TYPE_jschar: { |
| // Convert the jschar to a 1-character string. |
| JSString* str = JS_NewUCStringCopyN(cx, static_cast<jschar*>(data), 1); |
| if (!str) |
| return false; |
| |
| *result = STRING_TO_JSVAL(str); |
| break; |
| } |
| case TYPE_pointer: |
| case TYPE_array: |
| case TYPE_struct: { |
| // We're about to create a new CData object to return. If the caller doesn't |
| // want this, return early. |
| if (wantPrimitive) { |
| JS_ReportError(cx, "cannot convert to primitive value"); |
| return false; |
| } |
| |
| JSObject* obj = CData::Create(cx, typeObj, parentObj, data, ownResult); |
| if (!obj) |
| return false; |
| |
| *result = OBJECT_TO_JSVAL(obj); |
| break; |
| } |
| case TYPE_function: |
| JS_NOT_REACHED("cannot return a FunctionType"); |
| } |
| |
| return true; |
| } |
| |
| // Determine if the contents of a typed array can be converted without |
| // ambiguity to a C type. Elements of a Int8Array are converted to |
| // ctypes.int8_t, UInt8Array to ctypes.uint8_t, etc. |
| bool CanConvertTypedArrayItemTo(JSObject *baseType, JSObject *valObj, JSContext *cx) { |
| TypeCode baseTypeCode = CType::GetTypeCode(baseType); |
| if (baseTypeCode == TYPE_void_t) { |
| return true; |
| } |
| TypeCode elementTypeCode; |
| switch (JS_GetArrayBufferViewType(valObj)) { |
| case TypedArray::TYPE_INT8: |
| elementTypeCode = TYPE_int8_t; |
| break; |
| case TypedArray::TYPE_UINT8: |
| case TypedArray::TYPE_UINT8_CLAMPED: |
| elementTypeCode = TYPE_uint8_t; |
| break; |
| case TypedArray::TYPE_INT16: |
| elementTypeCode = TYPE_int16_t; |
| break; |
| case TypedArray::TYPE_UINT16: |
| elementTypeCode = TYPE_uint16_t; |
| break; |
| case TypedArray::TYPE_INT32: |
| elementTypeCode = TYPE_int32_t; |
| break; |
| case TypedArray::TYPE_UINT32: |
| elementTypeCode = TYPE_uint32_t; |
| break; |
| case TypedArray::TYPE_FLOAT32: |
| elementTypeCode = TYPE_float32_t; |
| break; |
| case TypedArray::TYPE_FLOAT64: |
| elementTypeCode = TYPE_float64_t; |
| break; |
| default: |
| return false; |
| } |
| return elementTypeCode == baseTypeCode; |
| } |
| |
| // Implicitly convert jsval 'val' to a C binary representation of CType |
| // 'targetType', storing the result in 'buffer'. Adequate space must be |
| // provided in 'buffer' by the caller. This function generally does minimal |
| // coercion between types. There are two cases in which this function is used: |
| // 1) The target buffer is internal to a CData object; we simply write data |
| // into it. |
| // 2) We are converting an argument for an ffi call, in which case 'isArgument' |
| // will be true. This allows us to handle a special case: if necessary, |
| // we can autoconvert a JS string primitive to a pointer-to-character type. |
| // In this case, ownership of the allocated string is handed off to the |
| // caller; 'freePointer' will be set to indicate this. |
| JSBool |
| ImplicitConvert(JSContext* cx, |
| HandleValue val, |
| JSObject* targetType_, |
| void* buffer, |
| bool isArgument, |
| bool* freePointer) |
| { |
| RootedObject targetType(cx, targetType_); |
| JS_ASSERT(CType::IsSizeDefined(targetType)); |
| |
| // First, check if val is either a CData object or a CDataFinalizer |
| // of type targetType. |
| JSObject* sourceData = NULL; |
| JSObject* sourceType = NULL; |
| RootedObject valObj(cx, NULL); |
| if (!JSVAL_IS_PRIMITIVE(val)) { |
| valObj = JSVAL_TO_OBJECT(val); |
| if (CData::IsCData(valObj)) { |
| sourceData = valObj; |
| sourceType = CData::GetCType(sourceData); |
| |
| // If the types are equal, copy the buffer contained within the CData. |
| // (Note that the buffers may overlap partially or completely.) |
| if (CType::TypesEqual(sourceType, targetType)) { |
| size_t size = CType::GetSize(sourceType); |
| memmove(buffer, CData::GetData(sourceData), size); |
| return true; |
| } |
| } else if (CDataFinalizer::IsCDataFinalizer(valObj)) { |
| sourceData = valObj; |
| sourceType = CDataFinalizer::GetCType(cx, sourceData); |
| |
| CDataFinalizer::Private *p = (CDataFinalizer::Private *) |
| JS_GetPrivate(sourceData); |
| |
| if (!p) { |
| // We have called |dispose| or |forget| already. |
| JS_ReportError(cx, "Attempting to convert an empty CDataFinalizer"); |
| return JS_FALSE; |
| } |
| |
| // If the types are equal, copy the buffer contained within the CData. |
| if (CType::TypesEqual(sourceType, targetType)) { |
| memmove(buffer, p->cargs, p->cargs_size); |
| return true; |
| } |
| } |
| } |
| |
| TypeCode targetCode = CType::GetTypeCode(targetType); |
| |
| switch (targetCode) { |
| case TYPE_bool: { |
| // Do not implicitly lose bits, but allow the values 0, 1, and -0. |
| // Programs can convert explicitly, if needed, using `Boolean(v)` or `!!v`. |
| bool result; |
| if (!jsvalToBool(cx, val, &result)) |
| return TypeError(cx, "boolean", val); |
| *static_cast<bool*>(buffer) = result; |
| break; |
| } |
| #define DEFINE_INT_TYPE(name, type, ffiType) \ |
| case TYPE_##name: { \ |
| /* Do not implicitly lose bits. */ \ |
| type result; \ |
| if (!jsvalToInteger(cx, val, &result)) \ |
| return TypeError(cx, #name, val); \ |
| *static_cast<type*>(buffer) = result; \ |
| break; \ |
| } |
| #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) |
| #define DEFINE_FLOAT_TYPE(name, type, ffiType) \ |
| case TYPE_##name: { \ |
| type result; \ |
| if (!jsvalToFloat(cx, val, &result)) \ |
| return TypeError(cx, #name, val); \ |
| *static_cast<type*>(buffer) = result; \ |
| break; \ |
| } |
| #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) |
| #define DEFINE_JSCHAR_TYPE(name, type, ffiType) \ |
| case TYPE_##name: { \ |
| /* Convert from a 1-character string, regardless of encoding, */ \ |
| /* or from an integer, provided the result fits in 'type'. */ \ |
| type result; \ |
| if (JSVAL_IS_STRING(val)) { \ |
| JSString* str = JSVAL_TO_STRING(val); \ |
| if (str->length() != 1) \ |
| return TypeError(cx, #name, val); \ |
| const jschar *chars = str->getChars(cx); \ |
| if (!chars) \ |
| return false; \ |
| result = chars[0]; \ |
| } else if (!jsvalToInteger(cx, val, &result)) { \ |
| return TypeError(cx, #name, val); \ |
| } \ |
| *static_cast<type*>(buffer) = result; \ |
| break; \ |
| } |
| #include "typedefs.h" |
| case TYPE_pointer: { |
| if (JSVAL_IS_NULL(val)) { |
| // Convert to a null pointer. |
| *static_cast<void**>(buffer) = NULL; |
| break; |
| } |
| |
| JS::Rooted<JSObject*> baseType(cx, PointerType::GetBaseType(targetType)); |
| if (sourceData) { |
| // First, determine if the targetType is ctypes.void_t.ptr. |
| TypeCode sourceCode = CType::GetTypeCode(sourceType); |
| void* sourceBuffer = CData::GetData(sourceData); |
| bool voidptrTarget = CType::GetTypeCode(baseType) == TYPE_void_t; |
| |
| if (sourceCode == TYPE_pointer && voidptrTarget) { |
| // Autoconvert if targetType is ctypes.voidptr_t. |
| *static_cast<void**>(buffer) = *static_cast<void**>(sourceBuffer); |
| break; |
| } |
| if (sourceCode == TYPE_array) { |
| // Autoconvert an array to a ctypes.void_t.ptr or to |
| // sourceType.elementType.ptr, just like C. |
| JSObject* elementType = ArrayType::GetBaseType(sourceType); |
| if (voidptrTarget || CType::TypesEqual(baseType, elementType)) { |
| *static_cast<void**>(buffer) = sourceBuffer; |
| break; |
| } |
| } |
| |
| } else if (isArgument && JSVAL_IS_STRING(val)) { |
| // Convert the string for the ffi call. This requires allocating space |
| // which the caller assumes ownership of. |
| // TODO: Extend this so we can safely convert strings at other times also. |
| JSString* sourceString = JSVAL_TO_STRING(val); |
| size_t sourceLength = sourceString->length(); |
| const jschar* sourceChars = sourceString->getChars(cx); |
| if (!sourceChars) |
| return false; |
| |
| switch (CType::GetTypeCode(baseType)) { |
| case TYPE_char: |
| case TYPE_signed_char: |
| case TYPE_unsigned_char: { |
| // Convert from UTF-16 to UTF-8. |
| size_t nbytes = |
| GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength); |
| if (nbytes == (size_t) -1) |
| return false; |
| |
| char** charBuffer = static_cast<char**>(buffer); |
| *charBuffer = cx->pod_malloc<char>(nbytes + 1); |
| if (!*charBuffer) { |
| JS_ReportAllocationOverflow(cx); |
| return false; |
| } |
| |
| ASSERT_OK(DeflateStringToUTF8Buffer(cx, sourceChars, sourceLength, |
| *charBuffer, &nbytes)); |
| (*charBuffer)[nbytes] = 0; |
| *freePointer = true; |
| break; |
| } |
| case TYPE_jschar: { |
| // Copy the jschar string data. (We could provide direct access to the |
| // JSString's buffer, but this approach is safer if the caller happens |
| // to modify the string.) |
| jschar** jscharBuffer = static_cast<jschar**>(buffer); |
| *jscharBuffer = cx->pod_malloc<jschar>(sourceLength + 1); |
| if (!*jscharBuffer) { |
| JS_ReportAllocationOverflow(cx); |
| return false; |
| } |
| |
| *freePointer = true; |
| memcpy(*jscharBuffer, sourceChars, sourceLength * sizeof(jschar)); |
| (*jscharBuffer)[sourceLength] = 0; |
| break; |
| } |
| default: |
| return TypeError(cx, "string pointer", val); |
| } |
| break; |
| } else if (!JSVAL_IS_PRIMITIVE(val) && JS_IsArrayBufferObject(valObj)) { |
| // Convert ArrayBuffer to pointer without any copy. |
| // Just as with C arrays, we make no effort to |
| // keep the ArrayBuffer alive. |
| *static_cast<void**>(buffer) = JS_GetArrayBufferData(valObj); |
| break; |
| } if (!JSVAL_IS_PRIMITIVE(val) && JS_IsTypedArrayObject(valObj)) { |
| if(!CanConvertTypedArrayItemTo(baseType, valObj, cx)) { |
| return TypeError(cx, "typed array with the appropriate type", val); |
| } |
| |
| // Convert TypedArray to pointer without any copy. |
| // Just as with C arrays, we make no effort to |
| // keep the TypedArray alive. |
| *static_cast<void**>(buffer) = JS_GetArrayBufferViewData(valObj); |
| break; |
| } |
| return TypeError(cx, "pointer", val); |
| } |
| case TYPE_array: { |
| RootedObject baseType(cx, ArrayType::GetBaseType(targetType)); |
| size_t targetLength = ArrayType::GetLength(targetType); |
| |
| if (JSVAL_IS_STRING(val)) { |
| JSString* sourceString = JSVAL_TO_STRING(val); |
| size_t sourceLength = sourceString->length(); |
| const jschar* sourceChars = sourceString->getChars(cx); |
| if (!sourceChars) |
| return false; |
| |
| switch (CType::GetTypeCode(baseType)) { |
| case TYPE_char: |
| case TYPE_signed_char: |
| case TYPE_unsigned_char: { |
| // Convert from UTF-16 to UTF-8. |
| size_t nbytes = |
| GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength); |
| if (nbytes == (size_t) -1) |
| return false; |
| |
| if (targetLength < nbytes) { |
| JS_ReportError(cx, "ArrayType has insufficient length"); |
| return false; |
| } |
| |
| char* charBuffer = static_cast<char*>(buffer); |
| ASSERT_OK(DeflateStringToUTF8Buffer(cx, sourceChars, sourceLength, |
| charBuffer, &nbytes)); |
| |
| if (targetLength > nbytes) |
| charBuffer[nbytes] = 0; |
| |
| break; |
| } |
| case TYPE_jschar: { |
| // Copy the string data, jschar for jschar, including the terminator |
| // if there's space. |
| if (targetLength < sourceLength) { |
| JS_ReportError(cx, "ArrayType has insufficient length"); |
| return false; |
| } |
| |
| memcpy(buffer, sourceChars, sourceLength * sizeof(jschar)); |
| if (targetLength > sourceLength) |
| static_cast<jschar*>(buffer)[sourceLength] = 0; |
| |
| break; |
| } |
| default: |
| return TypeError(cx, "array", val); |
| } |
| |
| } else if (!JSVAL_IS_PRIMITIVE(val) && JS_IsArrayObject(cx, valObj)) { |
| // Convert each element of the array by calling ImplicitConvert. |
| uint32_t sourceLength; |
| if (!JS_GetArrayLength(cx, valObj, &sourceLength) || |
| targetLength != size_t(sourceLength)) { |
| JS_ReportError(cx, "ArrayType length does not match source array length"); |
| return false; |
| } |
| |
| // Convert into an intermediate, in case of failure. |
| size_t elementSize = CType::GetSize(baseType); |
| size_t arraySize = elementSize * targetLength; |
| AutoPtr<char> intermediate(cx->pod_malloc<char>(arraySize)); |
| if (!intermediate) { |
| JS_ReportAllocationOverflow(cx); |
| return false; |
| } |
| |
| for (uint32_t i = 0; i < sourceLength; ++i) { |
| RootedValue item(cx); |
| if (!JS_GetElement(cx, valObj, i, item.address())) |
| return false; |
| |
| char* data = intermediate.get() + elementSize * i; |
| if (!ImplicitConvert(cx, item, baseType, data, false, NULL)) |
| return false; |
| } |
| |
| memcpy(buffer, intermediate.get(), arraySize); |
| |
| } else if (!JSVAL_IS_PRIMITIVE(val) && |
| JS_IsArrayBufferObject(valObj)) { |
| // Check that array is consistent with type, then |
| // copy the array. |
| uint32_t sourceLength = JS_GetArrayBufferByteLength(valObj); |
| size_t elementSize = CType::GetSize(baseType); |
| size_t arraySize = elementSize * targetLength; |
| if (arraySize != size_t(sourceLength)) { |
| JS_ReportError(cx, "ArrayType length does not match source ArrayBuffer length"); |
| return false; |
| } |
| memcpy(buffer, JS_GetArrayBufferData(valObj), sourceLength); |
| break; |
| } else if (!JSVAL_IS_PRIMITIVE(val) && |
| JS_IsTypedArrayObject(valObj)) { |
| // Check that array is consistent with type, then |
| // copy the array. |
| if(!CanConvertTypedArrayItemTo(baseType, valObj, cx)) { |
| return TypeError(cx, "typed array with the appropriate type", val); |
| } |
| |
| uint32_t sourceLength = JS_GetTypedArrayByteLength(valObj); |
| size_t elementSize = CType::GetSize(baseType); |
| size_t arraySize = elementSize * targetLength; |
| if (arraySize != size_t(sourceLength)) { |
| JS_ReportError(cx, "typed array length does not match source TypedArray length"); |
| return false; |
| } |
| memcpy(buffer, JS_GetArrayBufferViewData(valObj), sourceLength); |
| break; |
| } else { |
| // Don't implicitly convert to string. Users can implicitly convert |
| // with `String(x)` or `""+x`. |
| return TypeError(cx, "array", val); |
| } |
| break; |
| } |
| case TYPE_struct: { |
| if (!JSVAL_IS_PRIMITIVE(val) && !sourceData) { |
| // Enumerate the properties of the object; if they match the struct |
| // specification, convert the fields. |
| RootedObject iter(cx, JS_NewPropertyIterator(cx, valObj)); |
| if (!iter) |
| return false; |
| |
| // Convert into an intermediate, in case of failure. |
| size_t structSize = CType::GetSize(targetType); |
| AutoPtr<char> intermediate(cx->pod_malloc<char>(structSize)); |
| if (!intermediate) { |
| JS_ReportAllocationOverflow(cx); |
| return false; |
| } |
| |
| RootedId id(cx); |
| size_t i = 0; |
| while (1) { |
| if (!JS_NextProperty(cx, iter, id.address())) |
| return false; |
| if (JSID_IS_VOID(id)) |
| break; |
| |
| if (!JSID_IS_STRING(id)) { |
| JS_ReportError(cx, "property name is not a string"); |
| return false; |
| } |
| |
| JSFlatString *name = JSID_TO_FLAT_STRING(id); |
| const FieldInfo* field = StructType::LookupField(cx, targetType, name); |
| if (!field) |
| return false; |
| |
| RootedValue prop(cx); |
| if (!JS_GetPropertyById(cx, valObj, id, prop.address())) |
| return false; |
| |
| // Convert the field via ImplicitConvert(). |
| char* fieldData = intermediate.get() + field->mOffset; |
| if (!ImplicitConvert(cx, prop, field->mType, fieldData, false, NULL)) |
| return false; |
| |
| ++i; |
| } |
| |
| const FieldInfoHash* fields = StructType::GetFieldInfo(targetType); |
| if (i != fields->count()) { |
| JS_ReportError(cx, "missing fields"); |
| return false; |
| } |
| |
| memcpy(buffer, intermediate.get(), structSize); |
| break; |
| } |
| |
| return TypeError(cx, "struct", val); |
| } |
| case TYPE_void_t: |
| case TYPE_function: |
| JS_NOT_REACHED("invalid type"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Convert jsval 'val' to a C binary representation of CType 'targetType', |
| // storing the result in 'buffer'. This function is more forceful than |
| // ImplicitConvert. |
| JSBool |
| ExplicitConvert(JSContext* cx, HandleValue val, HandleObject targetType, void* buffer) |
| { |
| // If ImplicitConvert succeeds, use that result. |
| if (ImplicitConvert(cx, val, targetType, buffer, false, NULL)) |
| return true; |
| |
| // If ImplicitConvert failed, and there is no pending exception, then assume |
| // hard failure (out of memory, or some other similarly serious condition). |
| // We store any pending exception in case we need to re-throw it. |
| RootedValue ex(cx); |
| if (!JS_GetPendingException(cx, ex.address())) |
| return false; |
| |
| // Otherwise, assume soft failure. Clear the pending exception so that we |
| // can throw a different one as required. |
| JS_ClearPendingException(cx); |
| |
| TypeCode type = CType::GetTypeCode(targetType); |
| |
| switch (type) { |
| case TYPE_bool: { |
| // Convert according to the ECMAScript ToBoolean() function. |
| JSBool result; |
| ASSERT_OK(JS_ValueToBoolean(cx, val, &result)); |
| *static_cast<bool*>(buffer) = result != JS_FALSE; |
| break; |
| } |
| #define DEFINE_INT_TYPE(name, type, ffiType) \ |
| case TYPE_##name: { \ |
| /* Convert numeric values with a C-style cast, and */ \ |
| /* allow conversion from a base-10 or base-16 string. */ \ |
| type result; \ |
| if (!jsvalToIntegerExplicit(val, &result) && \ |
| (!JSVAL_IS_STRING(val) || \ |
| !StringToInteger(cx, JSVAL_TO_STRING(val), &result))) \ |
| return TypeError(cx, #name, val); \ |
| *static_cast<type*>(buffer) = result; \ |
| break; \ |
| } |
| #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) |
| #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) |
| #define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_CHAR_TYPE(x, y, z) |
| #include "typedefs.h" |
| case TYPE_pointer: { |
| // Convert a number, Int64 object, or UInt64 object to a pointer. |
| uintptr_t result; |
| if (!jsvalToPtrExplicit(cx, val, &result)) |
| return TypeError(cx, "pointer", val); |
| *static_cast<uintptr_t*>(buffer) = result; |
| break; |
| } |
| case TYPE_float32_t: |
| case TYPE_float64_t: |
| case TYPE_float: |
| case TYPE_double: |
| case TYPE_array: |
| case TYPE_struct: |
| // ImplicitConvert is sufficient. Re-throw the exception it generated. |
| JS_SetPendingException(cx, ex); |
| return false; |
| case TYPE_void_t: |
| case TYPE_function: |
| JS_NOT_REACHED("invalid type"); |
| return false; |
| } |
| return true; |
| } |
| |
| // Given a CType 'typeObj', generate a string describing the C type declaration |
| // corresponding to 'typeObj'. For instance, the CType constructed from |
| // 'ctypes.int32_t.ptr.array(4).ptr.ptr' will result in the type string |
| // 'int32_t*(**)[4]'. |
| static JSString* |
| BuildTypeName(JSContext* cx, JSObject* typeObj_) |
| { |
| AutoString result; |
| RootedObject typeObj(cx, typeObj_); |
| |
| // Walk the hierarchy of types, outermost to innermost, building up the type |
| // string. This consists of the base type, which goes on the left. |
| // Derived type modifiers (* and []) build from the inside outward, with |
| // pointers on the left and arrays on the right. An excellent description |
| // of the rules for building C type declarations can be found at: |
| // http://unixwiz.net/techtips/reading-cdecl.html |
| TypeCode prevGrouping = CType::GetTypeCode(typeObj), currentGrouping; |
| while (1) { |
| currentGrouping = CType::GetTypeCode(typeObj); |
| switch (currentGrouping) { |
| case TYPE_pointer: { |
| // Pointer types go on the left. |
| PrependString(result, "*"); |
| |
| typeObj = PointerType::GetBaseType(typeObj); |
| prevGrouping = currentGrouping; |
| continue; |
| } |
| case TYPE_array: { |
| if (prevGrouping == TYPE_pointer) { |
| // Outer type is pointer, inner type is array. Grouping is required. |
| PrependString(result, "("); |
| AppendString(result, ")"); |
| } |
| |
| // Array types go on the right. |
| AppendString(result, "["); |
| size_t length; |
| if (ArrayType::GetSafeLength(typeObj, &length)) |
| IntegerToString(length, 10, result); |
| |
| AppendString(result, "]"); |
| |
| typeObj = ArrayType::GetBaseType(typeObj); |
| prevGrouping = currentGrouping; |
| continue; |
| } |
| case TYPE_function: { |
| FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj); |
| |
| // Add in the calling convention, if it's not cdecl. |
| // There's no trailing or leading space needed here, as none of the |
| // modifiers can produce a string beginning with an identifier --- |
| // except for TYPE_function itself, which is fine because functions |
| // can't return functions. |
| ABICode abi = GetABICode(fninfo->mABI); |
| if (abi == ABI_STDCALL) |
| PrependString(result, "__stdcall"); |
| else if (abi == ABI_WINAPI) |
| PrependString(result, "WINAPI"); |
| |
| // Function application binds more tightly than dereferencing, so |
| // wrap pointer types in parens. Functions can't return functions |
| // (only pointers to them), and arrays can't hold functions |
| // (similarly), so we don't need to address those cases. |
| if (prevGrouping == TYPE_pointer) { |
| PrependString(result, "("); |
| AppendString(result, ")"); |
| } |
| |
| // Argument list goes on the right. |
| AppendString(result, "("); |
| for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) { |
| RootedObject argType(cx, fninfo->mArgTypes[i]); |
| JSString* argName = CType::GetName(cx, argType); |
| AppendString(result, argName); |
| if (i != fninfo->mArgTypes.length() - 1 || |
| fninfo->mIsVariadic) |
| AppendString(result, ", "); |
| } |
| if (fninfo->mIsVariadic) |
| AppendString(result, "..."); |
| AppendString(result, ")"); |
| |
| // Set 'typeObj' to the return type, and let the loop process it. |
| // 'prevGrouping' doesn't matter here, because functions cannot return |
| // arrays -- thus the parenthetical rules don't get tickled. |
| typeObj = fninfo->mReturnType; |
| continue; |
| } |
| default: |
| // Either a basic or struct type. Use the type's name as the base type. |
| break; |
| } |
| break; |
| } |
| |
| // If prepending the base type name directly would splice two |
| // identifiers, insert a space. |
| if (('a' <= result[0] && result[0] <= 'z') || |
| ('A' <= result[0] && result[0] <= 'Z') || |
| (result[0] == '_')) |
| PrependString(result, " "); |
| |
| // Stick the base type and derived type parts together. |
| JSString* baseName = CType::GetName(cx, typeObj); |
| PrependString(result, baseName); |
| return NewUCString(cx, result); |
| } |
| |
| // Given a CType 'typeObj', generate a string 'result' such that 'eval(result)' |
| // would construct the same CType. If 'makeShort' is true, assume that any |
| // StructType 't' is bound to an in-scope variable of name 't.name', and use |
| // that variable in place of generating a string to construct the type 't'. |
| // (This means the type comparison function CType::TypesEqual will return true |
| // when comparing the input and output of BuildTypeSource, since struct |
| // equality is determined by strict JSObject pointer equality.) |
| static void |
| BuildTypeSource(JSContext* cx, |
| JSObject* typeObj_, |
| bool makeShort, |
| AutoString& result) |
| { |
| RootedObject typeObj(cx, typeObj_); |
| |
| // Walk the types, building up the toSource() string. |
| switch (CType::GetTypeCode(typeObj)) { |
| case TYPE_void_t: |
| #define DEFINE_TYPE(name, type, ffiType) \ |
| case TYPE_##name: |
| #include "typedefs.h" |
| { |
| AppendString(result, "ctypes."); |
| JSString* nameStr = CType::GetName(cx, typeObj); |
| AppendString(result, nameStr); |
| break; |
| } |
| case TYPE_pointer: { |
| RootedObject baseType(cx, PointerType::GetBaseType(typeObj)); |
| |
| // Specialcase ctypes.voidptr_t. |
| if (CType::GetTypeCode(baseType) == TYPE_void_t) { |
| AppendString(result, "ctypes.voidptr_t"); |
| break; |
| } |
| |
| // Recursively build the source string, and append '.ptr'. |
| BuildTypeSource(cx, baseType, makeShort, result); |
| AppendString(result, ".ptr"); |
| break; |
| } |
| case TYPE_function: { |
| FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj); |
| |
| AppendString(result, "ctypes.FunctionType("); |
| |
| switch (GetABICode(fninfo->mABI)) { |
| case ABI_DEFAULT: |
| AppendString(result, "ctypes.default_abi, "); |
| break; |
| case ABI_STDCALL: |
| AppendString(result, "ctypes.stdcall_abi, "); |
| break; |
| case ABI_WINAPI: |
| AppendString(result, "ctypes.winapi_abi, "); |
| break; |
| case INVALID_ABI: |
| JS_NOT_REACHED("invalid abi"); |
| break; |
| } |
| |
| // Recursively build the source string describing the function return and |
| // argument types. |
| BuildTypeSource(cx, fninfo->mReturnType, true, result); |
| |
| if (fninfo->mArgTypes.length() > 0) { |
| AppendString(result, ", ["); |
| for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) { |
| BuildTypeSource(cx, fninfo->mArgTypes[i], true, result); |
| if (i != fninfo->mArgTypes.length() - 1 || |
| fninfo->mIsVariadic) |
| AppendString(result, ", "); |
| } |
| if (fninfo->mIsVariadic) |
| AppendString(result, "\"...\""); |
| AppendString(result, "]"); |
| } |
| |
| AppendString(result, ")"); |
| break; |
| } |
| case TYPE_array: { |
| // Recursively build the source string, and append '.array(n)', |
| // where n is the array length, or the empty string if the array length |
| // is undefined. |
|