diff --git a/js/ctypes/CTypes.cpp b/js/ctypes/CTypes.cpp
new file mode 100644
index 000000000000..7fc15a5224db
--- /dev/null
+++ b/js/ctypes/CTypes.cpp
@@ -0,0 +1,4284 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is js-ctypes.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation .
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Dan Witte
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "CTypes.h"
+#include "nsAutoPtr.h"
+#include "nsUTF8Utils.h"
+#include "nsCRTGlue.h"
+#include "prlog.h"
+#include "prdtoa.h"
+#include "jscntxt.h"
+#include "jsnum.h"
+
+namespace mozilla {
+namespace ctypes {
+
+/*******************************************************************************
+** JSClass definitions and initialization functions
+*******************************************************************************/
+
+static JSClass sCABIClass = {
+ "CABI",
+ JSCLASS_HAS_RESERVED_SLOTS(CABI_SLOTS),
+ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+ JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+// Class representing ctypes.CType.prototype.
+// This exists to provide three reserved slots for stashing
+// ctypes.{Pointer,Array,Struct}Type.prototype.
+static JSClass sCTypeProto = {
+ "CType",
+ JSCLASS_HAS_RESERVED_SLOTS(CTYPEPROTO_SLOTS),
+ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+ JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static JSClass sCTypeClass = {
+ "CType",
+ JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS),
+ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+ JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CType::Finalize,
+ NULL, NULL, CType::ConstructData, CType::ConstructData, NULL, NULL, NULL, NULL
+};
+
+static JSClass sCDataClass = {
+ "CData",
+ JSCLASS_HAS_RESERVED_SLOTS(CDATA_SLOTS),
+ JS_PropertyStub, JS_PropertyStub, ArrayType::Getter, ArrayType::Setter,
+ JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CData::Finalize,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+#define CTYPESFN_FLAGS \
+ (JSFUN_FAST_NATIVE | JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
+
+static JSFunctionSpec sCTypeFunctions[] = {
+ JS_FN("array", CType::Array, 0, CTYPESFN_FLAGS),
+ JS_FN("toString", CType::ToString, 0, CTYPESFN_FLAGS),
+ JS_FN("toSource", CType::ToSource, 0, CTYPESFN_FLAGS),
+ JS_FS_END
+};
+
+static JSFunctionSpec sCDataFunctions[] = {
+ JS_FN("address", CData::Address, 0, CTYPESFN_FLAGS),
+ JS_FN("readString", CData::ReadString, 0, CTYPESFN_FLAGS),
+ JS_FN("toSource", CData::ToSource, 0, CTYPESFN_FLAGS),
+ JS_FN("toString", CData::ToSource, 0, CTYPESFN_FLAGS),
+ JS_FS_END
+};
+
+static JSFunctionSpec sPointerFunction =
+ JS_FN("PointerType", PointerType::Create, 1, CTYPESFN_FLAGS);
+
+static JSFunctionSpec sArrayFunction =
+ JS_FN("ArrayType", ArrayType::Create, 1, CTYPESFN_FLAGS);
+
+static JSFunctionSpec sStructFunction =
+ JS_FN("StructType", StructType::Create, 2, CTYPESFN_FLAGS);
+
+static JSFunctionSpec sArrayInstanceFunctions[] = {
+ JS_FN("addressOfElement", ArrayType::AddressOfElement, 1, CTYPESFN_FLAGS),
+ JS_FS_END
+};
+
+static JSFunctionSpec sStructInstanceFunctions[] = {
+ JS_FN("addressOfField", StructType::AddressOfField, 1, CTYPESFN_FLAGS),
+ JS_FS_END
+};
+
+static JSClass sInt64Proto = {
+ "Int64",
+ 0,
+ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+ JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+static JSClass sUInt64Proto = {
+ "UInt64",
+ 0,
+ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+ JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+static JSClass sInt64Class = {
+ "Int64",
+ JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS),
+ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+ JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Int64Base::Finalize,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+static JSClass sUInt64Class = {
+ "UInt64",
+ JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS),
+ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+ JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Int64Base::Finalize,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+static 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 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 JSFunctionSpec sInt64Functions[] = {
+ JS_FN("toString", Int64::ToString, 0, CTYPESFN_FLAGS),
+ JS_FN("toSource", Int64::ToSource, 0, CTYPESFN_FLAGS),
+ JS_FS_END
+};
+
+static JSFunctionSpec sUInt64Functions[] = {
+ JS_FN("toString", UInt64::ToString, 0, CTYPESFN_FLAGS),
+ JS_FN("toSource", UInt64::ToSource, 0, CTYPESFN_FLAGS),
+ JS_FS_END
+};
+
+ABICode
+GetABICode(JSContext* cx, JSObject* obj)
+{
+ // make sure we have an object representing a CABI class,
+ // and extract the enumerated class type from the reserved slot.
+ if (JS_GET_CLASS(cx, obj) != &sCABIClass)
+ return INVALID_ABI;
+
+ jsval result;
+ JS_GetReservedSlot(cx, obj, SLOT_ABICODE, &result);
+
+ 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 uintN errorNumber)
+{
+ if (0 < errorNumber && errorNumber < CTYPESERR_LIMIT)
+ return &ErrorFormatString[errorNumber];
+ return NULL;
+}
+
+static const char*
+ToSource(JSContext* cx, jsval vp)
+{
+ JSString* str = JS_ValueToSource(cx, vp);
+ if (str)
+ return JS_GetStringBytesZ(cx, str);
+
+ JS_ClearPendingException(cx);
+ return "<>";
+}
+
+bool
+TypeError(JSContext* cx, const char* expected, jsval actual)
+{
+ const char* src = ToSource(cx, actual);
+ JS_ReportErrorNumber(cx, GetErrorMessage, NULL,
+ CTYPESMSG_TYPE_ERROR, expected, src);
+ return false;
+}
+
+static bool
+DefineABIConstant(JSContext* cx,
+ JSObject* parent,
+ const char* name,
+ ABICode code)
+{
+ JSObject* obj = JS_DefineObject(cx, parent, name, &sCABIClass, NULL,
+ JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
+ if (!obj)
+ return false;
+ if (!JS_SetReservedSlot(cx, obj, SLOT_ABICODE, INT_TO_JSVAL(code)))
+ return false;
+ return JS_SealObject(cx, obj, JS_FALSE) != JS_FALSE;
+}
+
+static JSObject*
+InitSpecialType(JSContext* cx,
+ JSObject* parent,
+ JSObject* CTypeProto,
+ JSFunctionSpec spec)
+{
+ JSFunction* fun = JS_DefineFunction(cx, parent, spec.name, spec.call,
+ spec.nargs, spec.flags);
+ if (!fun)
+ return NULL;
+
+ JSObject* obj = JS_GetFunctionObject(fun);
+ if (!obj)
+ return NULL;
+
+ // Set up the .prototype and .prototype.constructor properties.
+ JSObject* prototype = JS_NewObject(cx, NULL, CTypeProto, obj);
+ if (!prototype)
+ return NULL;
+
+ if (!JS_DefineProperty(cx, obj, "prototype", OBJECT_TO_JSVAL(prototype),
+ NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
+ return NULL;
+
+ if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(obj),
+ NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
+ return NULL;
+
+ if (!JS_SealObject(cx, obj, JS_FALSE) ||
+ !JS_SealObject(cx, prototype, JS_FALSE))
+ return NULL;
+
+ return prototype;
+}
+
+JSObject*
+InitInt64Class(JSContext* cx,
+ JSObject* parent,
+ JSClass* clasp,
+ JSNative construct,
+ JSFunctionSpec* fs,
+ JSFunctionSpec* static_fs)
+{
+ // Init type class and constructor
+ JSObject* prototype = JS_InitClass(cx, parent, NULL, clasp, construct,
+ 0, NULL, fs, NULL, static_fs);
+ if (!prototype)
+ return NULL;
+
+ // Set up ctypes.{Int64,UInt64}.prototype.__proto__ === Function.prototype.
+ // (This is the same as ctypes.{Int64,UInt64}.prototype.constructor.__proto__.)
+ JSObject* ctor = JS_GetConstructor(cx, prototype);
+ if (!ctor)
+ return NULL;
+ if (!JS_SealObject(cx, ctor, JS_FALSE))
+ return NULL;
+ JSObject* proto = JS_GetPrototype(cx, ctor);
+ if (!proto)
+ return NULL;
+ if (!JS_SetPrototype(cx, prototype, proto))
+ return NULL;
+
+ // Stash ctypes.{Int64,UInt64}.prototype on a reserved slot of the 'join'
+ // function.
+ jsval join;
+ JS_GetProperty(cx, ctor, "join", &join);
+ if (!JS_SetReservedSlot(cx, JSVAL_TO_OBJECT(join), SLOT_FN_INT64PROTO,
+ OBJECT_TO_JSVAL(prototype)))
+ return NULL;
+
+ if (!JS_SealObject(cx, prototype, JS_FALSE))
+ return NULL;
+
+ return prototype;
+}
+
+bool
+InitTypeClasses(JSContext* cx, JSObject* 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 === ctypes.CType.__proto__
+ // * 'constructor' property === ctypes.CType
+ JSObject* CTypeProto = JS_InitClass(cx, parent, NULL, &sCTypeProto,
+ CType::ConstructAbstract, 0, NULL, sCTypeFunctions, NULL, NULL);
+ if (!CTypeProto)
+ return false;
+
+ // Set up CTypeProto.__proto__ === Function.prototype.
+ // (This is the same as CTypeProto.constructor.__proto__.)
+ JSObject* ctor = JS_GetConstructor(cx, CTypeProto);
+ if (!ctor)
+ return false;
+ if (!JS_SealObject(cx, ctor, JS_FALSE))
+ return false;
+ JSObject* proto = JS_GetPrototype(cx, ctor);
+ if (!proto)
+ return false;
+ if (!JS_SetPrototype(cx, CTypeProto, proto))
+ return false;
+ if (!JS_SealObject(cx, CTypeProto, JS_FALSE))
+ return false;
+
+ // Attach objects representing ABI constants.
+ if (!DefineABIConstant(cx, parent, "default_abi", ABI_DEFAULT) ||
+ !DefineABIConstant(cx, parent, "stdcall_abi", ABI_STDCALL))
+ 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 [[Object]]
+ // * __proto__ === Object.prototype
+ // * 'constructor' property === 't'
+ JSObject* typeObj;
+#define DEFINE_TYPE(name, type, ffiType) \
+ typeObj = CType::DefineBuiltin(cx, parent, #name, CTypeProto, #name, \
+ TYPE_##name, INT_TO_JSVAL(sizeof(type)), \
+ INT_TO_JSVAL(ffiType.alignment), &ffiType); \
+ if (!typeObj) \
+ return false;
+#include "typedefs.h"
+
+ // Create and attach the special class constructors:
+ // ctypes.PointerType, ctypes.ArrayType, and ctypes.StructType.
+ // Each of these has, respectively:
+ // * Class [[Function]]
+ // * __proto__ === Function.prototype
+ // * A constructor that creates a user-defined type.
+ // * 'prototype' property:
+ // * Class [[Object]]
+ // * __proto__ === ctypes.CType.prototype
+ // * 'constructor' property === ctypes.{Pointer,Array,Struct}Type
+ JSObject* pointerProto = InitSpecialType(cx, parent, CTypeProto, sPointerFunction);
+ JSObject* arrayProto = InitSpecialType(cx, parent, CTypeProto, sArrayFunction);
+ JSObject* structProto = InitSpecialType(cx, parent, CTypeProto, sStructFunction);
+
+ // 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}]]
+ // * __proto__ === Function.prototype === ctypes.{Int64,UInt64}.__proto__
+ // * 'constructor' property === ctypes.{Int64,UInt64}
+ JSObject* Int64Proto = InitInt64Class(cx, parent, &sInt64Proto,
+ Int64::Construct, sInt64Functions, sInt64StaticFunctions);
+ if (!Int64Proto)
+ return false;
+
+ JSObject* UInt64Proto = InitInt64Class(cx, parent, &sUInt64Proto,
+ UInt64::Construct, sUInt64Functions, sUInt64StaticFunctions);
+ if (!UInt64Proto)
+ return false;
+
+ // Attach the five prototypes just created to ctypes.CType.prototype,
+ // so we can access them when constructing instances of those types. An
+ // instance 't' of a ctypes.{Pointer,Array,Struct}Type will have, resp.:
+ // * Class [[CType]]
+ // * __proto__ === ctypes.{Pointer,Array,Struct}Type.prototype
+ // * A constructor which creates and returns a CData object, containing
+ // binary data of the given type.
+ // * 'prototype' property:
+ // * Class [[Object]]
+ // * __proto__ === Object.prototype
+ // * 'constructor' property === t
+ if (!JS_SetReservedSlot(cx, CTypeProto, SLOT_POINTERPROTO, OBJECT_TO_JSVAL(pointerProto)) ||
+ !JS_SetReservedSlot(cx, CTypeProto, SLOT_ARRAYPROTO, OBJECT_TO_JSVAL(arrayProto)) ||
+ !JS_SetReservedSlot(cx, CTypeProto, SLOT_STRUCTPROTO, OBJECT_TO_JSVAL(structProto)) ||
+ !JS_SetReservedSlot(cx, CTypeProto, SLOT_INT64PROTO, OBJECT_TO_JSVAL(Int64Proto)) ||
+ !JS_SetReservedSlot(cx, CTypeProto, SLOT_UINT64PROTO, OBJECT_TO_JSVAL(UInt64Proto)))
+ return false;
+
+ // Create objects representing the special types void_t and voidptr_t.
+ typeObj = CType::DefineBuiltin(cx, parent, "void_t", CTypeProto, "void",
+ TYPE_void_t, JSVAL_VOID, JSVAL_VOID, &ffi_type_void);
+ if (!typeObj)
+ return false;
+
+ typeObj = PointerType::CreateInternal(cx, NULL, typeObj, NULL);
+ 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;
+}
+
+/*******************************************************************************
+** Type conversion functions
+*******************************************************************************/
+
+// Enforce some sanity checks on type widths.
+// 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.)
+PR_STATIC_ASSERT(sizeof(bool) == 1 || sizeof(bool) == 4);
+PR_STATIC_ASSERT(sizeof(char) == 1);
+PR_STATIC_ASSERT(sizeof(short) == 2);
+PR_STATIC_ASSERT(sizeof(int) == 4);
+PR_STATIC_ASSERT(sizeof(unsigned) == 4);
+PR_STATIC_ASSERT(sizeof(long) == 4 || sizeof(long) == 8);
+PR_STATIC_ASSERT(sizeof(long long) == 8);
+PR_STATIC_ASSERT(sizeof(size_t) == sizeof(uintptr_t));
+PR_STATIC_ASSERT(sizeof(float) == 4);
+
+template
+static JS_ALWAYS_INLINE IntegerType
+Convert(jsdouble d)
+{
+ return IntegerType(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<>
+JS_ALWAYS_INLINE PRUint64
+Convert(jsdouble d)
+{
+ return d > 0x7fffffffffffffffui64 ?
+ PRUint64(d - 0x8000000000000000ui64) + 0x8000000000000000ui64 :
+ PRUint64(d);
+}
+#endif
+
+template static JS_ALWAYS_INLINE bool IsUnsigned() { return (Type(0) - Type(1)) > Type(0); }
+
+template static JS_ALWAYS_INLINE bool IsDoublePrecision();
+template<> JS_ALWAYS_INLINE bool IsDoublePrecision () { return false; }
+template<> JS_ALWAYS_INLINE bool IsDoublePrecision() { return true; }
+
+// Implicitly convert val to bool, allowing JSBool, jsint, and jsdouble
+// 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)) {
+ jsint i = JSVAL_TO_INT(val);
+ *result = i != 0;
+ return i == 0 || i == 1;
+ }
+ if (JSVAL_IS_DOUBLE(val)) {
+ jsdouble d = *JSVAL_TO_DOUBLE(val);
+ *result = d != 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, jsint, jsdouble,
+// Int64, UInt64, and CData integer types 't' where all values of 't' are
+// representable by IntegerType.
+template
+static bool
+jsvalToInteger(JSContext* cx, jsval val, IntegerType* result)
+{
+ if (JSVAL_IS_INT(val)) {
+ jsint i = JSVAL_TO_INT(val);
+ *result = IntegerType(i);
+
+ // Make sure the integer fits in the alotted precision, and has the right
+ // sign.
+ if (IsUnsigned() && i < 0)
+ return false;
+ return jsint(*result) == i;
+ }
+ if (JSVAL_IS_DOUBLE(val)) {
+ jsdouble d = *JSVAL_TO_DOUBLE(val);
+ *result = Convert(d);
+
+ // Don't silently lose bits here -- check that val really is an
+ // integer value, and has the right sign.
+ if (IsUnsigned() && d < 0)
+ return false;
+ return jsdouble(*result) == d;
+ }
+ if (JSVAL_IS_OBJECT(val) && !JSVAL_IS_NULL(val)) {
+ JSObject* obj = JSVAL_TO_OBJECT(val);
+ if (CData::IsCData(cx, obj)) {
+ JSObject* typeObj = CData::GetCType(cx, obj);
+ void* data = CData::GetData(cx, 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(cx, typeObj)) {
+#define DEFINE_INT_TYPE(name, fromType, ffiType) \
+ case TYPE_##name: \
+ if (IsUnsigned() && IsUnsigned() && \
+ sizeof(IntegerType) < sizeof(fromType)) \
+ return false; \
+ if (!IsUnsigned() && !IsUnsigned() && \
+ sizeof(IntegerType) < sizeof(fromType)) \
+ return false; \
+ if (IsUnsigned() && !IsUnsigned() && \
+ sizeof(IntegerType) <= sizeof(fromType)) \
+ return false; \
+ if (!IsUnsigned() && IsUnsigned()) \
+ return false; \
+ *result = *static_cast(data); \
+ break;
+#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_array:
+ case TYPE_struct:
+ // Not a compatible number type.
+ return false;
+ }
+
+ return true;
+ }
+
+ if (Int64::IsInt64(cx, obj)) {
+ PRInt64 i = Int64Base::GetInt(cx, obj);
+ *result = IntegerType(i);
+
+ // Make sure the integer fits in IntegerType, and is nonnegative.
+ if (IsUnsigned() && i < 0)
+ return false;
+ return PRInt64(*result) == i;
+ }
+
+ if (UInt64::IsUInt64(cx, obj)) {
+ PRUint64 i = Int64Base::GetInt(cx, obj);
+ *result = IntegerType(i);
+
+ // Make sure the integer fits in IntegerType.
+ if (!IsUnsigned() && *result < 0)
+ return false;
+ return PRUint64(*result) == i;
+ }
+
+ 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 jsint, jsdouble,
+// Int64, UInt64, and CData numeric types 't' where all values of 't' are
+// representable by FloatType.
+template
+static bool
+jsvalToFloat(JSContext *cx, jsval val, FloatType* result)
+{
+ // 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_OBJECT(val) && !JSVAL_IS_NULL(val)) {
+ JSObject* obj = JSVAL_TO_OBJECT(val);
+ if (CData::IsCData(cx, obj)) {
+ JSObject* typeObj = CData::GetCType(cx, obj);
+ void* data = CData::GetData(cx, 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(cx, typeObj)) {
+#define DEFINE_FLOAT_TYPE(name, fromType, ffiType) \
+ case TYPE_##name: \
+ if (!IsDoublePrecision() && IsDoublePrecision()) \
+ return false; \
+ *result = *static_cast(data); \
+ break;
+#define DEFINE_INT_TYPE(name, fromType, ffiType) \
+ case TYPE_##name: \
+ if (sizeof(fromType) > 4) \
+ return false; \
+ if (sizeof(fromType) == 4 && !IsDoublePrecision()) \
+ return false; \
+ *result = *static_cast(data); \
+ break;
+#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_array:
+ case TYPE_struct:
+ // Not a compatible number type.
+ return false;
+ }
+
+ return true;
+ }
+
+ if (Int64::IsInt64(cx, obj)) {
+ PRInt64 i = Int64Base::GetInt(cx, obj);
+ *result = FloatType(i);
+ return true;
+ }
+
+ if (UInt64::IsUInt64(cx, obj)) {
+ PRUint64 i = Int64Base::GetInt(cx, obj);
+ *result = FloatType(i);
+ return true;
+ }
+ }
+ // 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;
+}
+
+// Implicitly convert val to IntegerType, allowing jsint, jsdouble,
+// Int64, UInt64, and optionally a decimal or hexadecimal string argument.
+// (This is used where a primitive is expected and we are converting to a
+// size_t value or constructing an Int64 or UInt64 object, and thus it is
+// implied that IntegerType is one of the wrapped integer types.)
+template
+static bool
+jsvalToBigInteger(JSContext* cx,
+ jsval val,
+ bool allowString,
+ IntegerType* result)
+{
+ if (JSVAL_IS_INT(val)) {
+ jsint i = JSVAL_TO_INT(val);
+ *result = IntegerType(i);
+
+ // Make sure the integer fits in the alotted precision, and has the right
+ // sign.
+ if (IsUnsigned() && i < 0)
+ return false;
+ return jsint(*result) == i;
+ }
+ if (JSVAL_IS_DOUBLE(val)) {
+ jsdouble d = *JSVAL_TO_DOUBLE(val);
+ *result = Convert(d);
+
+ // Don't silently lose bits here -- check that val really is an
+ // integer value, and has the right sign.
+ if (IsUnsigned() && d < 0)
+ return false;
+ return jsdouble(*result) == d;
+ }
+ 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_OBJECT(val) && !JSVAL_IS_NULL(val)) {
+ // Allow conversion from an Int64 or UInt64 object directly.
+ JSObject* obj = JSVAL_TO_OBJECT(val);
+ if (UInt64::IsUInt64(cx, obj)) {
+ PRUint64 i = Int64Base::GetInt(cx, obj);
+ *result = IntegerType(i);
+
+ // Make sure the integer fits in IntegerType.
+ if (!IsUnsigned() && *result < 0)
+ return false;
+ return PRUint64(*result) == i;
+ }
+
+ if (Int64::IsInt64(cx, obj)) {
+ PRInt64 i = Int64Base::GetInt(cx, obj);
+ *result = IntegerType(i);
+
+ // Make sure the integer is nonnegative and fits in IntegerType.
+ if (IsUnsigned() && i < 0)
+ return false;
+ return PRInt64(*result) == i;
+ }
+ }
+ return false;
+}
+
+// Implicitly convert val to a size value, where the size value is represented
+// by size_t but must also fit in a jsdouble.
+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 jsdouble.
+ return Convert(jsdouble(*result)) == *result;
+}
+
+// Implicitly convert a size value to a jsval, ensuring that the size_t value
+// fits in a jsdouble.
+static bool
+SizeTojsval(JSContext* cx, size_t size, jsval* result)
+{
+ if (Convert(jsdouble(size)) != size) {
+ JS_ReportError(cx, "size overflow");
+ return false;
+ }
+
+ return JS_NewNumberValue(cx, jsdouble(size), result) != JS_FALSE;
+}
+
+// Forcefully convert val to IntegerType when explicitly requested.
+template
+static bool
+jsvalToIntegerExplicit(JSContext* cx, jsval val, IntegerType* result)
+{
+ if (JSVAL_IS_DOUBLE(val)) {
+ // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
+ jsdouble d = *JSVAL_TO_DOUBLE(val);
+ *result = JSDOUBLE_IS_FINITE(d) ? IntegerType(d) : 0;
+ return true;
+ }
+ if (JSVAL_IS_OBJECT(val) && !JSVAL_IS_NULL(val)) {
+ // Convert Int64 and UInt64 values by C-style cast.
+ JSObject* obj = JSVAL_TO_OBJECT(val);
+ if (Int64::IsInt64(cx, obj)) {
+ PRInt64 i = Int64Base::GetInt(cx, obj);
+ *result = IntegerType(i);
+ return true;
+ }
+ if (UInt64::IsUInt64(cx, obj)) {
+ PRUint64 i = Int64Base::GetInt(cx, 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)) {
+ // jsint always fits in intptr_t. If the integer is negative, cast through
+ // an intptr_t intermediate to sign-extend.
+ jsint i = JSVAL_TO_INT(val);
+ *result = i < 0 ? uintptr_t(intptr_t(i)) : uintptr_t(i);
+ return true;
+ }
+ if (JSVAL_IS_DOUBLE(val)) {
+ jsdouble d = *JSVAL_TO_DOUBLE(val);
+ if (d < 0) {
+ // Cast through an intptr_t intermediate to sign-extend.
+ intptr_t i = Convert(d);
+ if (jsdouble(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(d);
+ return jsdouble(*result) == d;
+ }
+ if (JSVAL_IS_OBJECT(val) && !JSVAL_IS_NULL(val)) {
+ JSObject* obj = JSVAL_TO_OBJECT(val);
+ if (Int64::IsInt64(cx, obj)) {
+ PRInt64 i = Int64Base::GetInt(cx, obj);
+ if (i < 0) {
+ // Cast through an intptr_t intermediate to sign-extend.
+ if (PRInt64(intptr_t(i)) != i)
+ return false;
+
+ *result = uintptr_t(intptr_t(i));
+ return true;
+ }
+
+ // Make sure the integer fits in the alotted precision.
+ *result = uintptr_t(i);
+ return PRInt64(*result) == i;
+ }
+
+ if (UInt64::IsUInt64(cx, obj)) {
+ PRUint64 i = Int64Base::GetInt(cx, obj);
+
+ // Make sure the integer fits in the alotted precision.
+ *result = uintptr_t(i);
+ return PRUint64(*result) == i;
+ }
+ }
+ return false;
+}
+
+template
+nsCAutoString
+IntegerToString(IntegerType i, jsuint radix)
+{
+ // The buffer must be big enough for all the bits of IntegerType to fit,
+ // in base-2, including '-'.
+ char buffer[sizeof(IntegerType) * 8 + 1];
+ char *cp = buffer + sizeof(buffer);
+
+ // Build the string in reverse. We use multiplication and subtraction
+ // instead of modulus because that's much faster.
+ bool isNegative = !IsUnsigned() && i < 0;
+ 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);
+ return nsCAutoString(cp, buffer + sizeof(buffer) - cp);
+}
+
+template
+static bool
+StringToInteger(JSContext* cx, JSString* string, IntegerType* result)
+{
+ const char* cp = JS_GetStringBytesZ(cx, string);
+ if (!cp)
+ return false;
+
+ const char* end = cp + JS_GetStringLength(string);
+ if (cp == end)
+ return false;
+
+ IntegerType sign = 1;
+ if (cp[0] == '-') {
+ if (IsUnsigned())
+ 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) {
+ unsigned char 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;
+}
+
+static bool
+IsUTF16(const jschar* string, size_t length)
+{
+ PRBool error;
+ const PRUnichar* buffer = reinterpret_cast(string);
+ const PRUnichar* end = buffer + length;
+ while (buffer != end) {
+ UTF16CharEnumerator::NextChar(&buffer, end, &error);
+ if (error)
+ return false;
+ }
+
+ return true;
+}
+
+template
+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 the parent's buffer.
+// * If 'parentObj' is null, the new CData object will make an owning copy of
+// 'data'.
+bool
+ConvertToJS(JSContext* cx,
+ JSObject* typeObj,
+ JSObject* parentObj,
+ void* data,
+ bool wantPrimitive,
+ jsval* result)
+{
+ JS_ASSERT(!parentObj || CData::IsCData(cx, parentObj));
+
+ TypeCode typeCode = CType::GetTypeCode(cx, typeObj);
+
+ switch (typeCode) {
+ case TYPE_void_t:
+ *result = JSVAL_VOID;
+ break;
+ case TYPE_bool:
+ *result = *static_cast(data) ? JSVAL_TRUE : JSVAL_FALSE;
+ break;
+#define DEFINE_INT_TYPE(name, type, ffiType) \
+ case TYPE_##name: { \
+ type value = *static_cast(data); \
+ if (sizeof(type) < 4) \
+ *result = INT_TO_JSVAL(jsint(value)); \
+ else if (!JS_NewNumberValue(cx, jsdouble(value), result)) \
+ return false; \
+ 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. */ \
+ PRUint64 value; \
+ JSObject* proto; \
+ if (IsUnsigned()) { \
+ value = *static_cast(data); \
+ /* Get ctypes.UInt64.prototype from ctypes.CType.prototype. */ \
+ proto = CType::GetProtoFromType(cx, typeObj, SLOT_UINT64PROTO); \
+ } else { \
+ value = PRInt64(*static_cast(data)); \
+ /* Get ctypes.Int64.prototype from ctypes.CType.prototype. */ \
+ proto = CType::GetProtoFromType(cx, typeObj, SLOT_INT64PROTO); \
+ } \
+ \
+ JSObject* obj = Int64Base::Construct(cx, proto, value, IsUnsigned());\
+ if (!obj) \
+ return false; \
+ *result = OBJECT_TO_JSVAL(obj); \
+ break; \
+ }
+#define DEFINE_FLOAT_TYPE(name, type, ffiType) \
+ case TYPE_##name: { \
+ type value = *static_cast(data); \
+ if (!JS_NewNumberValue(cx, jsdouble(value), result)) \
+ return false; \
+ break; \
+ }
+#include "typedefs.h"
+ case TYPE_jschar: {
+ // Convert the jschar to a 1-character string.
+ JSString* str = JS_NewUCStringCopyN(cx, static_cast(data), 1);
+ if (!str)
+ return false;
+
+ *result = STRING_TO_JSVAL(str);
+ break;
+ }
+ case TYPE_char:
+ case TYPE_signed_char:
+ case TYPE_unsigned_char: {
+ // Promote the unsigned 1-byte character type to a jschar, regardless of
+ // encoding, and convert to a 1-character string.
+ // TODO: Check IsASCII(data)?
+ jschar promoted = *static_cast(data);
+ JSString* str = JS_NewUCStringCopyN(cx, &promoted, 1);
+ if (!str)
+ return false;
+
+ *result = STRING_TO_JSVAL(str);
+ break;
+ }
+ case TYPE_pointer: {
+ // 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 = PointerType::ConstructInternal(cx, typeObj, parentObj, data);
+ if (!obj)
+ return false;
+
+ *result = OBJECT_TO_JSVAL(obj);
+ break;
+ }
+ case TYPE_array: {
+ // 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 = ArrayType::ConstructInternal(cx, typeObj, parentObj, data);
+ if (!obj)
+ return false;
+
+ *result = OBJECT_TO_JSVAL(obj);
+ break;
+ }
+ 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 = StructType::ConstructInternal(cx, typeObj, parentObj, data);
+ if (!obj)
+ return false;
+
+ *result = OBJECT_TO_JSVAL(obj);
+ break;
+ }
+ }
+
+ return true;
+}
+
+// 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.
+bool
+ImplicitConvert(JSContext* cx,
+ jsval val,
+ JSObject* targetType,
+ void* buffer,
+ bool isArgument,
+ bool* freePointer)
+{
+ JS_ASSERT(CType::IsSizeDefined(cx, targetType));
+
+ // First, check if val is a CData object of type targetType.
+ JSObject* sourceData = NULL;
+ JSObject* sourceType = NULL;
+ if (JSVAL_IS_OBJECT(val) && !JSVAL_IS_NULL(val) &&
+ CData::IsCData(cx, JSVAL_TO_OBJECT(val))) {
+ sourceData = JSVAL_TO_OBJECT(val);
+ sourceType = CData::GetCType(cx, 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(cx, sourceType, targetType)) {
+ size_t size = CType::GetSize(cx, sourceType);
+ memmove(buffer, CData::GetData(cx, sourceData), size);
+ return true;
+ }
+ }
+
+ TypeCode targetCode = CType::GetTypeCode(cx, 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(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(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(buffer) = result; \
+ break; \
+ }
+#define DEFINE_CHAR_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'. */ \
+ /* TODO: Check IsASCII(chars) for 8-bit char types? */ \
+ type result; \
+ if (JSVAL_IS_STRING(val)) { \
+ JSString* str = JSVAL_TO_STRING(val); \
+ if (JS_GetStringLength(str) != 1) \
+ return TypeError(cx, #name, val); \
+ \
+ jschar c = *JS_GetStringCharsZ(cx, str); \
+ result = c; \
+ if (jschar(result) != c) \
+ return TypeError(cx, #name, val); \
+ \
+ } else if (!jsvalToInteger(cx, val, &result)) { \
+ return TypeError(cx, #name, val); \
+ } \
+ *static_cast(buffer) = result; \
+ break; \
+ }
+#include "typedefs.h"
+ case TYPE_pointer: {
+ JSObject* baseType = PointerType::GetBaseType(cx, targetType);
+
+ if (JSVAL_IS_NULL(val)) {
+ // Convert to a null pointer.
+ *static_cast(buffer) = NULL;
+ break;
+
+ } else if (sourceData) {
+ // First, determine if the targetType is ctypes.void_t.ptr.
+ TypeCode sourceCode = CType::GetTypeCode(cx, sourceType);
+ void* sourceBuffer = CData::GetData(cx, sourceData);
+ bool voidptrTarget = baseType &&
+ CType::GetTypeCode(cx, baseType) == TYPE_void_t;
+
+ if (sourceCode == TYPE_pointer && voidptrTarget) {
+ // Autoconvert if targetType is ctypes.voidptr_t.
+ *static_cast(buffer) = *static_cast(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(cx, sourceType);
+ if (voidptrTarget || CType::TypesEqual(cx, baseType, elementType)) {
+ *static_cast(buffer) = sourceBuffer;
+ break;
+ }
+ }
+
+ } else if (isArgument && baseType && 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);
+ const jschar* sourceChars = JS_GetStringCharsZ(cx, sourceString);
+ size_t sourceLength = JS_GetStringLength(sourceString);
+
+ switch (CType::GetTypeCode(cx, baseType)) {
+ case TYPE_char:
+ case TYPE_signed_char:
+ case TYPE_unsigned_char: {
+ // Convert from UTF-16 to UTF-8.
+ if (!IsUTF16(sourceChars, sourceLength))
+ return TypeError(cx, "UTF-16 string", val);
+
+ NS_ConvertUTF16toUTF8 converted(
+ reinterpret_cast(sourceChars), sourceLength);
+
+ char** charBuffer = static_cast(buffer);
+ *charBuffer = new char[converted.Length() + 1];
+ if (!*charBuffer) {
+ JS_ReportAllocationOverflow(cx);
+ return false;
+ }
+
+ *freePointer = true;
+ memcpy(*charBuffer, converted.get(), converted.Length() + 1);
+ 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(buffer);
+ *jscharBuffer = new jschar[sourceLength + 1];
+ if (!*jscharBuffer) {
+ JS_ReportAllocationOverflow(cx);
+ return false;
+ }
+
+ *freePointer = true;
+ memcpy(*jscharBuffer, sourceChars, (sourceLength + 1) * sizeof(jschar));
+ break;
+ }
+ default:
+ return TypeError(cx, "pointer", val);
+ }
+ break;
+ }
+ return TypeError(cx, "pointer", val);
+ }
+ case TYPE_array: {
+ JSObject* baseType = ArrayType::GetBaseType(cx, targetType);
+ size_t targetLength = ArrayType::GetLength(cx, targetType);
+
+ if (JSVAL_IS_STRING(val)) {
+ JSString* sourceString = JSVAL_TO_STRING(val);
+ const jschar* sourceChars = JS_GetStringCharsZ(cx, sourceString);
+ size_t sourceLength = JS_GetStringLength(sourceString);
+
+ switch (CType::GetTypeCode(cx, baseType)) {
+ case TYPE_char:
+ case TYPE_signed_char:
+ case TYPE_unsigned_char: {
+ // Convert from UTF-16 to UTF-8.
+ if (!IsUTF16(sourceChars, sourceLength))
+ return TypeError(cx, "UTF-16 string", val);
+
+ NS_ConvertUTF16toUTF8 converted(
+ reinterpret_cast(sourceChars), sourceLength);
+
+ if (targetLength < converted.Length()) {
+ JS_ReportError(cx, "ArrayType has insufficient length");
+ return false;
+ }
+
+ memcpy(buffer, converted.get(), converted.Length());
+ if (targetLength > converted.Length())
+ static_cast(buffer)[converted.Length()] = 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(buffer)[sourceLength] = 0;
+
+ break;
+ }
+ default:
+ return TypeError(cx, "array", val);
+ }
+
+ } else if (JSVAL_IS_OBJECT(val) && !JSVAL_IS_NULL(val) &&
+ JS_IsArrayObject(cx, JSVAL_TO_OBJECT(val))) {
+ // Convert each element of the array by calling ImplicitConvert.
+ JSObject* sourceArray = JSVAL_TO_OBJECT(val);
+ jsuint sourceLength;
+ if (!JS_GetArrayLength(cx, sourceArray, &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(cx, baseType);
+ size_t arraySize = elementSize * targetLength;
+ nsAutoPtr intermediate(new char[arraySize]);
+ if (!intermediate) {
+ JS_ReportAllocationOverflow(cx);
+ return false;
+ }
+
+ for (jsuint i = 0; i < sourceLength; ++i) {
+ jsval item;
+ if (!JS_GetElement(cx, sourceArray, i, &item))
+ return false;
+
+ char* data = intermediate + elementSize * i;
+ if (!ImplicitConvert(cx, item, baseType, data, false, NULL))
+ return false;
+ }
+
+ memcpy(buffer, intermediate, arraySize);
+
+ } 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_OBJECT(val) && !JSVAL_IS_NULL(val) && !sourceData) {
+ nsTArray* fields = StructType::GetFieldInfo(cx, targetType);
+
+ // Enumerate the properties of the object; if they match the struct
+ // specification, convert the fields.
+ JSObject* obj = JSVAL_TO_OBJECT(val);
+ JSObject* iter = JS_NewPropertyIterator(cx, obj);
+ if (!iter)
+ return false;
+ JSAutoTempValueRooter iterroot(cx, iter);
+
+ // Convert into an intermediate, in case of failure.
+ size_t structSize = CType::GetSize(cx, targetType);
+ nsAutoPtr intermediate(new char[structSize]);
+ if (!intermediate) {
+ JS_ReportAllocationOverflow(cx);
+ return false;
+ }
+
+ jsid id;
+ jsuint i = 0;
+ while (JS_NextProperty(cx, iter, &id) && !JSVAL_IS_VOID(id)) {
+ jsval fieldVal;
+ if (!JS_IdToValue(cx, id, &fieldVal) || !JSVAL_IS_STRING(fieldVal)) {
+ JS_ReportError(cx, "property name is not a string");
+ return false;
+ }
+ JSAutoTempValueRooter nameroot(cx, fieldVal);
+
+ const char* name = JS_GetStringBytesZ(cx, JSVAL_TO_STRING(fieldVal));
+ FieldInfo* field = StructType::LookupField(cx, targetType, fieldVal);
+ if (!field) {
+ JS_ReportError(cx, "couldn't locate field %s", name);
+ return false;
+ }
+
+ jsval prop;
+ if (!JS_GetProperty(cx, obj, name, &prop))
+ return false;
+
+ // Convert the field via ImplicitConvert().
+ char* fieldData = intermediate + field->mOffset;
+ if (!ImplicitConvert(cx, prop, field->mType, fieldData, false, NULL))
+ return false;
+
+ ++i;
+ }
+
+ if (i != fields->Length()) {
+ JS_ReportError(cx, "missing fields");
+ return false;
+ }
+
+ memcpy(buffer, intermediate, structSize);
+ break;
+ }
+
+ return TypeError(cx, "struct", val);
+ }
+ case TYPE_void_t:
+ 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.
+bool
+ExplicitConvert(JSContext* cx, jsval val, JSObject* 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.
+ jsval ex;
+ if (!JS_GetPendingException(cx, &ex))
+ return false;
+
+ // Otherwise, assume soft failure. Clear the pending exception so that we
+ // can throw a different one as required.
+ JSAutoTempValueRooter root(cx, ex);
+ JS_ClearPendingException(cx);
+
+ TypeCode type = CType::GetTypeCode(cx, targetType);
+
+ switch (type) {
+ case TYPE_bool: {
+ // Convert according to the ECMAScript ToBoolean() function.
+ JSBool result;
+ JS_ValueToBoolean(cx, val, &result);
+ *static_cast(buffer) = result != JS_FALSE;
+ break;
+ }
+#define DEFINE_INT_TYPE(name, type, ffiType) \
+ case TYPE_##name: { \
+ /* Convert numeric values with a C-style cast. */ \
+ type result; \
+ if (!jsvalToIntegerExplicit(cx, val, &result)) \
+ return TypeError(cx, #name, val); \
+ *static_cast(buffer) = result; \
+ break; \
+ }
+#define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
+#define DEFINE_WRAPPED_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(cx, val, &result) && \
+ (!JSVAL_IS_STRING(val) || \
+ !StringToInteger(cx, JSVAL_TO_STRING(val), &result))) \
+ return TypeError(cx, #name, val); \
+ *static_cast(buffer) = result; \
+ break; \
+ }
+#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(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:
+ 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 nsCAutoString
+BuildTypeName(JSContext* cx, JSObject* 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
+ nsCAutoString result;
+ JSObject* currentType = typeObj;
+ JSObject* nextType;
+ TypeCode prevGrouping = CType::GetTypeCode(cx, currentType), currentGrouping;
+ while (1) {
+ currentGrouping = CType::GetTypeCode(cx, currentType);
+ switch (currentGrouping) {
+ case TYPE_pointer: {
+ nextType = PointerType::GetBaseType(cx, currentType);
+ if (!nextType) {
+ // Opaque pointer type. Use the type's name as the base type.
+ break;
+ }
+
+ // Pointer types go on the left.
+ result.Insert('*', 0);
+
+ currentType = nextType;
+ prevGrouping = currentGrouping;
+ continue;
+ }
+ case TYPE_array: {
+ if (prevGrouping == TYPE_pointer) {
+ // Outer type is pointer, inner type is array. Grouping is required.
+ result.Insert('(', 0);
+ result.Append(')');
+ }
+
+ // Array types go on the right.
+ result.Append('[');
+ size_t length;
+ if (ArrayType::GetSafeLength(cx, currentType, &length)) {
+ result.Append(IntegerToString(length, 10));
+ }
+ result.Append(']');
+
+ currentType = ArrayType::GetBaseType(cx, currentType);
+ prevGrouping = currentGrouping;
+ continue;
+ }
+ default:
+ // Either a basic or struct type. Use the type's name as the base type.
+ break;
+ }
+ break;
+ }
+
+ // Stick the base type and derived type parts together.
+ JSString* baseName = CType::GetName(cx, currentType);
+ result.Insert(JS_GetStringBytesZ(cx, baseName), 0);
+ return 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 nsCAutoString
+BuildTypeSource(JSContext* cx, JSObject* typeObj, bool makeShort)
+{
+ // Walk the types, building up the toSource() string.
+ nsCAutoString result;
+ switch (CType::GetTypeCode(cx, typeObj)) {
+ case TYPE_void_t:
+ result.Append("ctypes.void_t");
+ break;
+#define DEFINE_TYPE(name, type, ffiType) \
+ case TYPE_##name: \
+ result.Append("ctypes." #name); \
+ break;
+#include "typedefs.h"
+ case TYPE_pointer: {
+ JSObject* baseType = PointerType::GetBaseType(cx, typeObj);
+ if (!baseType) {
+ // Opaque pointer type. Use the type's name.
+ JSString* baseName = CType::GetName(cx, typeObj);
+ result.Append("ctypes.PointerType(\"");
+ result.Append(JS_GetStringBytesZ(cx, baseName));
+ result.Append('"');
+ break;
+ }
+
+ // Specialcase ctypes.voidptr_t.
+ if (CType::GetTypeCode(cx, baseType) == TYPE_void_t) {
+ result.Append("ctypes.voidptr_t");
+ break;
+ }
+
+ // Recursively build the source string, and append '.ptr'.
+ result.Append(BuildTypeSource(cx, baseType, makeShort));
+ result.Append(".ptr");
+ 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.
+ JSObject* baseType = ArrayType::GetBaseType(cx, typeObj);
+ result.Append(BuildTypeSource(cx, baseType, makeShort));
+ result.Append(".array(");
+
+ size_t length;
+ if (ArrayType::GetSafeLength(cx, typeObj, &length))
+ result.Append(IntegerToString(length, 10));
+
+ result.Append(')');
+ break;
+ }
+ case TYPE_struct: {
+ JSString* name = CType::GetName(cx, typeObj);
+
+ if (makeShort) {
+ // Shorten the type declaration by assuming that StructType 't' is bound
+ // to an in-scope variable of name 't.name'.
+ result.Append(JS_GetStringBytesZ(cx, name));
+ break;
+ }
+
+ // Write the full struct declaration.
+ result.Append("ctypes.StructType(\"");
+ result.Append(JS_GetStringBytesZ(cx, name));
+ result.Append("\", [");
+
+ nsTArray* fields = StructType::GetFieldInfo(cx, typeObj);
+ for (PRUint32 i = 0; i < fields->Length(); ++i) {
+ const FieldInfo& field = fields->ElementAt(i);
+ result.Append("{ \"");
+ result.Append(field.mName);
+ result.Append("\": ");
+ result.Append(BuildTypeSource(cx, field.mType, makeShort));
+ result.Append(" }");
+ if (i != fields->Length() - 1)
+ result.Append(", ");
+ }
+
+ result.Append("])");
+ break;
+ }
+ }
+
+ return result;
+}
+
+// Given a CData object of CType 'typeObj' with binary value 'data', generate a
+// string 'result' such that 'eval(result)' would construct a CData object with
+// the same CType and containing the same binary value. This assumes that any
+// StructType 't' is bound to an in-scope variable of name 't.name'. (This means
+// the type comparison function CType::TypesEqual will return true when
+// comparing the types, since struct equality is determined by strict JSObject
+// pointer equality.) Further, if 'isImplicit' is true, ensure that the
+// resulting string can ImplicitConvert successfully if passed to another data
+// constructor. (This is important when called recursively, since fields of
+// structs and arrays are converted with ImplicitConvert.)
+static nsCAutoString
+BuildDataSource(JSContext* cx, JSObject* typeObj, void* data, bool isImplicit)
+{
+ nsCAutoString result;
+ TypeCode type = CType::GetTypeCode(cx, typeObj);
+ switch (type) {
+ case TYPE_bool:
+ result.Append(*static_cast(data) ? "true" : "false");
+ break;
+#define DEFINE_INT_TYPE(name, type, ffiType) \
+ case TYPE_##name: \
+ /* Serialize as a primitive decimal integer. */ \
+ result.Append(IntegerToString(*static_cast(data), 10)); \
+ break;
+#define DEFINE_WRAPPED_INT_TYPE(name, type, ffiType) \
+ case TYPE_##name: \
+ /* Serialize as a wrapped decimal integer. */ \
+ if (IsUnsigned()) \
+ result.Append("ctypes.UInt64(\""); \
+ else \
+ result.Append("ctypes.Int64(\""); \
+ \
+ result.Append(IntegerToString(*static_cast(data), 10)); \
+ result.Append("\")"); \
+ break;
+#define DEFINE_FLOAT_TYPE(name, type, ffiType) \
+ case TYPE_##name: { \
+ /* Serialize as a primitive double. */ \
+ PRFloat64 fp = *static_cast(data); \
+ PRIntn decpt, sign; \
+ char buf[128]; \
+ PR_dtoa(fp, 0, 0, &decpt, &sign, NULL, buf, sizeof(buf)); \
+ result.Append(buf); \
+ break; \
+ }
+#define DEFINE_CHAR_TYPE(name, type, ffiType) \
+ case TYPE_##name: \
+ /* Serialize as an integer. */ \
+ result.Append(IntegerToString(*static_cast(data), 10)); \
+ break;
+#include "typedefs.h"
+ case TYPE_pointer: {
+ if (isImplicit) {
+ // The result must be able to ImplicitConvert successfully.
+ // Wrap in a type constructor, then serialize for ExplicitConvert.
+ result.Append(BuildTypeSource(cx, typeObj, true));
+ result.Append('(');
+ }
+
+ // Serialize the pointer value as a wrapped hexadecimal integer.
+ uintptr_t ptr = *static_cast(data);
+ result.Append("ctypes.UInt64(\"0x");
+ result.Append(IntegerToString(ptr, 16));
+ result.Append("\")");
+
+ if (isImplicit)
+ result.Append(')');
+
+ break;
+ }
+ case TYPE_array: {
+ // Serialize each element of the array recursively. Each element must
+ // be able to ImplicitConvert successfully.
+ JSObject* baseType = ArrayType::GetBaseType(cx, typeObj);
+ result.Append("[ ");
+
+
+ size_t length = ArrayType::GetLength(cx, typeObj);
+ size_t elementSize = CType::GetSize(cx, baseType);
+ for (size_t i = 0; i < length; ++i) {
+ char* element = static_cast(data) + elementSize * i;
+ result.Append(BuildDataSource(cx, baseType, element, true));
+ if (i + 1 < length)
+ result.Append(", ");
+ }
+ result.Append(" ]");
+ break;
+ }
+ case TYPE_struct: {
+ if (isImplicit) {
+ // The result must be able to ImplicitConvert successfully.
+ // Serialize the data as an object with properties, rather than
+ // a sequence of arguments to the StructType constructor.
+ result.Append("{ ");
+ }
+
+ // Serialize each field of the struct recursively. Each field must
+ // be able to ImplicitConvert successfully.
+ nsTArray* fields = StructType::GetFieldInfo(cx, typeObj);
+ for (size_t i = 0; i < fields->Length(); ++i) {
+ const FieldInfo& field = fields->ElementAt(i);
+ char* fieldData = static_cast(data) + field.mOffset;
+
+ if (isImplicit) {
+ result.Append('"');
+ result.Append(field.mName);
+ result.Append("\": ");
+ }
+
+ result.Append(BuildDataSource(cx, field.mType, fieldData, true));
+ if (i + 1 != fields->Length())
+ result.Append(", ");
+ }
+
+ if (isImplicit)
+ result.Append(" }");
+
+ break;
+ }
+ case TYPE_void_t:
+ JS_NOT_REACHED("invalid type");
+ break;
+ }
+
+ return result;
+}
+
+/*******************************************************************************
+** CType implementation
+*******************************************************************************/
+
+JSBool
+CType::ConstructAbstract(JSContext* cx,
+ JSObject* obj,
+ uintN argc,
+ jsval* argv,
+ jsval* rval)
+{
+ // Calling the CType abstract base class constructor is disallowed.
+ JS_ReportError(cx, "cannot construct from abstract type");
+ return JS_FALSE;
+}
+
+JSBool
+CType::ConstructData(JSContext* cx,
+ JSObject* obj,
+ uintN argc,
+ jsval* argv,
+ jsval* rval)
+{
+ // get the callee object...
+ obj = JSVAL_TO_OBJECT(JS_ARGV_CALLEE(argv));
+ if (!CType::IsCType(cx, obj)) {
+ JS_ReportError(cx, "not a CType");
+ return JS_FALSE;
+ }
+
+ // How we construct the CData object depends on what type we represent.
+ // An instance 'd' of a CData object of type 't' has:
+ // * Class [[CData]]
+ // * __proto__ === t.prototype
+ switch (GetTypeCode(cx, obj)) {
+ case TYPE_void_t:
+ JS_ReportError(cx, "cannot construct from void_t");
+ return JS_FALSE;
+ case TYPE_pointer:
+ return PointerType::ConstructData(cx, obj, argc, argv, rval);
+ case TYPE_array:
+ return ArrayType::ConstructData(cx, obj, argc, argv, rval);
+ case TYPE_struct:
+ return StructType::ConstructData(cx, obj, argc, argv, rval);
+ default:
+ return ConstructBasic(cx, obj, argc, argv, rval);
+ }
+}
+
+JSBool
+CType::ConstructBasic(JSContext* cx,
+ JSObject* obj,
+ uintN argc,
+ jsval* argv,
+ jsval* rval)
+{
+ if (argc > 1) {
+ JS_ReportError(cx, "CType constructor takes zero or one argument");
+ return JS_FALSE;
+ }
+
+ // construct a CData object
+ JSObject* result = CData::Create(cx, obj, NULL, NULL);
+ if (!result)
+ return JS_FALSE;
+
+ *rval = OBJECT_TO_JSVAL(result);
+
+ if (argc == 1) {
+ // perform explicit conversion
+ if (!ExplicitConvert(cx, argv[0], obj, CData::GetData(cx, result)))
+ return JS_FALSE;
+ }
+
+ return JS_TRUE;
+}
+
+JSObject*
+CType::Create(JSContext* cx,
+ JSObject* proto,
+ JSString* name,
+ TypeCode type,
+ jsval size,
+ jsval align,
+ ffi_type* ffiType)
+{
+ // Create a CType object with the properties and slots common to all CTypes.
+ // Each type object 't' has:
+ // * Class [[CType]]
+ // * __proto__ === 'proto'; one of ctypes.{CType,PointerType,ArrayType,
+ // StructType}.prototype
+ // * A constructor which creates and returns a CData object, containing
+ // binary data of the given type.
+ // * 'prototype' property:
+ // * Class [[Object]]
+ // * __proto__ === Object.prototype
+ // * 'constructor' property === 't'
+ JSObject* typeObj = JS_NewObject(cx, &sCTypeClass, proto, NULL);
+ if (!typeObj)
+ return NULL;
+ JSAutoTempValueRooter root(cx, typeObj);
+
+ // Define properties common to all CTypes.
+ if (name &&
+ !JS_DefineProperty(cx, typeObj, "name", STRING_TO_JSVAL(name),
+ NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
+ return NULL;
+ if (!JS_DefineProperty(cx, typeObj, "ptr", JSVAL_VOID,
+ PtrGetter, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
+ return NULL;
+ if (!JS_DefineProperty(cx, typeObj, "size", size,
+ NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
+ return NULL;
+
+ // Set the TypeCode.
+ if (!JS_SetReservedSlot(cx, typeObj, SLOT_TYPECODE, INT_TO_JSVAL(type)))
+ return NULL;
+
+ // Set the ffi_type.
+ if (!JS_SetReservedSlot(cx, typeObj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(ffiType)))
+ return NULL;
+
+ // Set the type alignment.
+ if (!JS_SetReservedSlot(cx, typeObj, SLOT_ALIGN, align))
+ return NULL;
+
+ // Set up the 'prototype' and 'prototype.constructor' properties.
+ JSObject* prototype = JS_NewObject(cx, NULL, NULL, typeObj);
+ if (!prototype)
+ return NULL;
+
+ if (!JS_DefineProperty(cx, typeObj, "prototype", OBJECT_TO_JSVAL(prototype),
+ NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
+ return NULL;
+
+ if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(typeObj),
+ NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
+ return NULL;
+
+ // Seal the 'prototype' property.
+ if (!JS_SealObject(cx, prototype, JS_FALSE))
+ return NULL;
+
+ return typeObj;
+}
+
+JSObject*
+CType::DefineBuiltin(JSContext* cx,
+ JSObject* parent,
+ const char* propName,
+ JSObject* proto,
+ const char* name,
+ TypeCode type,
+ jsval size,
+ jsval align,
+ ffi_type* ffiType)
+{
+ JSString* nameStr = JS_NewStringCopyZ(cx, name);
+ if (!nameStr)
+ return NULL;
+ JSAutoTempValueRooter nameRoot(cx, nameStr);
+
+ // Create a new CType object with the common properties and slots.
+ JSObject* typeObj = Create(cx, proto, nameStr, type, size, align, ffiType);
+ if (!typeObj)
+ return NULL;
+
+ // Define the CType as a 'propName' property on 'parent'.
+ if (!JS_DefineProperty(cx, parent, propName, OBJECT_TO_JSVAL(typeObj),
+ NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
+ return NULL;
+
+ if (!JS_SealObject(cx, typeObj, JS_FALSE))
+ return NULL;
+
+ return typeObj;
+}
+
+void
+CType::Finalize(JSContext* cx, JSObject* obj)
+{
+ // Make sure our TypeCode slot is legit. If it's not, bail.
+ jsval slot;
+ if (!JS_GetReservedSlot(cx, obj, SLOT_TYPECODE, &slot) || JSVAL_IS_VOID(slot))
+ return;
+
+ // The contents of our slots depends on what kind of type we are.
+ switch (GetTypeCode(cx, obj)) {
+ case TYPE_struct:
+ // Free the FieldInfo array.
+ JS_GetReservedSlot(cx, obj, SLOT_FIELDINFO, &slot);
+ if (!JSVAL_IS_VOID(slot))
+ delete static_cast*>(JSVAL_TO_PRIVATE(slot));
+
+ // Fall through.
+ case TYPE_array: {
+ // Free the ffi_type info.
+ jsval slot;
+ JS_GetReservedSlot(cx, obj, SLOT_FFITYPE, &slot);
+ if (!JSVAL_IS_VOID(slot) && JSVAL_TO_PRIVATE(slot)) {
+ ffi_type* ffiType = static_cast(JSVAL_TO_PRIVATE(slot));
+ delete ffiType->elements;
+ delete ffiType;
+ }
+
+ break;
+ }
+ default:
+ // Nothing to do here.
+ break;
+ }
+}
+
+bool
+CType::IsCType(JSContext* cx, JSObject* obj)
+{
+ return JS_GET_CLASS(cx, obj) == &sCTypeClass;
+}
+
+TypeCode
+CType::GetTypeCode(JSContext* cx, JSObject* typeObj)
+{
+ JS_ASSERT(IsCType(cx, typeObj));
+
+ jsval result;
+ JS_GetReservedSlot(cx, typeObj, SLOT_TYPECODE, &result);
+ return TypeCode(JSVAL_TO_INT(result));
+}
+
+bool
+CType::TypesEqual(JSContext* cx, JSObject* t1, JSObject* t2)
+{
+ JS_ASSERT(IsCType(cx, t1) && IsCType(cx, t2));
+
+ // First, perform shallow comparison.
+ TypeCode c1 = GetTypeCode(cx, t1);
+ TypeCode c2 = GetTypeCode(cx, t2);
+ if (c1 != c2)
+ return false;
+
+ // Determine whether the types require shallow or deep comparison.
+ switch (c1) {
+ case TYPE_pointer: {
+ JSObject* b1 = PointerType::GetBaseType(cx, t1);
+ JSObject* b2 = PointerType::GetBaseType(cx, t2);
+
+ if (!b1 || !b2) {
+ // One or both pointers are opaque.
+ // If both are opaque, compare names.
+ JSString* n1 = GetName(cx, t1);
+ JSString* n2 = GetName(cx, t2);
+ return b1 == b2 && JS_CompareStrings(n1, n2) == 0;
+ }
+
+ // Compare base types.
+ return TypesEqual(cx, b1, b2);
+ }
+ case TYPE_array: {
+ // Compare length, then base types.
+ // An undefined length array matches any length.
+ size_t s1, s2;
+ bool d1 = ArrayType::GetSafeLength(cx, t1, &s1);
+ bool d2 = ArrayType::GetSafeLength(cx, t2, &s2);
+ if (d1 && d2 && s1 != s2)
+ return false;
+
+ JSObject* b1 = ArrayType::GetBaseType(cx, t1);
+ JSObject* b2 = ArrayType::GetBaseType(cx, t2);
+ return TypesEqual(cx, b1, b2);
+ }
+ case TYPE_struct:
+ // Require exact type object equality.
+ return t1 == t2;
+ default:
+ // Shallow comparison is sufficient.
+ return true;
+ }
+}
+
+bool
+CType::GetSafeSize(JSContext* cx, JSObject* obj, size_t* result)
+{
+ JS_ASSERT(CType::IsCType(cx, obj));
+
+ jsval size;
+ JS_GetProperty(cx, obj, "size", &size);
+
+ // The "size" property can be a jsint, a jsdouble, or JSVAL_VOID
+ // (for arrays of undefined length), and must always fit in a size_t.
+ if (JSVAL_IS_INT(size)) {
+ *result = JSVAL_TO_INT(size);
+ return true;
+ }
+ if (JSVAL_IS_DOUBLE(size)) {
+ *result = Convert(*JSVAL_TO_DOUBLE(size));
+ return true;
+ }
+
+ JS_ASSERT(JSVAL_IS_VOID(size));
+ return false;
+}
+
+size_t
+CType::GetSize(JSContext* cx, JSObject* obj)
+{
+ JS_ASSERT(CType::IsCType(cx, obj));
+
+ jsval size;
+ JS_GetProperty(cx, obj, "size", &size);
+
+ JS_ASSERT(!JSVAL_IS_VOID(size));
+
+ // The "size" property can be a jsint, a jsdouble, or JSVAL_VOID
+ // (for arrays of undefined length), and must always fit in a size_t.
+ // For callers who know it can never be JSVAL_VOID, return a size_t directly.
+ if (JSVAL_IS_INT(size))
+ return JSVAL_TO_INT(size);
+ return Convert(*JSVAL_TO_DOUBLE(size));
+}
+
+bool
+CType::IsSizeDefined(JSContext* cx, JSObject* obj)
+{
+ JS_ASSERT(CType::IsCType(cx, obj));
+
+ jsval size;
+ JS_GetProperty(cx, obj, "size", &size);
+
+ // The "size" property can be a jsint, a jsdouble, or JSVAL_VOID
+ // (for arrays of undefined length), and must always fit in a size_t.
+ JS_ASSERT(JSVAL_IS_INT(size) || JSVAL_IS_DOUBLE(size) || JSVAL_IS_VOID(size));
+ return !JSVAL_IS_VOID(size);
+}
+
+size_t
+CType::GetAlignment(JSContext* cx, JSObject* obj)
+{
+ JS_ASSERT(CType::IsCType(cx, obj));
+
+ jsval slot;
+ JS_GetReservedSlot(cx, obj, SLOT_ALIGN, &slot);
+ return static_cast(JSVAL_TO_INT(slot));
+}
+
+ffi_type*
+CType::GetFFIType(JSContext* cx, JSObject* obj)
+{
+ JS_ASSERT(CType::IsCType(cx, obj));
+
+ jsval slot;
+ JS_GetReservedSlot(cx, obj, SLOT_FFITYPE, &slot);
+
+ ffi_type* result = static_cast(JSVAL_TO_PRIVATE(slot));
+ JS_ASSERT(result);
+ return result;
+}
+
+JSString*
+CType::GetName(JSContext* cx, JSObject* obj)
+{
+ JS_ASSERT(CType::IsCType(cx, obj));
+
+ jsval string;
+ JS_GetProperty(cx, obj, "name", &string);
+ return JSVAL_TO_STRING(string);
+}
+
+JSObject*
+CType::GetProtoFromCtor(JSContext* cx, JSObject* obj, CTypeProtoSlot slot)
+{
+ // Look at the 'prototype' property of the type constructor...
+ jsval prototype;
+ JS_GetProperty(cx, obj, "prototype", &prototype);
+ JS_ASSERT(JSVAL_IS_OBJECT(prototype) && !JSVAL_IS_NULL(prototype));
+
+ // ... and get ctypes.CType.prototype.
+ JSObject* proto = JS_GetPrototype(cx, JSVAL_TO_OBJECT(prototype));
+ JS_ASSERT(proto);
+ JS_ASSERT(JS_GET_CLASS(cx, proto) == &sCTypeProto);
+
+ // Finally, get the requested ctypes.{Pointer,Array,Struct}Type.prototype.
+ jsval result;
+ JS_GetReservedSlot(cx, proto, slot, &result);
+ return JSVAL_TO_OBJECT(result);
+}
+
+JSObject*
+CType::GetProtoFromType(JSContext* cx, JSObject* obj, CTypeProtoSlot slot)
+{
+ JS_ASSERT(IsCType(cx, obj));
+
+ // Get the prototype of the type object.
+ JSObject* proto = JS_GetPrototype(cx, obj);
+ JS_ASSERT(proto);
+
+ if (JS_GET_CLASS(cx, proto) != &sCTypeProto) {
+ // We have a special type constructor. Get ctypes.CType.prototype from it.
+ proto = JS_GetPrototype(cx, proto);
+ JS_ASSERT(proto);
+ JS_ASSERT(JS_GET_CLASS(cx, proto) == &sCTypeProto);
+ }
+
+ // Finally, get the requested ctypes.{Pointer,Array,Struct}Type.prototype.
+ jsval result;
+ JS_GetReservedSlot(cx, proto, slot, &result);
+ return JSVAL_TO_OBJECT(result);
+}
+
+JSBool
+CType::PtrGetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp)
+{
+ if (!CType::IsCType(cx, obj)) {
+ JS_ReportError(cx, "not a CType");
+ return JS_FALSE;
+ }
+
+ JSObject* pointerType = PointerType::CreateInternal(cx, NULL, obj, NULL);
+ if (!pointerType)
+ return JS_FALSE;
+
+ *vp = OBJECT_TO_JSVAL(pointerType);
+ return JS_TRUE;
+}
+
+JSBool
+CType::Array(JSContext* cx, uintN argc, jsval *vp)
+{
+ JSObject* baseType = JS_THIS_OBJECT(cx, vp);
+ JS_ASSERT(baseType);
+
+ if (!CType::IsCType(cx, baseType)) {
+ JS_ReportError(cx, "not a CType");
+ return JS_FALSE;
+ }
+
+ // Construct and return a new ArrayType object.
+ if (argc > 1) {
+ JS_ReportError(cx, "array takes zero or one argument");
+ return JS_FALSE;
+ }
+
+ // Convert the length argument to a size_t.
+ jsval* argv = JS_ARGV(cx, vp);
+ size_t length = 0;
+ if (argc == 1 && !jsvalToSize(cx, argv[0], false, &length)) {
+ JS_ReportError(cx, "argument must be a nonnegative integer");
+ return JS_FALSE;
+ }
+
+ JSObject* result = ArrayType::CreateInternal(cx, baseType, length, argc == 1);
+ if (!result)
+ return JS_FALSE;
+
+ JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
+ return JS_TRUE;
+}
+
+JSBool
+CType::ToString(JSContext* cx, uintN argc, jsval *vp)
+{
+ JSObject* obj = JS_THIS_OBJECT(cx, vp);
+ JS_ASSERT(obj);
+
+ if (!CType::IsCType(cx, obj)) {
+ JS_ReportError(cx, "not a CType");
+ return JS_FALSE;
+ }
+
+ nsCAutoString type("type ");
+ JSString* right = GetName(cx, obj);
+ type.Append(JS_GetStringBytesZ(cx, right));
+
+ JSString* result = JS_NewStringCopyN(cx, type.get(), type.Length());
+ if (!result)
+ return JS_FALSE;
+
+ JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
+ return JS_TRUE;
+}
+
+JSBool
+CType::ToSource(JSContext* cx, uintN argc, jsval *vp)
+{
+ JSObject* obj = JS_THIS_OBJECT(cx, vp);
+ JS_ASSERT(obj);
+
+ if (!CType::IsCType(cx, obj)) {
+ JS_ReportError(cx, "not a CType");
+ return JS_FALSE;
+ }
+
+ nsCAutoString source = BuildTypeSource(cx, obj, false);
+ JSString* result = JS_NewStringCopyN(cx, source.get(), source.Length());
+ if (!result)
+ return JS_FALSE;
+
+ JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
+ return JS_TRUE;
+}
+
+/*******************************************************************************
+** PointerType implementation
+*******************************************************************************/
+
+JSBool
+PointerType::Create(JSContext* cx, uintN argc, jsval* vp)
+{
+ // Construct and return a new PointerType object.
+ if (argc != 1) {
+ JS_ReportError(cx, "PointerType takes one argument");
+ return JS_FALSE;
+ }
+
+ jsval arg = JS_ARGV(cx, vp)[0];
+ JSObject* baseType = NULL;
+ JSString* name = NULL;
+ if (JSVAL_IS_OBJECT(arg) && !JSVAL_IS_NULL(arg) &&
+ CType::IsCType(cx, JSVAL_TO_OBJECT(arg))) {
+ baseType = JSVAL_TO_OBJECT(arg);
+
+ } else if (JSVAL_IS_STRING(arg)) {
+ // Construct an opaque pointer type from a string.
+ name = JSVAL_TO_STRING(arg);
+
+ } else {
+ JS_ReportError(cx, "first argument must be a CType or a string");
+ return JS_FALSE;
+ }
+
+ JSObject* callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
+ JSObject* result = CreateInternal(cx, callee, baseType, name);
+ if (!result)
+ return JS_FALSE;
+
+ JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
+ return JS_TRUE;
+}
+
+JSObject*
+PointerType::CreateInternal(JSContext* cx,
+ JSObject* ctor,
+ JSObject* baseType,
+ JSString* name)
+{
+ JS_ASSERT(ctor || baseType);
+ JS_ASSERT((baseType && !name) || (!baseType && name));
+
+ if (baseType) {
+ // check if we have a cached PointerType on our base CType.
+ jsval slot;
+ JS_GetReservedSlot(cx, baseType, SLOT_PTR, &slot);
+ if (JSVAL_IS_OBJECT(slot))
+ return JSVAL_TO_OBJECT(slot);
+ }
+
+ // Get ctypes.PointerType.prototype, either from ctor or the baseType,
+ // whichever was provided.
+ JSObject* proto;
+ if (ctor)
+ proto = CType::GetProtoFromCtor(cx, ctor, SLOT_POINTERPROTO);
+ else
+ proto = CType::GetProtoFromType(cx, baseType, SLOT_POINTERPROTO);
+
+ // Create a new CType object with the common properties and slots.
+ JSObject* typeObj = CType::Create(cx, proto, name, TYPE_pointer,
+ INT_TO_JSVAL(sizeof(void*)),
+ INT_TO_JSVAL(ffi_type_pointer.alignment),
+ &ffi_type_pointer);
+ if (!typeObj)
+ return NULL;
+ JSAutoTempValueRooter root(cx, typeObj);
+
+ // Define the 'targetType' property.
+ if (!JS_DefineProperty(cx, typeObj, "targetType", OBJECT_TO_JSVAL(baseType),
+ NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
+ return NULL;
+
+ if (baseType) {
+ // Determine the name of the PointerType, if it wasn't supplied.
+ nsCAutoString typeName = BuildTypeName(cx, typeObj);
+ name = JS_NewStringCopyN(cx, typeName.get(), typeName.Length());
+ if (!name ||
+ !JS_DefineProperty(cx, typeObj, "name", STRING_TO_JSVAL(name),
+ NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
+ return NULL;
+ }
+
+ // Finally, cache our newly-created PointerType on our pointed-to CType,
+ // if we have one.
+ if (baseType &&
+ !JS_SetReservedSlot(cx, baseType, SLOT_PTR, OBJECT_TO_JSVAL(typeObj)))
+ return NULL;
+
+ if (!JS_SealObject(cx, typeObj, JS_FALSE))
+ return NULL;
+
+ return typeObj;
+}
+
+JSBool
+PointerType::ConstructData(JSContext* cx,
+ JSObject* obj,
+ uintN argc,
+ jsval* argv,
+ jsval* rval)
+{
+ if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_pointer) {
+ JS_ReportError(cx, "not a PointerType");
+ return JS_FALSE;
+ }
+
+ if (argc > 1) {
+ JS_ReportError(cx, "constructor takes zero or one argument");
+ return JS_FALSE;
+ }
+
+ JSObject* result = ConstructInternal(cx, obj, NULL, NULL);
+ if (!result)
+ return JS_FALSE;
+
+ *rval = OBJECT_TO_JSVAL(result);
+
+ if (argc == 1) {
+ // perform explicit conversion
+ if (!ExplicitConvert(cx, argv[0], obj, CData::GetData(cx, result)))
+ return JS_FALSE;
+ }
+
+ return JS_TRUE;
+}
+
+JSObject*
+PointerType::ConstructInternal(JSContext* cx,
+ JSObject* typeObj,
+ JSObject* parentObj,
+ void* data)
+{
+ // construct a CData object
+ JSObject* result = CData::Create(cx, typeObj, parentObj, data);
+ if (!result)
+ return NULL;
+ JSAutoTempValueRooter root(cx, result);
+
+ if (!JS_DefineProperty(cx, result, "contents", JSVAL_VOID,
+ PointerType::ContentsGetter, PointerType::ContentsSetter,
+ JSPROP_ENUMERATE | JSPROP_PERMANENT))
+ return NULL;
+
+ return result;
+}
+
+JSObject*
+PointerType::GetBaseType(JSContext* cx, JSObject* obj)
+{
+ JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_pointer);
+
+ jsval type;
+ JS_GetProperty(cx, obj, "targetType", &type);
+ return JSVAL_TO_OBJECT(type);
+}
+
+JSBool
+PointerType::ContentsGetter(JSContext* cx,
+ JSObject* obj,
+ jsval idval,
+ jsval* vp)
+{
+ if (!CData::IsCData(cx, obj)) {
+ JS_ReportError(cx, "not a CData");
+ return JS_FALSE;
+ }
+
+ // Get pointer type and base type.
+ JSObject* typeObj = CData::GetCType(cx, obj);
+ if (CType::GetTypeCode(cx, typeObj) != TYPE_pointer) {
+ JS_ReportError(cx, "not a PointerType");
+ return JS_FALSE;
+ }
+
+ JSObject* baseType = GetBaseType(cx, typeObj);
+ if (!baseType) {
+ JS_ReportError(cx, "cannot get contents of an opaque pointer type");
+ return JS_FALSE;
+ }
+
+ if (!CType::IsSizeDefined(cx, baseType)) {
+ JS_ReportError(cx, "cannot get contents of undefined size");
+ return JS_FALSE;
+ }
+
+ void* data = *static_cast(CData::GetData(cx, obj));
+ if (data == NULL) {
+ JS_ReportError(cx, "cannot read contents of null pointer");
+ return JS_FALSE;
+ }
+
+ jsval result;
+ if (!ConvertToJS(cx, baseType, obj, data, false, &result))
+ return JS_FALSE;
+
+ JS_SET_RVAL(cx, vp, result);
+ return JS_TRUE;
+}
+
+JSBool
+PointerType::ContentsSetter(JSContext* cx,
+ JSObject* obj,
+ jsval idval,
+ jsval* vp)
+{
+ if (!CData::IsCData(cx, obj)) {
+ JS_ReportError(cx, "not a CData");
+ return JS_FALSE;
+ }
+
+ // Get pointer type and base type.
+ JSObject* typeObj = CData::GetCType(cx, obj);
+ if (CType::GetTypeCode(cx, typeObj) != TYPE_struct) {
+ JS_ReportError(cx, "not a PointerType");
+ return JS_FALSE;
+ }
+
+ JSObject* baseType = GetBaseType(cx, typeObj);
+ if (!baseType) {
+ JS_ReportError(cx, "cannot set contents of an opaque pointer type");
+ return JS_FALSE;
+ }
+
+ if (!CType::IsSizeDefined(cx, baseType)) {
+ JS_ReportError(cx, "cannot get contents of undefined size");
+ return JS_FALSE;
+ }
+
+ void* data = *static_cast(CData::GetData(cx, obj));
+ if (data == NULL) {
+ JS_ReportError(cx, "cannot write contents to null pointer");
+ return JS_FALSE;
+ }
+
+ return ImplicitConvert(cx, *vp, baseType, data, false, NULL);
+}
+
+/*******************************************************************************
+** ArrayType implementation
+*******************************************************************************/
+
+JSBool
+ArrayType::Create(JSContext* cx, uintN argc, jsval* vp)
+{
+ // Construct and return a new ArrayType object.
+ if (argc > 2) {
+ JS_ReportError(cx, "ArrayType takes one or two arguments");
+ return JS_FALSE;
+ }
+
+ jsval* argv = JS_ARGV(cx, vp);
+ if (!JSVAL_IS_OBJECT(argv[0]) || JSVAL_IS_NULL(argv[0] ||
+ !CType::IsCType(cx, JSVAL_TO_OBJECT(argv[0])))) {
+ JS_ReportError(cx, "first argument must be a CType");
+ return JS_FALSE;
+ }
+
+ // Convert the length argument to a size_t.
+ size_t length = 0;
+ if (argc == 2 && !jsvalToSize(cx, argv[1], false, &length)) {
+ JS_ReportError(cx, "second argument must be a nonnegative integer");
+ return JS_FALSE;
+ }
+
+ JSObject* baseType = JSVAL_TO_OBJECT(argv[0]);
+ JSObject* result = CreateInternal(cx, baseType, length, argc == 2);
+ if (!result)
+ return JS_FALSE;
+
+ JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
+ return JS_TRUE;
+}
+
+JSObject*
+ArrayType::CreateInternal(JSContext* cx,
+ JSObject* baseType,
+ size_t length,
+ bool lengthDefined)
+{
+ // Get ctypes.ArrayType.prototype from ctypes.CType.prototype.
+ JSObject* proto = CType::GetProtoFromType(cx, baseType, SLOT_ARRAYPROTO);
+
+ // Determine the size of the array from the base type, if possible.
+ // The size of the base type must be defined.
+ // If our length is undefined, both our size and length will be undefined.
+ size_t baseSize;
+ if (!CType::GetSafeSize(cx, baseType, &baseSize)) {
+ JS_ReportError(cx, "base size must be defined");
+ return NULL;
+ }
+
+ size_t size;
+ jsval sizeVal = JSVAL_VOID;
+ jsval lengthVal = JSVAL_VOID;
+ if (lengthDefined) {
+ // Check for overflow, and convert to a jsint or jsdouble as required.
+ size = length * baseSize;
+ if (length > 0 && size / length != baseSize) {
+ JS_ReportError(cx, "size overflow");
+ return NULL;
+ }
+ if (!SizeTojsval(cx, size, &sizeVal) ||
+ !SizeTojsval(cx, length, &lengthVal))
+ return NULL;
+ }
+
+ size_t align = CType::GetAlignment(cx, baseType);
+
+ ffi_type* ffiType = NULL;
+ if (lengthDefined) {
+ // Create an ffi_type to represent the array. This is necessary for the case
+ // where the array is part of a struct. Since libffi has no intrinsic
+ // support for array types, we approximate it by creating a struct type
+ // with elements of type 'baseType' and with appropriate size and alignment
+ // values.
+ ffiType = new ffi_type;
+ if (!ffiType) {
+ JS_ReportOutOfMemory(cx);
+ return NULL;
+ }
+
+ ffiType->type = FFI_TYPE_STRUCT;
+ ffiType->size = size;
+ ffiType->alignment = align;
+ ffiType->elements = new ffi_type*[length + 1];
+ if (!ffiType->elements) {
+ delete ffiType;
+ JS_ReportAllocationOverflow(cx);
+ return NULL;
+ }
+
+ ffi_type* ffiBaseType = CType::GetFFIType(cx, baseType);
+ for (size_t i = 0; i < length; ++i)
+ ffiType->elements[i] = ffiBaseType;
+ ffiType->elements[length] = NULL;
+ }
+
+ // Create a new CType object with the common properties and slots.
+ JSObject* typeObj = CType::Create(cx, proto, NULL, TYPE_array,
+ sizeVal, INT_TO_JSVAL(align), ffiType);
+ if (!typeObj)
+ return NULL;
+ JSAutoTempValueRooter root(cx, typeObj);
+
+ // Define additional properties.
+ if (!JS_DefineProperty(cx, typeObj, "length", lengthVal,
+ NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
+ return NULL;
+ if (!JS_DefineProperty(cx, typeObj, "elementType", OBJECT_TO_JSVAL(baseType),
+ NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
+ return NULL;
+
+ // Determine the name of the ArrayType.
+ nsCAutoString typeName = BuildTypeName(cx, typeObj);
+ JSString* name = JS_NewStringCopyN(cx, typeName.get(), typeName.Length());
+ if (!name ||
+ !JS_DefineProperty(cx, typeObj, "name", STRING_TO_JSVAL(name),
+ NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
+ return NULL;
+
+ if (!JS_SealObject(cx, typeObj, JS_FALSE))
+ return NULL;
+
+ return typeObj;
+}
+
+JSBool
+ArrayType::ConstructData(JSContext* cx,
+ JSObject* obj,
+ uintN argc,
+ jsval* argv,
+ jsval* rval)
+{
+ if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_array) {
+ JS_ReportError(cx, "not an ArrayType");
+ return JS_FALSE;
+ }
+
+ // Decide whether we have an object to initialize from. We'll override this
+ // if we get a length argument instead.
+ bool convertObject = argc == 1;
+
+ // Check if we're an array of undefined length. If we are, allow construction
+ // with a length argument, or with an actual JS array.
+ if (CType::IsSizeDefined(cx, obj)) {
+ if (argc > 1) {
+ JS_ReportError(cx, "constructor takes zero or one argument");
+ return JS_FALSE;
+ }
+
+ } else {
+ if (argc != 1) {
+ JS_ReportError(cx, "constructor takes one argument");
+ return JS_FALSE;
+ }
+
+ JSObject* baseType = GetBaseType(cx, obj);
+
+ size_t length;
+ if (jsvalToSize(cx, argv[0], false, &length)) {
+ // Have a length, rather than an object to initialize from.
+ convertObject = false;
+
+ } else if (JSVAL_IS_OBJECT(argv[0])) {
+ // We were given an object with a .length property.
+ // This could be a JS array, or a CData array.
+ JSObject* arg = JSVAL_TO_OBJECT(argv[0]);
+ jsval lengthVal;
+ if (!JS_GetProperty(cx, arg, "length", &lengthVal) ||
+ !jsvalToSize(cx, lengthVal, false, &length)) {
+ JS_ReportError(cx, "argument must be an array object or length");
+ return JS_FALSE;
+ }
+
+ } else if (JSVAL_IS_STRING(argv[0])) {
+ // We were given a string. Size the array to the appropriate length,
+ // including space for the terminator.
+ JSString* sourceString = JSVAL_TO_STRING(argv[0]);
+ const jschar* sourceChars = JS_GetStringCharsZ(cx, sourceString);
+ size_t sourceLength = JS_GetStringLength(sourceString);
+
+ switch (CType::GetTypeCode(cx, baseType)) {
+ case TYPE_char:
+ case TYPE_signed_char:
+ case TYPE_unsigned_char: {
+ // Convert from UTF-16 to UTF-8 to determine the length. :(
+ if (!IsUTF16(sourceChars, sourceLength))
+ return TypeError(cx, "UTF-16 string", argv[0]);
+
+ NS_ConvertUTF16toUTF8 converted(
+ reinterpret_cast(sourceChars), sourceLength);
+
+ length = converted.Length() + 1;
+ break;
+ }
+ case TYPE_jschar:
+ length = sourceLength + 1;
+ break;
+ default:
+ return TypeError(cx, "array", argv[0]);
+ }
+
+ } else {
+ JS_ReportError(cx, "argument must be an array object or length");
+ return JS_FALSE;
+ }
+
+ // Construct a new ArrayType of defined length, for the new CData object.
+ obj = CreateInternal(cx, baseType, length, true);
+ if (!obj)
+ return JS_FALSE;
+ }
+
+ // Root the CType object, in case we created one above.
+ JSAutoTempValueRooter root(cx, obj);
+
+ JSObject* result = ConstructInternal(cx, obj, NULL, NULL);
+ if (!result)
+ return JS_FALSE;
+
+ *rval = OBJECT_TO_JSVAL(result);
+
+ if (convertObject) {
+ // perform explicit conversion
+ if (!ExplicitConvert(cx, argv[0], obj, CData::GetData(cx, result)))
+ return JS_FALSE;
+ }
+
+ return JS_TRUE;
+}
+
+JSObject*
+ArrayType::ConstructInternal(JSContext* cx,
+ JSObject* typeObj,
+ JSObject* parentObj,
+ void* data)
+{
+ // construct a CData object
+ JSObject* result = CData::Create(cx, typeObj, parentObj, data);
+ if (!result)
+ return NULL;
+ JSAutoTempValueRooter root(cx, result);
+
+ if (!JS_DefineFunctions(cx, result, sArrayInstanceFunctions))
+ return NULL;
+
+ // Duplicate the 'constructor.length' property as a 'length' property,
+ // for consistency with JS array objects.
+ jsval lengthVal;
+ if (!JS_GetProperty(cx, typeObj, "length", &lengthVal) ||
+ !JS_DefineProperty(cx, result, "length", lengthVal,
+ NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
+ return NULL;
+
+ return result;
+}
+
+JSObject*
+ArrayType::GetBaseType(JSContext* cx, JSObject* obj)
+{
+ JS_ASSERT(CType::IsCType(cx, obj));
+ JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_array);
+
+ jsval type;
+ JS_GetProperty(cx, obj, "elementType", &type);
+ return JSVAL_TO_OBJECT(type);
+}
+
+bool
+ArrayType::GetSafeLength(JSContext* cx, JSObject* obj, size_t* result)
+{
+ JS_ASSERT(CType::IsCType(cx, obj));
+ JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_array);
+
+ jsval length;
+ JS_GetProperty(cx, obj, "length", &length);
+
+ // The "length" property can be a jsint, a jsdouble, or JSVAL_VOID
+ // (for arrays of undefined length), and must always fit in a size_t.
+ if (JSVAL_IS_INT(length)) {
+ *result = JSVAL_TO_INT(length);
+ return true;
+ }
+ if (JSVAL_IS_DOUBLE(length)) {
+ *result = Convert(*JSVAL_TO_DOUBLE(length));
+ return true;
+ }
+
+ JS_ASSERT(JSVAL_IS_VOID(length));
+ return false;
+}
+
+size_t
+ArrayType::GetLength(JSContext* cx, JSObject* obj)
+{
+ JS_ASSERT(CType::IsCType(cx, obj));
+ JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_array);
+
+ jsval length;
+ JS_GetProperty(cx, obj, "length", &length);
+
+ JS_ASSERT(!JSVAL_IS_VOID(length));
+
+ // The "length" property can be a jsint, a jsdouble, or JSVAL_VOID
+ // (for arrays of undefined length), and must always fit in a size_t.
+ // For callers who know it can never be JSVAL_VOID, return a size_t directly.
+ if (JSVAL_IS_INT(length))
+ return JSVAL_TO_INT(length);
+ return Convert(*JSVAL_TO_DOUBLE(length));
+}
+
+JSBool
+ArrayType::Getter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp)
+{
+ if (!CData::IsCData(cx, obj))
+ return JS_TRUE;
+
+ // Bail early if we're not an ArrayType. (This setter is present for all
+ // CData, regardless of CType.)
+ JSObject* typeObj = CData::GetCType(cx, obj);
+ if (CType::GetTypeCode(cx, typeObj) != TYPE_array)
+ return JS_TRUE;
+
+ // Convert the index to a size_t and bounds-check it.
+ size_t index;
+ size_t length = GetLength(cx, typeObj);
+ bool ok = jsvalToSize(cx, idval, true, &index);
+ if (!ok && JSVAL_IS_STRING(idval)) {
+ // String either isn't a number, or doesn't fit in size_t.
+ // Chances are it's a regular property lookup, so return.
+ return JS_TRUE;
+ }
+ if (!ok || index >= length) {
+ JS_ReportError(cx, "invalid index");
+ return JS_FALSE;
+ }
+
+ JSObject* baseType = GetBaseType(cx, typeObj);
+ size_t elementSize = CType::GetSize(cx, baseType);
+ char* data = static_cast(CData::GetData(cx, obj)) + elementSize * index;
+ return ConvertToJS(cx, baseType, obj, data, false, vp);
+}
+
+JSBool
+ArrayType::Setter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp)
+{
+ if (!CData::IsCData(cx, obj))
+ return JS_TRUE;
+
+ // Bail early if we're not an ArrayType. (This setter is present for all
+ // CData, regardless of CType.)
+ JSObject* typeObj = CData::GetCType(cx, obj);
+ if (CType::GetTypeCode(cx, typeObj) != TYPE_array)
+ return JS_TRUE;
+
+ // Convert the index to a size_t and bounds-check it.
+ size_t index;
+ size_t length = GetLength(cx, typeObj);
+ bool ok = jsvalToSize(cx, idval, true, &index);
+ if (!ok && JSVAL_IS_STRING(idval)) {
+ // String either isn't a number, or doesn't fit in size_t.
+ // Chances are it's a regular property lookup, so return.
+ return JS_TRUE;
+ }
+ if (!ok || index >= length) {
+ JS_ReportError(cx, "invalid index");
+ return JS_FALSE;
+ }
+
+ JSObject* baseType = GetBaseType(cx, typeObj);
+ size_t elementSize = CType::GetSize(cx, baseType);
+ char* data = static_cast(CData::GetData(cx, obj)) + elementSize * index;
+ return ImplicitConvert(cx, *vp, baseType, data, false, NULL);
+}
+
+JSBool
+ArrayType::AddressOfElement(JSContext* cx, uintN argc, jsval *vp)
+{
+ JSObject* obj = JS_THIS_OBJECT(cx, vp);
+ JS_ASSERT(obj);
+
+ if (!CData::IsCData(cx, obj)) {
+ JS_ReportError(cx, "not a CData");
+ return JS_FALSE;
+ }
+
+ JSObject* typeObj = CData::GetCType(cx, obj);
+ if (CType::GetTypeCode(cx, typeObj) != TYPE_array) {
+ JS_ReportError(cx, "not an ArrayType");
+ return JS_FALSE;
+ }
+
+ if (argc != 1) {
+ JS_ReportError(cx, "addressOfElement takes one argument");
+ return JS_FALSE;
+ }
+
+ JSObject* baseType = GetBaseType(cx, typeObj);
+ JSObject* pointerType = PointerType::CreateInternal(cx, NULL, baseType, NULL);
+ if (!pointerType)
+ return JS_FALSE;
+ JSAutoTempValueRooter root(cx, pointerType);
+
+ // Create a PointerType CData object containing null.
+ JSObject* result = PointerType::ConstructInternal(cx, pointerType, NULL, NULL);
+ if (!result)
+ return JS_FALSE;
+
+ JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
+
+ // Convert the index to a size_t and bounds-check it.
+ size_t index;
+ size_t length = GetLength(cx, typeObj);
+ if (!jsvalToSize(cx, JS_ARGV(cx, vp)[0], false, &index) ||
+ index >= length) {
+ JS_ReportError(cx, "invalid index");
+ return JS_FALSE;
+ }
+
+ // Manually set the pointer inside the object, so we skip the conversion step.
+ void** data = static_cast(CData::GetData(cx, result));
+ size_t elementSize = CType::GetSize(cx, baseType);
+ *data = static_cast(CData::GetData(cx, obj)) + elementSize * index;
+ return JS_TRUE;
+}
+
+/*******************************************************************************
+** StructType implementation
+*******************************************************************************/
+
+// For a struct field descriptor 'val' of the form { name : type }, extract
+// 'name' and 'type', and populate 'field' with the information.
+static bool
+ExtractStructField(JSContext* cx, jsval val, FieldInfo* field)
+{
+ if (!JSVAL_IS_OBJECT(val) || JSVAL_IS_NULL(val))
+ return false;
+
+ JSObject* obj = JSVAL_TO_OBJECT(val);
+ JSObject* iter = JS_NewPropertyIterator(cx, obj);
+ if (!iter)
+ return false;
+ JSAutoTempValueRooter iterroot(cx, iter);
+
+ jsid id;
+ if (!JS_NextProperty(cx, iter, &id))
+ return false;
+
+ jsval nameVal;
+ if (!JS_IdToValue(cx, id, &nameVal) || !JSVAL_IS_STRING(nameVal))
+ return false;
+ JSAutoTempValueRooter nameroot(cx, nameVal);
+
+ // make sure we have one, and only one, property
+ if (JS_NextProperty(cx, iter, &id) && !JSVAL_IS_VOID(id))
+ return false;
+
+ const char* name = JS_GetStringBytesZ(cx, JSVAL_TO_STRING(nameVal));
+ field->mName = name;
+
+ jsval propVal;
+ if (!JS_GetProperty(cx, obj, name, &propVal) ||
+ !JSVAL_IS_OBJECT(propVal) || JSVAL_IS_NULL(propVal) ||
+ !CType::IsCType(cx, JSVAL_TO_OBJECT(propVal)))
+ return false;
+
+ // Undefined size or zero size struct members are illegal.
+ // (Zero-size arrays are legal as struct members in C++, but libffi will
+ // choke on a zero-size struct, so we disallow them.)
+ field->mType = JSVAL_TO_OBJECT(propVal);
+ size_t size;
+ if (!CType::GetSafeSize(cx, field->mType, &size) || size == 0)
+ return false;
+
+ return true;
+}
+
+// For a struct field with 'name' and 'type', add an element to field
+// descriptor array 'arrayObj' of the form { name : type }.
+static bool
+AddFieldToArray(JSContext* cx,
+ JSObject* arrayObj,
+ jsuint index,
+ const char* name,
+ JSObject* typeObj)
+{
+ JSObject* fieldObj = JS_NewObject(cx, NULL, NULL, NULL);
+ if (!fieldObj)
+ return false;
+
+ if (!JS_DefineElement(cx, arrayObj, index, OBJECT_TO_JSVAL(fieldObj),
+ NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
+ return false;
+
+ if (!JS_DefineProperty(cx, fieldObj, name, OBJECT_TO_JSVAL(typeObj),
+ NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
+ return false;
+
+ return JS_SealObject(cx, fieldObj, JS_FALSE) != JS_FALSE;
+}
+
+JSBool
+StructType::Create(JSContext* cx, uintN argc, jsval* vp)
+{
+ // Construct and return a new StructType object.
+ if (argc < 2) {
+ JS_ReportError(cx, "StructType takes at least two arguments");
+ return JS_FALSE;
+ }
+
+ jsval* argv = JS_ARGV(cx, vp);
+ jsval name = argv[0];
+ if (!JSVAL_IS_STRING(name)) {
+ JS_ReportError(cx, "first argument must be a string");
+ return JS_FALSE;
+ }
+
+ if (!JSVAL_IS_OBJECT(argv[1]) ||
+ !JS_IsArrayObject(cx, JSVAL_TO_OBJECT(argv[1]))) {
+ JS_ReportError(cx, "second argument must be an array");
+ return JS_FALSE;
+ }
+
+ // Prepare a new array for the .fields property of the StructType.
+ JSObject* fieldsProp = JS_NewArrayObject(cx, 0, NULL);
+ if (!fieldsProp)
+ return JS_FALSE;
+ JSAutoTempValueRooter root(cx, fieldsProp);
+
+ // Process the field types and fill in the ffi_type fields.
+ JSObject* fieldsObj = JSVAL_TO_OBJECT(argv[1]);
+ jsuint len;
+ JS_GetArrayLength(cx, fieldsObj, &len);
+
+ nsAutoPtr ffiType(new ffi_type);
+ if (!ffiType) {
+ JS_ReportOutOfMemory(cx);
+ return JS_FALSE;
+ }
+ ffiType->type = FFI_TYPE_STRUCT;
+
+ nsAutoPtr< nsTArray > fields(new nsTArray(len));
+ if (!fields) {
+ JS_ReportOutOfMemory(cx);
+ return JS_FALSE;
+ }
+ nsAutoPtr elements;
+
+ size_t structSize = 0, structAlign = 0;
+ if (len != 0) {
+ elements = new ffi_type*[len + 1];
+ if (!elements) {
+ JS_ReportOutOfMemory(cx);
+ return JS_FALSE;
+ }
+ elements[len] = NULL;
+
+ for (jsuint i = 0; i < len; ++i) {
+ jsval item;
+ JS_GetElement(cx, fieldsObj, i, &item);
+
+ FieldInfo* info = fields->AppendElement();
+ if (!info) {
+ JS_ReportOutOfMemory(cx);
+ return JS_FALSE;
+ }
+ if (!ExtractStructField(cx, item, info)) {
+ JS_ReportError(cx, "struct field descriptors require a valid name and type");
+ return JS_FALSE;
+ }
+
+ // Duplicate the object for the fields property.
+ if (!AddFieldToArray(cx, fieldsProp, i, info->mName.get(), info->mType))
+ return JS_FALSE;
+
+ // Make sure each field name is unique.
+ for (PRUint32 j = 0; j < fields->Length() - 1; ++j) {
+ if (fields->ElementAt(j).mName == info->mName) {
+ JS_ReportError(cx, "struct fields must have unique names");
+ return JS_FALSE;
+ }
+ }
+
+ elements[i] = CType::GetFFIType(cx, info->mType);
+
+ size_t fieldAlign = CType::GetAlignment(cx, info->mType);
+ size_t padding = (fieldAlign - structSize % fieldAlign) % fieldAlign;
+ info->mOffset = structSize + padding;
+ size_t delta = padding + CType::GetSize(cx, info->mType);
+ size_t oldSize = structSize;
+ structSize = structSize + delta;
+ if (structSize - delta != oldSize) {
+ JS_ReportError(cx, "size overflow");
+ return JS_FALSE;
+ }
+
+ if (fieldAlign > structAlign)
+ structAlign = fieldAlign;
+ }
+
+ // Pad the struct tail according to struct alignment.
+ size_t oldSize = structSize;
+ size_t delta = (structAlign - structSize % structAlign) % structAlign;
+ structSize = structSize + delta;
+ if (structSize - delta != oldSize) {
+ JS_ReportError(cx, "size overflow");
+ return JS_FALSE;
+ }
+
+ } else {
+ // Empty structs are illegal in C, but are legal and have a size of
+ // 1 byte in C++. We're going to allow them, and trick libffi into
+ // believing this by adding a char member. The resulting struct will have
+ // no getters or setters, and will be initialized to zero.
+ structSize = 1;
+ structAlign = 1;
+ elements = new ffi_type*[2];
+ if (!elements) {
+ JS_ReportOutOfMemory(cx);
+ return JS_FALSE;
+ }
+ elements[0] = &ffi_type_uint8;
+ elements[1] = NULL;
+ }
+
+ ffiType->elements = elements;
+
+#ifdef DEBUG
+ // Perform a sanity check: the result of our struct size and alignment
+ // calculations should match libffi's. We force it to do this calculation
+ // by calling ffi_prep_cif.
+ ffi_cif cif;
+ ffiType->size = 0;
+ ffiType->alignment = 0;
+ ffi_status status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 0, ffiType, NULL);
+ JS_ASSERT(status == FFI_OK);
+ JS_ASSERT(structSize == ffiType->size);
+ JS_ASSERT(structAlign == ffiType->alignment);
+#else
+ // Fill in the ffi_type's size and align fields. This makes libffi treat the
+ // type as initialized; it will not recompute the values. (We assume
+ // everything agrees; if it doesn't, we really want to know about it, which
+ // is the purpose of the above debug-only check.)
+ ffiType->size = structSize;
+ ffiType->alignment = structAlign;
+#endif
+
+ jsval sizeVal;
+ if (!SizeTojsval(cx, structSize, &sizeVal))
+ return JS_FALSE;
+
+ // Get ctypes.StructType.prototype from the ctypes.StructType constructor.
+ JSObject* callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
+ JSObject* proto = CType::GetProtoFromCtor(cx, callee, SLOT_STRUCTPROTO);
+
+ // Create a new CType object with the common properties and slots.
+ JSObject* typeObj = CType::Create(cx, proto, JSVAL_TO_STRING(name),
+ TYPE_struct, sizeVal, INT_TO_JSVAL(structAlign), ffiType);
+ if (!typeObj)
+ return JS_FALSE;
+ ffiType.forget();
+ elements.forget();
+
+ JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(typeObj));
+
+ // Seal and attach the fields array. (The fields array also prevents the
+ // type objects we depend on from being GC'ed).
+ if (!JS_SealObject(cx, fieldsProp, JS_FALSE) ||
+ !JS_DefineProperty(cx, typeObj, "fields", OBJECT_TO_JSVAL(fieldsProp),
+ NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
+ return JS_FALSE;
+
+ // Stash the FieldInfo array in a reserved slot.
+ if (!JS_SetReservedSlot(cx, typeObj, SLOT_FIELDINFO,
+ PRIVATE_TO_JSVAL(fields.get())))
+ return JS_FALSE;
+ fields.forget();
+
+ if (!JS_SealObject(cx, typeObj, JS_FALSE))
+ return JS_FALSE;
+
+ return JS_TRUE;
+}
+
+JSBool
+StructType::ConstructData(JSContext* cx,
+ JSObject* obj,
+ uintN argc,
+ jsval* argv,
+ jsval* rval)
+{
+ if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_struct) {
+ JS_ReportError(cx, "not a StructType");
+ return JS_FALSE;
+ }
+
+ nsTArray* fields = GetFieldInfo(cx, obj);
+
+ if (argc != 0 && argc != fields->Length()) {
+ JS_ReportError(cx, "constructor takes zero or %u arguments", fields->Length());
+ return JS_FALSE;
+ }
+
+ JSObject* result = ConstructInternal(cx, obj, NULL, NULL);
+ if (!result)
+ return JS_FALSE;
+
+ *rval = OBJECT_TO_JSVAL(result);
+
+ if (argc != 0) {
+ // convert each field
+ char* buffer = static_cast(CData::GetData(cx, result));
+ for (PRUint32 i = 0; i < fields->Length(); ++i) {
+ FieldInfo& field = fields->ElementAt(i);
+ if (!ExplicitConvert(cx, argv[i], field.mType, buffer + field.mOffset))
+ return JS_FALSE;
+ }
+ }
+
+ return JS_TRUE;
+}
+
+JSObject*
+StructType::ConstructInternal(JSContext* cx,
+ JSObject* typeObj,
+ JSObject* parentObj,
+ void* data)
+{
+ // construct a CData object
+ JSObject* result = CData::Create(cx, typeObj, parentObj, data);
+ if (!result)
+ return NULL;
+ JSAutoTempValueRooter root(cx, result);
+
+ nsTArray* fields = GetFieldInfo(cx, typeObj);
+
+ // add getters/setters for the fields
+ for (PRUint32 i = 0; i < fields->Length(); ++i) {
+ FieldInfo& field = fields->ElementAt(i);
+
+ if (!JS_DefineProperty(cx, result, field.mName.get(), JSVAL_VOID,
+ StructType::FieldGetter, StructType::FieldSetter,
+ JSPROP_ENUMERATE | JSPROP_PERMANENT))
+ return NULL;
+ }
+
+ if (!JS_DefineFunctions(cx, result, sStructInstanceFunctions))
+ return NULL;
+
+ return result;
+}
+
+nsTArray*
+StructType::GetFieldInfo(JSContext* cx, JSObject* obj)
+{
+ JS_ASSERT(CType::IsCType(cx, obj));
+ JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_struct);
+
+ jsval slot;
+ JS_GetReservedSlot(cx, obj, SLOT_FIELDINFO, &slot);
+ JS_ASSERT(!JSVAL_IS_VOID(slot) && JSVAL_TO_PRIVATE(slot));
+
+ return static_cast*>(JSVAL_TO_PRIVATE(slot));
+}
+
+FieldInfo*
+StructType::LookupField(JSContext* cx, JSObject* obj, jsval idval)
+{
+ JS_ASSERT(CType::IsCType(cx, obj));
+ JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_struct);
+
+ nsTArray* fields = GetFieldInfo(cx, obj);
+
+ PRUint32 i;
+ const char* name = JS_GetStringBytesZ(cx, JSVAL_TO_STRING(idval));
+ for (i = 0; i < fields->Length(); ++i) {
+ if (fields->ElementAt(i).mName.Equals(name))
+ break;
+ }
+
+ if (i == fields->Length())
+ return NULL;
+ return &fields->ElementAt(i);
+}
+
+JSBool
+StructType::FieldGetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp)
+{
+ if (!CData::IsCData(cx, obj)) {
+ JS_ReportError(cx, "not a CData");
+ return JS_FALSE;
+ }
+
+ JSObject* typeObj = CData::GetCType(cx, obj);
+ if (CType::GetTypeCode(cx, typeObj) != TYPE_struct) {
+ JS_ReportError(cx, "not a StructType");
+ return JS_FALSE;
+ }
+
+ FieldInfo* field = LookupField(cx, typeObj, idval);
+ JS_ASSERT(field);
+
+ char* data = static_cast(CData::GetData(cx, obj)) + field->mOffset;
+ return ConvertToJS(cx, field->mType, obj, data, false, vp);
+}
+
+JSBool
+StructType::FieldSetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp)
+{
+ if (!CData::IsCData(cx, obj)) {
+ JS_ReportError(cx, "not a CData");
+ return JS_FALSE;
+ }
+
+ JSObject* typeObj = CData::GetCType(cx, obj);
+ if (CType::GetTypeCode(cx, typeObj) != TYPE_struct) {
+ JS_ReportError(cx, "not a StructType");
+ return JS_FALSE;
+ }
+
+ FieldInfo* field = LookupField(cx, typeObj, idval);
+ JS_ASSERT(field);
+
+ char* data = static_cast(CData::GetData(cx, obj)) + field->mOffset;
+ return ImplicitConvert(cx, *vp, field->mType, data, false, NULL);
+}
+
+JSBool
+StructType::AddressOfField(JSContext* cx, uintN argc, jsval *vp)
+{
+ JSObject* obj = JS_THIS_OBJECT(cx, vp);
+ JS_ASSERT(obj);
+
+ if (!CData::IsCData(cx, obj)) {
+ JS_ReportError(cx, "not a CData");
+ return JS_FALSE;
+ }
+
+ JSObject* typeObj = CData::GetCType(cx, obj);
+ if (CType::GetTypeCode(cx, typeObj) != TYPE_struct) {
+ JS_ReportError(cx, "not a StructType");
+ return JS_FALSE;
+ }
+
+ if (argc != 1) {
+ JS_ReportError(cx, "addressOfField takes one argument");
+ return JS_FALSE;
+ }
+
+ FieldInfo* field = LookupField(cx, typeObj, JS_ARGV(cx, vp)[0]);
+ if (!field) {
+ JS_ReportError(cx, "argument does not name a field");
+ return JS_FALSE;
+ }
+
+ JSObject* baseType = field->mType;
+ JSObject* pointerType = PointerType::CreateInternal(cx, NULL, baseType, NULL);
+ if (!pointerType)
+ return JS_FALSE;
+ JSAutoTempValueRooter root(cx, pointerType);
+
+ // Create a PointerType CData object containing null.
+ JSObject* result = PointerType::ConstructInternal(cx, pointerType, NULL, NULL);
+ if (!result)
+ return JS_FALSE;
+
+ JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
+
+ // Manually set the pointer inside the object, so we skip the conversion step.
+ void** data = static_cast(CData::GetData(cx, result));
+ *data = static_cast(CData::GetData(cx, obj)) + field->mOffset;
+ return JS_TRUE;
+}
+
+/*******************************************************************************
+** CData implementation
+*******************************************************************************/
+
+// Create a new CData object of type 'typeObj' containing binary data supplied
+// in 'source', optionally with a referent CData object 'baseObj'. The following
+// semantics apply:
+// * 'typeObj' must be a CType of defined (but possibly zero) size.
+// * If a CData object 'parentObj' is supplied, the new CData object becomes
+// dependent on the given parent and its buffer refers to the parent's buffer,
+// supplied in 'source'. 'parentObj' will be held alive by the resulting CData
+// object.
+// * If 'parentObj' is null, the new CData object will create a new buffer of
+// size given by 'typeObj'. If 'source' data is supplied, the data will be
+// copied from 'source' into the new buffer; otherwise, the entirety of the
+// new buffer will be initialized to zero.
+JSObject*
+CData::Create(JSContext* cx, JSObject* typeObj, JSObject* baseObj, void* source)
+{
+ JS_ASSERT(typeObj);
+ JS_ASSERT(CType::IsCType(cx, typeObj));
+ JS_ASSERT(CType::IsSizeDefined(cx, typeObj));
+ JS_ASSERT(!baseObj || CData::IsCData(cx, baseObj));
+ JS_ASSERT(!baseObj || source);
+
+ // Get the 'prototype' property from the type.
+ jsval protoVal;
+ JS_GetProperty(cx, typeObj, "prototype", &protoVal);
+ JS_ASSERT(JSVAL_IS_OBJECT(protoVal) && !JSVAL_IS_NULL(protoVal));
+
+ JSObject* proto = JSVAL_TO_OBJECT(protoVal);
+ JSObject* dataObj = JS_NewObject(cx, &sCDataClass, proto, NULL);
+ if (!dataObj)
+ return NULL;
+ JSAutoTempValueRooter root(cx, dataObj);
+
+ if (!JS_DefineFunctions(cx, dataObj, sCDataFunctions))
+ return NULL;
+
+ if (!JS_DefineProperty(cx, dataObj, "value", JSVAL_VOID,
+ ValueGetter, ValueSetter, JSPROP_ENUMERATE | JSPROP_PERMANENT))
+ return NULL;
+
+ // set the CData's associated type
+ if (!JS_SetReservedSlot(cx, dataObj, SLOT_CTYPE, OBJECT_TO_JSVAL(typeObj)))
+ return NULL;
+
+ // root the base object, if any
+ if (!JS_SetReservedSlot(cx, dataObj, SLOT_REFERENT, OBJECT_TO_JSVAL(baseObj)))
+ return NULL;
+
+ // attach the buffer. since it might not be 2-byte aligned, we need to
+ // allocate an aligned space for it and store it there. :(
+ char** buffer = new char*;
+ if (!buffer) {
+ JS_ReportOutOfMemory(cx);
+ return NULL;
+ }
+
+ char* data;
+ if (baseObj) {
+ data = static_cast(source);
+ } else {
+ // There is no parent object to depend on; initialize our own buffer.
+ size_t size = CType::GetSize(cx, typeObj);
+ data = new char[size];
+ if (!data) {
+ // Report a catchable allocation error.
+ JS_ReportAllocationOverflow(cx);
+ delete buffer;
+ return NULL;
+ }
+
+ if (!source)
+ memset(data, 0, size);
+ else
+ memcpy(data, source, size);
+ }
+
+ *buffer = data;
+ if (!JS_SetReservedSlot(cx, dataObj, SLOT_DATA, PRIVATE_TO_JSVAL(buffer))) {
+ if (!baseObj)
+ delete data;
+ delete buffer;
+ return NULL;
+ }
+
+ return dataObj;
+}
+
+void
+CData::Finalize(JSContext* cx, JSObject* obj)
+{
+ // Delete our buffer, and the data it contains if we own it.
+ jsval slot;
+ if (!JS_GetReservedSlot(cx, obj, SLOT_REFERENT, &slot) || JSVAL_IS_VOID(slot))
+ return;
+ JSBool owns = JSVAL_IS_NULL(slot);
+
+ if (!JS_GetReservedSlot(cx, obj, SLOT_DATA, &slot) || JSVAL_IS_VOID(slot))
+ return;
+ char** buffer = static_cast(JSVAL_TO_PRIVATE(slot));
+
+ if (owns)
+ delete *buffer;
+ delete buffer;
+}
+
+JSObject*
+CData::GetCType(JSContext* cx, JSObject* dataObj)
+{
+ JS_ASSERT(CData::IsCData(cx, dataObj));
+
+ jsval slot;
+ JS_GetReservedSlot(cx, dataObj, SLOT_CTYPE, &slot);
+ JSObject* typeObj = JSVAL_TO_OBJECT(slot);
+ JS_ASSERT(CType::IsCType(cx, typeObj));
+ return typeObj;
+}
+
+void*
+CData::GetData(JSContext* cx, JSObject* dataObj)
+{
+ JS_ASSERT(CData::IsCData(cx, dataObj));
+
+ jsval slot;
+ JS_GetReservedSlot(cx, dataObj, SLOT_DATA, &slot);
+
+ void** buffer = static_cast(JSVAL_TO_PRIVATE(slot));
+ JS_ASSERT(buffer);
+ JS_ASSERT(*buffer);
+ return *buffer;
+}
+
+bool
+CData::IsCData(JSContext* cx, JSObject* obj)
+{
+ return JS_GET_CLASS(cx, obj) == &sCDataClass;
+}
+
+JSBool
+CData::ValueGetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp)
+{
+ if (!IsCData(cx, obj)) {
+ JS_ReportError(cx, "not a CData");
+ return JS_FALSE;
+ }
+
+ // Convert the value to a primitive; do not create a new CData object.
+ if (!ConvertToJS(cx, GetCType(cx, obj), NULL, GetData(cx, obj), true, vp))
+ return JS_FALSE;
+
+ return JS_TRUE;
+}
+
+JSBool
+CData::ValueSetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp)
+{
+ if (!IsCData(cx, obj)) {
+ JS_ReportError(cx, "not a CData");
+ return JS_FALSE;
+ }
+
+ return ImplicitConvert(cx, *vp, GetCType(cx, obj), GetData(cx, obj), false, NULL);
+}
+
+JSBool
+CData::Address(JSContext* cx, uintN argc, jsval *vp)
+{
+ if (argc != 0) {
+ JS_ReportError(cx, "address takes zero arguments");
+ return JS_FALSE;
+ }
+
+ JSObject* obj = JS_THIS_OBJECT(cx, vp);
+ JS_ASSERT(obj);
+
+ if (!IsCData(cx, obj)) {
+ JS_ReportError(cx, "not a CData");
+ return JS_FALSE;
+ }
+
+ JSObject* typeObj = CData::GetCType(cx, obj);
+ JSObject* pointerType = PointerType::CreateInternal(cx, NULL, typeObj, NULL);
+ if (!pointerType)
+ return JS_FALSE;
+ JSAutoTempValueRooter root(cx, pointerType);
+
+ // Create a PointerType CData object containing null.
+ JSObject* result = PointerType::ConstructInternal(cx, pointerType, NULL, NULL);
+ if (!result)
+ return JS_FALSE;
+
+ JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
+
+ // Manually set the pointer inside the object, so we skip the conversion step.
+ void** data = static_cast(GetData(cx, result));
+ *data = GetData(cx, obj);
+ return JS_TRUE;
+}
+
+JSBool
+CData::Cast(JSContext* cx, uintN argc, jsval *vp)
+{
+ if (argc != 2) {
+ JS_ReportError(cx, "cast takes two arguments");
+ return JS_FALSE;
+ }
+
+ jsval* argv = JS_ARGV(cx, vp);
+ if (!JSVAL_IS_OBJECT(argv[0]) || JSVAL_IS_NULL(argv[0]) ||
+ !CData::IsCData(cx, JSVAL_TO_OBJECT(argv[0]))) {
+ JS_ReportError(cx, "first argument must be a CData");
+ return JS_FALSE;
+ }
+ JSObject* sourceData = JSVAL_TO_OBJECT(argv[0]);
+ JSObject* sourceType = CData::GetCType(cx, sourceData);
+
+ if (!JSVAL_IS_OBJECT(argv[1]) || JSVAL_IS_NULL(argv[1]) ||
+ !CType::IsCType(cx, JSVAL_TO_OBJECT(argv[1]))) {
+ JS_ReportError(cx, "second argument must be a CType");
+ return JS_FALSE;
+ }
+
+ JSObject* targetType = JSVAL_TO_OBJECT(argv[1]);
+ size_t targetSize;
+ if (!CType::GetSafeSize(cx, targetType, &targetSize) ||
+ targetSize > CType::GetSize(cx, sourceType)) {
+ JS_ReportError(cx,
+ "target CType has undefined or larger size than source CType");
+ return JS_FALSE;
+ }
+
+ // Construct a new CData object with a type of 'targetType' and a referent
+ // of 'sourceData'.
+ JSObject* result;
+ void* data = CData::GetData(cx, sourceData);
+ switch (CType::GetTypeCode(cx, targetType)) {
+ case TYPE_pointer:
+ result = PointerType::ConstructInternal(cx, targetType, sourceData, data);
+ break;
+ case TYPE_array:
+ result = ArrayType::ConstructInternal(cx, targetType, sourceData, data);
+ break;
+ case TYPE_struct:
+ result = StructType::ConstructInternal(cx, targetType, sourceData, data);
+ break;
+ default:
+ result = CData::Create(cx, targetType, sourceData, data);
+ break;
+ }
+
+ if (!result)
+ return JS_FALSE;
+
+ JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
+ return JS_TRUE;
+}
+
+JSBool
+CData::ReadString(JSContext* cx, uintN argc, jsval *vp)
+{
+ if (argc != 0) {
+ JS_ReportError(cx, "readString takes zero arguments");
+ return JS_FALSE;
+ }
+
+ JSObject* obj = JS_THIS_OBJECT(cx, vp);
+ JS_ASSERT(obj);
+
+ if (!IsCData(cx, obj)) {
+ JS_ReportError(cx, "not a CData");
+ return JS_FALSE;
+ }
+
+ // Make sure we are a pointer to, or an array of, an 8-bit or 16-bit
+ // character or integer type.
+ JSObject* baseType;
+ JSObject* typeObj = GetCType(cx, obj);
+ TypeCode typeCode = CType::GetTypeCode(cx, typeObj);
+ void* data;
+ size_t maxLength = -1;
+ switch (typeCode) {
+ case TYPE_pointer:
+ baseType = PointerType::GetBaseType(cx, typeObj);
+ if (!baseType) {
+ JS_ReportError(cx, "cannot read contents of pointer to opaque type");
+ return JS_FALSE;
+ }
+
+ data = *static_cast(GetData(cx, obj));
+ if (data == NULL) {
+ JS_ReportError(cx, "cannot read contents of null pointer");
+ return JS_FALSE;
+ }
+ break;
+ case TYPE_array:
+ baseType = ArrayType::GetBaseType(cx, typeObj);
+ data = GetData(cx, obj);
+ maxLength = ArrayType::GetLength(cx, typeObj);
+ break;
+ default:
+ JS_ReportError(cx, "not a PointerType or ArrayType");
+ return JS_FALSE;
+ }
+
+ // Convert the string buffer, taking care to determine the correct string
+ // length in the case of arrays (which may contain embedded nulls).
+ JSString* result;
+ switch (CType::GetTypeCode(cx, baseType)) {
+ case TYPE_int8_t:
+ case TYPE_uint8_t:
+ case TYPE_char:
+ case TYPE_signed_char:
+ case TYPE_unsigned_char: {
+ char* bytes = static_cast(data);
+ size_t length = strnlen(bytes, maxLength);
+ nsDependentCSubstring string(bytes, bytes + length);
+ if (!IsUTF8(string)) {
+ JS_ReportError(cx, "not a UTF-8 string");
+ return JS_FALSE;
+ }
+
+ NS_ConvertUTF8toUTF16 converted(string);
+ result = JS_NewUCStringCopyN(cx, converted.get(), converted.Length());
+ break;
+ }
+ case TYPE_int16_t:
+ case TYPE_uint16_t:
+ case TYPE_short:
+ case TYPE_unsigned_short:
+ case TYPE_jschar: {
+ jschar* chars = static_cast(data);
+ size_t length = strnlen(chars, maxLength);
+ result = JS_NewUCStringCopyN(cx, chars, length);
+ break;
+ }
+ default:
+ JS_ReportError(cx,
+ "base type is not an 8-bit or 16-bit integer or character type");
+ return JS_FALSE;
+ }
+
+ if (!result)
+ return JS_FALSE;
+
+ JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
+ return JS_TRUE;
+}
+
+JSBool
+CData::ToSource(JSContext* cx, uintN argc, jsval *vp)
+{
+ if (argc != 0) {
+ JS_ReportError(cx, "toSource takes zero arguments");
+ return JS_FALSE;
+ }
+
+ JSObject* obj = JS_THIS_OBJECT(cx, vp);
+ if (!CData::IsCData(cx, obj)) {
+ JS_ReportError(cx, "not a CData");
+ return JS_FALSE;
+ }
+
+ JSObject* typeObj = CData::GetCType(cx, obj);
+ void* data = CData::GetData(cx, obj);
+
+ // Walk the types, building up the toSource() string.
+ // First, we build up the type expression:
+ // 't.ptr' for pointers;
+ // 't.array([n])' for arrays;
+ // 'n' for structs, where n = t.name, the struct's name. (We assume this is
+ // bound to a variable in the current scope.)
+ nsCAutoString source = BuildTypeSource(cx, typeObj, true);
+ source.Append('(');
+ source.Append(BuildDataSource(cx, typeObj, data, false));
+ source.Append(')');
+
+ JSString* result = JS_NewStringCopyN(cx, source.get(), source.Length());
+ if (!result)
+ return JS_FALSE;
+
+ JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
+ return JS_TRUE;
+}
+
+/*******************************************************************************
+** Int64 and UInt64 implementation
+*******************************************************************************/
+
+JSObject*
+Int64Base::Construct(JSContext* cx,
+ JSObject* proto,
+ PRUint64 data,
+ bool isUnsigned)
+{
+ JSClass* clasp = isUnsigned ? &sUInt64Class : &sInt64Class;
+ JSObject* result = JS_NewObject(cx, clasp, proto, NULL);
+ if (!result)
+ return NULL;
+ JSAutoTempValueRooter root(cx, result);
+
+ // attach the Int64's data
+ PRUint64* buffer = new PRUint64(data);
+ if (!buffer) {
+ JS_ReportOutOfMemory(cx);
+ return NULL;
+ }
+
+ if (!JS_SetReservedSlot(cx, result, SLOT_INT64, PRIVATE_TO_JSVAL(buffer))) {
+ delete buffer;
+ return NULL;
+ }
+
+ if (!JS_SealObject(cx, result, JS_FALSE))
+ return NULL;
+
+ return result;
+}
+
+void
+Int64Base::Finalize(JSContext* cx, JSObject* obj)
+{
+ jsval slot;
+ if (!JS_GetReservedSlot(cx, obj, SLOT_INT64, &slot) || JSVAL_IS_VOID(slot))
+ return;
+
+ delete static_cast(JSVAL_TO_PRIVATE(slot));
+}
+
+PRUint64
+Int64Base::GetInt(JSContext* cx, JSObject* obj) {
+ JS_ASSERT(Int64::IsInt64(cx, obj) || UInt64::IsUInt64(cx, obj));
+
+ jsval slot;
+ JS_GetReservedSlot(cx, obj, SLOT_INT64, &slot);
+ return *static_cast(JSVAL_TO_PRIVATE(slot));
+}
+
+JSBool
+Int64Base::ToString(JSContext* cx,
+ JSObject* obj,
+ uintN argc,
+ jsval *vp,
+ bool isUnsigned)
+{
+ if (argc > 1) {
+ JS_ReportError(cx, "toString takes zero or one argument");
+ return JS_FALSE;
+ }
+
+ jsuint radix = 10;
+ if (argc == 1) {
+ jsval arg = JS_ARGV(cx, vp)[0];
+ if (JSVAL_IS_INT(arg))
+ radix = JSVAL_TO_INT(arg);
+ if (!JSVAL_IS_INT(arg) || radix < 2 || radix > 36) {
+ JS_ReportError(cx, "radix argument must be an integer between 2 and 36");
+ return JS_FALSE;
+ }
+ }
+
+ nsCAutoString intString;
+ if (isUnsigned) {
+ intString = IntegerToString(GetInt(cx, obj), radix);
+ } else {
+ intString = IntegerToString(static_cast(GetInt(cx, obj)), radix);
+ }
+
+ JSString *result = JS_NewStringCopyN(cx, intString.get(), intString.Length());
+ if (!result)
+ return JS_FALSE;
+
+ JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
+ return JS_TRUE;
+}
+
+JSBool
+Int64Base::ToSource(JSContext* cx,
+ JSObject* obj,
+ uintN argc,
+ jsval *vp,
+ bool isUnsigned)
+{
+ if (argc != 0) {
+ JS_ReportError(cx, "toSource takes zero arguments");
+ return JS_FALSE;
+ }
+
+ // Return a decimal string suitable for constructing the number.
+ nsCAutoString source;
+ if (isUnsigned) {
+ source.Append("ctypes.UInt64(\"");
+ source.Append(IntegerToString(GetInt(cx, obj), 10));
+ } else {
+ source.Append("ctypes.Int64(\"");
+ source.Append(IntegerToString(static_cast(GetInt(cx, obj)), 10));
+ }
+ source.Append(')');
+
+ JSString *result = JS_NewStringCopyN(cx, source.get(), source.Length());
+ if (!result)
+ return JS_FALSE;
+
+ JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
+ return JS_TRUE;
+}
+
+JSBool
+Int64::Construct(JSContext* cx,
+ JSObject* obj,
+ uintN argc,
+ jsval* argv,
+ jsval* rval)
+{
+ // Construct and return a new Int64 object.
+ if (argc != 1) {
+ JS_ReportError(cx, "Int64 takes one argument");
+ return JS_FALSE;
+ }
+
+ PRInt64 i;
+ if (!jsvalToBigInteger(cx, argv[0], true, &i))
+ return TypeError(cx, "int64", argv[0]);
+
+ // Get ctypes.Int64.prototype from the 'prototype' property of the ctor.
+ jsval slot;
+ JS_GetProperty(cx, JSVAL_TO_OBJECT(JS_ARGV_CALLEE(argv)), "prototype", &slot);
+ JSObject* proto = JSVAL_TO_OBJECT(slot);
+ JS_ASSERT(JS_GET_CLASS(cx, proto) == &sInt64Proto);
+
+ JSObject* result = Int64Base::Construct(cx, proto, i, false);
+ if (!result)
+ return JS_FALSE;
+
+ *rval = OBJECT_TO_JSVAL(result);
+ return JS_TRUE;
+}
+
+bool
+Int64::IsInt64(JSContext* cx, JSObject* obj)
+{
+ return JS_GET_CLASS(cx, obj) == &sInt64Class;
+}
+
+JSBool
+Int64::ToString(JSContext* cx, uintN argc, jsval *vp)
+{
+ JSObject* obj = JS_THIS_OBJECT(cx, vp);
+ if (!Int64::IsInt64(cx, obj)) {
+ JS_ReportError(cx, "not an Int64");
+ return JS_FALSE;
+ }
+
+ return Int64Base::ToString(cx, obj, argc, vp, false);
+}
+
+JSBool
+Int64::ToSource(JSContext* cx, uintN argc, jsval *vp)
+{
+ JSObject* obj = JS_THIS_OBJECT(cx, vp);
+ if (!Int64::IsInt64(cx, obj)) {
+ JS_ReportError(cx, "not an Int64");
+ return JS_FALSE;
+ }
+
+ return Int64Base::ToSource(cx, obj, argc, vp, false);
+}
+
+JSBool
+Int64::Compare(JSContext* cx, uintN argc, jsval* vp)
+{
+ jsval* argv = JS_ARGV(cx, vp);
+ if (argc != 2 ||
+ !JSVAL_IS_OBJECT(argv[0]) || JSVAL_IS_NULL(argv[0]) ||
+ !JSVAL_IS_OBJECT(argv[1]) || JSVAL_IS_NULL(argv[1]) ||
+ !Int64::IsInt64(cx, JSVAL_TO_OBJECT(argv[0])) ||
+ !Int64::IsInt64(cx, JSVAL_TO_OBJECT(argv[1]))) {
+ JS_ReportError(cx, "compare takes two Int64 arguments");
+ return JS_FALSE;
+ }
+
+ JSObject* obj1 = JSVAL_TO_OBJECT(argv[0]);
+ JSObject* obj2 = JSVAL_TO_OBJECT(argv[1]);
+
+ PRInt64 i1 = GetInt(cx, obj1);
+ PRInt64 i2 = GetInt(cx, obj2);
+
+ if (i1 == i2)
+ JS_SET_RVAL(cx, vp, INT_TO_JSVAL(0));
+ else if (i1 < i2)
+ JS_SET_RVAL(cx, vp, INT_TO_JSVAL(-1));
+ else
+ JS_SET_RVAL(cx, vp, INT_TO_JSVAL(1));
+
+ return JS_TRUE;
+}
+
+#define LO_MASK ((PRUint64(1) << 32) - 1)
+#define INT64_LO(i) ((i) & LO_MASK)
+#define INT64_HI(i) ((i) >> 32)
+
+JSBool
+Int64::Lo(JSContext* cx, uintN argc, jsval* vp)
+{
+ jsval arg = JS_ARGV(cx, vp)[0];
+ if (argc != 1 || !JSVAL_IS_OBJECT(arg) || JSVAL_IS_NULL(arg) ||
+ !Int64::IsInt64(cx, JSVAL_TO_OBJECT(arg))) {
+ JS_ReportError(cx, "lo takes one Int64 argument");
+ return JS_FALSE;
+ }
+
+ JSObject* obj = JSVAL_TO_OBJECT(arg);
+ PRInt64 u = GetInt(cx, obj);
+ jsdouble d = PRUint32(INT64_LO(u));
+
+ jsval result;
+ if (!JS_NewNumberValue(cx, d, &result))
+ return JS_FALSE;
+
+ JS_SET_RVAL(cx, vp, result);
+ return JS_TRUE;
+}
+
+JSBool
+Int64::Hi(JSContext* cx, uintN argc, jsval* vp)
+{
+ jsval arg = JS_ARGV(cx, vp)[0];
+ if (argc != 1 || !JSVAL_IS_OBJECT(arg) || JSVAL_IS_NULL(arg) ||
+ !Int64::IsInt64(cx, JSVAL_TO_OBJECT(arg))) {
+ JS_ReportError(cx, "lo takes one Int64 argument");
+ return JS_FALSE;
+ }
+
+ JSObject* obj = JSVAL_TO_OBJECT(arg);
+ PRInt64 u = GetInt(cx, obj);
+ jsdouble d = PRInt32(INT64_HI(u));
+
+ jsval result;
+ if (!JS_NewNumberValue(cx, d, &result))
+ return JS_FALSE;
+
+ JS_SET_RVAL(cx, vp, result);
+ return JS_TRUE;
+}
+
+JSBool
+Int64::Join(JSContext* cx, uintN argc, jsval* vp)
+{
+ if (argc != 2) {
+ JS_ReportError(cx, "join takes two arguments");
+ return JS_FALSE;
+ }
+
+ jsval* argv = JS_ARGV(cx, vp);
+ PRInt32 hi;
+ PRUint32 lo;
+ if (!jsvalToInteger(cx, argv[0], &hi))
+ return TypeError(cx, "int32", argv[0]);
+ if (!jsvalToInteger(cx, argv[1], &lo))
+ return TypeError(cx, "uint32", argv[1]);
+
+ PRInt64 i = (PRInt64(hi) << 32) + PRInt64(lo);
+
+ // Get Int64.prototype from the function's reserved slot.
+ JSObject* callee = JSVAL_TO_OBJECT(JS_ARGV_CALLEE(argv));
+
+ jsval slot;
+ JS_GetReservedSlot(cx, callee, SLOT_FN_INT64PROTO, &slot);
+ JSObject* proto = JSVAL_TO_OBJECT(slot);
+ JS_ASSERT(JS_GET_CLASS(cx, proto) == &sInt64Proto);
+
+ JSObject* result = Int64Base::Construct(cx, proto, i, false);
+ if (!result)
+ return JS_FALSE;
+
+ JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
+ return JS_TRUE;
+}
+
+JSBool
+UInt64::Construct(JSContext* cx,
+ JSObject* obj,
+ uintN argc,
+ jsval* argv,
+ jsval* rval)
+{
+ // Construct and return a new UInt64 object.
+ if (argc != 1) {
+ JS_ReportError(cx, "UInt64 takes one argument");
+ return JS_FALSE;
+ }
+
+ PRUint64 u;
+ if (!jsvalToBigInteger(cx, argv[0], true, &u))
+ return TypeError(cx, "uint64", argv[0]);
+
+ // Get ctypes.UInt64.prototype from the 'prototype' property of the ctor.
+ jsval slot;
+ JS_GetProperty(cx, JSVAL_TO_OBJECT(JS_ARGV_CALLEE(argv)), "prototype", &slot);
+ JSObject* proto = JSVAL_TO_OBJECT(slot);
+ JS_ASSERT(JS_GET_CLASS(cx, proto) == &sUInt64Proto);
+
+ JSObject* result = Int64Base::Construct(cx, proto, u, true);
+ if (!result)
+ return JS_FALSE;
+
+ *rval = OBJECT_TO_JSVAL(result);
+ return JS_TRUE;
+}
+
+bool
+UInt64::IsUInt64(JSContext* cx, JSObject* obj)
+{
+ return JS_GET_CLASS(cx, obj) == &sUInt64Class;
+}
+
+JSBool
+UInt64::ToString(JSContext* cx, uintN argc, jsval *vp)
+{
+ JSObject* obj = JS_THIS_OBJECT(cx, vp);
+ if (!UInt64::IsUInt64(cx, obj)) {
+ JS_ReportError(cx, "not a UInt64");
+ return JS_FALSE;
+ }
+
+ return Int64Base::ToString(cx, obj, argc, vp, true);
+}
+
+JSBool
+UInt64::ToSource(JSContext* cx, uintN argc, jsval *vp)
+{
+ JSObject* obj = JS_THIS_OBJECT(cx, vp);
+ if (!UInt64::IsUInt64(cx, obj)) {
+ JS_ReportError(cx, "not a UInt64");
+ return JS_FALSE;
+ }
+
+ return Int64Base::ToSource(cx, obj, argc, vp, true);
+}
+
+JSBool
+UInt64::Compare(JSContext* cx, uintN argc, jsval* vp)
+{
+ jsval* argv = JS_ARGV(cx, vp);
+ if (argc != 2 ||
+ !JSVAL_IS_OBJECT(argv[0]) || JSVAL_IS_NULL(argv[0]) ||
+ !JSVAL_IS_OBJECT(argv[1]) || JSVAL_IS_NULL(argv[1]) ||
+ !UInt64::IsUInt64(cx, JSVAL_TO_OBJECT(argv[0])) ||
+ !UInt64::IsUInt64(cx, JSVAL_TO_OBJECT(argv[1]))) {
+ JS_ReportError(cx, "compare takes two UInt64 arguments");
+ return JS_FALSE;
+ }
+
+ JSObject* obj1 = JSVAL_TO_OBJECT(argv[0]);
+ JSObject* obj2 = JSVAL_TO_OBJECT(argv[1]);
+
+ PRUint64 u1 = GetInt(cx, obj1);
+ PRUint64 u2 = GetInt(cx, obj2);
+
+ if (u1 == u2)
+ JS_SET_RVAL(cx, vp, INT_TO_JSVAL(0));
+ else if (u1 < u2)
+ JS_SET_RVAL(cx, vp, INT_TO_JSVAL(-1));
+ else
+ JS_SET_RVAL(cx, vp, INT_TO_JSVAL(1));
+
+ return JS_TRUE;
+}
+
+JSBool
+UInt64::Lo(JSContext* cx, uintN argc, jsval* vp)
+{
+ jsval arg = JS_ARGV(cx, vp)[0];
+ if (argc != 1 || !JSVAL_IS_OBJECT(arg) || JSVAL_IS_NULL(arg) ||
+ !UInt64::IsUInt64(cx, JSVAL_TO_OBJECT(arg))) {
+ JS_ReportError(cx, "lo takes one UInt64 argument");
+ return JS_FALSE;
+ }
+
+ JSObject* obj = JSVAL_TO_OBJECT(arg);
+ PRUint64 u = GetInt(cx, obj);
+ jsdouble d = PRUint32(INT64_LO(u));
+
+ jsval result;
+ if (!JS_NewNumberValue(cx, d, &result))
+ return JS_FALSE;
+
+ JS_SET_RVAL(cx, vp, result);
+ return JS_TRUE;
+}
+
+JSBool
+UInt64::Hi(JSContext* cx, uintN argc, jsval* vp)
+{
+ jsval arg = JS_ARGV(cx, vp)[0];
+ if (argc != 1 || !JSVAL_IS_OBJECT(arg) || JSVAL_IS_NULL(arg) ||
+ !UInt64::IsUInt64(cx, JSVAL_TO_OBJECT(arg))) {
+ JS_ReportError(cx, "lo takes one UInt64 argument");
+ return JS_FALSE;
+ }
+
+ JSObject* obj = JSVAL_TO_OBJECT(arg);
+ PRUint64 u = GetInt(cx, obj);
+ jsdouble d = PRUint32(INT64_HI(u));
+
+ jsval result;
+ if (!JS_NewNumberValue(cx, d, &result))
+ return JS_FALSE;
+
+ JS_SET_RVAL(cx, vp, result);
+ return JS_TRUE;
+}
+
+JSBool
+UInt64::Join(JSContext* cx, uintN argc, jsval* vp)
+{
+ if (argc != 2) {
+ JS_ReportError(cx, "join takes two arguments");
+ return JS_FALSE;
+ }
+
+ jsval* argv = JS_ARGV(cx, vp);
+ PRUint32 hi;
+ PRUint32 lo;
+ if (!jsvalToInteger(cx, argv[0], &hi))
+ return TypeError(cx, "uint32_t", argv[0]);
+ if (!jsvalToInteger(cx, argv[1], &lo))
+ return TypeError(cx, "uint32_t", argv[1]);
+
+ PRUint64 u = (PRUint64(hi) << 32) + PRUint64(lo);
+
+ // Get Int64.prototype from the function's reserved slot.
+ JSObject* callee = JSVAL_TO_OBJECT(JS_ARGV_CALLEE(argv));
+
+ jsval slot;
+ JS_GetReservedSlot(cx, callee, SLOT_FN_INT64PROTO, &slot);
+ JSObject* proto = JSVAL_TO_OBJECT(slot);
+ JS_ASSERT(JS_GET_CLASS(cx, proto) == &sUInt64Proto);
+
+ JSObject* result = Int64Base::Construct(cx, proto, u, true);
+ if (!result)
+ return JS_FALSE;
+
+ JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
+ return JS_TRUE;
+}
+
+}
+}
+
diff --git a/js/ctypes/CTypes.h b/js/ctypes/CTypes.h
new file mode 100644
index 000000000000..e2e984a542a0
--- /dev/null
+++ b/js/ctypes/CTypes.h
@@ -0,0 +1,277 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is js-ctypes.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation .
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Dan Witte
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef CTYPES_H
+#define CTYPES_H
+
+#include "jsapi.h"
+#include "nsString.h"
+#include "ffi.h"
+
+namespace mozilla {
+namespace ctypes {
+
+// for JS error reporting
+enum ErrorNum {
+#define MSG_DEF(name, number, count, exception, format) \
+ name = number,
+#include "ctypes.msg"
+#undef MSG_DEF
+ CTYPESERR_LIMIT
+};
+
+const JSErrorFormatString*
+GetErrorMessage(void* userRef, const char* locale, const uintN errorNumber);
+bool TypeError(JSContext* cx, const char* expected, jsval actual);
+
+/**
+ * ABI constants that specify the calling convention to use.
+ * ctypes.default_abi corresponds to the cdecl convention, and in almost all
+ * cases is the correct choice. ctypes.stdcall_abi is provided for calling
+ * functions in the Microsoft Win32 API.
+ */
+enum ABICode {
+ ABI_DEFAULT,
+ ABI_STDCALL,
+ INVALID_ABI
+};
+
+enum TypeCode {
+ TYPE_void_t,
+#define DEFINE_TYPE(name, type, ffiType) TYPE_##name,
+#include "typedefs.h"
+ TYPE_pointer,
+ TYPE_array,
+ TYPE_struct
+};
+
+ABICode GetABICode(JSContext* cx, JSObject* obj);
+
+struct FieldInfo
+{
+ nsCString mName;
+ JSObject* mType;
+ size_t mOffset;
+};
+
+bool InitTypeClasses(JSContext* cx, JSObject* parent);
+
+bool ConvertToJS(JSContext* cx, JSObject* typeObj, JSObject* dataObj, void* data, bool wantPrimitive, jsval* result);
+bool ImplicitConvert(JSContext* cx, jsval val, JSObject* targetType, void* buffer, bool isArgument, bool* freePointer);
+bool ExplicitConvert(JSContext* cx, jsval val, JSObject* targetType, void* buffer);
+
+// Contents of the various slots on each JSClass. The slot indexes are given
+// enumerated names for readability.
+enum CABISlot {
+ SLOT_ABICODE = 0, // ABICode of the CABI object
+ CABI_SLOTS
+};
+
+enum CTypeProtoSlot {
+ SLOT_POINTERPROTO = 0, // ctypes.PointerType.prototype object
+ SLOT_ARRAYPROTO = 1, // ctypes.ArrayType.prototype object
+ SLOT_STRUCTPROTO = 2, // ctypes.StructType.prototype object
+ SLOT_INT64PROTO = 3, // ctypes.Int64.prototype object
+ SLOT_UINT64PROTO = 4, // ctypes.UInt64.prototype object
+ CTYPEPROTO_SLOTS
+};
+
+enum CTypeSlot {
+ SLOT_TYPECODE = 0, // TypeCode of the CType object
+ SLOT_FFITYPE = 1, // ffi_type representing the type
+ SLOT_ALIGN = 2, // alignment of the data type, in bytes
+ SLOT_PTR = 3, // cached PointerType object for type.ptr
+ SLOT_FIELDINFO = 4, // (StructTypes only) FieldInfo array
+ CTYPE_SLOTS
+};
+
+enum CDataSlot {
+ SLOT_CTYPE = 0, // CType object representing the underlying type
+ SLOT_REFERENT = 1, // CData object this object refers to, if any
+ SLOT_DATA = 2, // pointer to a buffer containing the binary data
+ CDATA_SLOTS
+};
+
+enum Int64Slot {
+ SLOT_INT64 = 0, // pointer to a 64-bit buffer containing the integer
+ INT64_SLOTS
+};
+
+enum Int64FunctionSlot {
+ SLOT_FN_INT64PROTO = 0 // ctypes.{Int64,UInt64}.prototype object
+ // JSFunction objects always get exactly two slots.
+};
+
+class CType {
+public:
+ static JSObject* Create(JSContext* cx, JSObject* proto, JSString* name, TypeCode type, jsval size, jsval align, ffi_type* ffiType);
+ static JSObject* DefineBuiltin(JSContext* cx, JSObject* parent, const char* propName, JSObject* proto, const char* name, TypeCode type, jsval size, jsval align, ffi_type* ffiType);
+ static void Finalize(JSContext* cx, JSObject* obj);
+
+ static JSBool ConstructAbstract(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval);
+ static JSBool ConstructData(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval);
+ static JSBool ConstructBasic(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval);
+
+ static bool IsCType(JSContext* cx, JSObject* obj);
+ static TypeCode GetTypeCode(JSContext* cx, JSObject* typeObj);
+ static bool TypesEqual(JSContext* cx, JSObject* t1, JSObject* t2);
+ static size_t GetSize(JSContext* cx, JSObject* obj);
+ static bool GetSafeSize(JSContext* cx, JSObject* obj, size_t* result);
+ static bool IsSizeDefined(JSContext* cx, JSObject* obj);
+ static size_t GetAlignment(JSContext* cx, JSObject* obj);
+ static ffi_type* GetFFIType(JSContext* cx, JSObject* obj);
+ static JSString* GetName(JSContext* cx, JSObject* obj);
+ static JSObject* GetProtoFromCtor(JSContext* cx, JSObject* obj, CTypeProtoSlot slot);
+ static JSObject* GetProtoFromType(JSContext* cx, JSObject* obj, CTypeProtoSlot slot);
+
+ static JSBool PtrGetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp);
+ static JSBool Array(JSContext* cx, uintN argc, jsval* vp);
+ static JSBool ToString(JSContext* cx, uintN argc, jsval* vp);
+ static JSBool ToSource(JSContext* cx, uintN argc, jsval* vp);
+};
+
+class PointerType {
+public:
+ static JSBool Create(JSContext* cx, uintN argc, jsval* vp);
+ static JSObject* CreateInternal(JSContext* cx, JSObject* ctor, JSObject* baseType, JSString* name);
+
+ static JSBool ConstructData(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval);
+ static JSObject* ConstructInternal(JSContext* cx, JSObject* typeObj, JSObject* parentObj, void* data);
+
+ static JSObject* GetBaseType(JSContext* cx, JSObject* obj);
+
+ static JSBool ContentsGetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp);
+ static JSBool ContentsSetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp);
+};
+
+class ArrayType {
+public:
+ static JSBool Create(JSContext* cx, uintN argc, jsval* vp);
+ static JSObject* CreateInternal(JSContext* cx, JSObject* baseType, size_t length, bool lengthDefined);
+
+ static JSBool ConstructData(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval);
+ static JSObject* ConstructInternal(JSContext* cx, JSObject* typeObj, JSObject* parentObj, void* data);
+
+ static JSObject* GetBaseType(JSContext* cx, JSObject* obj);
+ static size_t GetLength(JSContext* cx, JSObject* obj);
+ static bool GetSafeLength(JSContext* cx, JSObject* obj, size_t* result);
+
+ static JSBool Getter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp);
+ static JSBool Setter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp);
+ static JSBool AddressOfElement(JSContext* cx, uintN argc, jsval* vp);
+};
+
+class StructType {
+public:
+ static JSBool Create(JSContext* cx, uintN argc, jsval* vp);
+
+ static JSBool ConstructData(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval);
+ static JSObject* ConstructInternal(JSContext* cx, JSObject* typeObj, JSObject* parentObj, void* data);
+
+ static nsTArray* GetFieldInfo(JSContext* cx, JSObject* obj);
+ static FieldInfo* LookupField(JSContext* cx, JSObject* obj, jsval idval);
+
+ static JSBool FieldGetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp);
+ static JSBool FieldSetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp);
+ static JSBool AddressOfField(JSContext* cx, uintN argc, jsval* vp);
+};
+
+class CData {
+public:
+ static JSObject* Create(JSContext* cx, JSObject* type, JSObject* base, void* data);
+ static void Finalize(JSContext* cx, JSObject* obj);
+
+ static JSObject* GetCType(JSContext* cx, JSObject* dataObj);
+ static void* GetData(JSContext* cx, JSObject* dataObj);
+ static bool IsCData(JSContext* cx, JSObject* obj);
+
+ static JSBool ValueGetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp);
+ static JSBool ValueSetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp);
+ static JSBool Address(JSContext* cx, uintN argc, jsval* vp);
+ static JSBool Cast(JSContext* cx, uintN argc, jsval* vp);
+ static JSBool ReadString(JSContext* cx, uintN argc, jsval* vp);
+ static JSBool ToSource(JSContext* cx, uintN argc, jsval* vp);
+};
+
+class Int64Base {
+public:
+ static JSObject* Construct(JSContext* cx, JSObject* proto, PRUint64 data, bool isUnsigned);
+ static void Finalize(JSContext* cx, JSObject* obj);
+
+ static PRUint64 GetInt(JSContext* cx, JSObject* obj);
+
+ static JSBool ToString(JSContext* cx, JSObject* obj, uintN argc, jsval* vp, bool isUnsigned);
+ static JSBool ToSource(JSContext* cx, JSObject* obj, uintN argc, jsval* vp, bool isUnsigned);
+};
+
+class Int64 : public Int64Base {
+public:
+ static JSBool Construct(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval);
+
+ static bool IsInt64(JSContext* cx, JSObject* obj);
+
+ static JSBool ToString(JSContext* cx, uintN argc, jsval* vp);
+ static JSBool ToSource(JSContext* cx, uintN argc, jsval* vp);
+
+ // ctypes.Int64 static functions
+ static JSBool Compare(JSContext* cx, uintN argc, jsval* vp);
+ static JSBool Lo(JSContext* cx, uintN argc, jsval* vp);
+ static JSBool Hi(JSContext* cx, uintN argc, jsval* vp);
+ static JSBool Join(JSContext* cx, uintN argc, jsval* vp);
+};
+
+class UInt64 : public Int64Base {
+public:
+ static JSBool Construct(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval);
+
+ static bool IsUInt64(JSContext* cx, JSObject* obj);
+
+ static JSBool ToString(JSContext* cx, uintN argc, jsval* vp);
+ static JSBool ToSource(JSContext* cx, uintN argc, jsval* vp);
+
+ // ctypes.UInt64 static functions
+ static JSBool Compare(JSContext* cx, uintN argc, jsval* vp);
+ static JSBool Lo(JSContext* cx, uintN argc, jsval* vp);
+ static JSBool Hi(JSContext* cx, uintN argc, jsval* vp);
+ static JSBool Join(JSContext* cx, uintN argc, jsval* vp);
+};
+
+}
+}
+
+#endif
diff --git a/js/ctypes/Function.cpp b/js/ctypes/Function.cpp
index c333094d12df..0747be44d9b7 100644
--- a/js/ctypes/Function.cpp
+++ b/js/ctypes/Function.cpp
@@ -50,439 +50,101 @@ namespace ctypes {
** Static helpers
*******************************************************************************/
-template
-static IntegerType
-Convert(jsdouble d)
-{
- return IntegerType(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<>
-static PRUint64
-Convert(jsdouble d)
-{
- return d > 0x7fffffffffffffffui64 ?
- PRUint64(d - 0x8000000000000000ui64) + 0x8000000000000000ui64 :
- PRUint64(d);
-}
-#endif
-
-template
-static bool
-jsvalToIntStrict(jsval aValue, IntegerType *aResult)
-{
- if (JSVAL_IS_INT(aValue)) {
- jsint i = JSVAL_TO_INT(aValue);
- *aResult = IntegerType(i);
-
- // Make sure the integer fits in the alotted precision, and has the right sign.
- return jsint(*aResult) == i &&
- (i < 0) == (*aResult < 0);
- }
- if (JSVAL_IS_DOUBLE(aValue)) {
- jsdouble d = *JSVAL_TO_DOUBLE(aValue);
- *aResult = Convert(d);
-
- // Don't silently lose bits here -- check that aValue really is an
- // integer value, and has the right sign.
- return jsdouble(*aResult) == d &&
- (d < 0) == (*aResult < 0);
- }
- if (JSVAL_IS_BOOLEAN(aValue)) {
- // Implicitly promote boolean values to 0 or 1, like C.
- *aResult = JSVAL_TO_BOOLEAN(aValue);
- NS_ASSERTION(*aResult == 0 || *aResult == 1, "invalid boolean");
- return true;
- }
- // Don't silently convert null to an integer. It's probably a mistake.
- return false;
-}
-
-static bool
-jsvalToDoubleStrict(jsval aValue, jsdouble *dp)
-{
- // 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.
- if (JSVAL_IS_INT(aValue)) {
- *dp = JSVAL_TO_INT(aValue);
- return true;
- }
- if (JSVAL_IS_DOUBLE(aValue)) {
- *dp = *JSVAL_TO_DOUBLE(aValue);
- return true;
- }
- return false;
-}
-
-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 uintN errorNumber)
-{
- if (0 < errorNumber && errorNumber < CTYPESERR_LIMIT)
- return &ErrorFormatString[errorNumber];
- return NULL;
-}
-
-static const char*
-ToSource(JSContext* cx, jsval vp)
-{
- JSString* str = JS_ValueToSource(cx, vp);
- if (str)
- return JS_GetStringBytes(str);
-
- JS_ClearPendingException(cx);
- return "<>";
-}
-
-static bool
-TypeError(JSContext* cx, const char* expected, jsval actual)
-{
- const char* src = ToSource(cx, actual);
- JS_ReportErrorNumber(cx, GetErrorMessage, NULL,
- CTYPESMSG_TYPE_ERROR, expected, src);
- return false;
-}
-
static bool
GetABI(JSContext* cx, jsval aCallType, ffi_abi& aResult)
{
- ABICode abi = Module::GetABICode(cx, aCallType);
+ if (!JSVAL_IS_OBJECT(aCallType) || JSVAL_IS_NULL(aCallType))
+ return false;
+
+ ABICode abi = GetABICode(cx, JSVAL_TO_OBJECT(aCallType));
// determine the ABI from the subset of those available on the
- // given platform. TYPE_DEFAULT specifies the default
+ // given platform. ABI_DEFAULT specifies the default
// C calling convention (cdecl) on each platform.
switch (abi) {
- case ABI_default_abi:
+ case ABI_DEFAULT:
aResult = FFI_DEFAULT_ABI;
return true;
+ case ABI_STDCALL:
#if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
- case ABI_stdcall_abi:
aResult = FFI_STDCALL;
return true;
#endif
- default:
- return false;
+ case INVALID_ABI:
+ break;
}
+ return false;
}
static bool
PrepareType(JSContext* aContext, jsval aType, Type& aResult)
{
- aResult.mType = Module::GetTypeCode(aContext, aType);
-
- switch (aResult.mType) {
- case TYPE_void_t:
- aResult.mFFIType = ffi_type_void;
- break;
- case TYPE_int8_t:
- aResult.mFFIType = ffi_type_sint8;
- break;
- case TYPE_int16_t:
- aResult.mFFIType = ffi_type_sint16;
- break;
- case TYPE_int32_t:
- aResult.mFFIType = ffi_type_sint32;
- break;
- case TYPE_int64_t:
- aResult.mFFIType = ffi_type_sint64;
- break;
- case TYPE_bool:
- case TYPE_uint8_t:
- aResult.mFFIType = ffi_type_uint8;
- break;
- case TYPE_uint16_t:
- aResult.mFFIType = ffi_type_uint16;
- break;
- case TYPE_uint32_t:
- aResult.mFFIType = ffi_type_uint32;
- break;
- case TYPE_uint64_t:
- aResult.mFFIType = ffi_type_uint64;
- break;
- case TYPE_float:
- aResult.mFFIType = ffi_type_float;
- break;
- case TYPE_double:
- aResult.mFFIType = ffi_type_double;
- break;
- case TYPE_string:
- case TYPE_ustring:
- aResult.mFFIType = ffi_type_pointer;
- break;
- default:
- JS_ReportError(aContext, "Invalid type specification");
+ if (!JSVAL_IS_OBJECT(aType) ||
+ JSVAL_IS_NULL(aType) ||
+ !CType::IsCType(aContext, JSVAL_TO_OBJECT(aType))) {
+ JS_ReportError(aContext, "not a ctypes type");
return false;
}
+ JSObject* typeObj = JSVAL_TO_OBJECT(aType);
+ TypeCode typeCode = CType::GetTypeCode(aContext, typeObj);
+
+ if (typeCode == TYPE_array) {
+ // convert array argument types to pointers, just like C.
+ // ImplicitConvert will do the same, when passing an array as data.
+ JSObject* baseType = ArrayType::GetBaseType(aContext, typeObj);
+ typeObj = PointerType::CreateInternal(aContext, NULL, baseType, NULL);
+ if (!typeObj) {
+ JS_ReportError(aContext, "couldn't create pointer type from array");
+ return false;
+ }
+ } else if (typeCode == TYPE_void_t) {
+ // disallow void argument types
+ JS_ReportError(aContext, "Cannot have void argument type");
+ return false;
+ }
+
+ // libffi cannot pass types of zero size by value.
+ JS_ASSERT(CType::GetSize(aContext, typeObj) != 0);
+
+ aResult.mType = typeObj;
+ aResult.mFFIType = *CType::GetFFIType(aContext, typeObj);
return true;
}
static bool
-PrepareValue(JSContext* aContext, const Type& aType, jsval aValue, Value& aResult)
+PrepareResultType(JSContext* aContext, jsval aType, Type& aResult)
{
- jsdouble d;
-
- switch (aType.mType) {
- 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`.
- if (!jsvalToIntStrict(aValue, &aResult.mValue.mUint8) ||
- aResult.mValue.mUint8 > 1)
- return TypeError(aContext, "boolean", aValue);
-
- aResult.mData = &aResult.mValue.mUint8;
- break;
- case TYPE_int8_t:
- // Do not implicitly lose bits.
- if (!jsvalToIntStrict(aValue, &aResult.mValue.mInt8))
- return TypeError(aContext, "int8", aValue);
-
- aResult.mData = &aResult.mValue.mInt8;
- break;
- case TYPE_int16_t:
- // Do not implicitly lose bits.
- if (!jsvalToIntStrict(aValue, &aResult.mValue.mInt16))
- return TypeError(aContext, "int16", aValue);
-
- aResult.mData = &aResult.mValue.mInt16;
- break;
- case TYPE_int32_t:
- // Do not implicitly lose bits.
- if (!jsvalToIntStrict(aValue, &aResult.mValue.mInt32))
- return TypeError(aContext, "int32", aValue);
-
- aResult.mData = &aResult.mValue.mInt32;
- break;
- case TYPE_int64_t:
- // Do not implicitly lose bits.
- if (!jsvalToIntStrict(aValue, &aResult.mValue.mInt64))
- return TypeError(aContext, "int64", aValue);
-
- aResult.mData = &aResult.mValue.mInt64;
- break;
- case TYPE_uint8_t:
- // Do not implicitly lose bits.
- if (!jsvalToIntStrict(aValue, &aResult.mValue.mUint8))
- return TypeError(aContext, "uint8", aValue);
-
- aResult.mData = &aResult.mValue.mUint8;
- break;
- case TYPE_uint16_t:
- // Do not implicitly lose bits.
- if (!jsvalToIntStrict(aValue, &aResult.mValue.mUint16))
- return TypeError(aContext, "uint16", aValue);
-
- aResult.mData = &aResult.mValue.mUint16;
- break;
- case TYPE_uint32_t:
- // Do not implicitly lose bits.
- if (!jsvalToIntStrict(aValue, &aResult.mValue.mUint32))
- return TypeError(aContext, "uint32", aValue);
-
- aResult.mData = &aResult.mValue.mUint32;
- break;
- case TYPE_uint64_t:
- // Do not implicitly lose bits.
- if (!jsvalToIntStrict(aValue, &aResult.mValue.mUint64))
- return TypeError(aContext, "uint64", aValue);
-
- aResult.mData = &aResult.mValue.mUint64;
- break;
- case TYPE_float:
- if (!jsvalToDoubleStrict(aValue, &d))
- return TypeError(aContext, "float", aValue);
-
- // The following cast silently throws 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.
- aResult.mValue.mFloat = float(d);
- aResult.mData = &aResult.mValue.mFloat;
- break;
- case TYPE_double:
- if (!jsvalToDoubleStrict(aValue, &d))
- return TypeError(aContext, "double", aValue);
-
- aResult.mValue.mDouble = d;
- aResult.mData = &aResult.mValue.mDouble;
- break;
- case TYPE_string:
- if (JSVAL_IS_NULL(aValue)) {
- // Allow passing a null pointer.
- aResult.mValue.mPointer = nsnull;
- } else if (JSVAL_IS_STRING(aValue)) {
- aResult.mValue.mPointer = JS_GetStringBytes(JSVAL_TO_STRING(aValue));
- } else {
- // Don't implicitly convert to string. Users can implicitly convert
- // with `String(x)` or `""+x`.
- return TypeError(aContext, "string", aValue);
- }
-
- aResult.mData = &aResult.mValue.mPointer;
- break;
- case TYPE_ustring:
- if (JSVAL_IS_NULL(aValue)) {
- // Allow passing a null pointer.
- aResult.mValue.mPointer = nsnull;
- } else if (JSVAL_IS_STRING(aValue)) {
- aResult.mValue.mPointer = JS_GetStringChars(JSVAL_TO_STRING(aValue));
- } else {
- // Don't implicitly convert to string. Users can implicitly convert
- // with `String(x)` or `""+x`.
- return TypeError(aContext, "ustring", aValue);
- }
-
- aResult.mData = &aResult.mValue.mPointer;
- break;
- default:
- NS_NOTREACHED("invalid type");
+ if (!JSVAL_IS_OBJECT(aType) ||
+ JSVAL_IS_NULL(aType) ||
+ !CType::IsCType(aContext, JSVAL_TO_OBJECT(aType))) {
+ JS_ReportError(aContext, "not a ctypes type");
return false;
}
- return true;
-}
+ JSObject* typeObj = JSVAL_TO_OBJECT(aType);
+ TypeCode typeCode = CType::GetTypeCode(aContext, typeObj);
-static void
-PrepareReturnValue(const Type& aType, Value& aResult)
-{
- switch (aType.mType) {
- case TYPE_void_t:
- aResult.mData = nsnull;
- break;
- case TYPE_int8_t:
- aResult.mData = &aResult.mValue.mInt8;
- break;
- case TYPE_int16_t:
- aResult.mData = &aResult.mValue.mInt16;
- break;
- case TYPE_int32_t:
- aResult.mData = &aResult.mValue.mInt32;
- break;
- case TYPE_int64_t:
- aResult.mData = &aResult.mValue.mInt64;
- break;
- case TYPE_bool:
- case TYPE_uint8_t:
- aResult.mData = &aResult.mValue.mUint8;
- break;
- case TYPE_uint16_t:
- aResult.mData = &aResult.mValue.mUint16;
- break;
- case TYPE_uint32_t:
- aResult.mData = &aResult.mValue.mUint32;
- break;
- case TYPE_uint64_t:
- aResult.mData = &aResult.mValue.mUint64;
- break;
- case TYPE_float:
- aResult.mData = &aResult.mValue.mFloat;
- break;
- case TYPE_double:
- aResult.mData = &aResult.mValue.mDouble;
- break;
- case TYPE_string:
- case TYPE_ustring:
- aResult.mData = &aResult.mValue.mPointer;
- break;
- default:
- NS_NOTREACHED("invalid type");
- break;
- }
-}
-
-static bool
-ConvertReturnValue(JSContext* aContext,
- const Type& aResultType,
- const Value& aResultValue,
- jsval* aValue)
-{
- switch (aResultType.mType) {
- case TYPE_void_t:
- *aValue = JSVAL_VOID;
- break;
- case TYPE_bool:
- *aValue = aResultValue.mValue.mUint8 ? JSVAL_TRUE : JSVAL_FALSE;
- break;
- case TYPE_int8_t:
- *aValue = INT_TO_JSVAL(aResultValue.mValue.mInt8);
- break;
- case TYPE_int16_t:
- *aValue = INT_TO_JSVAL(aResultValue.mValue.mInt16);
- break;
- case TYPE_int32_t:
- if (!JS_NewNumberValue(aContext, jsdouble(aResultValue.mValue.mInt32), aValue))
- return false;
- break;
- case TYPE_int64_t:
- // Implicit conversion with loss of bits. :-[
- if (!JS_NewNumberValue(aContext, jsdouble(aResultValue.mValue.mInt64), aValue))
- return false;
- break;
- case TYPE_uint8_t:
- *aValue = INT_TO_JSVAL(aResultValue.mValue.mUint8);
- break;
- case TYPE_uint16_t:
- *aValue = INT_TO_JSVAL(aResultValue.mValue.mUint16);
- break;
- case TYPE_uint32_t:
- if (!JS_NewNumberValue(aContext, jsdouble(aResultValue.mValue.mUint32), aValue))
- return false;
- break;
- case TYPE_uint64_t:
- // Implicit conversion with loss of bits. :-[
- if (!JS_NewNumberValue(aContext, jsdouble(aResultValue.mValue.mUint64), aValue))
- return false;
- break;
- case TYPE_float:
- if (!JS_NewNumberValue(aContext, jsdouble(aResultValue.mValue.mFloat), aValue))
- return false;
- break;
- case TYPE_double:
- if (!JS_NewNumberValue(aContext, jsdouble(aResultValue.mValue.mDouble), aValue))
- return false;
- break;
- case TYPE_string: {
- if (!aResultValue.mValue.mPointer) {
- // Allow returning a null pointer.
- *aValue = JSVAL_NULL;
- } else {
- JSString *jsstring = JS_NewStringCopyZ(aContext,
- reinterpret_cast(aResultValue.mValue.mPointer));
- if (!jsstring)
- return false;
-
- *aValue = STRING_TO_JSVAL(jsstring);
- }
- break;
- }
- case TYPE_ustring: {
- if (!aResultValue.mValue.mPointer) {
- // Allow returning a null pointer.
- *aValue = JSVAL_NULL;
- } else {
- JSString *jsstring = JS_NewUCStringCopyZ(aContext,
- reinterpret_cast(aResultValue.mValue.mPointer));
- if (!jsstring)
- return false;
-
- *aValue = STRING_TO_JSVAL(jsstring);
- }
- break;
- }
- default:
- NS_NOTREACHED("invalid type");
+ // Arrays can never be return types.
+ if (typeCode == TYPE_array) {
+ JS_ReportError(aContext, "Result type cannot be an array");
return false;
}
+#ifdef _MSC_VER
+ // Our libffi_msvc fork doesn't support returning structs by value yet.
+ if (typeCode == TYPE_struct) {
+ JS_ReportError(aContext,
+ "Returning structs by value is unsupported on Windows");
+ return false;
+ }
+#endif
+
+ // libffi cannot pass types of zero size by value.
+ JS_ASSERT(typeCode == TYPE_void_t || CType::GetSize(aContext, typeObj) != 0);
+
+ aResult.mType = typeObj;
+ aResult.mFFIType = *CType::GetFFIType(aContext, typeObj);
return true;
}
@@ -516,7 +178,7 @@ Function::Init(JSContext* aContext,
}
// prepare the result type
- if (!PrepareType(aContext, aResultType, mResultType))
+ if (!PrepareResultType(aContext, aResultType, mResultType))
return false;
// prepare the argument types
@@ -525,12 +187,6 @@ Function::Init(JSContext* aContext,
if (!PrepareType(aContext, aArgTypes[i], *mArgTypes.AppendElement()))
return false;
- // disallow void argument types
- if (mArgTypes[i].mType == TYPE_void_t) {
- JS_ReportError(aContext, "Cannot have void argument type");
- return false;
- }
-
// ffi_prep_cif requires an array of ffi_types; prepare it separately.
mFFITypes.AppendElement(&mArgTypes[i].mFFIType);
}
@@ -561,39 +217,63 @@ Function::Execute(JSContext* cx, PRUint32 argc, jsval* vp)
}
// prepare the values for each argument
- nsAutoTArray values;
+ nsAutoTArray values;
+ nsAutoTArray strings;
for (PRUint32 i = 0; i < mArgTypes.Length(); ++i) {
- if (!PrepareValue(cx, mArgTypes[i], JS_ARGV(cx, vp)[i], *values.AppendElement()))
+ AutoValue& value = *values.AppendElement();
+ jsval arg = JS_ARGV(cx, vp)[i];
+ bool freePointer = false;
+ if (!value.SizeToType(cx, mArgTypes[i].mType)) {
+ JS_ReportAllocationOverflow(cx);
return false;
- }
+ }
- // create an array of pointers to each value, for passing to ffi_call
- nsAutoTArray ffiValues;
- for (PRUint32 i = 0; i < mArgTypes.Length(); ++i) {
- ffiValues.AppendElement(values[i].mData);
+ if (!ImplicitConvert(cx, arg, mArgTypes[i].mType, value.mData, true, &freePointer))
+ return false;
+
+ if (freePointer) {
+ // ImplicitConvert converted a string for us, which we have to free.
+ // Keep track of it.
+ strings.AppendElement()->mData = *static_cast(value.mData);
+ }
}
// initialize a pointer to an appropriate location, for storing the result
- Value resultValue;
- PrepareReturnValue(mResultType, resultValue);
+ AutoValue resultValue;
+ if (CType::GetTypeCode(cx, mResultType.mType) != TYPE_void_t &&
+ !resultValue.SizeToType(cx, mResultType.mType)) {
+ JS_ReportAllocationOverflow(cx);
+ return false;
+ }
// suspend the request before we call into the function, since the call
// may block or otherwise take a long time to return.
jsrefcount rc = JS_SuspendRequest(cx);
- ffi_call(&mCIF, FFI_FN(mFunc), resultValue.mData, ffiValues.Elements());
+ ffi_call(&mCIF, FFI_FN(mFunc), resultValue.mData, reinterpret_cast(values.Elements()));
JS_ResumeRequest(cx, rc);
// prepare a JS object from the result
jsval rval;
- if (!ConvertReturnValue(cx, mResultType, resultValue, &rval))
+ if (!ConvertToJS(cx, mResultType.mType, NULL, resultValue.mData, false, &rval))
return false;
JS_SET_RVAL(cx, vp, rval);
return true;
}
+void
+Function::Trace(JSTracer *trc)
+{
+ // Identify the result CType to the tracer.
+ JS_CALL_TRACER(trc, mResultType.mType, JSTRACE_OBJECT, "CType");
+
+ // Identify each argument CType to the tracer.
+ for (PRUint32 i = 0; i < mArgTypes.Length(); ++i)
+ JS_CALL_TRACER(trc, mArgTypes[i].mType, JSTRACE_OBJECT, "CType");
+}
+
/*******************************************************************************
** JSObject implementation
*******************************************************************************/
@@ -627,15 +307,16 @@ Function::Create(JSContext* aContext,
JSAutoTempValueRooter fnRoot(aContext, fnObj);
// stash a pointer to self, which Function::Call will need at call time
- if (!JS_SetReservedSlot(aContext, fnObj, 0, PRIVATE_TO_JSVAL(self.get())))
+ if (!JS_SetReservedSlot(aContext, fnObj, SLOT_FUNCTION, PRIVATE_TO_JSVAL(self.get())))
return NULL;
// make a strong reference to the library for GC-safety
- if (!JS_SetReservedSlot(aContext, fnObj, 1, OBJECT_TO_JSVAL(aLibrary)))
+ if (!JS_SetReservedSlot(aContext, fnObj, SLOT_LIBRARYOBJ, OBJECT_TO_JSVAL(aLibrary)))
return NULL;
- // tell the library we exist, so it can delete our Function instance
- // when it comes time to finalize. (JS functions don't have finalizers.)
+ // Tell the library we exist, so it can (a) identify our CTypes to the tracer
+ // and (b) delete our Function instance when it comes time to finalize.
+ // (JS functions don't have finalizers.)
if (!Library::AddFunction(aContext, aLibrary, self))
return NULL;
@@ -647,7 +328,7 @@ static Function*
GetFunction(JSContext* cx, JSObject* obj)
{
jsval slot;
- JS_GetReservedSlot(cx, obj, 0, &slot);
+ JS_GetReservedSlot(cx, obj, SLOT_FUNCTION, &slot);
return static_cast(JSVAL_TO_PRIVATE(slot));
}
@@ -657,7 +338,7 @@ Function::Call(JSContext* cx, uintN argc, jsval* vp)
JSObject* callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
jsval slot;
- JS_GetReservedSlot(cx, callee, 1, &slot);
+ JS_GetReservedSlot(cx, callee, SLOT_LIBRARYOBJ, &slot);
PRLibrary* library = Library::GetLibrary(cx, JSVAL_TO_OBJECT(slot));
if (!library) {
diff --git a/js/ctypes/Function.h b/js/ctypes/Function.h
index 7986bdeb2280..33a8d08ac716 100644
--- a/js/ctypes/Function.h
+++ b/js/ctypes/Function.h
@@ -40,48 +40,45 @@
#ifndef FUNCTION_H
#define FUNCTION_H
-#include "Module.h"
+#include "CTypes.h"
#include "nsTArray.h"
#include "prlink.h"
-#include "ffi.h"
namespace mozilla {
namespace ctypes {
-// for JS error reporting
-enum ErrorNum {
-#define MSG_DEF(name, number, count, exception, format) \
- name = number,
-#include "ctypes.msg"
-#undef MSG_DEF
- CTYPESERR_LIMIT
+enum FunctionSlot
+{
+ SLOT_FUNCTION = 0,
+ SLOT_LIBRARYOBJ = 1
+ // JSFunction objects always get exactly two slots.
};
-const JSErrorFormatString*
-GetErrorMessage(void* userRef, const char* locale, const uintN errorNumber);
-
struct Type
{
ffi_type mFFIType;
- TypeCode mType;
+ JSObject* mType;
};
-struct Value
+struct AutoValue
{
+ AutoValue() : mData(NULL) { }
+
+ ~AutoValue()
+ {
+ delete static_cast(mData);
+ }
+
+ bool SizeToType(JSContext* cx, JSObject* type)
+ {
+ size_t size = CType::GetSize(cx, type);
+ mData = new char[size];
+ if (mData)
+ memset(mData, 0, size);
+ return mData != NULL;
+ }
+
void* mData;
- union {
- PRInt8 mInt8;
- PRInt16 mInt16;
- PRInt32 mInt32;
- PRInt64 mInt64;
- PRUint8 mUint8;
- PRUint16 mUint16;
- PRUint32 mUint32;
- PRUint64 mUint64;
- float mFloat;
- double mDouble;
- void* mPointer;
- } mValue;
};
class Function
@@ -90,6 +87,7 @@ public:
Function();
Function*& Next() { return mNext; }
+ void Trace(JSTracer *trc);
static JSObject* Create(JSContext* aContext, JSObject* aLibrary, PRFuncPtr aFunc, const char* aName, jsval aCallType, jsval aResultType, jsval* aArgTypes, uintN aArgLength);
static JSBool Call(JSContext* cx, uintN argc, jsval* vp);
diff --git a/js/ctypes/Library.cpp b/js/ctypes/Library.cpp
index edbd2f2fdb97..9aded58465d9 100644
--- a/js/ctypes/Library.cpp
+++ b/js/ctypes/Library.cpp
@@ -38,6 +38,7 @@
*
* ***** END LICENSE BLOCK ***** */
+#include "jscntxt.h"
#include "Library.h"
#include "Function.h"
#include "nsServiceManagerUtils.h"
@@ -55,15 +56,18 @@ namespace ctypes {
static JSClass sLibraryClass = {
"Library",
- JSCLASS_HAS_RESERVED_SLOTS(2),
+ JSCLASS_HAS_RESERVED_SLOTS(LIBRARY_SLOTS) | JSCLASS_MARK_IS_TRACE,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub,JS_ResolveStub, JS_ConvertStub, Library::Finalize,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+ NULL, NULL, NULL, NULL, NULL, NULL, JS_CLASS_TRACE(Library::Trace), NULL
};
+#define CTYPESFN_FLAGS \
+ (JSFUN_FAST_NATIVE | JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
+
static JSFunctionSpec sLibraryFunctions[] = {
- JS_FN("close", Library::Close, 0, JSFUN_FAST_NATIVE | JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT),
- JS_FN("declare", Library::Declare, 0, JSFUN_FAST_NATIVE | JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT),
+ JS_FN("close", Library::Close, 0, CTYPESFN_FLAGS),
+ JS_FN("declare", Library::Declare, 0, CTYPESFN_FLAGS),
JS_FS_END
};
@@ -73,13 +77,15 @@ Library::Create(JSContext* cx, jsval aPath)
JSObject* libraryObj = JS_NewObject(cx, &sLibraryClass, NULL, NULL);
if (!libraryObj)
return NULL;
+ JSAutoTempValueRooter root(cx, libraryObj);
// initialize the library
- if (!JS_SetReservedSlot(cx, libraryObj, 0, PRIVATE_TO_JSVAL(NULL)))
+ if (!JS_SetReservedSlot(cx, libraryObj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(NULL)))
return NULL;
// initialize our Function list to empty
- if (!JS_SetReservedSlot(cx, libraryObj, 1, PRIVATE_TO_JSVAL(NULL)))
+ if (!JS_SetReservedSlot(cx, libraryObj, SLOT_FUNCTIONLIST,
+ PRIVATE_TO_JSVAL(NULL)))
return NULL;
// attach API functions
@@ -92,21 +98,22 @@ Library::Create(JSContext* cx, jsval aPath)
// get the path argument. we accept either an nsILocalFile or a string path.
// determine which we have...
if (JSVAL_IS_STRING(aPath)) {
- const jschar* path = JS_GetStringChars(JSVAL_TO_STRING(aPath));
+ const PRUnichar* path = reinterpret_cast(
+ JS_GetStringCharsZ(cx, JSVAL_TO_STRING(aPath)));
if (!path)
return NULL;
- // We don't use nsILocalFile.
- // Because this interface doesn't resolve library path to use system search rule.
+ // We don't use nsILocalFile, because it doesn't use the system search
+ // rules when resolving library path.
PRLibSpec libSpec;
#ifdef XP_WIN
// On Windows, converting to native charset may corrupt path string.
// So, we have to use Unicode path directly.
- libSpec.value.pathname_u = reinterpret_cast(path);
+ libSpec.value.pathname_u = path;
libSpec.type = PR_LibSpec_PathnameU;
#else
nsCAutoString nativePath;
- NS_CopyUnicodeToNative(nsDependentString(reinterpret_cast(path)), nativePath);
+ NS_CopyUnicodeToNative(nsDependentString(path), nativePath);
libSpec.value.pathname = nativePath.get();
libSpec.type = PR_LibSpec_Pathname;
#endif
@@ -132,7 +139,8 @@ Library::Create(JSContext* cx, jsval aPath)
}
// stash the library
- if (!JS_SetReservedSlot(cx, libraryObj, 0, PRIVATE_TO_JSVAL(library)))
+ if (!JS_SetReservedSlot(cx, libraryObj, SLOT_LIBRARY,
+ PRIVATE_TO_JSVAL(library)))
return NULL;
return libraryObj;
@@ -144,7 +152,7 @@ Library::GetLibrary(JSContext* cx, JSObject* obj)
JS_ASSERT(JS_GET_CLASS(cx, obj) == &sLibraryClass);
jsval slot;
- JS_GetReservedSlot(cx, obj, 0, &slot);
+ JS_GetReservedSlot(cx, obj, SLOT_LIBRARY, &slot);
return static_cast(JSVAL_TO_PRIVATE(slot));
}
@@ -154,7 +162,7 @@ GetFunctionList(JSContext* cx, JSObject* obj)
JS_ASSERT(JS_GET_CLASS(cx, obj) == &sLibraryClass);
jsval slot;
- JS_GetReservedSlot(cx, obj, 1, &slot);
+ JS_GetReservedSlot(cx, obj, SLOT_FUNCTIONLIST, &slot);
return static_cast(JSVAL_TO_PRIVATE(slot));
}
@@ -163,7 +171,20 @@ Library::AddFunction(JSContext* cx, JSObject* aLibrary, Function* aFunction)
{
// add the new Function instance to the head of the list
aFunction->Next() = GetFunctionList(cx, aLibrary);
- return JS_SetReservedSlot(cx, aLibrary, 1, PRIVATE_TO_JSVAL(aFunction));
+ return JS_SetReservedSlot(cx, aLibrary, SLOT_FUNCTIONLIST,
+ PRIVATE_TO_JSVAL(aFunction)) != JS_FALSE;
+}
+
+void
+Library::Trace(JSTracer *trc, JSObject* obj)
+{
+ // Walk the Function list and for each Function, identify each CType
+ // associated with it to the tracer.
+ Function* current = GetFunctionList(trc->context, obj);
+ while (current) {
+ current->Trace(trc);
+ current = current->Next();
+ }
}
void
@@ -186,7 +207,7 @@ Library::Finalize(JSContext* cx, JSObject* obj)
JSBool
Library::Open(JSContext* cx, uintN argc, jsval *vp)
{
- if (argc != 1) {
+ if (argc != 1 || JSVAL_IS_VOID(JS_ARGV(cx, vp)[0])) {
JS_ReportError(cx, "open requires a single argument");
return JS_FALSE;
}
@@ -217,8 +238,8 @@ Library::Close(JSContext* cx, uintN argc, jsval* vp)
// delete our internal objects
Finalize(cx, obj);
- JS_SetReservedSlot(cx, obj, 0, PRIVATE_TO_JSVAL(NULL));
- JS_SetReservedSlot(cx, obj, 1, PRIVATE_TO_JSVAL(NULL));
+ JS_SetReservedSlot(cx, obj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(NULL));
+ JS_SetReservedSlot(cx, obj, SLOT_FUNCTIONLIST, PRIVATE_TO_JSVAL(NULL));
JS_SET_RVAL(cx, vp, JSVAL_VOID);
return JS_TRUE;
@@ -251,14 +272,15 @@ Library::Declare(JSContext* cx, uintN argc, jsval* vp)
return JS_FALSE;
}
- const char* name = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
+ const char* name = JS_GetStringBytesZ(cx, JSVAL_TO_STRING(argv[0]));
PRFuncPtr func = PR_FindFunctionSymbol(library, name);
if (!func) {
JS_ReportError(cx, "couldn't find function symbol in library");
return JS_FALSE;
}
- JSObject* fn = Function::Create(cx, obj, func, name, argv[1], argv[2], &argv[3], argc - 3);
+ JSObject* fn = Function::Create(cx, obj, func, name, argv[1], argv[2],
+ &argv[3], argc - 3);
if (!fn)
return JS_FALSE;
diff --git a/js/ctypes/Library.h b/js/ctypes/Library.h
index 8f3bb25f2943..87f67421800d 100644
--- a/js/ctypes/Library.h
+++ b/js/ctypes/Library.h
@@ -40,19 +40,24 @@
#ifndef LIBRARY_H
#define LIBRARY_H
-#include "Function.h"
-#include "jsapi.h"
-
struct PRLibrary;
-class Function;
namespace mozilla {
namespace ctypes {
+class Function;
+
+enum LibrarySlot {
+ SLOT_LIBRARY = 0,
+ SLOT_FUNCTIONLIST = 1,
+ LIBRARY_SLOTS
+};
+
class Library
{
public:
static JSObject* Create(JSContext* cx, jsval aPath);
+ static void Trace(JSTracer *trc, JSObject* obj);
static void Finalize(JSContext* cx, JSObject* obj);
static PRLibrary* GetLibrary(JSContext* cx, JSObject* obj);
diff --git a/js/ctypes/Makefile.in b/js/ctypes/Makefile.in
index 49bacbc89aa5..f7309589832a 100644
--- a/js/ctypes/Makefile.in
+++ b/js/ctypes/Makefile.in
@@ -62,6 +62,7 @@ IS_COMPONENT = 1
CPPSRCS = \
Function.cpp \
Library.cpp \
+ CTypes.cpp \
Module.cpp \
$(NULL)
diff --git a/js/ctypes/Module.cpp b/js/ctypes/Module.cpp
index 01a099afaa3d..7b9bcc57792a 100644
--- a/js/ctypes/Module.cpp
+++ b/js/ctypes/Module.cpp
@@ -39,6 +39,7 @@
#include "Module.h"
#include "Library.h"
+#include "CTypes.h"
#include "nsIGenericFactory.h"
#include "nsMemory.h"
@@ -83,74 +84,15 @@ Module::Call(nsIXPConnectWrappedNative* wrapper,
return NS_OK;
}
-static JSClass sCABIClass = {
- "CABI",
- JSCLASS_HAS_RESERVED_SLOTS(1),
- JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
- JS_EnumerateStub,JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
-};
-
-static JSClass sCTypeClass = {
- "CType",
- JSCLASS_HAS_RESERVED_SLOTS(1),
- JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
- JS_EnumerateStub,JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
-};
+#define CTYPESFN_FLAGS \
+ (JSFUN_FAST_NATIVE | JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
static JSFunctionSpec sModuleFunctions[] = {
- JS_FN("open", Library::Open, 0, JSFUN_FAST_NATIVE | JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT),
+ JS_FN("open", Library::Open, 1, CTYPESFN_FLAGS),
+ JS_FN("cast", CData::Cast, 2, CTYPESFN_FLAGS),
JS_FS_END
};
-ABICode
-Module::GetABICode(JSContext* cx, jsval val)
-{
- // make sure we have an object representing a CABI class,
- // and extract the enumerated class type from the reserved slot.
- if (!JSVAL_IS_OBJECT(val))
- return INVALID_ABI;
-
- JSObject* obj = JSVAL_TO_OBJECT(val);
- if (JS_GET_CLASS(cx, obj) != &sCABIClass)
- return INVALID_ABI;
-
- jsval result;
- JS_GetReservedSlot(cx, obj, 0, &result);
-
- return ABICode(JSVAL_TO_INT(result));
-}
-
-TypeCode
-Module::GetTypeCode(JSContext* cx, jsval val)
-{
- // make sure we have an object representing a CType class,
- // and extract the enumerated class type from the reserved slot.
- if (!JSVAL_IS_OBJECT(val))
- return INVALID_TYPE;
-
- JSObject* obj = JSVAL_TO_OBJECT(val);
- if (JS_GET_CLASS(cx, obj) != &sCTypeClass)
- return INVALID_TYPE;
-
- jsval result;
- JS_GetReservedSlot(cx, obj, 0, &result);
-
- return TypeCode(JSVAL_TO_INT(result));
-}
-
-static bool
-DefineConstant(JSContext* cx, JSObject* parent, JSClass* clasp, const char* name, jsint code)
-{
- JSObject* obj = JS_DefineObject(cx, parent, name, clasp, NULL,
- JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
- if (!obj)
- return false;
-
- return JS_SetReservedSlot(cx, obj, 0, INT_TO_JSVAL(code));
-}
-
bool
Module::Init(JSContext* cx, JSObject* aGlobal)
{
@@ -163,22 +105,16 @@ Module::Init(JSContext* cx, JSObject* aGlobal)
NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
return false;
- // attach classes representing ABI and type constants
-#define DEFINE_ABI(name) \
- if (!DefineConstant(cx, ctypes, &sCABIClass, #name, ABI_##name)) \
+ if (!InitTypeClasses(cx, ctypes))
return false;
-#define DEFINE_TYPE(name) \
- if (!DefineConstant(cx, ctypes, &sCTypeClass, #name, TYPE_##name)) \
- return false;
-#include "types.h"
-#undef DEFINE_ABI
-#undef DEFINE_TYPE
// attach API functions
if (!JS_DefineFunctions(cx, ctypes, sModuleFunctions))
return false;
- return true;
+ // Seal the ctypes object, to prevent modification. (This single object
+ // instance is shared amongst everyone who imports the ctypes module.)
+ return JS_SealObject(cx, ctypes, JS_FALSE) != JS_FALSE;
}
}
diff --git a/js/ctypes/Module.h b/js/ctypes/Module.h
index a4a8cc897cdc..5f1ccfdff678 100644
--- a/js/ctypes/Module.h
+++ b/js/ctypes/Module.h
@@ -44,27 +44,6 @@
namespace mozilla {
namespace ctypes {
-// Each internal CABI and CType class (representing ABI and type constants,
-// respectively) has a unique identifier, stored in a reserved slot on the
-// JSObject.
-enum ABICode {
-#define DEFINE_ABI(name) ABI_##name,
-#define DEFINE_TYPE(name)
-#include "types.h"
-#undef DEFINE_ABI
-#undef DEFINE_TYPE
- INVALID_ABI
-};
-
-enum TypeCode {
-#define DEFINE_ABI(name)
-#define DEFINE_TYPE(name) TYPE_##name,
-#include "types.h"
-#undef DEFINE_ABI
-#undef DEFINE_TYPE
- INVALID_TYPE
-};
-
class Module : public nsIXPCScriptable
{
public:
@@ -76,9 +55,6 @@ public:
// Creates the ctypes object and attaches it to the global object.
bool Init(JSContext* aContext, JSObject* aGlobal);
- static ABICode GetABICode(JSContext* cx, jsval val);
- static TypeCode GetTypeCode(JSContext* cx, jsval val);
-
private:
~Module();
};
diff --git a/js/ctypes/tests/jsctypes-test.cpp b/js/ctypes/tests/jsctypes-test.cpp
index 6194196c3cf8..621e128449da 100644
--- a/js/ctypes/tests/jsctypes-test.cpp
+++ b/js/ctypes/tests/jsctypes-test.cpp
@@ -44,123 +44,127 @@
#include
void
-test_v()
+test_void_t_cdecl()
{
// do nothing
return;
}
-PRInt8
-test_i8()
-{
- return 123;
+#define DEFINE_TYPE(name, type, ffiType) \
+type \
+get_##name##_cdecl() \
+{ \
+ return 109.25; \
+} \
+ \
+type \
+set_##name##_cdecl(type x) \
+{ \
+ return x; \
+} \
+ \
+type \
+sum_##name##_cdecl(type x, type y) \
+{ \
+ return x + y; \
+} \
+ \
+type \
+sum_alignb_##name##_cdecl(char a, type x, char b, type y, char c) \
+{ \
+ return x + y; \
+} \
+ \
+type \
+sum_alignf_##name##_cdecl(float a, type x, float b, type y, float c) \
+{ \
+ return x + y; \
+} \
+ \
+type \
+sum_many_##name##_cdecl( \
+ type a, type b, type c, type d, type e, type f, type g, type h, type i, \
+ type j, type k, type l, type m, type n, type o, type p, type q, type r) \
+{ \
+ return a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r;\
+} \
+ \
+struct align_##name { \
+ char x; \
+ type y; \
+}; \
+struct nested_##name { \
+ char a; \
+ align_##name b; \
+ char c; \
+}; \
+ \
+void \
+get_##name##_stats(size_t* align, size_t* size, size_t* nalign, size_t* nsize, \
+ size_t offsets[]) \
+{ \
+ *align = offsetof(align_##name, y); \
+ *size = sizeof(align_##name); \
+ *nalign = offsetof(nested_##name, b); \
+ *nsize = sizeof(nested_##name); \
+ offsets[0] = offsetof(align_##name, y); \
+ offsets[1] = offsetof(nested_##name, b); \
+ offsets[2] = offsetof(nested_##name, c); \
}
-PRInt8
-test_i8_i8(PRInt8 number)
-{
- return number;
+#include "../typedefs.h"
+
+#if defined(_WIN32) && !defined(__WIN64)
+
+#define DEFINE_TYPE(name, type, ffiType) \
+type NS_STDCALL \
+get_##name##_stdcall() \
+{ \
+ return 109.25; \
+} \
+ \
+type NS_STDCALL \
+set_##name##_stdcall(type x) \
+{ \
+ return x; \
+} \
+ \
+type NS_STDCALL \
+sum_##name##_stdcall(type x, type y) \
+{ \
+ return x + y; \
+} \
+ \
+type NS_STDCALL \
+sum_alignb_##name##_stdcall(char a, type x, char b, type y, char c) \
+{ \
+ return x + y; \
+} \
+ \
+type NS_STDCALL \
+sum_alignf_##name##_stdcall(float a, type x, float b, type y, float c) \
+{ \
+ return x + y; \
+} \
+ \
+type NS_STDCALL \
+sum_many_##name##_stdcall( \
+ type a, type b, type c, type d, type e, type f, type g, type h, type i, \
+ type j, type k, type l, type m, type n, type o, type p, type q, type r) \
+{ \
+ return a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r;\
}
-PRInt8
-test_i8_i8_sum(PRInt8 number1, PRInt8 number2)
+#include "../typedefs.h"
+
+void NS_STDCALL
+test_void_t_stdcall()
{
- return number1 + number2;
+ // do nothing
+ return;
}
-PRInt16
-test_i16()
-{
- return 12345;
-}
-
-PRInt16
-test_i16_i16(PRInt16 number)
-{
- return number;
-}
-
-PRInt16
-test_i16_i16_sum(PRInt16 number1, PRInt16 number2)
-{
- return number1 + number2;
-}
-
-PRInt32
-test_i32()
-{
- return 123456789;
-}
-
-PRInt32
-test_i32_i32(PRInt32 number)
-{
- return number;
-}
-
-PRInt32
-test_i32_i32_sum(PRInt32 number1, PRInt32 number2)
-{
- return number1 + number2;
-}
-
-PRInt64
-test_i64()
-{
-#if defined(WIN32) && !defined(__GNUC__)
- return 0x28590a1c921de000i64;
-#else
- return 0x28590a1c921de000LL;
-#endif
-}
-
-PRInt64
-test_i64_i64(PRInt64 number)
-{
- return number;
-}
-
-PRInt64
-test_i64_i64_sum(PRInt64 number1, PRInt64 number2)
-{
- return number1 + number2;
-}
-
-float
-test_f()
-{
- return 123456.5f;
-}
-
-float
-test_f_f(float number)
-{
- return number;
-}
-
-float
-test_f_f_sum(float number1, float number2)
-{
- return (number1 + number2);
-}
-
-double
-test_d()
-{
- return 1234567890123456789.5;
-}
-
-double
-test_d_d(double number)
-{
- return number;
-}
-
-double
-test_d_d_sum(double number1, double number2)
-{
- return (number1 + number2);
-}
+#endif /* defined(_WIN32) && !defined(__WIN64) */
PRInt32
test_ansi_len(const char* string)
@@ -194,8 +198,33 @@ test_ansi_echo(const char* string)
}
PRInt32
-test_floor(PRInt32 number1, float number2)
+test_pt_in_rect(RECT rc, POINT pt)
{
- return PRInt32(floor(float(number1) + number2));
+ if (pt.x < rc.left || pt.x > rc.right)
+ return 0;
+ if (pt.y < rc.bottom || pt.y > rc.top)
+ return 0;
+ return 1;
+}
+
+void
+test_init_pt(POINT* pt, PRInt32 x, PRInt32 y)
+{
+ pt->x = x;
+ pt->y = y;
+}
+
+PRInt32
+test_nested_struct(NESTED n)
+{
+ return PRInt32(n.n1 + n.n2 + n.inner.i1 + n.inner.i2 + n.inner.i3 + n.n3 + n.n4);
+}
+
+POINT
+test_struct_return(RECT r)
+{
+ POINT p;
+ p.x = r.left; p.y = r.top;
+ return p;
}
diff --git a/js/ctypes/tests/jsctypes-test.h b/js/ctypes/tests/jsctypes-test.h
index 7e1f81080672..9d5ac30af906 100644
--- a/js/ctypes/tests/jsctypes-test.h
+++ b/js/ctypes/tests/jsctypes-test.h
@@ -40,34 +40,56 @@
#include "nscore.h"
#include "prtypes.h"
+#include "jsapi.h"
+
+#define EXPORT_CDECL(type) NS_EXPORT type
+#define EXPORT_STDCALL(type) NS_EXPORT type NS_STDCALL
NS_EXTERN_C
{
- NS_EXPORT void test_v();
+ EXPORT_CDECL(void) test_void_t_cdecl();
- NS_EXPORT PRInt8 test_i8();
- NS_EXPORT PRInt8 test_i8_i8(PRInt8);
- NS_EXPORT PRInt8 test_i8_i8_sum(PRInt8, PRInt8);
+ EXPORT_CDECL(void*) get_voidptr_t_cdecl();
+ EXPORT_CDECL(void*) set_voidptr_t_cdecl(void*);
- NS_EXPORT PRInt16 test_i16();
- NS_EXPORT PRInt16 test_i16_i16(PRInt16);
- NS_EXPORT PRInt16 test_i16_i16_sum(PRInt16, PRInt16);
+#define DEFINE_TYPE(name, type, ffiType) \
+ EXPORT_CDECL(type) get_##name##_cdecl(); \
+ EXPORT_CDECL(type) set_##name##_cdecl(type); \
+ EXPORT_CDECL(type) sum_##name##_cdecl(type, type); \
+ EXPORT_CDECL(type) sum_alignb_##name##_cdecl(char, type, char, type, char); \
+ EXPORT_CDECL(type) sum_alignf_##name##_cdecl( \
+ float, type, float, type, float); \
+ EXPORT_CDECL(type) sum_many_##name##_cdecl( \
+ type, type, type, type, type, type, type, type, type, \
+ type, type, type, type, type, type, type, type, type); \
+ \
+ EXPORT_CDECL(void) get_##name##_stats(size_t* align, size_t* size, \
+ size_t* nalign, size_t* nsize, \
+ size_t offsets[]);
- NS_EXPORT PRInt32 test_i32();
- NS_EXPORT PRInt32 test_i32_i32(PRInt32);
- NS_EXPORT PRInt32 test_i32_i32_sum(PRInt32, PRInt32);
+#include "../typedefs.h"
- NS_EXPORT PRInt64 test_i64();
- NS_EXPORT PRInt64 test_i64_i64(PRInt64);
- NS_EXPORT PRInt64 test_i64_i64_sum(PRInt64, PRInt64);
+#if defined(_WIN32) && !defined(__WIN64)
+ EXPORT_STDCALL(void) test_void_t_stdcall();
- NS_EXPORT float test_f();
- NS_EXPORT float test_f_f(float);
- NS_EXPORT float test_f_f_sum(float, float);
+ EXPORT_STDCALL(void*) get_voidptr_t_stdcall();
+ EXPORT_STDCALL(void*) set_voidptr_t_stdcall(void*);
- NS_EXPORT double test_d();
- NS_EXPORT double test_d_d(double);
- NS_EXPORT double test_d_d_sum(double, double);
+#define DEFINE_TYPE(name, type, ffiType) \
+ EXPORT_STDCALL(type) get_##name##_stdcall(); \
+ EXPORT_STDCALL(type) set_##name##_stdcall(type); \
+ EXPORT_STDCALL(type) sum_##name##_stdcall(type, type); \
+ EXPORT_STDCALL(type) sum_alignb_##name##_stdcall( \
+ char, type, char, type, char); \
+ EXPORT_STDCALL(type) sum_alignf_##name##_stdcall( \
+ float, type, float, type, float); \
+ EXPORT_STDCALL(type) sum_many_##name##_stdcall( \
+ type, type, type, type, type, type, type, type, type, \
+ type, type, type, type, type, type, type, type, type);
+
+#include "../typedefs.h"
+
+#endif /* defined(_WIN32) && !defined(__WIN64) */
NS_EXPORT PRInt32 test_ansi_len(const char*);
NS_EXPORT PRInt32 test_wide_len(const PRUnichar*);
@@ -75,6 +97,36 @@ NS_EXTERN_C
NS_EXPORT const PRUnichar* test_wide_ret();
NS_EXPORT char* test_ansi_echo(const char*);
- NS_EXPORT PRInt32 test_floor(PRInt32, float);
+ struct POINT {
+ PRInt32 x;
+ PRInt32 y;
+ };
+
+ struct RECT {
+ PRInt32 top;
+ PRInt32 left;
+ PRInt32 bottom;
+ PRInt32 right;
+ };
+
+ struct INNER {
+ PRUint8 i1;
+ PRInt64 i2;
+ PRUint8 i3;
+ };
+
+ struct NESTED {
+ PRInt32 n1;
+ PRInt16 n2;
+ INNER inner;
+ PRInt64 n3;
+ PRInt32 n4;
+ };
+
+ NS_EXPORT PRInt32 test_pt_in_rect(RECT, POINT);
+ NS_EXPORT void test_init_pt(POINT* pt, PRInt32 x, PRInt32 y);
+
+ NS_EXPORT PRInt32 test_nested_struct(NESTED);
+ NS_EXPORT POINT test_struct_return(RECT);
}
diff --git a/js/ctypes/tests/unit/test_jsctypes.js.in b/js/ctypes/tests/unit/test_jsctypes.js.in
index ddeebe67261e..c01d3aee6645 100644
--- a/js/ctypes/tests/unit/test_jsctypes.js.in
+++ b/js/ctypes/tests/unit/test_jsctypes.js.in
@@ -62,281 +62,1386 @@ function do_check_throws(f, type, stack)
function run_test()
{
- var libfile = do_get_file(CTYPES_TEST_LIB);
-
// open the library with an nsILocalFile
- var library = ctypes.open(libfile);
+ let libfile = do_get_file(CTYPES_TEST_LIB);
+ let library = ctypes.open(libfile);
+ // Make sure we can call a function in the library.
run_void_tests(library);
- run_int8_tests(library);
- run_int16_tests(library);
- run_int32_tests(library);
- run_int64_tests(library);
- run_float_tests(library);
- run_double_tests(library);
+
+ // Test Int64 and UInt64.
+ run_Int64_tests();
+ run_UInt64_tests();
+
+ // Test that ctypes.CType behaves like an abstract type.
+ do_check_throws(function() { ctypes.CType(); }, Error);
+ do_check_throws(function() { new ctypes.CType() }, Error);
+ do_check_throws(function() { ctypes.CType(12); }, Error);
+ do_check_throws(function() { new ctypes.CType(12) }, Error);
+
+ // Test the basic bool, integer, and float types.
+ run_bool_tests(library);
+
+ run_integer_tests(library, ctypes.int8_t, "int8_t", 1, true, [-0x80, 0x7f]);
+ run_integer_tests(library, ctypes.int16_t, "int16_t", 2, true, [-0x8000, 0x7fff]);
+ run_integer_tests(library, ctypes.int32_t, "int32_t", 4, true, [-0x80000000, 0x7fffffff]);
+ run_integer_tests(library, ctypes.uint8_t, "uint8_t", 1, false, [0, 0xff]);
+ run_integer_tests(library, ctypes.uint16_t, "uint16_t", 2, false, [0, 0xffff]);
+ run_integer_tests(library, ctypes.uint32_t, "uint32_t", 4, false, [0, 0xffffffff]);
+ run_integer_tests(library, ctypes.short, "short", 2, true, [-0x8000, 0x7fff]);
+ run_integer_tests(library, ctypes.unsigned_short, "unsigned_short", 2, false, [0, 0xffff]);
+ run_integer_tests(library, ctypes.int, "int", 4, true, [-0x80000000, 0x7fffffff]);
+ run_integer_tests(library, ctypes.unsigned_int, "unsigned_int", 4, false, [0, 0xffffffff]);
+ run_integer_tests(library, ctypes.unsigned, "unsigned", 4, false, [0, 0xffffffff]);
+
+ run_float_tests(library, ctypes.float32_t, "float32_t", 4);
+ run_float_tests(library, ctypes.float64_t, "float64_t", 8);
+ run_float_tests(library, ctypes.float, "float", 4);
+ run_float_tests(library, ctypes.double, "double", 8);
+
+ // Test the wrapped integer types.
+ s64limits = ["-9223372036854775808", "9223372036854775807",
+ "-9223372036854775809", "9223372036854775808"];
+ u64limits = ["0", "18446744073709551615", "-1", "18446744073709551616"];
+
+ run_wrapped_integer_tests(library, ctypes.int64_t, "int64_t", 8, true,
+ ctypes.Int64, "ctypes.Int64", s64limits);
+ run_wrapped_integer_tests(library, ctypes.uint64_t, "uint64_t", 8, false,
+ ctypes.UInt64, "ctypes.UInt64", u64limits);
+ run_wrapped_integer_tests(library, ctypes.long_long, "long_long", 8, true,
+ ctypes.Int64, "ctypes.Int64", s64limits);
+ run_wrapped_integer_tests(library, ctypes.unsigned_long_long, "unsigned_long_long", 8, false,
+ ctypes.UInt64, "ctypes.UInt64", u64limits);
+
+ s32limits = [-0x80000000, 0x7fffffff, -0x80000001, 0x80000000];
+ u32limits = [0, 0xffffffff, -1, 0x100000000];
+
+ let slimits, ulimits;
+ if (ctypes.long.size == 8) {
+ slimits = s64limits;
+ ulimits = u64limits;
+ } else if (ctypes.long.size == 4) {
+ slimits = s32limits;
+ ulimits = u32limits;
+ } else {
+ do_throw("ctypes.long is not 4 or 8 bytes");
+ }
+
+ run_wrapped_integer_tests(library, ctypes.long, "long", ctypes.long.size, true,
+ ctypes.Int64, "ctypes.Int64", slimits);
+ run_wrapped_integer_tests(library, ctypes.unsigned_long, "unsigned_long", ctypes.long.size, false,
+ ctypes.UInt64, "ctypes.UInt64", ulimits);
+
+ if (ctypes.size_t.size == 8) {
+ slimits = s64limits;
+ ulimits = u64limits;
+ } else if (ctypes.size_t.size == 4) {
+ slimits = s32limits;
+ ulimits = u32limits;
+ } else {
+ do_throw("ctypes.size_t is not 4 or 8 bytes");
+ }
+
+ run_wrapped_integer_tests(library, ctypes.size_t, "size_t", ctypes.size_t.size, false,
+ ctypes.UInt64, "ctypes.UInt64", ulimits);
+ run_wrapped_integer_tests(library, ctypes.ssize_t, "ssize_t", ctypes.size_t.size, true,
+ ctypes.Int64, "ctypes.Int64", slimits);
+ run_wrapped_integer_tests(library, ctypes.uintptr_t, "uintptr_t", ctypes.size_t.size, false,
+ ctypes.UInt64, "ctypes.UInt64", ulimits);
+ run_wrapped_integer_tests(library, ctypes.intptr_t, "intptr_t", ctypes.size_t.size, true,
+ ctypes.Int64, "ctypes.Int64", slimits);
+
+ // Test the character types.
+ run_char_tests(library, ctypes.char, "char", 1, true, [-0x80, 0x7f]);
+ run_char_tests(library, ctypes.signed_char, "signed_char", 1, true, [-0x80, 0x7f]);
+ run_char_tests(library, ctypes.unsigned_char, "unsigned_char", 1, false, [0, 0xff]);
+ run_char_tests(library, ctypes.jschar, "jschar", 2, false, [0, 0xffff]);
+
+ // Test the special types.
+ run_StructType_tests();
+ run_PointerType_tests();
+ run_ArrayType_tests();
+
+ // Test the 'name' and 'toSource' of a long typename.
+ let ptrTo_ptrTo_arrayOf4_ptrTo_int32s =
+ new ctypes.PointerType(
+ new ctypes.PointerType(
+ new ctypes.ArrayType(
+ new ctypes.PointerType(ctypes.int32_t), 4)));
+ do_check_eq(ptrTo_ptrTo_arrayOf4_ptrTo_int32s.name, "int32_t*(**)[4]");
+
+ let source_t = new ctypes.StructType("source",
+ [{ a: ptrTo_ptrTo_arrayOf4_ptrTo_int32s }, { b: ctypes.int64_t }]);
+ do_check_eq(source_t.toSource(),
+ 'ctypes.StructType("source", [{ "a": ctypes.int32_t.ptr.array(4).ptr.ptr }, ' +
+ '{ "b": ctypes.int64_t }])');
+
+ // Test ctypes.cast.
+ run_cast_tests();
+
run_string_tests(library);
- run_mixed_tests(library);
+ run_struct_tests(library);
// test the string version of ctypes.open() as well
- var libpath = libfile.path;
+ let libpath = libfile.path;
library = ctypes.open(libpath);
run_void_tests(library);
// test library.close
- var test_v = library.declare("test_v", ctypes.default_abi, ctypes.void_t);
library.close();
- do_check_throws(function () { test_v(); }, Error);
- do_check_throws(function () { library.declare("test_v", ctypes.default_abi, ctypes.void_t); }, Error);
+ do_check_throws(function () { run_void_tests(library); }, Error);
// test that library functions throw when bound to other objects
library = ctypes.open(libpath);
let obj = {};
obj.declare = library.declare;
- do_check_throws(function () { obj.declare("test_v", ctypes.default_abi, ctypes.void_t); }, Error);
+ do_check_throws(function () { run_void_tests(obj); }, Error);
obj.close = library.close;
do_check_throws(function () { obj.close(); }, Error);
// test that functions work as properties of other objects
- var test_i8 = library.declare("test_i8", ctypes.default_abi, ctypes.int8_t);
- do_check_eq(test_i8(), 123);
- obj.t = test_i8;
- do_check_eq(test_i8(), 123);
- do_check_eq(obj.t(), 123);
+ let getter = library.declare("get_int8_t_cdecl", ctypes.default_abi, ctypes.int8_t);
+ do_check_eq(getter(), 109);
+ obj.t = getter;
+ do_check_eq(obj.t(), 109);
// bug 521937
- do_check_throws(function () { var nolib = ctypes.open("notfoundlibrary.dll"); nolib.close(); }, Error);
+ do_check_throws(function () { let nolib = ctypes.open("notfoundlibrary.dll"); nolib.close(); }, Error);
// bug 522360
do_check_eq(run_load_system_library(), true);
}
+function run_Int64_tests() {
+ do_check_throws(function() { ctypes.Int64(); }, Error);
+
+ let i = ctypes.Int64(0);
+ do_check_true(i instanceof ctypes.Int64);
+
+ // Test Int64.toString([radix]).
+ do_check_eq(i.toString(), "0");
+ for (let radix = 2; radix <= 36; ++radix)
+ do_check_eq(i.toString(radix), "0");
+ do_check_throws(function() { i.toString(0); }, Error);
+ do_check_throws(function() { i.toString(1); }, Error);
+ do_check_throws(function() { i.toString(37); }, Error);
+
+ i = ctypes.Int64("0x28590a1c921def71");
+ do_check_eq(i.toString(), i.toString(10));
+ do_check_eq(i.toString(10), "2907366152271163249");
+ do_check_eq(i.toString(16), "28590a1c921def71");
+ do_check_eq(i.toString(2), "10100001011001000010100001110010010010000111011110111101110001");
+
+ i = ctypes.Int64("-0x28590a1c921def71");
+ do_check_eq(i.toString(), i.toString(10));
+ do_check_eq(i.toString(10), "-2907366152271163249");
+ do_check_eq(i.toString(16), "-28590a1c921def71");
+ do_check_eq(i.toString(2), "-10100001011001000010100001110010010010000111011110111101110001");
+
+ i = ctypes.Int64("-0X28590A1c921DEf71");
+ do_check_eq(i.toString(), i.toString(10));
+ do_check_eq(i.toString(10), "-2907366152271163249");
+ do_check_eq(i.toString(16), "-28590a1c921def71");
+ do_check_eq(i.toString(2), "-10100001011001000010100001110010010010000111011110111101110001");
+
+ // Test Int64(primitive double) constructor.
+ i = ctypes.Int64(-0);
+ do_check_eq(i.toString(), "0");
+
+ i = ctypes.Int64(0x7ffffffffffff000);
+ do_check_eq(i.toString(), i.toString(10));
+ do_check_eq(i.toString(10), "9223372036854771712");
+ do_check_eq(i.toString(16), "7ffffffffffff000");
+ do_check_eq(i.toString(2), "111111111111111111111111111111111111111111111111111000000000000");
+
+ i = ctypes.Int64(-0x8000000000000000);
+ do_check_eq(i.toString(), i.toString(10));
+ do_check_eq(i.toString(10), "-9223372036854775808");
+ do_check_eq(i.toString(16), "-8000000000000000");
+ do_check_eq(i.toString(2), "-1000000000000000000000000000000000000000000000000000000000000000");
+
+ // Test Int64(string) constructor.
+ i = ctypes.Int64("0x7fffffffffffffff");
+ do_check_eq(i.toString(), i.toString(10));
+ do_check_eq(i.toString(10), "9223372036854775807");
+ do_check_eq(i.toString(16), "7fffffffffffffff");
+ do_check_eq(i.toString(2), "111111111111111111111111111111111111111111111111111111111111111");
+
+ i = ctypes.Int64("-0x8000000000000000");
+ do_check_eq(i.toString(), i.toString(10));
+ do_check_eq(i.toString(10), "-9223372036854775808");
+ do_check_eq(i.toString(16), "-8000000000000000");
+ do_check_eq(i.toString(2), "-1000000000000000000000000000000000000000000000000000000000000000");
+
+ i = ctypes.Int64("9223372036854775807");
+ do_check_eq(i.toString(), i.toString(10));
+ do_check_eq(i.toString(10), "9223372036854775807");
+ do_check_eq(i.toString(16), "7fffffffffffffff");
+ do_check_eq(i.toString(2), "111111111111111111111111111111111111111111111111111111111111111");
+
+ i = ctypes.Int64("-9223372036854775808");
+ do_check_eq(i.toString(), i.toString(10));
+ do_check_eq(i.toString(10), "-9223372036854775808");
+ do_check_eq(i.toString(16), "-8000000000000000");
+ do_check_eq(i.toString(2), "-1000000000000000000000000000000000000000000000000000000000000000");
+
+ // Test Int64(other Int64) constructor.
+ i = ctypes.Int64(ctypes.Int64(0));
+ do_check_eq(i.toString(), "0");
+
+ i = ctypes.Int64(ctypes.Int64("0x7fffffffffffffff"));
+ do_check_eq(i.toString(), i.toString(10));
+ do_check_eq(i.toString(10), "9223372036854775807");
+ do_check_eq(i.toString(16), "7fffffffffffffff");
+ do_check_eq(i.toString(2), "111111111111111111111111111111111111111111111111111111111111111");
+
+ i = ctypes.Int64(ctypes.Int64("-0x8000000000000000"));
+ do_check_eq(i.toString(), i.toString(10));
+ do_check_eq(i.toString(10), "-9223372036854775808");
+ do_check_eq(i.toString(16), "-8000000000000000");
+ do_check_eq(i.toString(2), "-1000000000000000000000000000000000000000000000000000000000000000");
+
+ // Test Int64(other UInt64) constructor.
+ i = ctypes.Int64(ctypes.UInt64(0));
+ do_check_eq(i.toString(), "0");
+
+ i = ctypes.Int64(ctypes.UInt64("0x7fffffffffffffff"));
+ do_check_eq(i.toString(), i.toString(10));
+ do_check_eq(i.toString(10), "9223372036854775807");
+ do_check_eq(i.toString(16), "7fffffffffffffff");
+ do_check_eq(i.toString(2), "111111111111111111111111111111111111111111111111111111111111111");
+
+ let vals = [-0x8000000000001000, 0x8000000000000000,
+ "-0x8000000000000001", "0x8000000000000000",
+ ctypes.UInt64("0x8000000000000000"),
+ Infinity, -Infinity, NaN, 0.1,
+ 5.68e21, null, undefined, "", {}, [], new Number(16),
+ {toString: function () { return 7; }},
+ {valueOf: function () { return 7; }}];
+ for (let i = 0; i < vals.length; i++)
+ do_check_throws(function () { ctypes.Int64(vals[i]); }, TypeError);
+
+ // Test ctypes.Int64.compare.
+ do_check_eq(ctypes.Int64.compare(ctypes.Int64(5), ctypes.Int64(5)), 0);
+ do_check_eq(ctypes.Int64.compare(ctypes.Int64(5), ctypes.Int64(4)), 1);
+ do_check_eq(ctypes.Int64.compare(ctypes.Int64(4), ctypes.Int64(5)), -1);
+ do_check_eq(ctypes.Int64.compare(ctypes.Int64(-5), ctypes.Int64(-5)), 0);
+ do_check_eq(ctypes.Int64.compare(ctypes.Int64(-5), ctypes.Int64(-4)), -1);
+ do_check_eq(ctypes.Int64.compare(ctypes.Int64(-4), ctypes.Int64(-5)), 1);
+ do_check_throws(function() { ctypes.Int64.compare(ctypes.Int64(4), ctypes.UInt64(4)); }, Error);
+ do_check_throws(function() { ctypes.Int64.compare(4, 5); }, Error);
+
+ // Test ctypes.Int64.{lo,hi}.
+ do_check_eq(ctypes.Int64.lo(ctypes.Int64(0x28590a1c921de000)), 0x921de000);
+ do_check_eq(ctypes.Int64.hi(ctypes.Int64(0x28590a1c921de000)), 0x28590a1c);
+ do_check_eq(ctypes.Int64.lo(ctypes.Int64(-0x28590a1c921de000)), 0x6de22000);
+ do_check_eq(ctypes.Int64.hi(ctypes.Int64(-0x28590a1c921de000)), -0x28590a1d);
+ do_check_throws(function() { ctypes.Int64.lo(ctypes.UInt64(0)); }, Error);
+ do_check_throws(function() { ctypes.Int64.hi(ctypes.UInt64(0)); }, Error);
+ do_check_throws(function() { ctypes.Int64.lo(0); }, Error);
+ do_check_throws(function() { ctypes.Int64.hi(0); }, Error);
+
+ // Test ctypes.Int64.join.
+ do_check_eq(ctypes.Int64.join(0, 0).toString(), "0");
+ do_check_eq(ctypes.Int64.join(0x28590a1c, 0x921de000).toString(16), "28590a1c921de000");
+ do_check_eq(ctypes.Int64.join(-0x28590a1d, 0x6de22000).toString(16), "-28590a1c921de000");
+ do_check_eq(ctypes.Int64.join(0x7fffffff, 0xffffffff).toString(16), "7fffffffffffffff");
+ do_check_eq(ctypes.Int64.join(-0x80000000, 0x00000000).toString(16), "-8000000000000000");
+ do_check_throws(function() { ctypes.Int64.join(-0x80000001, 0); }, TypeError);
+ do_check_throws(function() { ctypes.Int64.join(0x80000000, 0); }, TypeError);
+ do_check_throws(function() { ctypes.Int64.join(0, -0x1); }, TypeError);
+ do_check_throws(function() { ctypes.Int64.join(0, 0x800000000); }, TypeError);
+}
+
+function run_UInt64_tests() {
+ do_check_throws(function() { ctypes.UInt64(); }, Error);
+
+ let i = ctypes.UInt64(0);
+ do_check_true(i instanceof ctypes.UInt64);
+
+ // Test UInt64.toString([radix]).
+ do_check_eq(i.toString(), "0");
+ for (let radix = 2; radix <= 36; ++radix)
+ do_check_eq(i.toString(radix), "0");
+ do_check_throws(function() { i.toString(0); }, Error);
+ do_check_throws(function() { i.toString(1); }, Error);
+ do_check_throws(function() { i.toString(37); }, Error);
+
+ i = ctypes.UInt64("0x28590a1c921def71");
+ do_check_eq(i.toString(), i.toString(10));
+ do_check_eq(i.toString(10), "2907366152271163249");
+ do_check_eq(i.toString(16), "28590a1c921def71");
+ do_check_eq(i.toString(2), "10100001011001000010100001110010010010000111011110111101110001");
+
+ i = ctypes.UInt64("0X28590A1c921DEf71");
+ do_check_eq(i.toString(), i.toString(10));
+ do_check_eq(i.toString(10), "2907366152271163249");
+ do_check_eq(i.toString(16), "28590a1c921def71");
+ do_check_eq(i.toString(2), "10100001011001000010100001110010010010000111011110111101110001");
+
+ // Test UInt64(primitive double) constructor.
+ i = ctypes.UInt64(-0);
+ do_check_eq(i.toString(), "0");
+
+ i = ctypes.UInt64(0xfffffffffffff000);
+ do_check_eq(i.toString(), i.toString(10));
+ do_check_eq(i.toString(10), "18446744073709547520");
+ do_check_eq(i.toString(16), "fffffffffffff000");
+ do_check_eq(i.toString(2), "1111111111111111111111111111111111111111111111111111000000000000");
+
+ // Test UInt64(string) constructor.
+ i = ctypes.UInt64("0xffffffffffffffff");
+ do_check_eq(i.toString(), i.toString(10));
+ do_check_eq(i.toString(10), "18446744073709551615");
+ do_check_eq(i.toString(16), "ffffffffffffffff");
+ do_check_eq(i.toString(2), "1111111111111111111111111111111111111111111111111111111111111111");
+
+ i = ctypes.UInt64("0x0");
+ do_check_eq(i.toString(), i.toString(10));
+ do_check_eq(i.toString(10), "0");
+ do_check_eq(i.toString(16), "0");
+ do_check_eq(i.toString(2), "0");
+
+ i = ctypes.UInt64("18446744073709551615");
+ do_check_eq(i.toString(), i.toString(10));
+ do_check_eq(i.toString(10), "18446744073709551615");
+ do_check_eq(i.toString(16), "ffffffffffffffff");
+ do_check_eq(i.toString(2), "1111111111111111111111111111111111111111111111111111111111111111");
+
+ i = ctypes.UInt64("0");
+ do_check_eq(i.toString(), "0");
+
+ // Test UInt64(other UInt64) constructor.
+ i = ctypes.UInt64(ctypes.UInt64(0));
+ do_check_eq(i.toString(), "0");
+
+ i = ctypes.UInt64(ctypes.UInt64("0xffffffffffffffff"));
+ do_check_eq(i.toString(), i.toString(10));
+ do_check_eq(i.toString(10), "18446744073709551615");
+ do_check_eq(i.toString(16), "ffffffffffffffff");
+ do_check_eq(i.toString(2), "1111111111111111111111111111111111111111111111111111111111111111");
+
+ i = ctypes.UInt64(ctypes.UInt64("0x0"));
+ do_check_eq(i.toString(), "0");
+
+ // Test UInt64(other Int64) constructor.
+ i = ctypes.UInt64(ctypes.Int64(0));
+ do_check_eq(i.toString(), "0");
+
+ i = ctypes.UInt64(ctypes.Int64("0x7fffffffffffffff"));
+ do_check_eq(i.toString(), i.toString(10));
+ do_check_eq(i.toString(10), "9223372036854775807");
+ do_check_eq(i.toString(16), "7fffffffffffffff");
+ do_check_eq(i.toString(2), "111111111111111111111111111111111111111111111111111111111111111");
+
+ let vals = [-1, 0x10000000000000000, "-1", "-0x1", "0x10000000000000000",
+ ctypes.Int64("-1"), Infinity, -Infinity, NaN, 0.1,
+ 5.68e21, null, undefined, "", {}, [], new Number(16),
+ {toString: function () { return 7; }},
+ {valueOf: function () { return 7; }}];
+ for (let i = 0; i < vals.length; i++)
+ do_check_throws(function () { ctypes.UInt64(vals[i]); }, TypeError);
+
+ // Test ctypes.UInt64.compare.
+ do_check_eq(ctypes.UInt64.compare(ctypes.UInt64(5), ctypes.UInt64(5)), 0);
+ do_check_eq(ctypes.UInt64.compare(ctypes.UInt64(5), ctypes.UInt64(4)), 1);
+ do_check_eq(ctypes.UInt64.compare(ctypes.UInt64(4), ctypes.UInt64(5)), -1);
+ do_check_throws(function() { ctypes.UInt64.compare(ctypes.UInt64(4), ctypes.Int64(4)); }, Error);
+ do_check_throws(function() { ctypes.UInt64.compare(4, 5); }, Error);
+
+ // Test ctypes.UInt64.{lo,hi}.
+ do_check_eq(ctypes.UInt64.lo(ctypes.UInt64(0x28590a1c921de000)), 0x921de000);
+ do_check_eq(ctypes.UInt64.hi(ctypes.UInt64(0x28590a1c921de000)), 0x28590a1c);
+ do_check_eq(ctypes.UInt64.lo(ctypes.UInt64(0xa8590a1c921de000)), 0x921de000);
+ do_check_eq(ctypes.UInt64.hi(ctypes.UInt64(0xa8590a1c921de000)), 0xa8590a1c);
+ do_check_throws(function() { ctypes.UInt64.lo(ctypes.Int64(0)); }, Error);
+ do_check_throws(function() { ctypes.UInt64.hi(ctypes.Int64(0)); }, Error);
+ do_check_throws(function() { ctypes.UInt64.lo(0); }, Error);
+ do_check_throws(function() { ctypes.UInt64.hi(0); }, Error);
+
+ // Test ctypes.UInt64.join.
+ do_check_eq(ctypes.UInt64.join(0, 0).toString(), "0");
+ do_check_eq(ctypes.UInt64.join(0x28590a1c, 0x921de000).toString(16), "28590a1c921de000");
+ do_check_eq(ctypes.UInt64.join(0xa8590a1c, 0x921de000).toString(16), "a8590a1c921de000");
+ do_check_eq(ctypes.UInt64.join(0xffffffff, 0xffffffff).toString(16), "ffffffffffffffff");
+ do_check_eq(ctypes.UInt64.join(0, 0).toString(16), "0");
+ do_check_throws(function() { ctypes.UInt64.join(-0x1, 0); }, TypeError);
+ do_check_throws(function() { ctypes.UInt64.join(0x100000000, 0); }, TypeError);
+ do_check_throws(function() { ctypes.UInt64.join(0, -0x1); }, TypeError);
+ do_check_throws(function() { ctypes.UInt64.join(0, 0x1000000000); }, TypeError);
+}
+
+function run_basic_abi_tests(library, t, name, toprimitive,
+ get_test, set_tests, sum_tests, sum_many_tests) {
+ // Test the function call ABI for calls involving the type.
+ run_single_abi_tests(library, ctypes.default_abi, t, name + "_cdecl",
+ toprimitive, get_test, set_tests, sum_tests, sum_many_tests);
+#ifdef _WIN32
+#ifndef _WIN64
+ run_single_abi_tests(library, ctypes.stdcall_abi, t, name + "_stdcall",
+ toprimitive, get_test, set_tests, sum_tests, sum_many_tests);
+#endif
+#endif
+
+ // Check the alignment of the type, and its behavior in a struct,
+ // against what C says.
+ check_struct_stats(library, t);
+}
+
+function run_single_abi_tests(library, abi, t, name, toprimitive,
+ get_test, set_tests, sum_tests, sum_many_tests) {
+ let getter = library.declare("get_" + name, abi, t);
+ do_check_eq(toprimitive(getter()), get_test);
+
+ let setter = library.declare("set_" + name, ctypes.default_abi, t, t);
+ for each (let i in set_tests)
+ do_check_eq(toprimitive(setter(i)), i);
+
+ let sum = library.declare("sum_" + name, ctypes.default_abi, t, t, t);
+ for each (let a in sum_tests)
+ do_check_eq(toprimitive(sum(a[0], a[1])), a[2]);
+
+ let sum_alignb = library.declare("sum_alignb_" + name, ctypes.default_abi, t,
+ ctypes.char, t, ctypes.char, t, ctypes.char);
+ let sum_alignf = library.declare("sum_alignf_" + name, ctypes.default_abi, t,
+ ctypes.float, t, ctypes.float, t, ctypes.float);
+ for each (let a in sum_tests) {
+ do_check_eq(toprimitive(sum_alignb(0, a[0], 0, a[1], 0)), a[2]);
+ do_check_eq(toprimitive(sum_alignb(1, a[0], 1, a[1], 1)), a[2]);
+ do_check_eq(toprimitive(sum_alignf(0, a[0], 0, a[1], 0)), a[2]);
+ do_check_eq(toprimitive(sum_alignf(1, a[0], 1, a[1], 1)), a[2]);
+ }
+
+ let sum_many = library.declare("sum_many_" + name, ctypes.default_abi, t,
+ t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t);
+ for each (let a in sum_many_tests)
+ do_check_eq(toprimitive(
+ sum_many(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7],
+ a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15],
+ a[16], a[17])), a[18]);
+}
+
+function check_struct_stats(library, t) {
+ let s_t = ctypes.StructType("s_t", [{ x: ctypes.char }, { y: t }]);
+ let n_t = ctypes.StructType("n_t", [{ a: ctypes.char }, { b: s_t }, { c: ctypes.char }]);
+ let get_stats = library.declare("get_" + t.name + "_stats",
+ ctypes.default_abi, ctypes.void_t,
+ ctypes.size_t.ptr, ctypes.size_t.ptr, ctypes.size_t.ptr, ctypes.size_t.ptr,
+ ctypes.size_t.array().ptr);
+
+ let align = ctypes.size_t();
+ let size = ctypes.size_t();
+ let nalign = ctypes.size_t();
+ let nsize = ctypes.size_t();
+ let offsets = ctypes.size_t.array(3)();
+ get_stats(align.address(), size.address(), nalign.address(), nsize.address(),
+ offsets.address());
+
+ do_check_eq(size.value, s_t.size);
+ do_check_eq(align.value, s_t.size - t.size);
+ do_check_eq(align.value, offsetof(s_t, "y"));
+ do_check_eq(nsize.value, n_t.size);
+ do_check_eq(nalign.value, offsetof(n_t, "b"));
+ do_check_eq(offsets[0], offsetof(s_t, "y"));
+ do_check_eq(offsets[1], offsetof(n_t, "b"));
+ do_check_eq(offsets[2], offsetof(n_t, "c"));
+}
+
+// Determine the offset, in bytes, of 'member' within 'struct'.
+function offsetof(struct, member) {
+ let instance = struct();
+ let memberptr = ptrValue(instance.addressOfField(member));
+ let chararray = ctypes.cast(instance, ctypes.char.array(struct.size));
+ let offset = 0;
+ while (memberptr != ptrValue(chararray.addressOfElement(offset)))
+ ++offset;
+ return offset;
+}
+
+function run_bool_tests(library) {
+ let t = ctypes.bool;
+ let name = "bool";
+ do_check_eq(t.name, name);
+ do_check_true(t.size == 1 || t.size == 4);
+
+ do_check_true(t instanceof ctypes.CType);
+ do_check_true(t.constructor === ctypes.CType);
+ do_check_true(t.__proto__ === ctypes.CType.prototype);
+ do_check_true(t.prototype.constructor === t);
+
+ do_check_eq(t.toString(), "type " + name);
+ do_check_eq(t.toSource(), "ctypes." + name);
+ do_check_true(t.ptr === ctypes.PointerType(t));
+ do_check_eq(t.array().name, name + "[]");
+ do_check_eq(t.array(5).name, name + "[5]");
+
+ let d = t();
+ do_check_true(d.__proto__ === t.prototype);
+ do_check_true(d.constructor === t);
+ do_check_eq(d.value, 0);
+ d.value = 1;
+ do_check_eq(d.value, 1);
+ d.value = -0;
+ do_check_eq(1/d.value, 1/0);
+ d.value = false;
+ do_check_eq(d.value, 0);
+ d.value = true;
+ do_check_eq(d.value, 1);
+ d = new t(1);
+ do_check_eq(d.value, 1);
+
+ // don't convert anything else
+ let vals = [-1, 2, Infinity, -Infinity, NaN, 0.1,
+ ctypes.Int64(0), ctypes.UInt64(0),
+ null, undefined, "", "0", {}, [], new Number(16),
+ {toString: function () { return 7; }},
+ {valueOf: function () { return 7; }}];
+ for (let i = 0; i < vals.length; i++)
+ do_check_throws(function () { d.value = vals[i]; }, TypeError);
+
+ do_check_true(d.address().constructor === t.ptr);
+ do_check_eq(d.address().contents, d.value);
+ do_check_eq(d.toSource(), "ctypes." + name + "(" + d.value + ")");
+ do_check_eq(d.toSource(), d.toString());
+
+ // Test the function call ABI for calls involving the type,
+ // and check the alignment of the type against what C says.
+ function toprimitive(a) { return a; }
+ run_basic_abi_tests(library, t, name, toprimitive,
+ true,
+ [ false, true ],
+ [ [ false, false, false ], [ false, true, true ],
+ [ true, false, true ], [true, true, true ] ],
+ [ [ false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false,
+ false ],
+ [ true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true,
+ true ] ]);
+}
+
+function run_integer_tests(library, t, name, size, signed, limits) {
+ do_check_eq(t.name, name);
+ do_check_eq(t.size, size);
+
+ do_check_true(t instanceof ctypes.CType);
+ do_check_true(t.constructor === ctypes.CType);
+ do_check_true(t.__proto__ === ctypes.CType.prototype);
+ do_check_true(t.prototype.constructor === t);
+
+ do_check_eq(t.toString(), "type " + name);
+ do_check_eq(t.toSource(), "ctypes." + name);
+ do_check_true(t.ptr === ctypes.PointerType(t));
+ do_check_eq(t.array().name, name + "[]");
+ do_check_eq(t.array(5).name, name + "[5]");
+
+ // Check the alignment of the type, and its behavior in a struct,
+ // against what C says.
+ check_struct_stats(library, t);
+
+ let d = t();
+ // TODO: should this work?
+ // do_check_true(d instanceof t);
+ do_check_true(d.__proto__ === t.prototype);
+ do_check_true(d.constructor === t);
+ do_check_eq(d.value, 0);
+ d.value = 5;
+ do_check_eq(d.value, 5);
+ d = t(10);
+ do_check_eq(d.value, 10);
+ if (signed) {
+ d.value = -10;
+ do_check_eq(d.value, -10);
+ }
+ d = new t(20);
+ do_check_eq(d.value, 20);
+
+ d.value = ctypes.Int64(5);
+ do_check_eq(d.value, 5);
+ if (signed) {
+ d.value = ctypes.Int64(-5);
+ do_check_eq(d.value, -5);
+ }
+ d.value = ctypes.UInt64(5);
+ do_check_eq(d.value, 5);
+
+ d.value = limits[0];
+ do_check_eq(d.value, limits[0]);
+ d.value = limits[1];
+ do_check_eq(d.value, limits[1]);
+ d.value = 0;
+ do_check_eq(d.value, 0);
+ d.value = -0;
+ do_check_eq(1/d.value, 1/0);
+ d.value = false;
+ do_check_eq(d.value, 0);
+ d.value = true;
+ do_check_eq(d.value, 1);
+
+ // don't convert anything else
+ let vals = [limits[0] - 1, limits[1] + 1, Infinity, -Infinity, NaN, 0.1,
+ null, undefined, "", "0", {}, [], new Number(16),
+ {toString: function () { return 7; }},
+ {valueOf: function () { return 7; }}];
+ for (let i = 0; i < vals.length; i++)
+ do_check_throws(function () { d.value = vals[i]; }, TypeError);
+
+ do_check_true(d.address().constructor === t.ptr);
+ do_check_eq(d.address().contents, d.value);
+ do_check_eq(d.toSource(), "ctypes." + name + "(" + d.value + ")");
+ do_check_eq(d.toSource(), d.toString());
+
+ // Test the function call ABI for calls involving the type,
+ // and check the alignment of the type against what C says.
+ function toprimitive(a) { return a; }
+ run_basic_abi_tests(library, t, name, toprimitive,
+ 109,
+ [ 0, limits[0], limits[1] ],
+ [ [ 0, 0, 0 ], [ 0, 1, 1 ], [ 1, 0, 1 ], [ 1, 1, 2 ],
+ [ limits[0], 1, limits[0] + 1 ],
+ [ limits[1], 1, limits[0] ] ],
+ [ [ 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 ],
+ [ 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 18 ] ]);
+}
+
+function run_float_tests(library, t, name, size) {
+ do_check_eq(t.name, name);
+ do_check_eq(t.size, size);
+
+ do_check_true(t instanceof ctypes.CType);
+ do_check_true(t.constructor === ctypes.CType);
+ do_check_true(t.__proto__ === ctypes.CType.prototype);
+ do_check_true(t.prototype.constructor === t);
+
+ do_check_eq(t.toString(), "type " + name);
+ do_check_eq(t.toSource(), "ctypes." + name);
+ do_check_true(t.ptr === ctypes.PointerType(t));
+ do_check_eq(t.array().name, name + "[]");
+ do_check_eq(t.array(5).name, name + "[5]");
+
+ let d = t();
+ do_check_true(d.__proto__ === t.prototype);
+ do_check_true(d.constructor === t);
+ do_check_eq(d.value, 0);
+ d.value = 5;
+ do_check_eq(d.value, 5);
+ d.value = 5.25;
+ do_check_eq(d.value, 5.25);
+ d = t(10);
+ do_check_eq(d.value, 10);
+ d.value = -10;
+ do_check_eq(d.value, -10);
+ d = new t(20);
+ do_check_eq(d.value, 20);
+
+ d.value = ctypes.Int64(5);
+ do_check_eq(d.value, 5);
+ d.value = ctypes.Int64(-5);
+ do_check_eq(d.value, -5);
+ d.value = ctypes.UInt64(5);
+ do_check_eq(d.value, 5);
+
+ if (size == 4) {
+ d.value = 0x7fffff;
+ do_check_eq(d.value, 0x7fffff);
+
+ // allow values that can't be represented precisely as a float
+ d.value = 0xffffffff;
+ let delta = 1 - d.value/0xffffffff;
+ do_check_true(delta != 0);
+ do_check_true(delta > -0.01 && delta < 0.01);
+ d.value = 1 + 1/0x80000000;
+ do_check_eq(d.value, 1);
+ } else {
+ d.value = 0xfffffffffffff000;
+ do_check_eq(d.value, 0xfffffffffffff000);
+
+ // allow values that can't be represented precisely as a double
+ d.value = ctypes.Int64("0x7fffffffffffffff");
+ do_check_eq(d.value, 0x7fffffffffffffff);
+ }
+
+ d.value = Infinity;
+ do_check_eq(d.value, Infinity);
+ d.value = -Infinity;
+ do_check_eq(d.value, -Infinity);
+ d.value = NaN;
+ do_check_true(isNaN(d.value));
+ d.value = 0;
+ do_check_eq(d.value, 0);
+ d.value = -0;
+ do_check_eq(1/d.value, 1/-0);
+
+ // don't convert anything else
+ let vals = [true, false, null, undefined, "", "0", {}, [], new Number(16),
+ {toString: function () { return 7; }},
+ {valueOf: function () { return 7; }}];
+ for (let i = 0; i < vals.length; i++)
+ do_check_throws(function () { d.value = vals[i]; }, TypeError);
+
+ do_check_true(d.address().constructor === t.ptr);
+ do_check_eq(d.address().contents, d.value);
+ do_check_eq(d.toSource(), "ctypes." + name + "(" + d.value + ")");
+ do_check_eq(d.toSource(), d.toString());
+
+ // Test the function call ABI for calls involving the type,
+ // and check the alignment of the type against what C says.
+ let operand = [];
+ if (size == 4) {
+ operand[0] = 503859.75;
+ operand[1] = 1012385.25;
+ operand[2] = 1516245;
+ } else {
+ operand[0] = 501823873859.75;
+ operand[1] = 171290577385.25;
+ operand[2] = 673114451245;
+ }
+ function toprimitive(a) { return a; }
+ run_basic_abi_tests(library, t, name, toprimitive,
+ 109.25,
+ [ 0, operand[0], operand[1] ],
+ [ [ 0, 0, 0 ], [ 0, 1, 1 ], [ 1, 0, 1 ], [ 1, 1, 2 ],
+ [ operand[0], operand[1], operand[2] ] ],
+ [ [ 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 ],
+ [ 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 18 ] ]);
+}
+
+function run_wrapped_integer_tests(library, t, name, size, signed, w, wname, limits) {
+ do_check_eq(t.name, name);
+ do_check_eq(t.size, size);
+
+ do_check_true(t instanceof ctypes.CType);
+ do_check_true(t.constructor === ctypes.CType);
+ do_check_true(t.__proto__ === ctypes.CType.prototype);
+ do_check_true(t.prototype.constructor === t);
+
+ do_check_eq(t.toString(), "type " + name);
+ do_check_eq(t.toSource(), "ctypes." + name);
+ do_check_true(t.ptr === ctypes.PointerType(t));
+ do_check_eq(t.array().name, name + "[]");
+ do_check_eq(t.array(5).name, name + "[5]");
+
+ let d = t();
+ do_check_true(d.__proto__ === t.prototype);
+ do_check_true(d.constructor === t);
+ do_check_true(d.value instanceof w);
+ do_check_eq(d.value, 0);
+ d.value = 5;
+ do_check_eq(d.value, 5);
+ d = t(10);
+ do_check_eq(d.value, 10);
+ if (signed) {
+ d.value = -10;
+ do_check_eq(d.value, -10);
+ }
+ d = new t(20);
+ do_check_eq(d.value, 20);
+
+ d.value = ctypes.Int64(5);
+ do_check_eq(d.value, 5);
+ if (signed) {
+ d.value = ctypes.Int64(-5);
+ do_check_eq(d.value, -5);
+ }
+ d.value = ctypes.UInt64(5);
+ do_check_eq(d.value, 5);
+
+ d.value = w(limits[0]);
+ do_check_eq(d.value, limits[0]);
+ d.value = w(limits[1]);
+ do_check_eq(d.value, limits[1]);
+ d.value = 0;
+ do_check_eq(d.value, 0);
+ d.value = -0;
+ do_check_eq(1/d.value, 1/0);
+ d.value = false;
+ do_check_eq(d.value, 0);
+ d.value = true;
+ do_check_eq(d.value, 1);
+
+ // don't convert anything else
+ let vals = [limits[2], limits[3], Infinity, -Infinity, NaN, 0.1,
+ null, undefined, "", "0", {}, [], new Number(16),
+ {toString: function () { return 7; }},
+ {valueOf: function () { return 7; }}];
+ for (let i = 0; i < vals.length; i++)
+ do_check_throws(function () { d.value = vals[i]; }, TypeError);
+
+ do_check_true(d.address().constructor === t.ptr);
+ do_check_eq(d.address().contents.toString(), d.value.toString());
+ do_check_eq(d.toSource(), "ctypes." + name + "(" + wname + "(\"" + d.value + "\"))");
+ do_check_eq(d.toSource(), d.toString());
+
+ // Test the function call ABI for calls involving the type,
+ // and check the alignment of the type against what C says.
+ function toprimitive(a) { return a.toString(); }
+ run_basic_abi_tests(library, t, name, toprimitive,
+ 109,
+ [ 0, w(limits[0]), w(limits[1]) ],
+ [ [ 0, 0, 0 ], [ 0, 1, 1 ], [ 1, 0, 1 ], [ 1, 1, 2 ],
+ signed ? [ w(limits[0]), -1, w(limits[1]) ]
+ : [ w(limits[1]), 1, w(limits[0]) ] ],
+ [ [ 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 ],
+ [ 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 18 ] ]);
+}
+
+function run_char_tests(library, t, name, size, signed, limits) {
+ do_check_eq(t.name, name);
+ do_check_eq(t.size, size);
+
+ do_check_true(t instanceof ctypes.CType);
+ do_check_true(t.constructor === ctypes.CType);
+ do_check_true(t.__proto__ === ctypes.CType.prototype);
+ do_check_true(t.prototype.constructor === t);
+
+ do_check_eq(t.toString(), "type " + name);
+ do_check_eq(t.toSource(), "ctypes." + name);
+ do_check_true(t.ptr === ctypes.PointerType(t));
+ do_check_eq(t.array().name, name + "[]");
+ do_check_eq(t.array(5).name, name + "[5]");
+
+ let d = t();
+ do_check_true(d.__proto__ === t.prototype);
+ do_check_true(d.constructor === t);
+ do_check_eq(d.value.length, 1);
+ do_check_eq(d.value.charCodeAt(0), 0);
+ d.value = 5;
+ do_check_eq(d.value.length, 1);
+ do_check_eq(d.value.charCodeAt(0), 5);
+ d = t(10);
+ do_check_eq(d.value.charCodeAt(0), 10);
+
+ function toprimitive(a) {
+ let r = a.charCodeAt(0);
+ if (signed) {
+ if (t.size == 1)
+ return r > 0x7f ? r - 0x100 : r;
+ return r > 0x7fff ? r - 0x10000 : r;
+ }
+ return r;
+ }
+
+ if (signed) {
+ d.value = -10;
+ do_check_eq(toprimitive(d.value), -10);
+ }
+ d = new t(20);
+ do_check_eq(d.value.charCodeAt(0), 20);
+
+ d.value = ctypes.Int64(5);
+ do_check_eq(d.value.charCodeAt(0), 5);
+ if (signed) {
+ d.value = ctypes.Int64(-10);
+ do_check_eq(toprimitive(d.value), -10);
+ }
+ d.value = ctypes.UInt64(5);
+ do_check_eq(d.value.charCodeAt(0), 5);
+
+ d.value = limits[0];
+ do_check_eq(toprimitive(d.value), limits[0]);
+ d.value = limits[1];
+ do_check_eq(d.value.charCodeAt(0), limits[1]);
+ d.value = 0;
+ do_check_eq(d.value.charCodeAt(0), 0);
+ d.value = -0;
+ do_check_eq(1/d.value.charCodeAt(0), 1/0);
+ d.value = false;
+ do_check_eq(d.value.charCodeAt(0), 0);
+ d.value = true;
+ do_check_eq(d.value.charCodeAt(0), 1);
+
+ d.value = "\0";
+ do_check_eq(d.value.charCodeAt(0), 0);
+ d.value = "a";
+ do_check_eq(d.value, "a");
+
+ // don't convert anything else
+ let vals = [limits[0] - 1, limits[1] + 1, Infinity, -Infinity, NaN, 0.1,
+ null, undefined, "", "aa", {}, [], new Number(16),
+ {toString: function () { return 7; }},
+ {valueOf: function () { return 7; }}];
+ for (let i = 0; i < vals.length; i++)
+ do_check_throws(function () { d.value = vals[i]; }, TypeError);
+
+ do_check_true(d.address().constructor === t.ptr);
+ do_check_eq(d.address().contents, "a");
+ do_check_eq(d.toSource(), "ctypes." + name + "(" + d.value.charCodeAt(0) + ")");
+ do_check_eq(d.toSource(), d.toString());
+
+ // Test string autoconversion (and lack thereof).
+ let literal = "autoconverted";
+ let s = t.array()(literal);
+ do_check_eq(s.readString(), literal);
+ do_check_eq(s.constructor.length, literal.length + 1);
+ s = t.array(50)(literal);
+ do_check_eq(s.readString(), literal);
+ do_check_throws(function() { t.array(3)(literal); }, Error);
+
+ do_check_throws(function() { t.ptr(literal); }, TypeError);
+ let p = t.ptr(s);
+ do_check_eq(p.readString(), literal);
+
+ // Test the function call ABI for calls involving the type,
+ // and check the alignment of the type against what C says.
+ run_basic_abi_tests(library, t, name, toprimitive,
+ 109,
+ [ 0, limits[0], limits[1] ],
+ [ [ 0, 0, 0 ], [ 0, 1, 1 ], [ 1, 0, 1 ], [ 1, 1, 2 ],
+ [ limits[0], 1, limits[0] + 1 ],
+ [ limits[1], 1, limits[0] ] ],
+ [ [ 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 ],
+ [ 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 18 ] ]);
+}
+
+function run_StructType_tests() {
+ do_check_throws(function() { ctypes.StructType(); }, Error);
+ do_check_throws(function() { ctypes.StructType("a"); }, Error);
+
+ // StructType size, alignment, and offset calculations have already been
+ // checked for each basic type. We do not need to check them again.
+ let name = "g_t";
+ let g_t = ctypes.StructType(name, [{ a: ctypes.int32_t }, { b: ctypes.double }]);
+ do_check_eq(g_t.name, name);
+
+ do_check_true(g_t instanceof ctypes.CType);
+ do_check_true(g_t instanceof ctypes.StructType);
+ do_check_true(g_t.constructor === ctypes.StructType);
+ do_check_true(g_t.__proto__ === ctypes.StructType.prototype);
+ do_check_true(g_t.prototype.constructor === g_t);
+
+ do_check_eq(g_t.toString(), "type " + name);
+ do_check_eq(g_t.toSource(),
+ "ctypes.StructType(\"g_t\", [{ \"a\": ctypes.int32_t }, { \"b\": ctypes.double }])");
+ do_check_true(g_t.ptr === ctypes.PointerType(g_t));
+ do_check_eq(g_t.array().name, name + "[]");
+ do_check_eq(g_t.array(5).name, name + "[5]");
+
+ let h_t = ctypes.StructType("h_t", [{ a: ctypes.int32_t }, { b: ctypes.int16_t }]);
+ let s_t = new ctypes.StructType("s_t", [{ a: ctypes.int32_t }, { b: g_t }, { c: ctypes.int8_t }]);
+
+ let fields = [{ a: ctypes.int32_t }, { b: ctypes.int8_t }, { c: g_t }, { d: ctypes.int8_t }];
+ let t_t = new ctypes.StructType("t_t", fields);
+ do_check_eq(t_t.fields.length, 4);
+ do_check_true(t_t.fields[0].a === ctypes.int32_t);
+ do_check_true(t_t.fields[1].b === ctypes.int8_t);
+ do_check_true(t_t.fields[2].c === g_t);
+ do_check_true(t_t.fields[3].d === ctypes.int8_t);
+ do_check_throws(function() { t_t.fields.z = 0; }, Error);
+ do_check_throws(function() { t_t.fields[4] = 0; }, Error);
+ do_check_throws(function() { t_t.fields[4].a = 0; }, Error);
+ do_check_throws(function() { t_t.fields[4].e = 0; }, Error);
+
+ let g = g_t();
+ do_check_eq(g.a, 0);
+ do_check_eq(g.b, 0);
+ g = new g_t(1, 2);
+ // TODO: should this work?
+ // do_check_true(g instanceof g_t);
+ do_check_true(g.__proto__ === g_t.prototype);
+ do_check_true(g.constructor === g_t);
+ do_check_eq(g.a, 1);
+ do_check_eq(g.b, 2);
+ do_check_throws(function() { g_t(1); }, Error);
+
+ let g_a = g.address();
+ do_check_true(g_a.constructor === g_t.ptr);
+ do_check_eq(g_a.contents.a, g.a);
+
+ let s = new s_t(4, g, 10);
+ do_check_eq(s.a, 4);
+ do_check_eq(s.b.a, 1);
+ do_check_eq(s.b.b, 2);
+ do_check_eq(s.c, 10);
+ let g2 = s.b;
+ g2.a = 7;
+ do_check_eq(s.b.a, 7);
+
+ g_a = s.addressOfField("b");
+ do_check_true(g_a.constructor === g_t.ptr);
+ do_check_eq(g_a.contents.a, s.b.a);
+ do_check_throws(function() { s.addressOfField(); }, Error);
+ do_check_throws(function() { s.addressOfField("d"); }, Error);
+ do_check_throws(function() { s.addressOfField("a", 2); }, Error);
+
+ do_check_eq(s.toSource(), "s_t(4, { \"a\": 7, \"b\": 2 }, 10)");
+ do_check_eq(s.toSource(), s.toString());
+ eval("let s2 = " + s.toSource());
+ do_check_true(s2.constructor === s_t);
+ do_check_eq(s.b.b, s2.b.b);
+
+ // Test that structs can be constructed through ImplicitConvert.
+ do_check_throws(function() { s.value; }, Error);
+ s.value = { "a": 2, "b": { "a": 9, "b": 5 }, "c": 13 };
+ do_check_eq(s.b.a, 9);
+ do_check_eq(s.c, 13);
+ do_check_throws(function() { s.value = 5; }, Error);
+ do_check_throws(function() { s.value = { "a": 2 }; }, Error);
+ do_check_throws(function() { s.value = { "a": 2, "b": 5, "c": 10 }; }, Error);
+ do_check_throws(function() {
+ s.value = { "a": 2, "b": { "a": 9, "b": 5 }, "c": 13, "d": 17 };
+ }, Error);
+ do_check_throws(function() {
+ s.value = { "a": 2, "b": { "a": 9, "b": 5, "e": 9 }, "c": 13 };
+ }, Error);
+
+ // Check that the empty struct has size 1.
+ let z_t = ctypes.StructType("z_t", []);
+ do_check_eq(z_t.size, 1);
+ do_check_eq(z_t.fields.length, 0);
+
+ // Check that structs containing arrays of undefined or zero length
+ // are illegal, but arrays of defined length work.
+ do_check_throws(function() {
+ ctypes.StructType("z_t", [{ a: ctypes.int32_t.array() }]);
+ }, Error);
+ do_check_throws(function() {
+ ctypes.StructType("z_t", [{ a: ctypes.int32_t.array(0) }]);
+ }, Error);
+ z_t = ctypes.StructType("z_t", [{ a: ctypes.int32_t.array(6) }]);
+ do_check_eq(z_t.size, ctypes.int32_t.size * 6);
+ let z = z_t([1, 2, 3, 4, 5, 6]);
+ do_check_eq(z.a[3], 4);
+}
+
+function ptrValue(p) {
+ return ctypes.cast(p, ctypes.uintptr_t).value.toString();
+}
+
+function run_PointerType_tests() {
+ do_check_throws(function() { ctypes.PointerType(); }, Error);
+
+ let name = "g_t";
+ let g_t = ctypes.StructType(name, [{ a: ctypes.int32_t }, { b: ctypes.double }]);
+ let g = g_t(1, 2);
+
+ let p_t = ctypes.PointerType(g_t);
+ do_check_eq(p_t.name, name + "*");
+ do_check_eq(p_t.size, ctypes.uintptr_t.size);
+ do_check_true(p_t.targetType === g_t);
+ do_check_true(p_t === g_t.ptr);
+
+ do_check_true(p_t instanceof ctypes.CType);
+ do_check_true(p_t instanceof ctypes.PointerType);
+ do_check_true(p_t.constructor === ctypes.PointerType);
+ do_check_true(p_t.__proto__ === ctypes.PointerType.prototype);
+ do_check_true(p_t.prototype.constructor === p_t);
+
+ do_check_eq(p_t.toString(), "type " + name + "*");
+ do_check_eq(p_t.toSource(),
+ "ctypes.StructType(\"g_t\", [{ \"a\": ctypes.int32_t }, { \"b\": ctypes.double }]).ptr");
+ do_check_true(p_t.ptr === ctypes.PointerType(p_t));
+ do_check_eq(p_t.array().name, name + "*[]");
+ do_check_eq(p_t.array(5).name, name + "*[5]");
+
+ // Test ExplicitConvert.
+ let p = p_t();
+ do_check_throws(function() { p.value; }, Error);
+ do_check_eq(ptrValue(p), 0);
+ do_check_throws(function() { p.contents; }, Error);
+ p = p_t(5);
+ do_check_eq(ptrValue(p), 5);
+ p = p_t(ctypes.UInt64(10));
+ do_check_eq(ptrValue(p), 10);
+
+ // Test ImplicitConvert.
+ p.value = null;
+ do_check_eq(ptrValue(p), 0);
+ do_check_throws(function() { p.value = 5; }, Error);
+
+ // Test opaque pointers.
+ let f_t = ctypes.PointerType("FILE*");
+ do_check_eq(f_t.name, "FILE*");
+ let f = new f_t();
+ do_check_throws(function() { f.contents; }, Error);
+
+ do_check_throws(function() { f_t(p); }, Error);
+ do_check_throws(function() { f.value = p; }, Error);
+ do_check_throws(function() { p.value = f; }, Error);
+
+ // Test void pointers.
+ let v_t = ctypes.PointerType(ctypes.void_t);
+ do_check_true(v_t === ctypes.voidptr_t);
+ let v = v_t(p);
+ do_check_eq(ptrValue(v), ptrValue(p));
+
+ // Test 'contents'.
+ let i = ctypes.int32_t(9);
+ p = i.address();
+ do_check_eq(p.contents, i.value);
+
+ // Check that pointers to arrays of undefined or zero length are legal,
+ // but that the former cannot be dereferenced.
+ let z_t = ctypes.int32_t.array().ptr;
+ do_check_eq(ptrValue(z_t()), 0);
+ do_check_throws(function() { z_t().contents }, Error);
+ z_t = ctypes.int32_t.array(0).ptr;
+ do_check_eq(ptrValue(z_t()), 0);
+ let z = ctypes.int32_t.array(0)().address();
+ do_check_eq(z.contents.length, 0);
+}
+
+function run_ArrayType_tests() {
+ do_check_throws(function() { ctypes.ArrayType(); }, Error);
+
+ let name = "g_t";
+ let g_t = ctypes.StructType(name, [{ a: ctypes.int32_t }, { b: ctypes.double }]);
+ let g = g_t(1, 2);
+
+ let a_t = ctypes.ArrayType(g_t, 10);
+ do_check_eq(a_t.name, name + "[10]");
+ do_check_eq(a_t.length, 10);
+ do_check_eq(a_t.size, g_t.size * 10);
+ do_check_true(a_t.elementType === g_t);
+
+ do_check_true(a_t instanceof ctypes.CType);
+ do_check_true(a_t instanceof ctypes.ArrayType);
+ do_check_true(a_t.constructor === ctypes.ArrayType);
+ do_check_true(a_t.__proto__ === ctypes.ArrayType.prototype);
+ do_check_true(a_t.prototype.constructor === a_t);
+
+ do_check_eq(a_t.toString(), "type " + name + "[10]");
+ do_check_eq(a_t.toSource(),
+ "ctypes.StructType(\"g_t\", [{ \"a\": ctypes.int32_t }, { \"b\": ctypes.double }]).array(10)");
+ do_check_eq(a_t.array().name, name + "[][10]");
+ do_check_eq(a_t.array(5).name, name + "[5][10]");
+ do_check_throws(function() { ctypes.int32_t.array().array(); }, Error);
+
+ let a = new a_t();
+ do_check_eq(a[0].a, 0);
+ do_check_eq(a[0].b, 0);
+ a[0] = g;
+ do_check_eq(a[0].a, 1);
+ do_check_eq(a[0].b, 2);
+ do_check_throws(function() { a[-1]; }, Error);
+ do_check_eq(a[9].a, 0);
+ do_check_throws(function() { a[10]; }, Error);
+
+ do_check_eq(a[ctypes.Int64(0)].a, 1);
+ do_check_eq(a[ctypes.UInt64(0)].b, 2);
+
+ let a_p = a.addressOfElement(0);
+ do_check_true(a_p.constructor.targetType === g_t);
+ do_check_true(a_p.constructor === g_t.ptr);
+ do_check_eq(a_p.contents.a, a[0].a);
+ do_check_eq(a_p.contents.b, a[0].b);
+ a_p.contents.a = 5;
+ do_check_eq(a[0].a, 5);
+
+ let a2_t = ctypes.ArrayType(g_t);
+ do_check_eq(a2_t.name, "g_t[]");
+ do_check_eq(a2_t.length, undefined);
+ do_check_eq(a2_t.size, undefined);
+ let a2 = new a2_t(5);
+ do_check_eq(a2.constructor.length, 5);
+ do_check_eq(a2.length, 5);
+ do_check_eq(a2.constructor.size, g_t.size * 5);
+ do_check_throws(function() { new a2_t(); }, Error);
+ do_check_throws(function() { ctypes.ArrayType(ctypes.ArrayType(g_t)); }, Error);
+ do_check_throws(function() { ctypes.ArrayType(ctypes.ArrayType(g_t), 5); }, Error);
+
+ let b_t = ctypes.int32_t.array(ctypes.UInt64(0xffff));
+ do_check_eq(b_t.length, 0xffff);
+ b_t = ctypes.int32_t.array(ctypes.Int64(0xffff));
+ do_check_eq(b_t.length, 0xffff);
+ do_check_throws(function() {
+ ctypes.int32_t.array(ctypes.UInt64(0xffffffffffffffff));
+ }, Error);
+ if (ctypes.size_t.size == 8) {
+ b_t = ctypes.int32_t.array(0xfffffffffffff000);
+ do_check_eq(b_t.length, 0xfffffffffffff000);
+ } else {
+ do_check_throws(function() { ctypes.int32_t.array(0xffffffffff); }, Error);
+ }
+
+ // Test that arrays ImplicitConvert to pointers.
+ let b = ctypes.int32_t.array(10)();
+ let p = ctypes.int32_t.ptr();
+ p.value = b;
+ do_check_eq(ptrValue(b.addressOfElement(0)), ptrValue(p));
+ p = ctypes.voidptr_t();
+ p.value = b;
+ do_check_eq(ptrValue(b.addressOfElement(0)), ptrValue(p));
+
+ // Test that arrays can be constructed through ImplicitConvert.
+ let c_t = ctypes.int32_t.array(6);
+ let c = c_t();
+ c.value = [1, 2, 3, 4, 5, 6];
+ do_check_eq(c.toSource(), "ctypes.int32_t.array(6)([ 1, 2, 3, 4, 5, 6 ])");
+ do_check_eq(c.toSource(), c.toString());
+ eval("let c2 = " + c.toSource());
+ do_check_eq(c2.constructor.name, "int32_t[6]");
+ do_check_eq(c2.length, 6);
+ do_check_eq(c2[3], c[3]);
+
+ c.value = c;
+ do_check_eq(c[3], 4);
+ do_check_throws(function() { c.value; }, Error);
+ do_check_throws(function() { c.value = [1, 2, 3, 4, 5]; }, Error);
+ do_check_throws(function() { c.value = [1, 2, 3, 4, 5, 6, 7]; }, Error);
+ do_check_throws(function() { c.value = [1, 2, 7.4, 4, 5, 6]; }, Error);
+ do_check_throws(function() { c.value = []; }, Error);
+}
+
+function run_cast_tests() {
+ // Test casting between basic types.
+ let i = ctypes.int32_t();
+ let j = ctypes.cast(i, ctypes.int16_t);
+ do_check_eq(ptrValue(i.address()), ptrValue(j.address()));
+ do_check_eq(i.value, j.value);
+ let k = ctypes.cast(i, ctypes.uint32_t);
+ do_check_eq(ptrValue(i.address()), ptrValue(k.address()));
+ do_check_eq(i.value, k.value);
+
+ // Test casting to a type of undefined or larger size.
+ do_check_throws(function() { ctypes.cast(i, ctypes.void_t); }, Error);
+ do_check_throws(function() { ctypes.cast(i, ctypes.int32_t.array()); }, Error);
+ do_check_throws(function() { ctypes.cast(i, ctypes.int64_t); }, Error);
+
+ // Test casting between special types.
+ let g_t = ctypes.StructType("g_t", [{ a: ctypes.int32_t }, { b: ctypes.double }]);
+ let a_t = ctypes.ArrayType(g_t, 4);
+ let p_t = ctypes.PointerType(g_t);
+
+ let a = a_t();
+ a[0] = { a: 5, b: 7.5 };
+ let g = ctypes.cast(a, g_t);
+ do_check_eq(ptrValue(a.address()), ptrValue(g.address()));
+ do_check_eq(a[0].a, g.a);
+
+ let a2 = ctypes.cast(g, g_t.array(1));
+ do_check_eq(ptrValue(a2.address()), ptrValue(g.address()));
+ do_check_eq(a2[0].a, g.a);
+ let a3 = ctypes.cast(g, g_t.array(0));
+
+ let p = g.address();
+ let ip = ctypes.cast(p, ctypes.int32_t.ptr);
+ do_check_eq(ptrValue(ip), ptrValue(p));
+ do_check_eq(ptrValue(ip.address()), ptrValue(p.address()));
+ do_check_eq(ip.contents, g.a);
+}
+
function run_void_tests(library) {
- var test_v = library.declare("test_v", ctypes.default_abi, ctypes.void_t);
- do_check_eq(test_v(), undefined);
-}
+ let test_void_t = library.declare("test_void_t_cdecl", ctypes.default_abi, ctypes.void_t);
+ do_check_eq(test_void_t(), undefined);
-function run_int8_tests(library) {
- var test_i8 = library.declare("test_i8", ctypes.default_abi, ctypes.int8_t);
- do_check_eq(test_i8(), 123);
-
- var test_i8_i8 = library.declare("test_i8_i8", ctypes.default_abi, ctypes.int8_t, ctypes.int8_t);
- do_check_eq(test_i8_i8(5), 5);
- do_check_eq(test_i8_i8(0), 0);
- do_check_eq(test_i8_i8(0x7f), 0x7f);
- do_check_eq(test_i8_i8(-0x80), -0x80);
- do_check_eq(1/test_i8_i8(-0), 1/0); // that is, test_i8_i8(-0) is +0
- do_check_eq(test_i8_i8(true), 1);
- do_check_eq(test_i8_i8(false), 0);
-
- // don't convert anything else to an int8
- var vals = [0x80, -0x81, 0x100000000, Infinity, -Infinity, NaN,
- null, undefined, "", "0", {}, [], new Number(16),
- {toString: function () { return 7; }},
- {valueOf: function () { return 7; }}];
- for (var i = 0; i < vals.length; i++)
- do_check_throws(function () { test_i8_i8(vals[i]); }, TypeError);
-
- var test_i8_i8_sum = library.declare("test_i8_i8_sum", ctypes.default_abi, ctypes.int8_t, ctypes.int8_t, ctypes.int8_t);
- do_check_eq(test_i8_i8_sum(5, 5), 10);
-
- // test the range of unsigned. (we can reuse the signed C function
- // here, since it's binary-compatible.)
- var test_ui8_ui8 = library.declare("test_i8_i8", ctypes.default_abi, ctypes.uint8_t, ctypes.uint8_t);
- do_check_eq(test_ui8_ui8(0xff), 0xff);
- do_check_throws(function () { test_ui8_ui8(0x100); }, TypeError);
- do_check_throws(function () { test_ui8_ui8(-1); }, TypeError);
-}
-
-function run_int16_tests(library) {
- var test_i16 = library.declare("test_i16", ctypes.default_abi, ctypes.int16_t);
- do_check_eq(test_i16(), 12345);
-
- var test_i16_i16 = library.declare("test_i16_i16", ctypes.default_abi, ctypes.int16_t, ctypes.int16_t);
- do_check_eq(test_i16_i16(5), 5);
- do_check_eq(test_i16_i16(0), 0);
- do_check_eq(test_i16_i16(0x7fff), 0x7fff);
- do_check_eq(test_i16_i16(-0x8000), -0x8000);
- do_check_eq(1/test_i16_i16(-0), 1/0); // that is, test_i16_i16(-0) is +0
- do_check_eq(test_i16_i16(true), 1);
- do_check_eq(test_i16_i16(false), 0);
-
- // don't convert anything else to an int16
- var vals = [0x8000, -0x8001, 0x100000000, Infinity, -Infinity, NaN,
- null, undefined, "", "0", {}, [], new Number(16),
- {toString: function () { return 7; }},
- {valueOf: function () { return 7; }}];
- for (var i = 0; i < vals.length; i++)
- do_check_throws(function () { test_i16_i16(vals[i]); }, TypeError);
-
- var test_i16_i16_sum = library.declare("test_i16_i16_sum", ctypes.default_abi, ctypes.int16_t, ctypes.int16_t, ctypes.int16_t);
- do_check_eq(test_i16_i16_sum(5, 5), 10);
-
- // test the range of unsigned. (we can reuse the signed C function
- // here, since it's binary-compatible.)
- var test_ui16_ui16 = library.declare("test_i16_i16", ctypes.default_abi, ctypes.uint16_t, ctypes.uint16_t);
- do_check_eq(test_ui16_ui16(0xffff), 0xffff);
- do_check_throws(function () { test_ui16_ui16(0x10000); }, TypeError);
- do_check_throws(function () { test_ui16_ui16(-1); }, TypeError);
-}
-
-function run_int32_tests(library) {
- var test_i32 = library.declare("test_i32", ctypes.default_abi, ctypes.int32_t);
- do_check_eq(test_i32(), 123456789);
-
- var test_i32_i32 = library.declare("test_i32_i32", ctypes.default_abi, ctypes.int32_t, ctypes.int32_t);
- do_check_eq(test_i32_i32(5), 5);
- do_check_eq(test_i32_i32(0), 0);
- do_check_eq(test_i32_i32(0x7fffffff), 0x7fffffff);
- do_check_eq(test_i32_i32(-0x80000000), -0x80000000);
- do_check_eq(1/test_i32_i32(-0), 1/0); // that is, test_i32_i32(-0) is +0
- do_check_eq(test_i32_i32(true), 1);
- do_check_eq(test_i32_i32(false), 0);
-
- // don't convert anything else to an int32
- var vals = [0x80000000, -0x80000001, Infinity, -Infinity, NaN,
- null, undefined, "", "0", {}, [], new Number(16),
- {toString: function () { return 7; }},
- {valueOf: function () { return 7; }}];
- for (var i = 0; i < vals.length; i++)
- do_check_throws(function () { test_i32_i32(vals[i]); }, TypeError);
-
- var test_i32_i32_sum = library.declare("test_i32_i32_sum", ctypes.default_abi, ctypes.int32_t, ctypes.int32_t, ctypes.int32_t);
- do_check_eq(test_i32_i32_sum(5, 5), 10);
-
- // test the range of unsigned. (we can reuse the signed C function
- // here, since it's binary-compatible.)
- var test_ui32_ui32 = library.declare("test_i32_i32", ctypes.default_abi, ctypes.uint32_t, ctypes.uint32_t);
- do_check_eq(test_ui32_ui32(0xffffffff), 0xffffffff);
- do_check_throws(function () { test_ui32_ui32(0x100000000); }, TypeError);
- do_check_throws(function () { test_ui32_ui32(-1); }, TypeError);
-}
-
-function run_int64_tests(library) {
- var test_i64 = library.declare("test_i64", ctypes.default_abi, ctypes.int64_t);
- // JS represents 64 bit ints as doubles, so we have to be careful how many
- // significant digits we use
- do_check_eq(test_i64(), 0x28590a1c921de000);
-
- var test_i64_i64 = library.declare("test_i64_i64", ctypes.default_abi, ctypes.int64_t, ctypes.int64_t);
- do_check_eq(test_i64_i64(5), 5);
- do_check_eq(test_i64_i64(0), 0);
- do_check_eq(test_i64_i64(0x7ffffffffffff000), 0x7ffffffffffff000);
- do_check_eq(test_i64_i64(-0x8000000000000000), -0x8000000000000000);
- do_check_eq(1/test_i64_i64(-0), 1/0); // that is, test_i64_i64(-0) is +0
- do_check_eq(test_i64_i64(true), 1);
- do_check_eq(test_i64_i64(false), 0);
-
- // don't convert anything else to an int64
- var vals = [0x8000000000000000, -0x8000000000001000, Infinity, -Infinity, NaN,
- null, undefined, "", "0", {}, [], new Number(16),
- {toString: function () { return 7; }},
- {valueOf: function () { return 7; }}];
- for (var i = 0; i < vals.length; i++)
- do_check_throws(function () { test_i64_i64(vals[i]); }, TypeError);
-
- var test_i64_i64_sum = library.declare("test_i64_i64_sum", ctypes.default_abi, ctypes.int64_t, ctypes.int64_t, ctypes.int64_t);
- do_check_eq(test_i64_i64_sum(5, 5), 10);
-
- // test the range of unsigned. (we can reuse the signed C function
- // here, since it's binary-compatible.)
- var test_ui64_ui64 = library.declare("test_i64_i64", ctypes.default_abi, ctypes.uint64_t, ctypes.uint64_t);
- do_check_eq(test_ui64_ui64(0xfffffffffffff000), 0xfffffffffffff000);
- do_check_throws(function () { test_ui64_ui64(0x10000000000000000); }, TypeError);
- do_check_throws(function () { test_ui64_ui64(-1); }, TypeError);
-}
-
-function run_float_tests(library) {
- var test_f = library.declare("test_f", ctypes.default_abi, ctypes.float);
- do_check_eq(test_f(), 123456.5);
-
- var test_f_f = library.declare("test_f_f", ctypes.default_abi, ctypes.float, ctypes.float);
- do_check_eq(test_f_f(5), 5);
- do_check_eq(test_f_f(5.25), 5.25);
- do_check_eq(test_f_f(Infinity), Infinity);
- do_check_eq(test_f_f(-Infinity), -Infinity);
- do_check_eq(isNaN(test_f_f(NaN)), true);
- do_check_eq(1/test_f_f(-0), 1/-0); // that is, test_f_f(-0) is -0
-
- // allow values that can't be represented precisely as a float
- do_check_eq(test_f_f(1 + 1/0x80000000), 1);
-
- // don't convert anything else to a float
- var vals = [true, false, null, undefined, "", "0", {}, [], new Number(16),
- {toString: function () { return 7; }},
- {valueOf: function () { return 7; }}];
- for (var i = 0; i < vals.length; i++)
- do_check_throws(function () { test_f_f(vals[i]); }, TypeError);
-
- var test_f_f_sum = library.declare("test_f_f_sum", ctypes.default_abi, ctypes.float, ctypes.float, ctypes.float);
- do_check_eq(test_f_f_sum(5, 5), 10);
- do_check_eq(test_f_f_sum(5.5, 5.5), 11);
-}
-
-function run_double_tests(library) {
- var test_d = library.declare("test_d", ctypes.default_abi, ctypes.double);
- do_check_eq(test_d(), 1234567890123456789.5);
-
- var test_d_d = library.declare("test_d_d", ctypes.default_abi, ctypes.double, ctypes.double);
- do_check_eq(test_d_d(5), 5);
- do_check_eq(test_d_d(5.25), 5.25);
- do_check_eq(test_d_d(Infinity), Infinity);
- do_check_eq(test_d_d(-Infinity), -Infinity);
- do_check_eq(isNaN(test_d_d(NaN)), true);
- do_check_eq(1/test_d_d(-0), 1/-0); // that is, test_d_d(-0) is -0
-
- // don't convert anything else to a double
- var vals = [true, false, null, undefined, "", "0", {}, [], new Number(16),
- {toString: function () { return 7; }},
- {valueOf: function () { return 7; }}];
- for (var i = 0; i < vals.length; i++)
- do_check_throws(function () { test_d_d(vals[i]); }, TypeError);
-
- var test_d_d_sum = library.declare("test_d_d_sum", ctypes.default_abi, ctypes.double, ctypes.double, ctypes.double);
- do_check_eq(test_d_d_sum(5, 5), 10);
- do_check_eq(test_d_d_sum(5.5, 5.5), 11);
+#ifdef _WIN32
+#ifndef _WIN64
+ test_void_t = library.declare("test_void_t_stdcall", ctypes.stdcall_abi, ctypes.void_t);
+ do_check_eq(test_void_t(), undefined);
+#endif
+#endif
}
function run_string_tests(library) {
- var test_ansi_len = library.declare("test_ansi_len", ctypes.default_abi, ctypes.int32_t, ctypes.string);
+ let test_ansi_len = library.declare("test_ansi_len", ctypes.default_abi, ctypes.int32_t, ctypes.char.ptr);
do_check_eq(test_ansi_len(""), 0);
do_check_eq(test_ansi_len("hello world"), 11);
// don't convert anything else to a string
- var vals = [true, 0, 1/3, undefined, {}, {toString: function () { return "bad"; }}, []];
- for (var i = 0; i < vals.length; i++)
+ let vals = [true, 0, 1/3, undefined, {}, {toString: function () { return "bad"; }}, []];
+ for (let i = 0; i < vals.length; i++)
do_check_throws(function() { test_ansi_len(vals[i]); }, TypeError);
- var test_wide_len = library.declare("test_wide_len", ctypes.default_abi, ctypes.int32_t, ctypes.ustring);
+ let test_wide_len = library.declare("test_wide_len", ctypes.default_abi, ctypes.int32_t, ctypes.jschar.ptr);
do_check_eq(test_wide_len("hello world"), 11);
- var test_ansi_ret = library.declare("test_ansi_ret", ctypes.default_abi, ctypes.string);
- do_check_eq(test_ansi_ret(), "success");
+ let test_ansi_ret = library.declare("test_ansi_ret", ctypes.default_abi, ctypes.char.ptr);
+ do_check_eq(test_ansi_ret().readString(), "success");
- var test_wide_ret = library.declare("test_wide_ret", ctypes.default_abi, ctypes.ustring);
- do_check_eq(test_wide_ret(), "success");
+ let test_wide_ret = library.declare("test_wide_ret", ctypes.default_abi, ctypes.jschar.ptr);
+ do_check_eq(test_wide_ret().readString(), "success");
- var test_ansi_echo = library.declare("test_ansi_echo", ctypes.default_abi, ctypes.string, ctypes.string);
- do_check_eq(test_ansi_echo("anybody in there?"), "anybody in there?");
- do_check_eq(test_ansi_echo(null), null);
+ let test_ansi_echo = library.declare("test_ansi_echo", ctypes.default_abi, ctypes.char.ptr, ctypes.char.ptr);
+ // We cannot pass a string literal directly into test_ansi_echo, since the
+ // conversion to ctypes.char.ptr is only valid for the duration of the ffi
+ // call. The escaped pointer that's returned will point to freed memory.
+ let arg = ctypes.char.array()("anybody in there?");
+ do_check_eq(test_ansi_echo(arg).readString(), "anybody in there?");
+ do_check_eq(ptrValue(test_ansi_echo(null)), 0);
}
-function run_mixed_tests(library) {
- var test_floor = library.declare("test_floor", ctypes.default_abi, ctypes.int32_t, ctypes.int32_t, ctypes.float);
- do_check_eq(test_floor(5, 5.5), 10);
- do_check_throws(function() { test_floor(5.5, 5); }, TypeError);
+function run_struct_tests(library) {
+ const point_t = new ctypes.StructType("POINT",
+ [{ x: ctypes.int32_t },
+ { y: ctypes.int32_t }]);
+ const rect_t = new ctypes.StructType("RECT",
+ [{ top : ctypes.int32_t },
+ { left : ctypes.int32_t },
+ { bottom: ctypes.int32_t },
+ { right : ctypes.int32_t }]);
+
+ let test_pt_in_rect = library.declare("test_pt_in_rect", ctypes.default_abi, ctypes.int32_t, rect_t, point_t);
+ let rect = new rect_t(10, 5, 5, 10);
+ let pt1 = new point_t(6, 6);
+ do_check_eq(test_pt_in_rect(rect, pt1), 1);
+ let pt2 = new point_t(2, 2);
+ do_check_eq(test_pt_in_rect(rect, pt2), 0);
+
+ const inner_t = new ctypes.StructType("INNER",
+ [{ i1: ctypes.uint8_t },
+ { i2: ctypes.int64_t },
+ { i3: ctypes.uint8_t }]);
+ const nested_t = new ctypes.StructType("NESTED",
+ [{ n1 : ctypes.int32_t },
+ { n2 : ctypes.int16_t },
+ { inner: inner_t },
+ { n3 : ctypes.int64_t },
+ { n4 : ctypes.int32_t }]);
+
+ let test_nested_struct = library.declare("test_nested_struct", ctypes.default_abi, ctypes.int32_t, nested_t);
+ let inner = new inner_t(161, 523412, 43);
+ let nested = new nested_t(13155, 1241, inner, 24512115, 1234111);
+ // add up all the numbers and make sure the C function agrees
+ do_check_eq(test_nested_struct(nested), 26284238);
+
+#ifdef XP_WIN
+ // Returning structs by value doesn't yet work on MSVC/Win32.
+ // Make sure it throws.
+ do_check_throws(function() {
+ library.declare("test_struct_return", ctypes.default_abi, point_t, rect_t);
+ }, Error);
+#else
+ // test returning a struct by value
+ let test_struct_return = library.declare("test_struct_return", ctypes.default_abi, point_t, rect_t);
+ let ret = test_struct_return(rect);
+ do_check_eq(ret.x, rect.left);
+ do_check_eq(ret.y, rect.top);
+#endif
+
+ // test passing a struct by pointer
+ let test_init_pt = library.declare("test_init_pt", ctypes.default_abi, ctypes.void_t, point_t.ptr, ctypes.int32_t, ctypes.int32_t);
+ test_init_pt(pt1.address(), 9, 10);
+ do_check_eq(pt1.x, 9);
+ do_check_eq(pt1.y, 10);
}
// bug 522360 - try loading system library without full path
function run_load_system_library()
{
#ifdef XP_WIN
- var syslib = ctypes.open("user32.dll");
+ let syslib = ctypes.open("user32.dll");
#elifdef XP_MACOSX
- var syslib = ctypes.open("libm.dylib");
+ let syslib = ctypes.open("libm.dylib");
#elifdef XP_UNIX
- var syslib = ctypes.open("libm.so");
+ let syslib = ctypes.open("libm.so");
#elifdef XP_OS2
- var syslib = ctypes.open("libc063.dll");
+ let syslib = ctypes.open("libc063.dll");
#else
do_throw("please add a system library for this test")
#endif
diff --git a/js/ctypes/typedefs.h b/js/ctypes/typedefs.h
new file mode 100644
index 000000000000..de678988a914
--- /dev/null
+++ b/js/ctypes/typedefs.h
@@ -0,0 +1,141 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is js-ctypes.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation .
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Dan Witte
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// If we're not breaking the types out, combine them together under one
+// DEFINE_TYPE macro. Otherwise, turn off whichever ones we're not using.
+#if defined(DEFINE_TYPE)
+# define DEFINE_CHAR_TYPE(x, y, z) DEFINE_TYPE(x, y, z)
+# define DEFINE_BOOL_TYPE(x, y, z) DEFINE_TYPE(x, y, z)
+# define DEFINE_INT_TYPE(x, y, z) DEFINE_TYPE(x, y, z)
+# define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_TYPE(x, y, z)
+# define DEFINE_FLOAT_TYPE(x, y, z) DEFINE_TYPE(x, y, z)
+#else
+# ifndef DEFINE_BOOL_TYPE
+# define DEFINE_BOOL_TYPE(x, y, z)
+# endif
+# ifndef DEFINE_CHAR_TYPE
+# define DEFINE_CHAR_TYPE(x, y, z)
+# endif
+# ifndef DEFINE_INT_TYPE
+# define DEFINE_INT_TYPE(x, y, z)
+# endif
+# ifndef DEFINE_WRAPPED_INT_TYPE
+# define DEFINE_WRAPPED_INT_TYPE(x, y, z)
+# endif
+# ifndef DEFINE_FLOAT_TYPE
+# define DEFINE_FLOAT_TYPE(x, y, z)
+# endif
+#endif
+
+// MSVC doesn't have ssize_t. Help it along a little.
+#ifndef _MSC_VER
+#define CTYPES_SSIZE_T ssize_t
+#else
+#define CTYPES_SSIZE_T intptr_t
+#endif
+
+// Some #defines to make handling of types whose length varies by platform
+// easier. These could be implemented as configure tests, but the expressions
+// are all statically resolvable so there's no need.
+#define CTYPES_FFI_BOOL (sizeof(bool) == 1 ? ffi_type_uint8 : ffi_type_uint32)
+#define CTYPES_FFI_LONG (sizeof(long) == 4 ? ffi_type_sint32 : ffi_type_sint64)
+#define CTYPES_FFI_ULONG (sizeof(long) == 4 ? ffi_type_uint32 : ffi_type_uint64)
+#define CTYPES_FFI_SIZE_T (sizeof(size_t) == 4 ? ffi_type_uint32 : ffi_type_uint64)
+#define CTYPES_FFI_SSIZE_T (sizeof(size_t) == 4 ? ffi_type_sint32 : ffi_type_sint64)
+#define CTYPES_FFI_INTPTR_T (sizeof(uintptr_t) == 4 ? ffi_type_sint32 : ffi_type_sint64)
+#define CTYPES_FFI_UINTPTR_T (sizeof(uintptr_t) == 4 ? ffi_type_uint32 : ffi_type_uint64)
+
+/**
+ * Builtin types available for arguments and return values, representing
+ * their C counterparts. Format is:
+ *
+ * DEFINE_X_TYPE(typename, ctype, ffitype)
+ *
+ * where 'typename' is the name of the type constructor (accessible as
+ * ctypes.typename), 'ctype' is the corresponding C type declaration (from
+ * which sizeof(ctype) and templated type conversions will be derived), and
+ * 'ffitype' is the ffi_type to use. (Special types, such as 'void' and the
+ * pointer, array, and struct types are handled separately.)
+ */
+DEFINE_BOOL_TYPE (bool, bool, CTYPES_FFI_BOOL)
+DEFINE_INT_TYPE (int8_t, PRInt8, ffi_type_sint8)
+DEFINE_INT_TYPE (int16_t, PRInt16, ffi_type_sint16)
+DEFINE_INT_TYPE (int32_t, PRInt32, ffi_type_sint32)
+DEFINE_INT_TYPE (uint8_t, PRUint8, ffi_type_uint8)
+DEFINE_INT_TYPE (uint16_t, PRUint16, ffi_type_uint16)
+DEFINE_INT_TYPE (uint32_t, PRUint32, ffi_type_uint32)
+DEFINE_INT_TYPE (short, short, ffi_type_sint16)
+DEFINE_INT_TYPE (unsigned_short, unsigned short, ffi_type_uint16)
+DEFINE_INT_TYPE (int, int, ffi_type_sint32)
+DEFINE_INT_TYPE (unsigned_int, unsigned int, ffi_type_uint32)
+DEFINE_INT_TYPE (unsigned, unsigned, ffi_type_uint32)
+DEFINE_WRAPPED_INT_TYPE(int64_t, PRInt64, ffi_type_sint64)
+DEFINE_WRAPPED_INT_TYPE(uint64_t, PRUint64, ffi_type_uint64)
+DEFINE_WRAPPED_INT_TYPE(long, long, CTYPES_FFI_LONG)
+DEFINE_WRAPPED_INT_TYPE(unsigned_long, unsigned long, CTYPES_FFI_ULONG)
+DEFINE_WRAPPED_INT_TYPE(long_long, long long, ffi_type_sint64)
+DEFINE_WRAPPED_INT_TYPE(unsigned_long_long, unsigned long long, ffi_type_uint64)
+DEFINE_WRAPPED_INT_TYPE(size_t, size_t, CTYPES_FFI_SIZE_T)
+DEFINE_WRAPPED_INT_TYPE(ssize_t, CTYPES_SSIZE_T, CTYPES_FFI_SSIZE_T)
+DEFINE_WRAPPED_INT_TYPE(intptr_t, intptr_t, CTYPES_FFI_INTPTR_T)
+DEFINE_WRAPPED_INT_TYPE(uintptr_t, uintptr_t, CTYPES_FFI_UINTPTR_T)
+DEFINE_FLOAT_TYPE (float32_t, float, ffi_type_float)
+DEFINE_FLOAT_TYPE (float64_t, double, ffi_type_double)
+DEFINE_FLOAT_TYPE (float, float, ffi_type_float)
+DEFINE_FLOAT_TYPE (double, double, ffi_type_double)
+DEFINE_CHAR_TYPE (char, char, ffi_type_uint8)
+DEFINE_CHAR_TYPE (signed_char, signed char, ffi_type_sint8)
+DEFINE_CHAR_TYPE (unsigned_char, unsigned char, ffi_type_uint8)
+DEFINE_CHAR_TYPE (jschar, jschar, ffi_type_uint16)
+
+#undef CTYPES_SSIZE_T
+#undef CTYPES_FFI_BOOL
+#undef CTYPES_FFI_LONG
+#undef CTYPES_FFI_ULONG
+#undef CTYPES_FFI_SIZE_T
+#undef CTYPES_FFI_SSIZE_T
+#undef CTYPES_FFI_INTPTR_T
+#undef CTYPES_FFI_UINTPTR_T
+
+#undef DEFINE_TYPE
+#undef DEFINE_CHAR_TYPE
+#undef DEFINE_BOOL_TYPE
+#undef DEFINE_INT_TYPE
+#undef DEFINE_WRAPPED_INT_TYPE
+#undef DEFINE_FLOAT_TYPE
+
diff --git a/js/ctypes/types.h b/js/ctypes/types.h
deleted file mode 100644
index 10bdc5043a56..000000000000
--- a/js/ctypes/types.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is js-ctypes.
- *
- * The Initial Developer of the Original Code is
- * The Mozilla Foundation .
- * Portions created by the Initial Developer are Copyright (C) 2009
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Dan Witte
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-/**
- * This file defines the constants available on the ctypes.types object (e.g.
- * ctypes.types.VOID). They do not have any interesting properties; they simply
- * exist as unique identifiers for the type they represent.
- */
-
-/**
- * ABI constants that specify the calling convention to use.
- * DEFAULT corresponds to the cdecl convention, and in almost all
- * cases is the correct choice. STDCALL is provided for calling
- * functions in the Microsoft Win32 API.
- */
-DEFINE_ABI(default_abi) // corresponds to cdecl
-DEFINE_ABI(stdcall_abi) // for calling Win32 API functions
-
-/**
- * Types available for arguments and return values, representing
- * their C counterparts.
- */
-DEFINE_TYPE(void_t) // Only allowed for return types.
-DEFINE_TYPE(bool) // _Bool type (assumed 8 bits wide).
-DEFINE_TYPE(int8_t) // int8_t (signed char) type.
-DEFINE_TYPE(int16_t) // int16_t (short) type.
-DEFINE_TYPE(int32_t) // int32_t (int) type.
-DEFINE_TYPE(int64_t) // int64_t (long long) type.
-DEFINE_TYPE(uint8_t) // uint8_t (unsigned char) type.
-DEFINE_TYPE(uint16_t) // uint16_t (unsigned short) type.
-DEFINE_TYPE(uint32_t) // uint32_t (unsigned int) type.
-DEFINE_TYPE(uint64_t) // uint64_t (unsigned long long) type.
-DEFINE_TYPE(float) // float type.
-DEFINE_TYPE(double) // double type.
-DEFINE_TYPE(string) // C string (char *).
-DEFINE_TYPE(ustring) // 16-bit string (char16_t *).
-