/js/src/ctypes/CTypes.cpp
http://github.com/zpao/v8monkey · C++ · 6444 lines · 4660 code · 932 blank · 852 comment · 959 complexity · 829baca86e5d654eabff0f1dc74200a4 MD5 · raw file
Large files are truncated click here to view the full file
- /* -*- 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 <http://www.mozilla.org/>.
- * Portions created by the Initial Developer are Copyright (C) 2009
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Dan Witte <dwitte@mozilla.com>
- *
- * 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 "Library.h"
- #include "jsnum.h"
- #include "jscompartment.h"
- #include "jsobjinlines.h"
- #include <limits>
- #include <math.h>
- #if defined(XP_WIN) || defined(XP_OS2)
- #include <float.h>
- #endif
- #if defined(SOLARIS)
- #include <ieeefp.h>
- #endif
- #ifdef HAVE_SSIZE_T
- #include <sys/types.h>
- #endif
- using namespace std;
- namespace js {
- namespace ctypes {
- /*******************************************************************************
- ** JSAPI function prototypes
- *******************************************************************************/
- static JSBool ConstructAbstract(JSContext* cx, uintN argc, jsval* vp);
- namespace CType {
- static JSBool ConstructData(JSContext* cx, uintN argc, jsval* vp);
- static JSBool ConstructBasic(JSContext* cx, JSObject* obj, uintN argc, jsval* vp);
- static void Trace(JSTracer* trc, JSObject* obj);
- static void Finalize(JSContext* cx, JSObject* obj);
- static void FinalizeProtoClass(JSContext* cx, JSObject* obj);
- static JSBool PrototypeGetter(JSContext* cx, JSObject* obj, jsid idval,
- jsval* vp);
- static JSBool NameGetter(JSContext* cx, JSObject* obj, jsid idval,
- jsval* vp);
- static JSBool SizeGetter(JSContext* cx, JSObject* obj, jsid idval,
- jsval* vp);
- static JSBool PtrGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp);
- static JSBool CreateArray(JSContext* cx, uintN argc, jsval* vp);
- static JSBool ToString(JSContext* cx, uintN argc, jsval* vp);
- static JSBool ToSource(JSContext* cx, uintN argc, jsval* vp);
- static JSBool HasInstance(JSContext* cx, JSObject* obj, const jsval* v, JSBool* bp);
- }
- namespace PointerType {
- static JSBool Create(JSContext* cx, uintN argc, jsval* vp);
- static JSBool ConstructData(JSContext* cx, JSObject* obj, uintN argc, jsval* vp);
- static JSBool TargetTypeGetter(JSContext* cx, JSObject* obj, jsid idval,
- jsval* vp);
- static JSBool ContentsGetter(JSContext* cx, JSObject* obj, jsid idval,
- jsval* vp);
- static JSBool ContentsSetter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict,
- jsval* vp);
- static JSBool IsNull(JSContext* cx, uintN argc, jsval* vp);
- static JSBool Increment(JSContext* cx, uintN argc, jsval* vp);
- static JSBool Decrement(JSContext* cx, uintN argc, jsval* vp);
- // The following is not an instance function, since we don't want to expose arbitrary
- // pointer arithmetic at this moment.
- static JSBool OffsetBy(JSContext* cx, intN offset, jsval* vp);
- }
- namespace ArrayType {
- static JSBool Create(JSContext* cx, uintN argc, jsval* vp);
- static JSBool ConstructData(JSContext* cx, JSObject* obj, uintN argc, jsval* vp);
- static JSBool ElementTypeGetter(JSContext* cx, JSObject* obj, jsid idval,
- jsval* vp);
- static JSBool LengthGetter(JSContext* cx, JSObject* obj, jsid idval,
- jsval* vp);
- static JSBool Getter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp);
- static JSBool Setter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict, jsval* vp);
- static JSBool AddressOfElement(JSContext* cx, uintN argc, jsval* vp);
- }
- namespace StructType {
- static JSBool Create(JSContext* cx, uintN argc, jsval* vp);
- static JSBool ConstructData(JSContext* cx, JSObject* obj, uintN argc, jsval* vp);
- static JSBool FieldsArrayGetter(JSContext* cx, JSObject* obj, jsid idval,
- jsval* vp);
- static JSBool FieldGetter(JSContext* cx, JSObject* obj, jsid idval,
- jsval* vp);
- static JSBool FieldSetter(JSContext* cx, JSObject* obj, jsid idval, JSBool strict,
- jsval* vp);
- static JSBool AddressOfField(JSContext* cx, uintN argc, jsval* vp);
- static JSBool Define(JSContext* cx, uintN argc, jsval* vp);
- }
- namespace FunctionType {
- static JSBool Create(JSContext* cx, uintN argc, jsval* vp);
- static JSBool ConstructData(JSContext* cx, JSObject* typeObj,
- JSObject* dataObj, JSObject* fnObj, JSObject* thisObj, jsval errVal);
- static JSBool Call(JSContext* cx, uintN argc, jsval* vp);
- static JSBool ArgTypesGetter(JSContext* cx, JSObject* obj, jsid idval,
- jsval* vp);
- static JSBool ReturnTypeGetter(JSContext* cx, JSObject* obj, jsid idval,
- jsval* vp);
- static JSBool ABIGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp);
- static JSBool IsVariadicGetter(JSContext* cx, JSObject* obj, jsid idval,
- jsval* vp);
- }
- namespace CClosure {
- static void Trace(JSTracer* trc, JSObject* obj);
- static void Finalize(JSContext* cx, JSObject* obj);
- // libffi callback
- static void ClosureStub(ffi_cif* cif, void* result, void** args,
- void* userData);
- }
- namespace CData {
- static void Finalize(JSContext* cx, JSObject* obj);
- static JSBool ValueGetter(JSContext* cx, JSObject* obj, jsid idval,
- jsval* vp);
- static JSBool ValueSetter(JSContext* cx, JSObject* obj, jsid idval,
- JSBool strict, jsval* vp);
- static JSBool Address(JSContext* cx, uintN argc, jsval* vp);
- static JSBool ReadString(JSContext* cx, uintN argc, jsval* vp);
- static JSBool ToSource(JSContext* cx, uintN argc, jsval* vp);
- }
- // Int64Base provides functions common to Int64 and UInt64.
- namespace Int64Base {
- JSObject* Construct(JSContext* cx, JSObject* proto, uint64_t data,
- bool isUnsigned);
- uint64_t GetInt(JSObject* obj);
- JSBool ToString(JSContext* cx, JSObject* obj, uintN argc, jsval* vp,
- bool isUnsigned);
- JSBool ToSource(JSContext* cx, JSObject* obj, uintN argc, jsval* vp,
- bool isUnsigned);
- static void Finalize(JSContext* cx, JSObject* obj);
- }
- namespace Int64 {
- static JSBool Construct(JSContext* cx, uintN argc, jsval* vp);
- static JSBool ToString(JSContext* cx, uintN argc, jsval* vp);
- static JSBool ToSource(JSContext* cx, uintN argc, jsval* vp);
- 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);
- }
- namespace UInt64 {
- static JSBool Construct(JSContext* cx, uintN argc, jsval* vp);
- static JSBool ToString(JSContext* cx, uintN argc, jsval* vp);
- static JSBool ToSource(JSContext* cx, uintN argc, jsval* vp);
- 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);
- }
- /*******************************************************************************
- ** JSClass definitions and initialization functions
- *******************************************************************************/
- // Class representing the 'ctypes' object itself. This exists to contain the
- // JSCTypesCallbacks set of function pointers.
- static JSClass sCTypesGlobalClass = {
- "ctypes",
- JSCLASS_HAS_RESERVED_SLOTS(CTYPESGLOBAL_SLOTS),
- JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
- JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
- JSCLASS_NO_OPTIONAL_MEMBERS
- };
- static JSClass sCABIClass = {
- "CABI",
- JSCLASS_HAS_RESERVED_SLOTS(CABI_SLOTS),
- JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
- JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
- JSCLASS_NO_OPTIONAL_MEMBERS
- };
- // Class representing ctypes.{C,Pointer,Array,Struct,Function}Type.prototype.
- // This exists to give said prototypes a class of "CType", and to provide
- // reserved slots for stashing various other prototype objects.
- static JSClass sCTypeProtoClass = {
- "CType",
- JSCLASS_HAS_RESERVED_SLOTS(CTYPEPROTO_SLOTS),
- JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
- JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CType::FinalizeProtoClass,
- NULL, NULL, ConstructAbstract, ConstructAbstract, NULL, NULL, NULL, NULL
- };
- // Class representing ctypes.CData.prototype and the 'prototype' properties
- // of CTypes. This exists to give said prototypes a class of "CData".
- static JSClass sCDataProtoClass = {
- "CData",
- 0,
- JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
- JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
- JSCLASS_NO_OPTIONAL_MEMBERS
- };
- static JSClass sCTypeClass = {
- "CType",
- JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS),
- JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
- JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CType::Finalize,
- NULL, NULL, CType::ConstructData, CType::ConstructData, NULL,
- CType::HasInstance, CType::Trace, 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,
- NULL, NULL, FunctionType::Call, FunctionType::Call, NULL, NULL, NULL, NULL
- };
- static JSClass sCClosureClass = {
- "CClosure",
- JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS),
- JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
- JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CClosure::Finalize,
- NULL, NULL, NULL, NULL, NULL, NULL, CClosure::Trace, NULL
- };
- #define CTYPESFN_FLAGS \
- (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
- #define CTYPESCTOR_FLAGS \
- (CTYPESFN_FLAGS | JSFUN_CONSTRUCTOR)
- #define CTYPESPROP_FLAGS \
- (JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
- #define CDATAFN_FLAGS \
- (JSPROP_READONLY | JSPROP_PERMANENT)
- static JSPropertySpec sCTypeProps[] = {
- { "name", 0, CTYPESPROP_FLAGS, CType::NameGetter, NULL },
- { "size", 0, CTYPESPROP_FLAGS, CType::SizeGetter, NULL },
- { "ptr", 0, CTYPESPROP_FLAGS, CType::PtrGetter, NULL },
- { "prototype", 0, CTYPESPROP_FLAGS, CType::PrototypeGetter, NULL },
- { 0, 0, 0, NULL, NULL }
- };
- static JSFunctionSpec sCTypeFunctions[] = {
- JS_FN("array", CType::CreateArray, 0, CTYPESFN_FLAGS),
- JS_FN("toString", CType::ToString, 0, CTYPESFN_FLAGS),
- JS_FN("toSource", CType::ToSource, 0, CTYPESFN_FLAGS),
- JS_FS_END
- };
- static JSPropertySpec sCDataProps[] = {
- { "value", 0, JSPROP_SHARED | JSPROP_PERMANENT,
- CData::ValueGetter, CData::ValueSetter },
- { 0, 0, 0, NULL, NULL }
- };
- static JSFunctionSpec sCDataFunctions[] = {
- JS_FN("address", CData::Address, 0, CDATAFN_FLAGS),
- JS_FN("readString", CData::ReadString, 0, CDATAFN_FLAGS),
- JS_FN("toSource", CData::ToSource, 0, CDATAFN_FLAGS),
- JS_FN("toString", CData::ToSource, 0, CDATAFN_FLAGS),
- JS_FS_END
- };
- static JSFunctionSpec sPointerFunction =
- JS_FN("PointerType", PointerType::Create, 1, CTYPESCTOR_FLAGS);
- static JSPropertySpec sPointerProps[] = {
- { "targetType", 0, CTYPESPROP_FLAGS, PointerType::TargetTypeGetter, NULL },
- { 0, 0, 0, NULL, NULL }
- };
- static JSFunctionSpec sPointerInstanceFunctions[] = {
- JS_FN("isNull", PointerType::IsNull, 0, CTYPESFN_FLAGS),
- JS_FN("increment", PointerType::Increment, 0, CTYPESFN_FLAGS),
- JS_FN("decrement", PointerType::Decrement, 0, CTYPESFN_FLAGS),
- JS_FS_END
- };
-
- static JSPropertySpec sPointerInstanceProps[] = {
- { "contents", 0, JSPROP_SHARED | JSPROP_PERMANENT,
- PointerType::ContentsGetter, PointerType::ContentsSetter },
- { 0, 0, 0, NULL, NULL }
- };
- static JSFunctionSpec sArrayFunction =
- JS_FN("ArrayType", ArrayType::Create, 1, CTYPESCTOR_FLAGS);
- static JSPropertySpec sArrayProps[] = {
- { "elementType", 0, CTYPESPROP_FLAGS, ArrayType::ElementTypeGetter, NULL },
- { "length", 0, CTYPESPROP_FLAGS, ArrayType::LengthGetter, NULL },
- { 0, 0, 0, NULL, NULL }
- };
- static JSFunctionSpec sArrayInstanceFunctions[] = {
- JS_FN("addressOfElement", ArrayType::AddressOfElement, 1, CDATAFN_FLAGS),
- JS_FS_END
- };
- static JSPropertySpec sArrayInstanceProps[] = {
- { "length", 0, JSPROP_SHARED | JSPROP_READONLY | JSPROP_PERMANENT,
- ArrayType::LengthGetter, NULL },
- { 0, 0, 0, NULL, NULL }
- };
- static JSFunctionSpec sStructFunction =
- JS_FN("StructType", StructType::Create, 2, CTYPESCTOR_FLAGS);
- static JSPropertySpec sStructProps[] = {
- { "fields", 0, CTYPESPROP_FLAGS, StructType::FieldsArrayGetter, NULL },
- { 0, 0, 0, NULL, NULL }
- };
- static JSFunctionSpec sStructFunctions[] = {
- JS_FN("define", StructType::Define, 1, CDATAFN_FLAGS),
- JS_FS_END
- };
- static JSFunctionSpec sStructInstanceFunctions[] = {
- JS_FN("addressOfField", StructType::AddressOfField, 1, CDATAFN_FLAGS),
- JS_FS_END
- };
- static JSFunctionSpec sFunctionFunction =
- JS_FN("FunctionType", FunctionType::Create, 2, CTYPESCTOR_FLAGS);
- static JSPropertySpec sFunctionProps[] = {
- { "argTypes", 0, CTYPESPROP_FLAGS, FunctionType::ArgTypesGetter, NULL },
- { "returnType", 0, CTYPESPROP_FLAGS, FunctionType::ReturnTypeGetter, NULL },
- { "abi", 0, CTYPESPROP_FLAGS, FunctionType::ABIGetter, NULL },
- { "isVariadic", 0, CTYPESPROP_FLAGS, FunctionType::IsVariadicGetter, NULL },
- { 0, 0, 0, NULL, NULL }
- };
- static JSClass sInt64ProtoClass = {
- "Int64",
- 0,
- JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
- JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
- JSCLASS_NO_OPTIONAL_MEMBERS
- };
- static JSClass sUInt64ProtoClass = {
- "UInt64",
- 0,
- JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
- 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_StrictPropertyStub,
- 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_StrictPropertyStub,
- 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
- };
- static JSFunctionSpec sModuleFunctions[] = {
- JS_FN("open", Library::Open, 1, CTYPESFN_FLAGS),
- JS_FN("cast", CData::Cast, 2, CTYPESFN_FLAGS),
- JS_FN("getRuntime", CData::GetRuntime, 1, CTYPESFN_FLAGS),
- JS_FN("libraryName", Library::Name, 1, CTYPESFN_FLAGS),
- JS_FS_END
- };
- static inline bool FloatIsFinite(jsdouble f) {
- #ifdef WIN32
- return _finite(f) != 0;
- #else
- return finite(f);
- #endif
- }
- JS_ALWAYS_INLINE JSString*
- NewUCString(JSContext* cx, const AutoString& from)
- {
- return JS_NewUCStringCopyN(cx, from.begin(), from.length());
- }
- JS_ALWAYS_INLINE size_t
- Align(size_t val, size_t align)
- {
- return ((val - 1) | (align - 1)) + 1;
- }
- static ABICode
- GetABICode(JSObject* obj)
- {
- // make sure we have an object representing a CABI class,
- // and extract the enumerated class type from the reserved slot.
- if (JS_GetClass(obj) != &sCABIClass)
- return INVALID_ABI;
- jsval result = JS_GetReservedSlot(obj, SLOT_ABICODE);
- return ABICode(JSVAL_TO_INT(result));
- }
- JSErrorFormatString ErrorFormatString[CTYPESERR_LIMIT] = {
- #define MSG_DEF(name, number, count, exception, format) \
- { format, count, exception } ,
- #include "ctypes.msg"
- #undef MSG_DEF
- };
- const JSErrorFormatString*
- GetErrorMessage(void* userRef, const char* locale, const uintN errorNumber)
- {
- if (0 < errorNumber && errorNumber < CTYPESERR_LIMIT)
- return &ErrorFormatString[errorNumber];
- return NULL;
- }
- JSBool
- TypeError(JSContext* cx, const char* expected, jsval actual)
- {
- JSString* str = JS_ValueToSource(cx, actual);
- JSAutoByteString bytes;
-
- const char* src;
- if (str) {
- src = bytes.encode(cx, str);
- if (!src)
- return false;
- } else {
- JS_ClearPendingException(cx);
- src = "<<error converting value to string>>";
- }
- JS_ReportErrorNumber(cx, GetErrorMessage, NULL,
- CTYPESMSG_TYPE_ERROR, expected, src);
- return false;
- }
- static JSObject*
- InitCTypeClass(JSContext* cx, JSObject* parent)
- {
- JSFunction* fun = JS_DefineFunction(cx, parent, "CType", ConstructAbstract, 0,
- CTYPESCTOR_FLAGS);
- if (!fun)
- return NULL;
- JSObject* ctor = JS_GetFunctionObject(fun);
- JSObject* fnproto = JS_GetPrototype(ctor);
- JS_ASSERT(ctor);
- JS_ASSERT(fnproto);
- // Set up ctypes.CType.prototype.
- JSObject* prototype = JS_NewObject(cx, &sCTypeProtoClass, fnproto, parent);
- if (!prototype)
- return NULL;
- if (!JS_DefineProperty(cx, ctor, "prototype", OBJECT_TO_JSVAL(prototype),
- NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
- return NULL;
- if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(ctor),
- NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
- return NULL;
- // Define properties and functions common to all CTypes.
- if (!JS_DefineProperties(cx, prototype, sCTypeProps) ||
- !JS_DefineFunctions(cx, prototype, sCTypeFunctions))
- return NULL;
- if (!JS_FreezeObject(cx, ctor) || !JS_FreezeObject(cx, prototype))
- return NULL;
- return prototype;
- }
- static JSObject*
- InitCDataClass(JSContext* cx, JSObject* parent, JSObject* CTypeProto)
- {
- JSFunction* fun = JS_DefineFunction(cx, parent, "CData", ConstructAbstract, 0,
- CTYPESCTOR_FLAGS);
- if (!fun)
- return NULL;
- JSObject* ctor = JS_GetFunctionObject(fun);
- JS_ASSERT(ctor);
- // Set up ctypes.CData.__proto__ === ctypes.CType.prototype.
- // (Note that 'ctypes.CData instanceof Function' is still true, thanks to the
- // prototype chain.)
- if (!JS_SetPrototype(cx, ctor, CTypeProto))
- return NULL;
- // Set up ctypes.CData.prototype.
- JSObject* prototype = JS_NewObject(cx, &sCDataProtoClass, NULL, parent);
- if (!prototype)
- return NULL;
- if (!JS_DefineProperty(cx, ctor, "prototype", OBJECT_TO_JSVAL(prototype),
- NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
- return NULL;
- if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(ctor),
- NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
- return NULL;
- // Define properties and functions common to all CDatas.
- if (!JS_DefineProperties(cx, prototype, sCDataProps) ||
- !JS_DefineFunctions(cx, prototype, sCDataFunctions))
- return NULL;
- if (//!JS_FreezeObject(cx, prototype) || // XXX fixme - see bug 541212!
- !JS_FreezeObject(cx, ctor))
- return NULL;
- return prototype;
- }
- static JSBool
- DefineABIConstant(JSContext* cx,
- 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;
- JS_SetReservedSlot(obj, SLOT_ABICODE, INT_TO_JSVAL(code));
- return JS_FreezeObject(cx, obj);
- }
- // Set up a single type constructor for
- // ctypes.{Pointer,Array,Struct,Function}Type.
- static JSBool
- InitTypeConstructor(JSContext* cx,
- JSObject* parent,
- JSObject* CTypeProto,
- JSObject* CDataProto,
- JSFunctionSpec spec,
- JSFunctionSpec* fns,
- JSPropertySpec* props,
- JSFunctionSpec* instanceFns,
- JSPropertySpec* instanceProps,
- JSObject*& typeProto,
- JSObject*& dataProto)
- {
- JSFunction* fun = js::DefineFunctionWithReserved(cx, parent, spec.name, spec.call,
- spec.nargs, spec.flags);
- if (!fun)
- return false;
- JSObject* obj = JS_GetFunctionObject(fun);
- if (!obj)
- return false;
- // Set up the .prototype and .prototype.constructor properties.
- typeProto = JS_NewObject(cx, &sCTypeProtoClass, CTypeProto, parent);
- if (!typeProto)
- return false;
- // Define property before proceeding, for GC safety.
- if (!JS_DefineProperty(cx, obj, "prototype", OBJECT_TO_JSVAL(typeProto),
- NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
- return false;
- if (fns && !JS_DefineFunctions(cx, typeProto, fns))
- return false;
- if (!JS_DefineProperties(cx, typeProto, props))
- return false;
- if (!JS_DefineProperty(cx, typeProto, "constructor", OBJECT_TO_JSVAL(obj),
- NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
- return false;
- // Stash ctypes.{Pointer,Array,Struct}Type.prototype on a reserved slot of
- // the type constructor, for faster lookup.
- js::SetFunctionNativeReserved(obj, SLOT_FN_CTORPROTO, OBJECT_TO_JSVAL(typeProto));
- // Create an object to serve as the common ancestor for all CData objects
- // created from the given type constructor. This has ctypes.CData.prototype
- // as its prototype, such that it inherits the properties and functions
- // common to all CDatas.
- dataProto = JS_NewObject(cx, &sCDataProtoClass, CDataProto, parent);
- if (!dataProto)
- return false;
- js::AutoObjectRooter protoroot(cx, dataProto);
- // Define functions and properties on the 'dataProto' object that are common
- // to all CData objects created from this type constructor. (These will
- // become functions and properties on CData objects created from this type.)
- if (instanceFns && !JS_DefineFunctions(cx, dataProto, instanceFns))
- return false;
- if (instanceProps && !JS_DefineProperties(cx, dataProto, instanceProps))
- return false;
- // Link the type prototype to the data prototype.
- JS_SetReservedSlot(typeProto, SLOT_OURDATAPROTO, OBJECT_TO_JSVAL(dataProto));
- if (!JS_FreezeObject(cx, obj) ||
- //!JS_FreezeObject(cx, dataProto) || // XXX fixme - see bug 541212!
- !JS_FreezeObject(cx, typeProto))
- return false;
- return true;
- }
- JSObject*
- InitInt64Class(JSContext* cx,
- 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;
- JSObject* ctor = JS_GetConstructor(cx, prototype);
- if (!ctor)
- return NULL;
- if (!JS_FreezeObject(cx, ctor))
- return NULL;
- // Redefine the 'join' function as an extended native and stash
- // ctypes.{Int64,UInt64}.prototype in a reserved slot of the new function.
- JS_ASSERT(clasp == &sInt64ProtoClass || clasp == &sUInt64ProtoClass);
- JSNative native = (clasp == &sInt64ProtoClass) ? Int64::Join : UInt64::Join;
- JSFunction* fun = js::DefineFunctionWithReserved(cx, ctor, "join", native,
- 2, CTYPESFN_FLAGS);
- if (!fun)
- return NULL;
- js::SetFunctionNativeReserved(fun, SLOT_FN_INT64PROTO,
- OBJECT_TO_JSVAL(prototype));
- if (!JS_FreezeObject(cx, prototype))
- return NULL;
- return prototype;
- }
- static void
- AttachProtos(JSObject* proto, JSObject** protos)
- {
- // For a given 'proto' of [[Class]] "CTypeProto", attach each of the 'protos'
- // to the appropriate CTypeProtoSlot. (SLOT_UINT64PROTO is the last slot
- // of [[Class]] "CTypeProto" that we fill in this automated manner.)
- for (uint32_t i = 0; i <= SLOT_UINT64PROTO; ++i)
- JS_SetReservedSlot(proto, i, OBJECT_TO_JSVAL(protos[i]));
- }
- JSBool
- 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
- // * A constructor that throws a TypeError. (You can't construct an
- // abstract type instance!)
- // * 'constructor' property === ctypes.CType
- // * Provides properties and functions common to all CTypes.
- JSObject* CTypeProto = InitCTypeClass(cx, parent);
- if (!CTypeProto)
- return false;
- // Initialize the ctypes.CData class. This acts as an abstract base class for
- // instances of the various types, and provides the common API functions.
- // It has:
- // * [[Class]] "Function"
- // * __proto__ === Function.prototype
- // * A constructor that throws a TypeError. (You can't construct an
- // abstract type instance!)
- // * 'prototype' property:
- // * [[Class]] "CDataProto"
- // * 'constructor' property === ctypes.CData
- // * Provides properties and functions common to all CDatas.
- JSObject* CDataProto = InitCDataClass(cx, parent, CTypeProto);
- if (!CDataProto)
- return false;
- // Link CTypeProto to CDataProto.
- JS_SetReservedSlot(CTypeProto, SLOT_OURDATAPROTO,
- OBJECT_TO_JSVAL(CDataProto));
- // Create and attach the special class constructors: ctypes.PointerType,
- // ctypes.ArrayType, ctypes.StructType, and ctypes.FunctionType.
- // Each of these constructors 'c' has, respectively:
- // * [[Class]] "Function"
- // * __proto__ === Function.prototype
- // * A constructor that creates a user-defined type.
- // * 'prototype' property:
- // * [[Class]] "CTypeProto"
- // * __proto__ === ctypes.CType.prototype
- // * 'constructor' property === 'c'
- // We also construct an object 'p' to serve, given a type object 't'
- // constructed from one of these type constructors, as
- // 't.prototype.__proto__'. This object has:
- // * [[Class]] "CDataProto"
- // * __proto__ === ctypes.CData.prototype
- // * Properties and functions common to all CDatas.
- // Therefore an instance 't' of ctypes.{Pointer,Array,Struct,Function}Type
- // will have, resp.:
- // * [[Class]] "CType"
- // * __proto__ === ctypes.{Pointer,Array,Struct,Function}Type.prototype
- // * A constructor which creates and returns a CData object, containing
- // binary data of the given type.
- // * 'prototype' property:
- // * [[Class]] "CDataProto"
- // * __proto__ === 'p', the prototype object from above
- // * 'constructor' property === 't'
- JSObject* protos[CTYPEPROTO_SLOTS];
- if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
- sPointerFunction, NULL, sPointerProps,
- sPointerInstanceFunctions, sPointerInstanceProps,
- protos[SLOT_POINTERPROTO], protos[SLOT_POINTERDATAPROTO]))
- return false;
- js::AutoObjectRooter proot(cx, protos[SLOT_POINTERDATAPROTO]);
- if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
- sArrayFunction, NULL, sArrayProps,
- sArrayInstanceFunctions, sArrayInstanceProps,
- protos[SLOT_ARRAYPROTO], protos[SLOT_ARRAYDATAPROTO]))
- return false;
- js::AutoObjectRooter aroot(cx, protos[SLOT_ARRAYDATAPROTO]);
- if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
- sStructFunction, sStructFunctions, sStructProps,
- sStructInstanceFunctions, NULL,
- protos[SLOT_STRUCTPROTO], protos[SLOT_STRUCTDATAPROTO]))
- return false;
- js::AutoObjectRooter sroot(cx, protos[SLOT_STRUCTDATAPROTO]);
- if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
- sFunctionFunction, NULL, sFunctionProps, NULL, NULL,
- protos[SLOT_FUNCTIONPROTO], protos[SLOT_FUNCTIONDATAPROTO]))
- return false;
- js::AutoObjectRooter froot(cx, protos[SLOT_FUNCTIONDATAPROTO]);
- protos[SLOT_CDATAPROTO] = CDataProto;
- // Create and attach the ctypes.{Int64,UInt64} constructors.
- // Each of these has, respectively:
- // * [[Class]] "Function"
- // * __proto__ === Function.prototype
- // * A constructor that creates a ctypes.{Int64,UInt64} object, respectively.
- // * 'prototype' property:
- // * [[Class]] {"Int64Proto","UInt64Proto"}
- // * 'constructor' property === ctypes.{Int64,UInt64}
- protos[SLOT_INT64PROTO] = InitInt64Class(cx, parent, &sInt64ProtoClass,
- Int64::Construct, sInt64Functions, sInt64StaticFunctions);
- if (!protos[SLOT_INT64PROTO])
- return false;
- protos[SLOT_UINT64PROTO] = InitInt64Class(cx, parent, &sUInt64ProtoClass,
- UInt64::Construct, sUInt64Functions, sUInt64StaticFunctions);
- if (!protos[SLOT_UINT64PROTO])
- return false;
- // Attach the prototypes just created to each of ctypes.CType.prototype,
- // and the special type constructors, so we can access them when constructing
- // instances of those types.
- AttachProtos(CTypeProto, protos);
- AttachProtos(protos[SLOT_POINTERPROTO], protos);
- AttachProtos(protos[SLOT_ARRAYPROTO], protos);
- AttachProtos(protos[SLOT_STRUCTPROTO], protos);
- AttachProtos(protos[SLOT_FUNCTIONPROTO], protos);
- // Attach objects representing ABI constants.
- if (!DefineABIConstant(cx, parent, "default_abi", ABI_DEFAULT) ||
- !DefineABIConstant(cx, parent, "stdcall_abi", ABI_STDCALL) ||
- !DefineABIConstant(cx, parent, "winapi_abi", ABI_WINAPI))
- return false;
- // Create objects representing the builtin types, and attach them to the
- // ctypes object. Each type object 't' has:
- // * [[Class]] "CType"
- // * __proto__ === ctypes.CType.prototype
- // * A constructor which creates and returns a CData object, containing
- // binary data of the given type.
- // * 'prototype' property:
- // * [[Class]] "CDataProto"
- // * __proto__ === ctypes.CData.prototype
- // * 'constructor' property === 't'
- #define DEFINE_TYPE(name, type, ffiType) \
- JSObject* typeObj_##name = \
- CType::DefineBuiltin(cx, parent, #name, CTypeProto, CDataProto, #name, \
- TYPE_##name, INT_TO_JSVAL(sizeof(type)), \
- INT_TO_JSVAL(ffiType.alignment), &ffiType); \
- if (!typeObj_##name) \
- return false;
- #include "typedefs.h"
- // Alias 'ctypes.unsigned' as 'ctypes.unsigned_int', since they represent
- // the same type in C.
- if (!JS_DefineProperty(cx, parent, "unsigned",
- OBJECT_TO_JSVAL(typeObj_unsigned_int), NULL, NULL,
- JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
- return false;
- // Create objects representing the special types void_t and voidptr_t.
- JSObject* typeObj =
- CType::DefineBuiltin(cx, parent, "void_t", CTypeProto, CDataProto, "void",
- TYPE_void_t, JSVAL_VOID, JSVAL_VOID, &ffi_type_void);
- if (!typeObj)
- return false;
- typeObj = PointerType::CreateInternal(cx, typeObj);
- if (!typeObj)
- return false;
- if (!JS_DefineProperty(cx, parent, "voidptr_t", OBJECT_TO_JSVAL(typeObj),
- NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
- return false;
- return true;
- }
- bool
- IsCTypesGlobal(JSObject* obj)
- {
- return JS_GetClass(obj) == &sCTypesGlobalClass;
- }
- // Get the JSCTypesCallbacks struct from the 'ctypes' object 'obj'.
- JSCTypesCallbacks*
- GetCallbacks(JSObject* obj)
- {
- JS_ASSERT(IsCTypesGlobal(obj));
- jsval result = JS_GetReservedSlot(obj, SLOT_CALLBACKS);
- if (JSVAL_IS_VOID(result))
- return NULL;
- return static_cast<JSCTypesCallbacks*>(JSVAL_TO_PRIVATE(result));
- }
- JS_BEGIN_EXTERN_C
- JS_PUBLIC_API(JSBool)
- JS_InitCTypesClass(JSContext* cx, JSObject* global)
- {
- // attach ctypes property to global object
- JSObject* ctypes = JS_NewObject(cx, &sCTypesGlobalClass, NULL, NULL);
- if (!ctypes)
- return false;
- if (!JS_DefineProperty(cx, global, "ctypes", OBJECT_TO_JSVAL(ctypes),
- JS_PropertyStub, JS_StrictPropertyStub, JSPROP_READONLY | JSPROP_PERMANENT)) {
- return false;
- }
- if (!InitTypeClasses(cx, ctypes))
- return false;
- // attach API functions
- if (!JS_DefineFunctions(cx, ctypes, sModuleFunctions))
- return false;
- // Seal the ctypes object, to prevent modification.
- return JS_FreezeObject(cx, ctypes);
- }
- JS_PUBLIC_API(void)
- JS_SetCTypesCallbacks(JSObject* ctypesObj,
- JSCTypesCallbacks* callbacks)
- {
- JS_ASSERT(callbacks);
- JS_ASSERT(IsCTypesGlobal(ctypesObj));
- // Set the callbacks on a reserved slot.
- JS_SetReservedSlot(ctypesObj, SLOT_CALLBACKS, PRIVATE_TO_JSVAL(callbacks));
- }
- JS_END_EXTERN_C
- /*******************************************************************************
- ** Type conversion functions
- *******************************************************************************/
- // Enforce some sanity checks on type widths and properties.
- // Where the architecture is 64-bit, make sure it's LP64 or LLP64. (ctypes.int
- // autoconverts to a primitive JS number; to support ILP64 architectures, it
- // would need to autoconvert to an Int64 object instead. Therefore we enforce
- // this invariant here.)
- JS_STATIC_ASSERT(sizeof(bool) == 1 || sizeof(bool) == 4);
- JS_STATIC_ASSERT(sizeof(char) == 1);
- JS_STATIC_ASSERT(sizeof(short) == 2);
- JS_STATIC_ASSERT(sizeof(int) == 4);
- JS_STATIC_ASSERT(sizeof(unsigned) == 4);
- JS_STATIC_ASSERT(sizeof(long) == 4 || sizeof(long) == 8);
- JS_STATIC_ASSERT(sizeof(long long) == 8);
- JS_STATIC_ASSERT(sizeof(size_t) == sizeof(uintptr_t));
- JS_STATIC_ASSERT(sizeof(float) == 4);
- JS_STATIC_ASSERT(sizeof(PRFuncPtr) == sizeof(void*));
- JS_STATIC_ASSERT(numeric_limits<double>::is_signed);
- // Templated helper to convert FromType to TargetType, for the default case
- // where the trivial POD constructor will do.
- template<class TargetType, class FromType>
- struct ConvertImpl {
- static JS_ALWAYS_INLINE TargetType Convert(FromType d) {
- return TargetType(d);
- }
- };
- #ifdef _MSC_VER
- // MSVC can't perform double to unsigned __int64 conversion when the
- // double is greater than 2^63 - 1. Help it along a little.
- template<>
- struct ConvertImpl<uint64_t, jsdouble> {
- static JS_ALWAYS_INLINE uint64_t Convert(jsdouble d) {
- return d > 0x7fffffffffffffffui64 ?
- uint64_t(d - 0x8000000000000000ui64) + 0x8000000000000000ui64 :
- uint64_t(d);
- }
- };
- #endif
- // C++ doesn't guarantee that exact values are the only ones that will
- // round-trip. In fact, on some platforms, including SPARC, there are pairs of
- // values, a uint64_t and a double, such that neither value is exactly
- // representable in the other type, but they cast to each other.
- #ifdef SPARC
- // Simulate x86 overflow behavior
- template<>
- struct ConvertImpl<uint64_t, jsdouble> {
- static JS_ALWAYS_INLINE uint64_t Convert(jsdouble d) {
- return d >= 0xffffffffffffffff ?
- 0x8000000000000000 : uint64_t(d);
- }
- };
- template<>
- struct ConvertImpl<int64_t, jsdouble> {
- static JS_ALWAYS_INLINE int64_t Convert(jsdouble d) {
- return d >= 0x7fffffffffffffff ?
- 0x8000000000000000 : int64_t(d);
- }
- };
- #endif
- template<class TargetType, class FromType>
- static JS_ALWAYS_INLINE TargetType Convert(FromType d)
- {
- return ConvertImpl<TargetType, FromType>::Convert(d);
- }
- template<class TargetType, class FromType>
- static JS_ALWAYS_INLINE bool IsAlwaysExact()
- {
- // Return 'true' if TargetType can always exactly represent FromType.
- // This means that:
- // 1) TargetType must be the same or more bits wide as FromType. For integers
- // represented in 'n' bits, unsigned variants will have 'n' digits while
- // signed will have 'n - 1'. For floating point types, 'digits' is the
- // mantissa width.
- // 2) If FromType is signed, TargetType must also be signed. (Floating point
- // types are always signed.)
- // 3) If TargetType is an exact integral type, FromType must be also.
- if (numeric_limits<TargetType>::digits < numeric_limits<FromType>::digits)
- return false;
- if (numeric_limits<FromType>::is_signed &&
- !numeric_limits<TargetType>::is_signed)
- return false;
- if (!numeric_limits<FromType>::is_exact &&
- numeric_limits<TargetType>::is_exact)
- return false;
- return true;
- }
- // Templated helper to determine if FromType 'i' converts losslessly to
- // TargetType 'j'. Default case where both types are the same signedness.
- template<class TargetType, class FromType, bool TargetSigned, bool FromSigned>
- struct IsExactImpl {
- static JS_ALWAYS_INLINE bool Test(FromType i, TargetType j) {
- JS_STATIC_ASSERT(numeric_limits<TargetType>::is_exact);
- return FromType(j) == i;
- }
- };
- // Specialization where TargetType is unsigned, FromType is signed.
- template<class TargetType, class FromType>
- struct IsExactImpl<TargetType, FromType, false, true> {
- static JS_ALWAYS_INLINE bool Test(FromType i, TargetType j) {
- JS_STATIC_ASSERT(numeric_limits<TargetType>::is_exact);
- return i >= 0 && FromType(j) == i;
- }
- };
- // Specialization where TargetType is signed, FromType is unsigned.
- template<class TargetType, class FromType>
- struct IsExactImpl<TargetType, FromType, true, false> {
- static JS_ALWAYS_INLINE bool Test(FromType i, TargetType j) {
- JS_STATIC_ASSERT(numeric_limits<TargetType>::is_exact);
- return TargetType(i) >= 0 && FromType(j) == i;
- }
- };
- // Convert FromType 'i' to TargetType 'result', returning true iff 'result'
- // is an exact representation of 'i'.
- template<class TargetType, class FromType>
- static JS_ALWAYS_INLINE bool ConvertExact(FromType i, TargetType* result)
- {
- // Require that TargetType is integral, to simplify conversion.
- JS_STATIC_ASSERT(numeric_limits<TargetType>::is_exact);
- *result = Convert<TargetType>(i);
- // See if we can avoid a dynamic check.
- if (IsAlwaysExact<TargetType, FromType>())
- return true;
- // Return 'true' if 'i' is exactly representable in 'TargetType'.
- return IsExactImpl<TargetType,
- FromType,
- numeric_limits<TargetType>::is_signed,
- numeric_limits<FromType>::is_signed>::Test(i, *result);
- }
- // Templated helper to determine if Type 'i' is negative. Default case
- // where IntegerType is unsigned.
- template<class Type, bool IsSigned>
- struct IsNegativeImpl {
- static JS_ALWAYS_INLINE bool Test(Type i) {
- return false;
- }
- };
- // Specialization where Type is signed.
- template<class Type>
- struct IsNegativeImpl<Type, true> {
- static JS_ALWAYS_INLINE bool Test(Type i) {
- return i < 0;
- }
- };
- // Determine whether Type 'i' is negative.
- template<class Type>
- static JS_ALWAYS_INLINE bool IsNegative(Type i)
- {
- return IsNegativeImpl<Type, numeric_limits<Type>::is_signed>::Test(i);
- }
- // Implicitly convert val to bool, allowing JSBool, 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;
- // Allow -0.
- return d == 1 || d == 0;
- }
- // Don't silently convert null to bool. It's probably a mistake.
- return false;
- }
- // Implicitly convert val to IntegerType, allowing JSBool, jsint, jsdouble,
- // Int64, UInt64, and CData integer types 't' where all values of 't' are
- // representable by IntegerType.
- template<class IntegerType>
- static bool
- jsvalToInteger(JSContext* cx, jsval val, IntegerType* result)
- {
- JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
- if (JSVAL_IS_INT(val)) {
- // Make sure the integer fits in the alotted precision, and has the right
- // sign.
- jsint i = JSVAL_TO_INT(val);
- return ConvertExact(i, result);
- }
- if (JSVAL_IS_DOUBLE(val)) {
- // Don't silently lose bits here -- check that val really is an
- // integer value, and has the right sign.
- jsdouble d = JSVAL_TO_DOUBLE(val);
- return ConvertExact(d, result);
- }
- if (!JSVAL_IS_PRIMITIVE(val)) {
- JSObject* obj = JSVAL_TO_OBJECT(val);
- if (CData::IsCData(obj)) {
- JSObject* typeObj = CData::GetCType(obj);
- void* data = CData::GetData(obj);
- // Check whether the source type is always representable, with exact
- // precision, by the target type. If it is, convert the value.
- switch (CType::GetTypeCode(typeObj)) {
- #define DEFINE_INT_TYPE(name, fromType, ffiType) \
- case TYPE_##name: \
- if (!IsAlwaysExact<IntegerType, fromType>()) \
- return false; \
- *result = IntegerType(*static_cast<fromType*>(data)); \
- return true;
- #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
- #include "typedefs.h"
- case TYPE_void_t:
- case TYPE_bool:
- case TYPE_float:
- case TYPE_double:
- case TYPE_float32_t:
- case TYPE_float64_t:
- case TYPE_char:
- case TYPE_signed_char:
- case TYPE_unsigned_char:
- case TYPE_jschar:
- case TYPE_pointer:
- case TYPE_function:
- case TYPE_array:
- case TYPE_struct:
- // Not a compatible number type.
- return false;
- }
- }
- if (Int64::IsInt64(obj)) {
- // Make sure the integer fits in IntegerType.
- int64_t i = Int64Base::GetInt(obj);
- return ConvertExact(i, result);
- }
- if (UInt64::IsUInt64(obj)) {
- // Make sure the integer fits in IntegerType.
- uint64_t i = Int64Base::GetInt(obj);
- return ConvertExact(i, result);
- }
- 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<class FloatType>
- static bool
- jsvalToFloat(JSContext *cx, jsval val, FloatType* result)
- {
- JS_STATIC_ASSERT(!numeric_limits<FloatType>::is_exact);
- // The following casts may silently throw away some bits, but there's
- // no good way around it. Sternly requiring that the 64-bit double
- // argument be exactly representable as a 32-bit float is
- // unrealistic: it would allow 1/2 to pass but not 1/3.
- if (JSVAL_IS_INT(val)) {
- *result = FloatType(JSVAL_TO_INT(val));
- return true;
- }
- if (JSVAL_IS_DOUBLE(val)) {
- *result = FloatType(JSVAL_TO_DOUBLE(val));
- return true;
- }
- if (!JSVAL_IS_PRIMITIVE(val)) {
- JSObject* obj = JSVAL_TO_OBJECT(val);
- if (CData::IsCData(obj)) {
- JSObject* typeObj = CData::GetCType(obj);
- void* data = CData::GetData(obj);
- // Check whether the source type is always representable, with exact
- // precision, by the target type. If it is, convert the value.
- switch (CType::GetTypeCode(typeObj)) {
- #define DEFINE_FLOAT_TYPE(name, fromType, ffiType) \
- case TYPE_##name: \
- if (!IsAlwaysExact<FloatType, fromType>()) \
- return false; \
- *result = FloatType(*static_cast<fromType*>(data)); \
- return true;
- #define DEFINE_INT_TYPE(x, y, z) DEFINE_FLOAT_TYPE(x, y, z)
- #define DEFINE_WRAPPED_INT_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
- #include "typedefs.h"
- case TYPE_void_t:
- case TYPE_bool:
- case TYPE_char:
- case TYPE_signed_char:
- case TYPE_unsigned_char:
- case TYPE_jschar:
- case TYPE_pointer:
- case TYPE_function:
- case TYPE_array:
- case TYPE_struct:
- // Not a compatible number type.
- return false;
- }
- }
- }
- // Don't silently convert true to 1.0 or false to 0.0, even though C/C++
- // does it. It's likely to be a mistake.
- return false;
- }
- template<class IntegerType>
- static bool
- StringToInteger(JSContext* cx, JSString* string, IntegerType* result)
- {
- JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
- const jschar* cp = string->getChars(NULL);
- if (!cp)
- return false;
- const jschar* end = cp + string->length();
- if (cp == end)
- return false;
- IntegerType sign = 1;
- if (cp[0] == '-') {
- if (!numeric_limits<IntegerType>::is_signed)
- return false;
- sign = -1;
- ++cp;
- }
- // Assume base-10, unless the string begins with '0x' or '0X'.
- IntegerType base = 10;
- if (end - cp > 2 && cp[0] == '0' && (cp[1] == 'x' || cp[1] == 'X')) {
- cp += 2;
- base = 16;
- }
- // Scan the string left to right and build the number,
- // checking for valid characters 0 - 9, a - f, A - F and overflow.
- IntegerType i = 0;
- while (cp != end) {
- jschar c = *cp++;
- if (c >= '0' && c <= '9')
- c -= '0';
- else if (base == 16 && c >= 'a' && c <= 'f')
- c = c - 'a' + 10;
- else if (base == 16 && c >= 'A' && c <= 'F')
- c = c - 'A' + 10;
- else
- return false;
- IntegerType ii = i;
- i = ii * base + sign * c;
- if (i / base != ii) // overflow
- return false;
- }
- *result = i;
- return true;
- }
- // Implicitly convert val to IntegerType, allowing jsint, jsdouble,
- // Int64, UInt64, and optionally a decimal or hexadecimal string argument.
- // (This is common code shared by jsvalToSize and the Int64/UInt64 constructors.)
- template<class IntegerType>
- static bool
- jsvalToBigInteger(JSContext* cx,
- jsval val,
- bool allowString,
- IntegerType* result)
- {
- JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
- if (JSVAL_IS_INT(val)) {
- // Make sure the integer fits in the alotted precision, and has the right
- // sign.
- jsint i = JSVAL_TO_INT(val);
- return ConvertExact(i, result);
- }
- if (JSVAL_IS_DOUBLE(val)) {
- // Don't silently lose bits here -- check that val really is an
- // integer value, and has the right sign.
- jsdouble d = JSVAL_TO_DOUBLE(val);
- return ConvertExact(d, result);
- }
- if (allowString && JSVAL_IS_STRING(val)) {
- // Allow conversion from base-10 or base-16 strings, provided the result
- // fits in IntegerType. (This allows an Int64 or UInt64 object to be passed
- // to the JS array element operator, which will automatically call
- // toString() on the object for us.)
- return StringToInteger(cx, JSVAL_TO_STRING(val), result);
- }
- if (!JSVAL_IS_PRIMITIVE(val)) {
- // Allow conversion from an Int64 or UInt64 object directly.
- JSObject* obj = JSVAL_TO_OBJECT(val);
- if (UInt64::IsUInt64(obj)) {
- // Make sure the integer fits in IntegerType.
- uint64_t i = Int64Base::GetInt(obj);
- return ConvertExact(i, result);
- }
- if (Int64::IsInt64(obj)) {
- // Make sure the integer fits in IntegerType.
- int64_t i = Int64Base::GetInt(obj);
- return ConvertExact(i, result);
- }
- }
- 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<size_t>(jsdouble(*result)) == *result;
- }
- // Implicitly convert val to IntegerType, allowing jsint, jsdouble,
- // Int64, UInt64, and optionally a decimal or hexadecimal string argument.
- // (This is common code shared by jsvalToSize and the Int64/UInt64 constructors.)
- template<class IntegerType>
- static bool
- jsidToBigInteger(JSContext* cx,
- jsid val,
- bool allowSt…