blob: d9489f0bad285d2ebc077f155e26940ca2dc84d5 [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ctypes/CTypes.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/NumericLimits.h"
#include "mozilla/Vector.h"
#include <math.h>
#include <stdint.h>
#if defined(XP_WIN)
#include <float.h>
#endif
#if defined(SOLARIS)
#include <ieeefp.h>
#endif
#ifdef HAVE_SSIZE_T
#include <sys/types.h>
#endif
#if defined(XP_UNIX)
#include <errno.h>
#elif defined(XP_WIN)
#include <windows.h>
#endif
#include "jscntxt.h"
#include "jsexn.h"
#include "jsfun.h"
#include "jsnum.h"
#include "jsprf.h"
#include "builtin/TypedObject.h"
#include "ctypes/Library.h"
#include "gc/Zone.h"
#include "js/Vector.h"
#include "jsatominlines.h"
#include "jsobjinlines.h"
using namespace std;
using mozilla::NumericLimits;
using JS::AutoCheckCannotGC;
namespace js {
namespace ctypes {
template <typename CharT>
size_t
GetDeflatedUTF8StringLength(JSContext* maybecx, const CharT* chars,
size_t nchars)
{
size_t nbytes;
const CharT* end;
unsigned c, c2;
char buffer[10];
nbytes = nchars;
for (end = chars + nchars; chars != end; chars++) {
c = *chars;
if (c < 0x80)
continue;
if (0xD800 <= c && c <= 0xDFFF) {
/* Surrogate pair. */
chars++;
/* nbytes sets 1 length since this is surrogate pair. */
nbytes--;
if (c >= 0xDC00 || chars == end)
goto bad_surrogate;
c2 = *chars;
if (c2 < 0xDC00 || c2 > 0xDFFF)
goto bad_surrogate;
c = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
}
c >>= 11;
nbytes++;
while (c) {
c >>= 5;
nbytes++;
}
}
return nbytes;
bad_surrogate:
if (maybecx) {
js::gc::AutoSuppressGC suppress(maybecx);
JS_snprintf(buffer, 10, "0x%x", c);
JS_ReportErrorFlagsAndNumber(maybecx, JSREPORT_ERROR, GetErrorMessage,
nullptr, JSMSG_BAD_SURROGATE_CHAR, buffer);
}
return (size_t) -1;
}
template size_t
GetDeflatedUTF8StringLength(JSContext* maybecx, const Latin1Char* chars,
size_t nchars);
template size_t
GetDeflatedUTF8StringLength(JSContext* maybecx, const char16_t* chars,
size_t nchars);
static size_t
GetDeflatedUTF8StringLength(JSContext* maybecx, JSLinearString* str)
{
size_t length = str->length();
JS::AutoCheckCannotGC nogc;
return str->hasLatin1Chars()
? GetDeflatedUTF8StringLength(maybecx, str->latin1Chars(nogc), length)
: GetDeflatedUTF8StringLength(maybecx, str->twoByteChars(nogc), length);
}
template <typename CharT>
bool
DeflateStringToUTF8Buffer(JSContext* maybecx, const CharT* src, size_t srclen,
char* dst, size_t* dstlenp)
{
size_t i, utf8Len;
char16_t c, c2;
uint32_t v;
uint8_t utf8buf[6];
size_t dstlen = *dstlenp;
size_t origDstlen = dstlen;
while (srclen) {
c = *src++;
srclen--;
if (c >= 0xDC00 && c <= 0xDFFF)
goto badSurrogate;
if (c < 0xD800 || c > 0xDBFF) {
v = c;
} else {
if (srclen < 1)
goto badSurrogate;
c2 = *src;
if ((c2 < 0xDC00) || (c2 > 0xDFFF))
goto badSurrogate;
src++;
srclen--;
v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
}
if (v < 0x0080) {
/* no encoding necessary - performance hack */
if (dstlen == 0)
goto bufferTooSmall;
*dst++ = (char) v;
utf8Len = 1;
} else {
utf8Len = js::OneUcs4ToUtf8Char(utf8buf, v);
if (utf8Len > dstlen)
goto bufferTooSmall;
for (i = 0; i < utf8Len; i++)
*dst++ = (char) utf8buf[i];
}
dstlen -= utf8Len;
}
*dstlenp = (origDstlen - dstlen);
return true;
badSurrogate:
*dstlenp = (origDstlen - dstlen);
/* Delegate error reporting to the measurement function. */
if (maybecx)
GetDeflatedUTF8StringLength(maybecx, src - 1, srclen + 1);
return false;
bufferTooSmall:
*dstlenp = (origDstlen - dstlen);
if (maybecx) {
js::gc::AutoSuppressGC suppress(maybecx);
JS_ReportErrorNumber(maybecx, GetErrorMessage, nullptr,
JSMSG_BUFFER_TOO_SMALL);
}
return false;
}
template bool
DeflateStringToUTF8Buffer(JSContext* maybecx, const Latin1Char* src, size_t srclen,
char* dst, size_t* dstlenp);
template bool
DeflateStringToUTF8Buffer(JSContext* maybecx, const char16_t* src, size_t srclen,
char* dst, size_t* dstlenp);
static bool
DeflateStringToUTF8Buffer(JSContext* maybecx, JSLinearString* str, char* dst,
size_t* dstlenp)
{
size_t length = str->length();
JS::AutoCheckCannotGC nogc;
return str->hasLatin1Chars()
? DeflateStringToUTF8Buffer(maybecx, str->latin1Chars(nogc), length, dst, dstlenp)
: DeflateStringToUTF8Buffer(maybecx, str->twoByteChars(nogc), length, dst, dstlenp);
}
/*******************************************************************************
** JSAPI function prototypes
*******************************************************************************/
// We use an enclosing struct here out of paranoia about the ability of gcc 4.4
// (and maybe 4.5) to correctly compile this if it were a template function.
// See also the comments in dom/workers/Events.cpp (and other adjacent files) by
// the |struct Property| there.
template<JS::IsAcceptableThis Test, JS::NativeImpl Impl>
struct Property
{
static bool
Fun(JSContext* cx, unsigned argc, JS::Value* vp)
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
return JS::CallNonGenericMethod<Test, Impl>(cx, args);
}
};
static bool ConstructAbstract(JSContext* cx, unsigned argc, Value* vp);
namespace CType {
static bool ConstructData(JSContext* cx, unsigned argc, Value* vp);
static bool ConstructBasic(JSContext* cx, HandleObject obj, const CallArgs& args);
static void Trace(JSTracer* trc, JSObject* obj);
static void Finalize(JSFreeOp* fop, JSObject* obj);
bool IsCType(HandleValue v);
bool IsCTypeOrProto(HandleValue v);
bool PrototypeGetter(JSContext* cx, const JS::CallArgs& args);
bool NameGetter(JSContext* cx, const JS::CallArgs& args);
bool SizeGetter(JSContext* cx, const JS::CallArgs& args);
bool PtrGetter(JSContext* cx, const JS::CallArgs& args);
static bool CreateArray(JSContext* cx, unsigned argc, Value* vp);
static bool ToString(JSContext* cx, unsigned argc, Value* vp);
static bool ToSource(JSContext* cx, unsigned argc, Value* vp);
static bool HasInstance(JSContext* cx, HandleObject obj, MutableHandleValue v, bool* bp);
/*
* Get the global "ctypes" object.
*
* |obj| must be a CType object.
*
* This function never returns nullptr.
*/
static JSObject* GetGlobalCTypes(JSContext* cx, JSObject* obj);
} // namespace CType
namespace ABI {
bool IsABI(JSObject* obj);
static bool ToSource(JSContext* cx, unsigned argc, Value* vp);
} // namespace ABI
namespace PointerType {
static bool Create(JSContext* cx, unsigned argc, Value* vp);
static bool ConstructData(JSContext* cx, HandleObject obj, const CallArgs& args);
bool IsPointerType(HandleValue v);
bool IsPointer(HandleValue v);
bool TargetTypeGetter(JSContext* cx, const JS::CallArgs& args);
bool ContentsGetter(JSContext* cx, const JS::CallArgs& args);
bool ContentsSetter(JSContext* cx, const JS::CallArgs& args);
static bool IsNull(JSContext* cx, unsigned argc, Value* vp);
static bool Increment(JSContext* cx, unsigned argc, Value* vp);
static bool Decrement(JSContext* cx, unsigned argc, Value* vp);
// The following is not an instance function, since we don't want to expose arbitrary
// pointer arithmetic at this moment.
static bool OffsetBy(JSContext* cx, const CallArgs& args, int offset);
} // namespace PointerType
namespace ArrayType {
bool IsArrayType(HandleValue v);
bool IsArrayOrArrayType(HandleValue v);
static bool Create(JSContext* cx, unsigned argc, Value* vp);
static bool ConstructData(JSContext* cx, HandleObject obj, const CallArgs& args);
bool ElementTypeGetter(JSContext* cx, const JS::CallArgs& args);
bool LengthGetter(JSContext* cx, const JS::CallArgs& args);
static bool Getter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp);
static bool Setter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp,
ObjectOpResult& result);
static bool AddressOfElement(JSContext* cx, unsigned argc, Value* vp);
} // namespace ArrayType
namespace StructType {
bool IsStruct(HandleValue v);
static bool Create(JSContext* cx, unsigned argc, Value* vp);
static bool ConstructData(JSContext* cx, HandleObject obj, const CallArgs& args);
bool FieldsArrayGetter(JSContext* cx, const JS::CallArgs& args);
enum {
SLOT_FIELDNAME
};
static bool FieldGetter(JSContext* cx, unsigned argc, Value* vp);
static bool FieldSetter(JSContext* cx, unsigned argc, Value* vp);
static bool AddressOfField(JSContext* cx, unsigned argc, Value* vp);
static bool Define(JSContext* cx, unsigned argc, Value* vp);
} // namespace StructType
namespace FunctionType {
static bool Create(JSContext* cx, unsigned argc, Value* vp);
static bool ConstructData(JSContext* cx, HandleObject typeObj,
HandleObject dataObj, HandleObject fnObj, HandleObject thisObj, Value errVal);
static bool Call(JSContext* cx, unsigned argc, Value* vp);
bool IsFunctionType(HandleValue v);
bool ArgTypesGetter(JSContext* cx, const JS::CallArgs& args);
bool ReturnTypeGetter(JSContext* cx, const JS::CallArgs& args);
bool ABIGetter(JSContext* cx, const JS::CallArgs& args);
bool IsVariadicGetter(JSContext* cx, const JS::CallArgs& args);
} // namespace FunctionType
namespace CClosure {
static void Trace(JSTracer* trc, JSObject* obj);
static void Finalize(JSFreeOp* fop, JSObject* obj);
// libffi callback
static void ClosureStub(ffi_cif* cif, void* result, void** args,
void* userData);
struct ArgClosure : public ScriptEnvironmentPreparer::Closure {
ArgClosure(ffi_cif* cifArg, void* resultArg, void** argsArg, ClosureInfo* cinfoArg)
: cif(cifArg), result(resultArg), args(argsArg), cinfo(cinfoArg) {}
bool operator()(JSContext *cx) override;
ffi_cif* cif;
void* result;
void** args;
ClosureInfo* cinfo;
};
} // namespace CClosure
namespace CData {
static void Finalize(JSFreeOp* fop, JSObject* obj);
bool ValueGetter(JSContext* cx, const JS::CallArgs& args);
bool ValueSetter(JSContext* cx, const JS::CallArgs& args);
static bool Address(JSContext* cx, unsigned argc, Value* vp);
static bool ReadString(JSContext* cx, unsigned argc, Value* vp);
static bool ReadStringReplaceMalformed(JSContext* cx, unsigned argc, Value* vp);
static bool ToSource(JSContext* cx, unsigned argc, Value* vp);
static JSString* GetSourceString(JSContext* cx, HandleObject typeObj,
void* data);
bool ErrnoGetter(JSContext* cx, const JS::CallArgs& args);
#if defined(XP_WIN)
bool LastErrorGetter(JSContext* cx, const JS::CallArgs& args);
#endif // defined(XP_WIN)
} // namespace CData
namespace CDataFinalizer {
/*
* Attach a C function as a finalizer to a JS object.
*
* This function is available from JS as |ctypes.withFinalizer|.
*
* JavaScript signature:
* function(CData, CData): CDataFinalizer
* value finalizer finalizable
*
* Where |finalizer| is a one-argument function taking a value
* with the same type as |value|.
*/
static bool Construct(JSContext* cx, unsigned argc, Value* vp);
/*
* Private data held by |CDataFinalizer|.
*
* See also |enum CDataFinalizerSlot| for the slots of
* |CDataFinalizer|.
*
* Note: the private data may be nullptr, if |dispose|, |forget| or the
* finalizer has already been called.
*/
struct Private {
/*
* The C data to pass to the code.
* Finalization/|dispose|/|forget| release this memory.
*/
void* cargs;
/*
* The total size of the buffer pointed by |cargs|
*/
size_t cargs_size;
/*
* Low-level signature information.
* Finalization/|dispose|/|forget| release this memory.
*/
ffi_cif CIF;
/*
* The C function to invoke during finalization.
* Do not deallocate this.
*/
uintptr_t code;
/*
* A buffer for holding the return value.
* Finalization/|dispose|/|forget| release this memory.
*/
void* rvalue;
};
/*
* Methods of instances of |CDataFinalizer|
*/
namespace Methods {
static bool Dispose(JSContext* cx, unsigned argc, Value* vp);
static bool Forget(JSContext* cx, unsigned argc, Value* vp);
static bool ToSource(JSContext* cx, unsigned argc, Value* vp);
static bool ToString(JSContext* cx, unsigned argc, Value* vp);
} // namespace Methods
/*
* Utility functions
*
* @return true if |obj| is a CDataFinalizer, false otherwise.
*/
static bool IsCDataFinalizer(JSObject* obj);
/*
* Clean up the finalization information of a CDataFinalizer.
*
* Used by |Finalize|, |Dispose| and |Forget|.
*
* @param p The private information of the CDataFinalizer. If nullptr,
* this function does nothing.
* @param obj Either nullptr, if the object should not be cleaned up (i.e.
* during finalization) or a CDataFinalizer JSObject. Always use nullptr
* if you are calling from a finalizer.
*/
static void Cleanup(Private* p, JSObject* obj);
/*
* Perform the actual call to the finalizer code.
*/
static void CallFinalizer(CDataFinalizer::Private* p,
int* errnoStatus,
int32_t* lastErrorStatus);
/*
* Return the CType of a CDataFinalizer object, or nullptr if the object
* has been cleaned-up already.
*/
static JSObject* GetCType(JSContext* cx, JSObject* obj);
/*
* Perform finalization of a |CDataFinalizer|
*/
static void Finalize(JSFreeOp* fop, JSObject* obj);
/*
* Return the Value contained by this finalizer.
*
* Note that the Value is actually not recorded, but converted back from C.
*/
static bool GetValue(JSContext* cx, JSObject* obj, MutableHandleValue result);
static JSObject* GetCData(JSContext* cx, JSObject* obj);
} // namespace CDataFinalizer
// Int64Base provides functions common to Int64 and UInt64.
namespace Int64Base {
JSObject* Construct(JSContext* cx, HandleObject proto, uint64_t data,
bool isUnsigned);
uint64_t GetInt(JSObject* obj);
bool ToString(JSContext* cx, JSObject* obj, const CallArgs& args,
bool isUnsigned);
bool ToSource(JSContext* cx, JSObject* obj, const CallArgs& args,
bool isUnsigned);
static void Finalize(JSFreeOp* fop, JSObject* obj);
} // namespace Int64Base
namespace Int64 {
static bool Construct(JSContext* cx, unsigned argc, Value* vp);
static bool ToString(JSContext* cx, unsigned argc, Value* vp);
static bool ToSource(JSContext* cx, unsigned argc, Value* vp);
static bool Compare(JSContext* cx, unsigned argc, Value* vp);
static bool Lo(JSContext* cx, unsigned argc, Value* vp);
static bool Hi(JSContext* cx, unsigned argc, Value* vp);
static bool Join(JSContext* cx, unsigned argc, Value* vp);
} // namespace Int64
namespace UInt64 {
static bool Construct(JSContext* cx, unsigned argc, Value* vp);
static bool ToString(JSContext* cx, unsigned argc, Value* vp);
static bool ToSource(JSContext* cx, unsigned argc, Value* vp);
static bool Compare(JSContext* cx, unsigned argc, Value* vp);
static bool Lo(JSContext* cx, unsigned argc, Value* vp);
static bool Hi(JSContext* cx, unsigned argc, Value* vp);
static bool Join(JSContext* cx, unsigned argc, Value* vp);
} // namespace UInt64
/*******************************************************************************
** JSClass definitions and initialization functions
*******************************************************************************/
// Class representing the 'ctypes' object itself. This exists to contain the
// JSCTypesCallbacks set of function pointers.
static const JSClass sCTypesGlobalClass = {
"ctypes",
JSCLASS_HAS_RESERVED_SLOTS(CTYPESGLOBAL_SLOTS)
};
static const JSClass sCABIClass = {
"CABI",
JSCLASS_HAS_RESERVED_SLOTS(CABI_SLOTS)
};
// 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 const JSClass sCTypeProtoClass = {
"CType",
JSCLASS_HAS_RESERVED_SLOTS(CTYPEPROTO_SLOTS),
nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr,
ConstructAbstract, nullptr, ConstructAbstract
};
// Class representing ctypes.CData.prototype and the 'prototype' properties
// of CTypes. This exists to give said prototypes a class of "CData".
static const JSClass sCDataProtoClass = {
"CData",
0
};
static const JSClass sCTypeClass = {
"CType",
JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS),
nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, CType::Finalize,
CType::ConstructData, CType::HasInstance, CType::ConstructData,
CType::Trace
};
static const JSClass sCDataClass = {
"CData",
JSCLASS_HAS_RESERVED_SLOTS(CDATA_SLOTS),
nullptr, nullptr, ArrayType::Getter, ArrayType::Setter,
nullptr, nullptr, nullptr, CData::Finalize,
FunctionType::Call, nullptr, FunctionType::Call
};
static const JSClass sCClosureClass = {
"CClosure",
JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS),
nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, CClosure::Finalize,
nullptr, nullptr, nullptr, CClosure::Trace
};
/*
* Class representing the prototype of CDataFinalizer.
*/
static const JSClass sCDataFinalizerProtoClass = {
"CDataFinalizer",
0
};
/*
* Class representing instances of CDataFinalizer.
*
* Instances of CDataFinalizer have both private data (with type
* |CDataFinalizer::Private|) and slots (see |CDataFinalizerSlots|).
*/
static const JSClass sCDataFinalizerClass = {
"CDataFinalizer",
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(CDATAFINALIZER_SLOTS),
nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, CDataFinalizer::Finalize
};
#define CTYPESFN_FLAGS \
(JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
#define CTYPESCTOR_FLAGS \
(CTYPESFN_FLAGS | JSFUN_CONSTRUCTOR)
#define CTYPESACC_FLAGS \
(JSPROP_ENUMERATE | JSPROP_PERMANENT)
#define CABIFN_FLAGS \
(JSPROP_READONLY | JSPROP_PERMANENT)
#define CDATAFN_FLAGS \
(JSPROP_READONLY | JSPROP_PERMANENT)
#define CDATAFINALIZERFN_FLAGS \
(JSPROP_READONLY | JSPROP_PERMANENT)
static const JSPropertySpec sCTypeProps[] = {
JS_PSG("name",
(Property<CType::IsCType, CType::NameGetter>::Fun),
CTYPESACC_FLAGS),
JS_PSG("size",
(Property<CType::IsCType, CType::SizeGetter>::Fun),
CTYPESACC_FLAGS),
JS_PSG("ptr",
(Property<CType::IsCType, CType::PtrGetter>::Fun),
CTYPESACC_FLAGS),
JS_PSG("prototype",
(Property<CType::IsCTypeOrProto, CType::PrototypeGetter>::Fun),
CTYPESACC_FLAGS),
JS_PS_END
};
static const JSFunctionSpec sCTypeFunctions[] = {
JS_FN("array", CType::CreateArray, 0, CTYPESFN_FLAGS),
JS_FN("toString", CType::ToString, 0, CTYPESFN_FLAGS),
JS_FN("toSource", CType::ToSource, 0, CTYPESFN_FLAGS),
JS_FS_END
};
static const JSFunctionSpec sCABIFunctions[] = {
JS_FN("toSource", ABI::ToSource, 0, CABIFN_FLAGS),
JS_FN("toString", ABI::ToSource, 0, CABIFN_FLAGS),
JS_FS_END
};
static const JSPropertySpec sCDataProps[] = {
JS_PSGS("value",
(Property<CData::IsCData, CData::ValueGetter>::Fun),
(Property<CData::IsCData, CData::ValueSetter>::Fun),
JSPROP_PERMANENT),
JS_PS_END
};
static const JSFunctionSpec sCDataFunctions[] = {
JS_FN("address", CData::Address, 0, CDATAFN_FLAGS),
JS_FN("readString", CData::ReadString, 0, CDATAFN_FLAGS),
JS_FN("readStringReplaceMalformed", CData::ReadStringReplaceMalformed, 0, CDATAFN_FLAGS),
JS_FN("toSource", CData::ToSource, 0, CDATAFN_FLAGS),
JS_FN("toString", CData::ToSource, 0, CDATAFN_FLAGS),
JS_FS_END
};
static const JSFunctionSpec sCDataFinalizerFunctions[] = {
JS_FN("dispose", CDataFinalizer::Methods::Dispose, 0, CDATAFINALIZERFN_FLAGS),
JS_FN("forget", CDataFinalizer::Methods::Forget, 0, CDATAFINALIZERFN_FLAGS),
JS_FN("readString",CData::ReadString, 0, CDATAFINALIZERFN_FLAGS),
JS_FN("toString", CDataFinalizer::Methods::ToString, 0, CDATAFINALIZERFN_FLAGS),
JS_FN("toSource", CDataFinalizer::Methods::ToSource, 0, CDATAFINALIZERFN_FLAGS),
JS_FS_END
};
static const JSFunctionSpec sPointerFunction =
JS_FN("PointerType", PointerType::Create, 1, CTYPESCTOR_FLAGS);
static const JSPropertySpec sPointerProps[] = {
JS_PSG("targetType",
(Property<PointerType::IsPointerType, PointerType::TargetTypeGetter>::Fun),
CTYPESACC_FLAGS),
JS_PS_END
};
static const JSFunctionSpec sPointerInstanceFunctions[] = {
JS_FN("isNull", PointerType::IsNull, 0, CTYPESFN_FLAGS),
JS_FN("increment", PointerType::Increment, 0, CTYPESFN_FLAGS),
JS_FN("decrement", PointerType::Decrement, 0, CTYPESFN_FLAGS),
JS_FS_END
};
static const JSPropertySpec sPointerInstanceProps[] = {
JS_PSGS("contents",
(Property<PointerType::IsPointer, PointerType::ContentsGetter>::Fun),
(Property<PointerType::IsPointer, PointerType::ContentsSetter>::Fun),
JSPROP_PERMANENT),
JS_PS_END
};
static const JSFunctionSpec sArrayFunction =
JS_FN("ArrayType", ArrayType::Create, 1, CTYPESCTOR_FLAGS);
static const JSPropertySpec sArrayProps[] = {
JS_PSG("elementType",
(Property<ArrayType::IsArrayType, ArrayType::ElementTypeGetter>::Fun),
CTYPESACC_FLAGS),
JS_PSG("length",
(Property<ArrayType::IsArrayOrArrayType, ArrayType::LengthGetter>::Fun),
CTYPESACC_FLAGS),
JS_PS_END
};
static const JSFunctionSpec sArrayInstanceFunctions[] = {
JS_FN("addressOfElement", ArrayType::AddressOfElement, 1, CDATAFN_FLAGS),
JS_FS_END
};
static const JSPropertySpec sArrayInstanceProps[] = {
JS_PSG("length",
(Property<ArrayType::IsArrayOrArrayType, ArrayType::LengthGetter>::Fun),
JSPROP_PERMANENT),
JS_PS_END
};
static const JSFunctionSpec sStructFunction =
JS_FN("StructType", StructType::Create, 2, CTYPESCTOR_FLAGS);
static const JSPropertySpec sStructProps[] = {
JS_PSG("fields",
(Property<StructType::IsStruct, StructType::FieldsArrayGetter>::Fun),
CTYPESACC_FLAGS),
JS_PS_END
};
static const JSFunctionSpec sStructFunctions[] = {
JS_FN("define", StructType::Define, 1, CDATAFN_FLAGS),
JS_FS_END
};
static const JSFunctionSpec sStructInstanceFunctions[] = {
JS_FN("addressOfField", StructType::AddressOfField, 1, CDATAFN_FLAGS),
JS_FS_END
};
static const JSFunctionSpec sFunctionFunction =
JS_FN("FunctionType", FunctionType::Create, 2, CTYPESCTOR_FLAGS);
static const JSPropertySpec sFunctionProps[] = {
JS_PSG("argTypes",
(Property<FunctionType::IsFunctionType, FunctionType::ArgTypesGetter>::Fun),
CTYPESACC_FLAGS),
JS_PSG("returnType",
(Property<FunctionType::IsFunctionType, FunctionType::ReturnTypeGetter>::Fun),
CTYPESACC_FLAGS),
JS_PSG("abi",
(Property<FunctionType::IsFunctionType, FunctionType::ABIGetter>::Fun),
CTYPESACC_FLAGS),
JS_PSG("isVariadic",
(Property<FunctionType::IsFunctionType, FunctionType::IsVariadicGetter>::Fun),
CTYPESACC_FLAGS),
JS_PS_END
};
static const JSFunctionSpec sFunctionInstanceFunctions[] = {
JS_FN("call", js::fun_call, 1, CDATAFN_FLAGS),
JS_FN("apply", js::fun_apply, 2, CDATAFN_FLAGS),
JS_FS_END
};
static const JSClass sInt64ProtoClass = {
"Int64",
0
};
static const JSClass sUInt64ProtoClass = {
"UInt64",
0
};
static const JSClass sInt64Class = {
"Int64",
JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS),
nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, Int64Base::Finalize
};
static const JSClass sUInt64Class = {
"UInt64",
JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS),
nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, Int64Base::Finalize
};
static const JSFunctionSpec sInt64StaticFunctions[] = {
JS_FN("compare", Int64::Compare, 2, CTYPESFN_FLAGS),
JS_FN("lo", Int64::Lo, 1, CTYPESFN_FLAGS),
JS_FN("hi", Int64::Hi, 1, CTYPESFN_FLAGS),
// "join" is defined specially; see InitInt64Class.
JS_FS_END
};
static const JSFunctionSpec sUInt64StaticFunctions[] = {
JS_FN("compare", UInt64::Compare, 2, CTYPESFN_FLAGS),
JS_FN("lo", UInt64::Lo, 1, CTYPESFN_FLAGS),
JS_FN("hi", UInt64::Hi, 1, CTYPESFN_FLAGS),
// "join" is defined specially; see InitInt64Class.
JS_FS_END
};
static const JSFunctionSpec sInt64Functions[] = {
JS_FN("toString", Int64::ToString, 0, CTYPESFN_FLAGS),
JS_FN("toSource", Int64::ToSource, 0, CTYPESFN_FLAGS),
JS_FS_END
};
static const JSFunctionSpec sUInt64Functions[] = {
JS_FN("toString", UInt64::ToString, 0, CTYPESFN_FLAGS),
JS_FN("toSource", UInt64::ToSource, 0, CTYPESFN_FLAGS),
JS_FS_END
};
static const JSPropertySpec sModuleProps[] = {
JS_PSG("errno",
(Property<IsCTypesGlobal, CData::ErrnoGetter>::Fun),
JSPROP_PERMANENT),
#if defined(XP_WIN)
JS_PSG("winLastError",
(Property<IsCTypesGlobal, CData::LastErrorGetter>::Fun),
JSPROP_PERMANENT),
#endif // defined(XP_WIN)
JS_PS_END
};
static const JSFunctionSpec sModuleFunctions[] = {
JS_FN("CDataFinalizer", CDataFinalizer::Construct, 2, CTYPESFN_FLAGS),
JS_FN("open", Library::Open, 1, CTYPESFN_FLAGS),
JS_FN("cast", CData::Cast, 2, CTYPESFN_FLAGS),
JS_FN("getRuntime", CData::GetRuntime, 1, CTYPESFN_FLAGS),
JS_FN("libraryName", Library::Name, 1, CTYPESFN_FLAGS),
JS_FS_END
};
static MOZ_ALWAYS_INLINE JSString*
NewUCString(JSContext* cx, const AutoString& from)
{
return JS_NewUCStringCopyN(cx, from.begin(), from.length());
}
/*
* Return a size rounded up to a multiple of a power of two.
*
* Note: |align| must be a power of 2.
*/
static MOZ_ALWAYS_INLINE size_t
Align(size_t val, size_t align)
{
// Ensure that align is a power of two.
MOZ_ASSERT(align != 0 && (align & (align - 1)) == 0);
return ((val - 1) | (align - 1)) + 1;
}
static ABICode
GetABICode(JSObject* obj)
{
// make sure we have an object representing a CABI class,
// and extract the enumerated class type from the reserved slot.
if (JS_GetClass(obj) != &sCABIClass)
return INVALID_ABI;
Value result = JS_GetReservedSlot(obj, SLOT_ABICODE);
return ABICode(result.toInt32());
}
static const JSErrorFormatString ErrorFormatString[CTYPESERR_LIMIT] = {
#define MSG_DEF(name, count, exception, format) \
{ format, count, exception } ,
#include "ctypes/ctypes.msg"
#undef MSG_DEF
};
static const JSErrorFormatString*
GetErrorMessage(void* userRef, const unsigned errorNumber)
{
if (0 < errorNumber && errorNumber < CTYPESERR_LIMIT)
return &ErrorFormatString[errorNumber];
return nullptr;
}
static const char*
EncodeLatin1(JSContext* cx, AutoString& str, JSAutoByteString& bytes)
{
return bytes.encodeLatin1(cx, NewUCString(cx, str));
}
static const char*
CTypesToSourceForError(JSContext* cx, HandleValue val, JSAutoByteString& bytes)
{
if (val.isObject() &&
(CType::IsCType(&val.toObject()) || CData::IsCData(&val.toObject()))) {
RootedString str(cx, JS_ValueToSource(cx, val));
return bytes.encodeLatin1(cx, str);
}
return ValueToSourceForError(cx, val, bytes);
}
static void
BuildCStyleFunctionTypeSource(JSContext* cx, HandleObject typeObj,
HandleString nameStr, unsigned ptrCount,
AutoString& source);
static void
BuildCStyleTypeSource(JSContext* cx, JSObject* typeObj_, AutoString& source)
{
RootedObject typeObj(cx, typeObj_);
MOZ_ASSERT(CType::IsCType(typeObj));
switch (CType::GetTypeCode(typeObj)) {
#define BUILD_SOURCE(name, fromType, ffiType) \
case TYPE_##name: \
AppendString(source, #name); \
break;
CTYPES_FOR_EACH_TYPE(BUILD_SOURCE)
#undef BUILD_SOURCE
case TYPE_void_t:
AppendString(source, "void");
break;
case TYPE_pointer: {
unsigned ptrCount = 0;
TypeCode type;
RootedObject baseTypeObj(cx, typeObj);
do {
baseTypeObj = PointerType::GetBaseType(baseTypeObj);
ptrCount++;
type = CType::GetTypeCode(baseTypeObj);
} while (type == TYPE_pointer || type == TYPE_array);
if (type == TYPE_function) {
BuildCStyleFunctionTypeSource(cx, baseTypeObj, nullptr, ptrCount,
source);
break;
}
BuildCStyleTypeSource(cx, baseTypeObj, source);
AppendChars(source, '*', ptrCount);
break;
}
case TYPE_struct: {
RootedString name(cx, CType::GetName(cx, typeObj));
AppendString(source, "struct ");
AppendString(source, name);
break;
}
case TYPE_function:
BuildCStyleFunctionTypeSource(cx, typeObj, nullptr, 0, source);
break;
case TYPE_array:
MOZ_CRASH("TYPE_array shouldn't appear in function type");
}
}
static void
BuildCStyleFunctionTypeSource(JSContext* cx, HandleObject typeObj,
HandleString nameStr, unsigned ptrCount,
AutoString& source)
{
MOZ_ASSERT(CType::IsCType(typeObj));
FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
BuildCStyleTypeSource(cx, fninfo->mReturnType, source);
AppendString(source, " ");
if (nameStr) {
MOZ_ASSERT(ptrCount == 0);
AppendString(source, nameStr);
} else if (ptrCount) {
AppendString(source, "(");
AppendChars(source, '*', ptrCount);
AppendString(source, ")");
}
AppendString(source, "(");
if (fninfo->mArgTypes.length() > 0) {
for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
BuildCStyleTypeSource(cx, fninfo->mArgTypes[i], source);
if (i != fninfo->mArgTypes.length() - 1 || fninfo->mIsVariadic) {
AppendString(source, ", ");
}
}
if (fninfo->mIsVariadic) {
AppendString(source, "...");
}
}
AppendString(source, ")");
}
static void
BuildFunctionTypeSource(JSContext* cx, HandleObject funObj, AutoString& source)
{
MOZ_ASSERT(CData::IsCData(funObj) || CType::IsCType(funObj));
if (CData::IsCData(funObj)) {
Value slot = JS_GetReservedSlot(funObj, SLOT_REFERENT);
if (!slot.isUndefined() && Library::IsLibrary(&slot.toObject())) {
slot = JS_GetReservedSlot(funObj, SLOT_FUNNAME);
MOZ_ASSERT(!slot.isUndefined());
RootedObject typeObj(cx, CData::GetCType(funObj));
RootedObject baseTypeObj(cx, PointerType::GetBaseType(typeObj));
RootedString nameStr(cx, slot.toString());
BuildCStyleFunctionTypeSource(cx, baseTypeObj, nameStr, 0, source);
return;
}
}
RootedValue funVal(cx, ObjectValue(*funObj));
RootedString funcStr(cx, JS_ValueToSource(cx, funVal));
if (!funcStr) {
JS_ClearPendingException(cx);
AppendString(source, "<<error converting function to string>>");
return;
}
AppendString(source, funcStr);
}
enum class ConversionType {
Argument = 0,
Construct,
Finalizer,
Return,
Setter
};
static void
BuildConversionPosition(JSContext* cx, ConversionType convType,
HandleObject funObj, unsigned argIndex,
AutoString& source)
{
switch (convType) {
case ConversionType::Argument: {
MOZ_ASSERT(funObj);
AppendString(source, " at argument ");
AppendUInt(source, argIndex + 1);
AppendString(source, " of ");
BuildFunctionTypeSource(cx, funObj, source);
break;
}
case ConversionType::Finalizer:
MOZ_ASSERT(funObj);
AppendString(source, " at argument 1 of ");
BuildFunctionTypeSource(cx, funObj, source);
break;
case ConversionType::Return:
MOZ_ASSERT(funObj);
AppendString(source, " at the return value of ");
BuildFunctionTypeSource(cx, funObj, source);
break;
default:
MOZ_ASSERT(!funObj);
break;
}
}
static JSFlatString*
GetFieldName(HandleObject structObj, unsigned fieldIndex)
{
const FieldInfoHash* fields = StructType::GetFieldInfo(structObj);
for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
if (r.front().value().mIndex == fieldIndex) {
return (&r.front())->key();
}
}
return nullptr;
}
static void
BuildTypeSource(JSContext* cx, JSObject* typeObj_, bool makeShort,
AutoString& result);
static bool
ConvError(JSContext* cx, const char* expectedStr, HandleValue actual,
ConversionType convType,
HandleObject funObj = nullptr, unsigned argIndex = 0,
HandleObject arrObj = nullptr, unsigned arrIndex = 0)
{
JSAutoByteString valBytes;
const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
if (!valStr)
return false;
if (arrObj) {
MOZ_ASSERT(CType::IsCType(arrObj));
switch (CType::GetTypeCode(arrObj)) {
case TYPE_array: {
MOZ_ASSERT(!funObj);
char indexStr[16];
JS_snprintf(indexStr, 16, "%u", arrIndex);
AutoString arrSource;
JSAutoByteString arrBytes;
BuildTypeSource(cx, arrObj, true, arrSource);
const char* arrStr = EncodeLatin1(cx, arrSource, arrBytes);
if (!arrStr)
return false;
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
CTYPESMSG_CONV_ERROR_ARRAY,
valStr, indexStr, arrStr);
break;
}
case TYPE_struct: {
JSFlatString* name = GetFieldName(arrObj, arrIndex);
MOZ_ASSERT(name);
JSAutoByteString nameBytes;
const char* nameStr = nameBytes.encodeLatin1(cx, name);
if (!nameStr)
return false;
AutoString structSource;
JSAutoByteString structBytes;
BuildTypeSource(cx, arrObj, true, structSource);
const char* structStr = EncodeLatin1(cx, structSource, structBytes);
if (!structStr)
return false;
JSAutoByteString posBytes;
const char* posStr;
if (funObj) {
AutoString posSource;
BuildConversionPosition(cx, convType, funObj, argIndex, posSource);
posStr = EncodeLatin1(cx, posSource, posBytes);
if (!posStr)
return false;
} else {
posStr = "";
}
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
CTYPESMSG_CONV_ERROR_STRUCT,
valStr, nameStr, expectedStr, structStr, posStr);
break;
}
default:
MOZ_CRASH("invalid arrObj value");
}
return false;
}
switch (convType) {
case ConversionType::Argument: {
MOZ_ASSERT(funObj);
char indexStr[16];
JS_snprintf(indexStr, 16, "%u", argIndex + 1);
AutoString funSource;
JSAutoByteString funBytes;
BuildFunctionTypeSource(cx, funObj, funSource);
const char* funStr = EncodeLatin1(cx, funSource, funBytes);
if (!funStr)
return false;
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
CTYPESMSG_CONV_ERROR_ARG,
valStr, indexStr, funStr);
break;
}
case ConversionType::Finalizer: {
MOZ_ASSERT(funObj);
AutoString funSource;
JSAutoByteString funBytes;
BuildFunctionTypeSource(cx, funObj, funSource);
const char* funStr = EncodeLatin1(cx, funSource, funBytes);
if (!funStr)
return false;
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
CTYPESMSG_CONV_ERROR_FIN, valStr, funStr);
break;
}
case ConversionType::Return: {
MOZ_ASSERT(funObj);
AutoString funSource;
JSAutoByteString funBytes;
BuildFunctionTypeSource(cx, funObj, funSource);
const char* funStr = EncodeLatin1(cx, funSource, funBytes);
if (!funStr)
return false;
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
CTYPESMSG_CONV_ERROR_RET, valStr, funStr);
break;
}
case ConversionType::Setter:
case ConversionType::Construct:
MOZ_ASSERT(!funObj);
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
CTYPESMSG_CONV_ERROR_SET, valStr, expectedStr);
break;
}
return false;
}
static bool
ConvError(JSContext* cx, HandleObject expectedType, HandleValue actual,
ConversionType convType,
HandleObject funObj = nullptr, unsigned argIndex = 0,
HandleObject arrObj = nullptr, unsigned arrIndex = 0)
{
MOZ_ASSERT(CType::IsCType(expectedType));
AutoString expectedSource;
JSAutoByteString expectedBytes;
BuildTypeSource(cx, expectedType, true, expectedSource);
const char* expectedStr = EncodeLatin1(cx, expectedSource, expectedBytes);
if (!expectedStr)
return false;
return ConvError(cx, expectedStr, actual, convType, funObj, argIndex,
arrObj, arrIndex);
}
static bool
ArgumentConvError(JSContext* cx, HandleValue actual, const char* funStr,
unsigned argIndex)
{
JSAutoByteString valBytes;
const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
if (!valStr)
return false;
char indexStr[16];
JS_snprintf(indexStr, 16, "%u", argIndex + 1);
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
CTYPESMSG_CONV_ERROR_ARG, valStr, indexStr, funStr);
return false;
}
static bool
ArgumentLengthError(JSContext* cx, const char* fun, const char* count,
const char* s)
{
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
CTYPESMSG_WRONG_ARG_LENGTH, fun, count, s);
return false;
}
static bool
ArrayLengthMismatch(JSContext* cx, unsigned expectedLength, HandleObject arrObj,
unsigned actualLength, HandleValue actual,
ConversionType convType)
{
MOZ_ASSERT(arrObj && CType::IsCType(arrObj));
JSAutoByteString valBytes;
const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
if (!valStr)
return false;
char expectedLengthStr[16];
JS_snprintf(expectedLengthStr, 16, "%u", expectedLength);
char actualLengthStr[16];
JS_snprintf(actualLengthStr, 16, "%u", actualLength);
AutoString arrSource;
JSAutoByteString arrBytes;
BuildTypeSource(cx, arrObj, true, arrSource);
const char* arrStr = EncodeLatin1(cx, arrSource, arrBytes);
if (!arrStr)
return false;
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
CTYPESMSG_ARRAY_MISMATCH,
valStr, arrStr, expectedLengthStr, actualLengthStr);
return false;
}
static bool
ArrayLengthOverflow(JSContext* cx, unsigned expectedLength, HandleObject arrObj,
unsigned actualLength, HandleValue actual,
ConversionType convType)
{
MOZ_ASSERT(arrObj && CType::IsCType(arrObj));
JSAutoByteString valBytes;
const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
if (!valStr)
return false;
char expectedLengthStr[16];
JS_snprintf(expectedLengthStr, 16, "%u", expectedLength);
char actualLengthStr[16];
JS_snprintf(actualLengthStr, 16, "%u", actualLength);
AutoString arrSource;
JSAutoByteString arrBytes;
BuildTypeSource(cx, arrObj, true, arrSource);
const char* arrStr = EncodeLatin1(cx, arrSource, arrBytes);
if (!arrStr)
return false;
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
CTYPESMSG_ARRAY_OVERFLOW,
valStr, arrStr, expectedLengthStr, actualLengthStr);
return false;
}
static bool
ArgumentRangeMismatch(JSContext* cx, const char* arg, const char* func,
const char* range)
{
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
CTYPESMSG_ARG_RANGE_MISMATCH, arg, func, range);
return false;
}
static bool
ArgumentTypeMismatch(JSContext* cx, const char* arg, const char* func,
const char* type)
{
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
CTYPESMSG_ARG_TYPE_MISMATCH, arg, func, type);
return false;
}
static bool
EmptyFinalizerError(JSContext* cx, ConversionType convType,
HandleObject funObj = nullptr, unsigned argIndex = 0)
{
JSAutoByteString posBytes;
const char* posStr;
if (funObj) {
AutoString posSource;
BuildConversionPosition(cx, convType, funObj, argIndex, posSource);
posStr = EncodeLatin1(cx, posSource, posBytes);
if (!posStr)
return false;
} else {
posStr = "";
}
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
CTYPESMSG_EMPTY_FIN, posStr);
return false;
}
static bool
FieldCountMismatch(JSContext* cx,
unsigned expectedCount, HandleObject structObj,
unsigned actualCount, HandleValue actual,
ConversionType convType,
HandleObject funObj = nullptr, unsigned argIndex = 0)
{
MOZ_ASSERT(structObj && CType::IsCType(structObj));
JSAutoByteString valBytes;
const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
if (!valStr)
return false;
AutoString structSource;
JSAutoByteString structBytes;
BuildTypeSource(cx, structObj, true, structSource);
const char* structStr = EncodeLatin1(cx, structSource, structBytes);
if (!structStr)
return false;
char expectedCountStr[16];
JS_snprintf(expectedCountStr, 16, "%u", expectedCount);
char actualCountStr[16];
JS_snprintf(actualCountStr, 16, "%u", actualCount);
JSAutoByteString posBytes;
const char* posStr;
if (funObj) {
AutoString posSource;
BuildConversionPosition(cx, convType, funObj, argIndex, posSource);
posStr = EncodeLatin1(cx, posSource, posBytes);
if (!posStr)
return false;
} else {
posStr = "";
}
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
CTYPESMSG_FIELD_MISMATCH,
valStr, structStr, expectedCountStr, actualCountStr,
posStr);
return false;
}
static bool
FinalizerSizeError(JSContext* cx, HandleObject funObj, HandleValue actual)
{
MOZ_ASSERT(CType::IsCType(funObj));
JSAutoByteString valBytes;
const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
if (!valStr)
return false;
AutoString funSource;
JSAutoByteString funBytes;
BuildFunctionTypeSource(cx, funObj, funSource);
const char* funStr = EncodeLatin1(cx, funSource, funBytes);
if (!funStr)
return false;
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
CTYPESMSG_FIN_SIZE_ERROR, funStr, valStr);
return false;
}
static bool
NonPrimitiveError(JSContext* cx, HandleObject typeObj)
{
MOZ_ASSERT(CType::IsCType(typeObj));
AutoString typeSource;
JSAutoByteString typeBytes;
BuildTypeSource(cx, typeObj, true, typeSource);
const char* typeStr = EncodeLatin1(cx, typeSource, typeBytes);
if (!typeStr)
return false;
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
CTYPESMSG_NON_PRIMITIVE, typeStr);
return false;
}
static bool
PropNameNonStringError(JSContext* cx, HandleId id, HandleValue actual,
ConversionType convType,
HandleObject funObj = nullptr, unsigned argIndex = 0)
{
JSAutoByteString valBytes;
const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
if (!valStr)
return false;
JSAutoByteString idBytes;
RootedValue idVal(cx, IdToValue(id));
const char* propStr = CTypesToSourceForError(cx, idVal, idBytes);
if (!propStr)
return false;
JSAutoByteString posBytes;
const char* posStr;
if (funObj) {
AutoString posSource;
BuildConversionPosition(cx, convType, funObj, argIndex, posSource);
posStr = EncodeLatin1(cx, posSource, posBytes);
if (!posStr)
return false;
} else {
posStr = "";
}
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
CTYPESMSG_PROP_NONSTRING, propStr, valStr, posStr);
return false;
}
static bool
TypeError(JSContext* cx, const char* expected, HandleValue actual)
{
JSAutoByteString bytes;
const char* src = CTypesToSourceForError(cx, actual, bytes);
if (!src)
return false;
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
CTYPESMSG_TYPE_ERROR, expected, src);
return false;
}
static JSObject*
InitCTypeClass(JSContext* cx, HandleObject ctypesObj)
{
JSFunction* fun = JS_DefineFunction(cx, ctypesObj, "CType", ConstructAbstract, 0,
CTYPESCTOR_FLAGS);
if (!fun)
return nullptr;
RootedObject ctor(cx, JS_GetFunctionObject(fun));
RootedObject fnproto(cx);
if (!JS_GetPrototype(cx, ctor, &fnproto))
return nullptr;
MOZ_ASSERT(ctor);
MOZ_ASSERT(fnproto);
// Set up ctypes.CType.prototype.
RootedObject prototype(cx, JS_NewObjectWithGivenProto(cx, &sCTypeProtoClass, fnproto));
if (!prototype)
return nullptr;
if (!JS_DefineProperty(cx, ctor, "prototype", prototype,
JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
return nullptr;
if (!JS_DefineProperty(cx, prototype, "constructor", ctor,
JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
return nullptr;
// Define properties and functions common to all CTypes.
if (!JS_DefineProperties(cx, prototype, sCTypeProps) ||
!JS_DefineFunctions(cx, prototype, sCTypeFunctions))
return nullptr;
if (!JS_FreezeObject(cx, ctor) || !JS_FreezeObject(cx, prototype))
return nullptr;
return prototype;
}
static JSObject*
InitABIClass(JSContext* cx)
{
RootedObject obj(cx, JS_NewPlainObject(cx));
if (!obj)
return nullptr;
if (!JS_DefineFunctions(cx, obj, sCABIFunctions))
return nullptr;
return obj;
}
static JSObject*
InitCDataClass(JSContext* cx, HandleObject parent, HandleObject CTypeProto)
{
JSFunction* fun = JS_DefineFunction(cx, parent, "CData", ConstructAbstract, 0,
CTYPESCTOR_FLAGS);
if (!fun)
return nullptr;
RootedObject ctor(cx, JS_GetFunctionObject(fun));
MOZ_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 nullptr;
// Set up ctypes.CData.prototype.
RootedObject prototype(cx, JS_NewObject(cx, &sCDataProtoClass));
if (!prototype)
return nullptr;
if (!JS_DefineProperty(cx, ctor, "prototype", prototype,
JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
return nullptr;
if (!JS_DefineProperty(cx, prototype, "constructor", ctor,
JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
return nullptr;
// Define properties and functions common to all CDatas.
if (!JS_DefineProperties(cx, prototype, sCDataProps) ||
!JS_DefineFunctions(cx, prototype, sCDataFunctions))
return nullptr;
if (//!JS_FreezeObject(cx, prototype) || // XXX fixme - see bug 541212!
!JS_FreezeObject(cx, ctor))
return nullptr;
return prototype;
}
static bool
DefineABIConstant(JSContext* cx,
HandleObject ctypesObj,
const char* name,
ABICode code,
HandleObject prototype)
{
RootedObject obj(cx, JS_NewObjectWithGivenProto(cx, &sCABIClass, prototype));
if (!obj)
return false;
JS_SetReservedSlot(obj, SLOT_ABICODE, Int32Value(code));
if (!JS_FreezeObject(cx, obj))
return false;
return JS_DefineProperty(cx, ctypesObj, name, obj,
JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
}
// Set up a single type constructor for
// ctypes.{Pointer,Array,Struct,Function}Type.
static bool
InitTypeConstructor(JSContext* cx,
HandleObject parent,
HandleObject CTypeProto,
HandleObject CDataProto,
const JSFunctionSpec spec,
const JSFunctionSpec* fns,
const JSPropertySpec* props,
const JSFunctionSpec* instanceFns,
const JSPropertySpec* instanceProps,
MutableHandleObject typeProto,
MutableHandleObject dataProto)
{
JSFunction* fun = js::DefineFunctionWithReserved(cx, parent, spec.name, spec.call.op,
spec.nargs, spec.flags);
if (!fun)
return false;
RootedObject obj(cx, JS_GetFunctionObject(fun));
if (!obj)
return false;
// Set up the .prototype and .prototype.constructor properties.
typeProto.set(JS_NewObjectWithGivenProto(cx, &sCTypeProtoClass, CTypeProto));
if (!typeProto)
return false;
// Define property before proceeding, for GC safety.
if (!JS_DefineProperty(cx, obj, "prototype", typeProto,
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", obj,
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, ObjectValue(*typeProto));
// Create an object to serve as the common ancestor for all CData objects
// created from the given type constructor. This has ctypes.CData.prototype
// as its prototype, such that it inherits the properties and functions
// common to all CDatas.
dataProto.set(JS_NewObjectWithGivenProto(cx, &sCDataProtoClass, CDataProto));
if (!dataProto)
return false;
// Define functions and properties on the 'dataProto' object that are common
// to all CData objects created from this type constructor. (These will
// become functions and properties on CData objects created from this type.)
if (instanceFns && !JS_DefineFunctions(cx, dataProto, instanceFns))
return false;
if (instanceProps && !JS_DefineProperties(cx, dataProto, instanceProps))
return false;
// Link the type prototype to the data prototype.
JS_SetReservedSlot(typeProto, SLOT_OURDATAPROTO, ObjectValue(*dataProto));
if (!JS_FreezeObject(cx, obj) ||
//!JS_FreezeObject(cx, dataProto) || // XXX fixme - see bug 541212!
!JS_FreezeObject(cx, typeProto))
return false;
return true;
}
static JSObject*
InitInt64Class(JSContext* cx,
HandleObject parent,
const JSClass* clasp,
JSNative construct,
const JSFunctionSpec* fs,
const JSFunctionSpec* static_fs)
{
// Init type class and constructor
RootedObject prototype(cx, JS_InitClass(cx, parent, nullptr, clasp, construct,
0, nullptr, fs, nullptr, static_fs));
if (!prototype)
return nullptr;
RootedObject ctor(cx, JS_GetConstructor(cx, prototype));
if (!ctor)
return nullptr;
// Define the 'join' function as an extended native and stash
// ctypes.{Int64,UInt64}.prototype in a reserved slot of the new function.
MOZ_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 nullptr;
js::SetFunctionNativeReserved(fun, SLOT_FN_INT64PROTO, ObjectValue(*prototype));
if (!JS_FreezeObject(cx, ctor))
return nullptr;
if (!JS_FreezeObject(cx, prototype))
return nullptr;
return prototype;
}
static void
AttachProtos(JSObject* proto, const AutoObjectVector& protos)
{
// For a given 'proto' of [[Class]] "CTypeProto", attach each of the 'protos'
// to the appropriate CTypeProtoSlot. (SLOT_CTYPES is the last slot
// of [[Class]] "CTypeProto" that we fill in this automated manner.)
for (uint32_t i = 0; i <= SLOT_CTYPES; ++i)
JS_SetReservedSlot(proto, i, ObjectOrNullValue(protos[i]));
}
static bool
InitTypeClasses(JSContext* cx, HandleObject ctypesObj)
{
// Initialize the ctypes.CType class. This acts as an abstract base class for
// the various types, and provides the common API functions. It has:
// * [[Class]] "Function"
// * __proto__ === Function.prototype
// * A constructor that throws a TypeError. (You can't construct an
// abstract type!)
// * 'prototype' property:
// * [[Class]] "CTypeProto"
// * __proto__ === Function.prototype
// * A constructor that throws a TypeError. (You can't construct an
// abstract type instance!)
// * 'constructor' property === ctypes.CType
// * Provides properties and functions common to all CTypes.
RootedObject CTypeProto(cx, InitCTypeClass(cx, ctypesObj));
if (!CTypeProto)
return false;
// Initialize the ctypes.CData class. This acts as an abstract base class for
// instances of the various types, and provides the common API functions.
// It has:
// * [[Class]] "Function"
// * __proto__ === Function.prototype
// * A constructor that throws a TypeError. (You can't construct an
// abstract type instance!)
// * 'prototype' property:
// * [[Class]] "CDataProto"
// * 'constructor' property === ctypes.CData
// * Provides properties and functions common to all CDatas.
RootedObject CDataProto(cx, InitCDataClass(cx, ctypesObj, CTypeProto));
if (!CDataProto)
return false;
// Link CTypeProto to CDataProto.
JS_SetReservedSlot(CTypeProto, SLOT_OURDATAPROTO, ObjectValue(*CDataProto));
// Create and attach the special class constructors: ctypes.PointerType,
// ctypes.ArrayType, ctypes.StructType, and ctypes.FunctionType.
// Each of these constructors 'c' has, respectively:
// * [[Class]] "Function"
// * __proto__ === Function.prototype
// * A constructor that creates a user-defined type.
// * 'prototype' property:
// * [[Class]] "CTypeProto"
// * __proto__ === ctypes.CType.prototype
// * 'constructor' property === 'c'
// We also construct an object 'p' to serve, given a type object 't'
// constructed from one of these type constructors, as
// 't.prototype.__proto__'. This object has:
// * [[Class]] "CDataProto"
// * __proto__ === ctypes.CData.prototype
// * Properties and functions common to all CDatas.
// Therefore an instance 't' of ctypes.{Pointer,Array,Struct,Function}Type
// will have, resp.:
// * [[Class]] "CType"
// * __proto__ === ctypes.{Pointer,Array,Struct,Function}Type.prototype
// * A constructor which creates and returns a CData object, containing
// binary data of the given type.
// * 'prototype' property:
// * [[Class]] "CDataProto"
// * __proto__ === 'p', the prototype object from above
// * 'constructor' property === 't'
AutoObjectVector protos(cx);
if (!protos.resize(CTYPEPROTO_SLOTS))
return false;
if (!InitTypeConstructor(cx, ctypesObj, CTypeProto, CDataProto,
sPointerFunction, nullptr, sPointerProps,
sPointerInstanceFunctions, sPointerInstanceProps,
protos[SLOT_POINTERPROTO], protos[SLOT_POINTERDATAPROTO]))
return false;
if (!InitTypeConstructor(cx, ctypesObj, CTypeProto, CDataProto,
sArrayFunction, nullptr, sArrayProps,
sArrayInstanceFunctions, sArrayInstanceProps,
protos[SLOT_ARRAYPROTO], protos[SLOT_ARRAYDATAPROTO]))
return false;
if (!InitTypeConstructor(cx, ctypesObj, CTypeProto, CDataProto,
sStructFunction, sStructFunctions, sStructProps,
sStructInstanceFunctions, nullptr,
protos[SLOT_STRUCTPROTO], protos[SLOT_STRUCTDATAPROTO]))
return false;
if (!InitTypeConstructor(cx, ctypesObj, CTypeProto, protos[SLOT_POINTERDATAPROTO],
sFunctionFunction, nullptr, sFunctionProps, sFunctionInstanceFunctions, nullptr,
protos[SLOT_FUNCTIONPROTO], protos[SLOT_FUNCTIONDATAPROTO]))
return false;
protos[SLOT_CDATAPROTO].set(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].set(InitInt64Class(cx, ctypesObj, &sInt64ProtoClass,
Int64::Construct, sInt64Functions, sInt64StaticFunctions));
if (!protos[SLOT_INT64PROTO])
return false;
protos[SLOT_UINT64PROTO].set(InitInt64Class(cx, ctypesObj, &sUInt64ProtoClass,
UInt64::Construct, sUInt64Functions, sUInt64StaticFunctions));
if (!protos[SLOT_UINT64PROTO])
return false;
// Finally, store a pointer to the global ctypes object.
// Note that there is no other reliable manner of locating this object.
protos[SLOT_CTYPES].set(ctypesObj);
// Attach the prototypes just created to each of ctypes.CType.prototype,
// and the special type constructors, so we can access them when constructing
// instances of those types.
AttachProtos(CTypeProto, protos);
AttachProtos(protos[SLOT_POINTERPROTO], protos);
AttachProtos(protos[SLOT_ARRAYPROTO], protos);
AttachProtos(protos[SLOT_STRUCTPROTO], protos);
AttachProtos(protos[SLOT_FUNCTIONPROTO], protos);
RootedObject ABIProto(cx, InitABIClass(cx));
if (!ABIProto)
return false;
// Attach objects representing ABI constants.
if (!DefineABIConstant(cx, ctypesObj, "default_abi", ABI_DEFAULT, ABIProto) ||
!DefineABIConstant(cx, ctypesObj, "stdcall_abi", ABI_STDCALL, ABIProto) ||
!DefineABIConstant(cx, ctypesObj, "winapi_abi", ABI_WINAPI, ABIProto))
return false;
// Create objects representing the builtin types, and attach them to the
// ctypes object. Each type object 't' has:
// * [[Class]] "CType"
// * __proto__ === ctypes.CType.prototype
// * A constructor which creates and returns a CData object, containing
// binary data of the given type.
// * 'prototype' property:
// * [[Class]] "CDataProto"
// * __proto__ === ctypes.CData.prototype
// * 'constructor' property === 't'
#define DEFINE_TYPE(name, type, ffiType) \
RootedObject typeObj_##name(cx, \
CType::DefineBuiltin(cx, ctypesObj, #name, CTypeProto, CDataProto, #name, \
TYPE_##name, Int32Value(sizeof(type)), \
Int32Value(ffiType.alignment), &ffiType)); \
if (!typeObj_##name) \
return false;
CTYPES_FOR_EACH_TYPE(DEFINE_TYPE)
#undef DEFINE_TYPE
// Alias 'ctypes.unsigned' as 'ctypes.unsigned_int', since they represent
// the same type in C.
if (!JS_DefineProperty(cx, ctypesObj, "unsigned", typeObj_unsigned_int,
JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
return false;
// Alias 'ctypes.jschar' as 'ctypes.char16_t' to prevent breaking addons
// that are still using jschar (bug 1064935).
if (!JS_DefineProperty(cx, ctypesObj, "jschar", typeObj_char16_t,
JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
return false;
// Create objects representing the special types void_t and voidptr_t.
RootedObject typeObj(cx,
CType::DefineBuiltin(cx, ctypesObj, "void_t", CTypeProto, CDataProto, "void",
TYPE_void_t, JS::UndefinedValue(), JS::UndefinedValue(),
&ffi_type_void));
if (!typeObj)
return false;
typeObj = PointerType::CreateInternal(cx, typeObj);
if (!typeObj)
return false;
if (!JS_DefineProperty(cx, ctypesObj, "voidptr_t", typeObj,
JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
return false;
return true;
}
bool
IsCTypesGlobal(JSObject* obj)
{
return JS_GetClass(obj) == &sCTypesGlobalClass;
}
bool
IsCTypesGlobal(HandleValue v)
{
return v.isObject() && IsCTypesGlobal(&v.toObject());
}
// Get the JSCTypesCallbacks struct from the 'ctypes' object 'obj'.
const JSCTypesCallbacks*
GetCallbacks(JSObject* obj)
{
MOZ_ASSERT(IsCTypesGlobal(obj));
Value result = JS_GetReservedSlot(obj, SLOT_CALLBACKS);
if (result.isUndefined())
return nullptr;
return static_cast<const JSCTypesCallbacks*>(result.toPrivate());
}
// Utility function to access a property of an object as an object
// returns false and sets the error if the property does not exist
// or is not an object
static bool GetObjectProperty(JSContext* cx, HandleObject obj,
const char* property, MutableHandleObject result)
{
RootedValue val(cx);
if (!JS_GetProperty(cx, obj, property, &val)) {
return false;
}
if (val.isPrimitive()) {
JS_ReportError(cx, "missing or non-object field");
return false;
}
result.set(val.toObjectOrNull());
return true;
}
} /* namespace ctypes */
} /* namespace js */
using namespace js;
using namespace js::ctypes;
JS_PUBLIC_API(bool)
JS_InitCTypesClass(JSContext* cx, HandleObject global)
{
// attach ctypes property to global object
RootedObject ctypes(cx, JS_NewObject(cx, &sCTypesGlobalClass));
if (!ctypes)
return false;
if (!JS_DefineProperty(cx, global, "ctypes", ctypes,
JSPROP_READONLY | JSPROP_PERMANENT,
JS_STUBGETTER, JS_STUBSETTER)){
return false;
}
if (!InitTypeClasses(cx, ctypes))
return false;
// attach API functions and properties
if (!JS_DefineFunctions(cx, ctypes, sModuleFunctions) ||
!JS_DefineProperties(cx, ctypes, sModuleProps))
return false;
// Set up ctypes.CDataFinalizer.prototype.
RootedObject ctor(cx);
if (!GetObjectProperty(cx, ctypes, "CDataFinalizer", &ctor))
return false;
RootedObject prototype(cx, JS_NewObject(cx, &sCDataFinalizerProtoClass));
if (!prototype)
return false;
if (!JS_DefineFunctions(cx, prototype, sCDataFinalizerFunctions))
return false;
if (!JS_DefineProperty(cx, ctor, "prototype", prototype,
JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
return false;
if (!JS_DefineProperty(cx, prototype, "constructor", ctor,
JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
return false;
// Seal the ctypes object, to prevent modification.
return JS_FreezeObject(cx, ctypes);
}
JS_PUBLIC_API(void)
JS_SetCTypesCallbacks(JSObject* ctypesObj, const JSCTypesCallbacks* callbacks)
{
MOZ_ASSERT(callbacks);
MOZ_ASSERT(IsCTypesGlobal(ctypesObj));
// Set the callbacks on a reserved slot.
JS_SetReservedSlot(ctypesObj, SLOT_CALLBACKS,
PrivateValue(const_cast<JSCTypesCallbacks*>(callbacks)));
}
namespace js {
JS_FRIEND_API(size_t)
SizeOfDataIfCDataObject(mozilla::MallocSizeOf mallocSizeOf, JSObject* obj)
{
if (!CData::IsCData(obj))
return 0;
size_t n = 0;
Value slot = JS_GetReservedSlot(obj, ctypes::SLOT_OWNS);
if (!slot.isUndefined()) {
bool owns = slot.toBoolean();
slot = JS_GetReservedSlot(obj, ctypes::SLOT_DATA);
if (!slot.isUndefined()) {
char** buffer = static_cast<char**>(slot.toPrivate());
n += mallocSizeOf(buffer);
if (owns)
n += mallocSizeOf(*buffer);
}
}
return n;
}
namespace ctypes {
/*******************************************************************************
** Type conversion functions
*******************************************************************************/
// Enforce some sanity checks on type widths and properties.
// Where the architecture is 64-bit, make sure it's LP64 or LLP64. (ctypes.int
// autoconverts to a primitive JS number; to support ILP64 architectures, it
// would need to autoconvert to an Int64 object instead. Therefore we enforce
// this invariant here.)
JS_STATIC_ASSERT(sizeof(bool) == 1 || sizeof(bool) == 4);
JS_STATIC_ASSERT(sizeof(char) == 1);
JS_STATIC_ASSERT(sizeof(short) == 2);
JS_STATIC_ASSERT(sizeof(int) == 4);
JS_STATIC_ASSERT(sizeof(unsigned) == 4);
JS_STATIC_ASSERT(sizeof(long) == 4 || sizeof(long) == 8);
JS_STATIC_ASSERT(sizeof(long long) == 8);
JS_STATIC_ASSERT(sizeof(size_t) == sizeof(uintptr_t));
JS_STATIC_ASSERT(sizeof(float) == 4);
JS_STATIC_ASSERT(sizeof(PRFuncPtr) == sizeof(void*));
JS_STATIC_ASSERT(NumericLimits<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 MOZ_ALWAYS_INLINE TargetType Convert(FromType d) {
return TargetType(d);
}
};
#ifdef _MSC_VER
// MSVC can't perform double to unsigned __int64 conversion when the
// double is greater than 2^63 - 1. Help it along a little.
template<>
struct ConvertImpl<uint64_t, double> {
static MOZ_ALWAYS_INLINE uint64_t Convert(double d) {
return d > 0x7fffffffffffffffui64 ?
uint64_t(d - 0x8000000000000000ui64) + 0x8000000000000000ui64 :
uint64_t(d);
}
};
#endif
// C++ doesn't guarantee that exact values are the only ones that will
// round-trip. In fact, on some platforms, including SPARC, there are pairs of
// values, a uint64_t and a double, such that neither value is exactly
// representable in the other type, but they cast to each other.
#if defined(SPARC) || defined(__powerpc__)
// Simulate x86 overflow behavior
template<>
struct ConvertImpl<uint64_t, double> {
static MOZ_ALWAYS_INLINE uint64_t Convert(double d) {
return d >= 0xffffffffffffffff ?
0x8000000000000000 : uint64_t(d);
}
};
template<>
struct ConvertImpl<int64_t, double> {
static MOZ_ALWAYS_INLINE int64_t Convert(double d) {
return d >= 0x7fffffffffffffff ?
0x8000000000000000 : int64_t(d);
}
};
#endif
template<class TargetType, class FromType>
static MOZ_ALWAYS_INLINE TargetType Convert(FromType d)
{
return ConvertImpl<TargetType, FromType>::Convert(d);
}
template<class TargetType, class FromType>
static MOZ_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 (NumericLimits<TargetType>::digits < NumericLimits<FromType>::digits)
return false;
if (NumericLimits<FromType>::is_signed &&
!NumericLimits<TargetType>::is_signed)
return false;
if (!NumericLimits<FromType>::is_exact &&
NumericLimits<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 MOZ_ALWAYS_INLINE bool Test(FromType i, TargetType j) {
JS_STATIC_ASSERT(NumericLimits<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 MOZ_ALWAYS_INLINE bool Test(FromType i, TargetType j) {
JS_STATIC_ASSERT(NumericLimits<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 MOZ_ALWAYS_INLINE bool Test(FromType i, TargetType j) {
JS_STATIC_ASSERT(NumericLimits<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 MOZ_ALWAYS_INLINE bool ConvertExact(FromType i, TargetType* result)
{
// Require that TargetType is integral, to simplify conversion.
JS_STATIC_ASSERT(NumericLimits<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,
NumericLimits<TargetType>::is_signed,
NumericLimits<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 MOZ_ALWAYS_INLINE bool Test(Type i) {
return false;
}
};
// Specialization where Type is signed.
template<class Type>
struct IsNegativeImpl<Type, true> {
static MOZ_ALWAYS_INLINE bool Test(Type i) {
return i < 0;
}
};
// Determine whether Type 'i' is negative.
template<class Type>
static MOZ_ALWAYS_INLINE bool IsNegative(Type i)
{
return IsNegativeImpl<Type, NumericLimits<Type>::is_signed>::Test(i);
}
// Implicitly convert val to bool, allowing bool, int, and double
// arguments numerically equal to 0 or 1.
static bool
jsvalToBool(JSContext* cx, Value val, bool* result)
{
if (val.isBoolean()) {
*result = val.toBoolean();
return true;
}
if (val.isInt32()) {
int32_t i = val.toInt32();
*result = i != 0;
return i == 0 || i == 1;
}
if (val.isDouble()) {
double d = val.toDouble();
*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 bool, int, double,
// Int64, UInt64, and CData integer types 't' where all values of 't' are
// representable by IntegerType.
template<class IntegerType>
static bool
jsvalToInteger(JSContext* cx, Value val, IntegerType* result)
{
JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact);
if (val.isInt32()) {
// Make sure the integer fits in the alotted precision, and has the right
// sign.
int32_t i = val.toInt32();
return ConvertExact(i, result);
}
if (val.isDouble()) {
// Don't silently lose bits here -- check that val really is an
// integer value, and has the right sign.
double d = val.toDouble();
return ConvertExact(d, result);
}
if (val.isObject()) {
JSObject* obj = &val.toObject();
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 INTEGER_CASE(name, fromType, ffiType) \
case TYPE_##name: \
if (!IsAlwaysExact<IntegerType, fromType>()) \
return false; \
*result = IntegerType(*static_cast<fromType*>(data)); \
return true;
CTYPES_FOR_EACH_INT_TYPE(INTEGER_CASE)
CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGER_CASE)
#undef INTEGER_CASE
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_char16_t:
case TYPE_pointer:
case TYPE_function:
case TYPE_array:
case TYPE_struct:
// Not a compatible number type.
return false;
}
}
if (Int64::IsInt64(obj)) {
// Make sure the integer fits in IntegerType.
int64_t i = Int64Base::GetInt(obj);
return ConvertExact(i, result);
}
if (UInt64::IsUInt64(obj)) {
// Make sure the integer fits in IntegerType.
uint64_t i = Int64Base::GetInt(obj);
return ConvertExact(i, result);
}
if (CDataFinalizer::IsCDataFinalizer(obj)) {
RootedValue innerData(cx);
if (!CDataFinalizer::GetValue(cx, obj, &innerData)) {
return false; // Nothing to convert
}
return jsvalToInteger(cx, innerData, result);
}
return false;
}
if (val.isBoolean()) {
// Implicitly promote boolean values to 0 or 1, like C.
*result = val.toBoolean();
MOZ_ASSERT(*result == 0 || *result == 1);
return true;
}
// Don't silently convert null to an integer. It's probably a mistake.
return false;
}
// Implicitly convert val to FloatType, allowing int, double,
// Int64, UInt64, and CData numeric types 't' where all values of 't' are
// representable by FloatType.
template<class FloatType>
static bool
jsvalToFloat(JSContext* cx, Value val, FloatType* result)
{
JS_STATIC_ASSERT(!NumericLimits<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 (val.isInt32()) {
*result = FloatType(val.toInt32());
return true;
}
if (val.isDouble()) {
*result = FloatType(val.toDouble());
return true;
}
if (val.isObject()) {
JSObject* obj = &val.toObject();
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 NUMERIC_CASE(name, fromType, ffiType) \
case TYPE_##name: \
if (!IsAlwaysExact<FloatType, fromType>()) \
return false; \
*result = FloatType(*static_cast<fromType*>(data)); \
return true;
CTYPES_FOR_EACH_FLOAT_TYPE(NUMERIC_CASE)
CTYPES_FOR_EACH_INT_TYPE(NUMERIC_CASE)
CTYPES_FOR_EACH_WRAPPED_INT_TYPE(NUMERIC_CASE)
#undef NUMERIC_CASE
case TYPE_void_t:
case TYPE_bool:
case TYPE_char:
case TYPE_signed_char:
case TYPE_unsigned_char:
case TYPE_char16_t:
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, class CharT>
static bool
StringToInteger(JSContext* cx, CharT* cp, size_t length, IntegerType* result)
{
JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact);
const CharT* end = cp + length;
if (cp == end)
return false;
IntegerType sign = 1;
if (cp[0] == '-') {
if (!NumericLimits<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) {
char16_t 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;
}
template<class IntegerType>
static bool
StringToInteger(JSContext* cx, JSString* string, IntegerType* result)
{
JSLinearString* linear = string->ensureLinear(cx);
if (!linear)
return false;
AutoCheckCannotGC nogc;
size_t length = linear->length();
return string->hasLatin1Chars()
? StringToInteger<IntegerType>(cx, linear->latin1Chars(nogc), length, result)
: StringToInteger<IntegerType>(cx, linear->twoByteChars(nogc), length, result);
}
// Implicitly convert val to IntegerType, allowing int, double,
// Int64, UInt64, and optionally a decimal or hexadecimal string argument.
// (This is common code shared by jsvalToSize and the Int64/UInt64 constructors.)
template<class IntegerType>
static bool
jsvalToBigInteger(JSContext* cx,
Value val,
bool allowString,
IntegerType* result)
{
JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact);
if (val.isInt32()) {
// Make sure the integer fits in the alotted precision, and has the right
// sign.
int32_t i = val.toInt32();
return ConvertExact(i, result);
}
if (val.isDouble()) {
// Don't silently lose bits here -- check that val really is an
// integer value, and has the right sign.
double d = val.toDouble();
return ConvertExact(d, result);
}
if (allowString && val.isString()) {
// 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, val.toString(), result);
}
if (val.isObject()) {
// Allow conversion from an Int64 or UInt64 object directly.
JSObject* obj = &val.toObject();
if (UInt64::IsUInt64(obj)) {
// Make sure the integer fits in IntegerType.
uint64_t i = Int64Base::GetInt(obj);
return ConvertExact(i, result);
}
if (Int64::IsInt64(obj)) {
// Make sure the integer fits in IntegerType.
int64_t i = Int64Base::GetInt(obj);
return ConvertExact(i, result);
}
if (CDataFinalizer::IsCDataFinalizer(obj)) {
RootedValue innerData(cx);
if (!CDataFinalizer::GetValue(cx, obj, &innerData)) {
return false; // Nothing to convert
}
return jsvalToBigInteger(cx, innerData, allowString, result);
}
}
return false;
}
// Implicitly convert val to a size value, where the size value is represented
// by size_t but must also fit in a double.
static bool
jsvalToSize(JSContext* cx, Value val, bool allowString, size_t* result)
{
if (!jsvalToBigInteger(cx, val, allowString, result))
return false;
// Also check that the result fits in a double.
return Convert<size_t>(double(*result)) == *result;
}
// Implicitly convert val to IntegerType, allowing int, double,
// Int64, UInt64, and optionally a decimal or hexadecimal string argument.
// (This is common code shared by jsvalToSize and the Int64/UInt64 constructors.)
template<class IntegerType>
static bool
jsidToBigInteger(JSContext* cx,
jsid val,
bool allowString,
IntegerType* result)
{
JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact);
if (JSID_IS_INT(val)) {
// Make sure the integer fits in the alotted precision, and has the right
// sign.
int32_t i = JSID_TO_INT(val);
return ConvertExact(i, result);
}
if (allowString && JSID_IS_STRING(val)) {
// Allow conversion from base-10 or base-16 strings, provided the result
// fits in IntegerType. (This allows an Int64 or UInt64 object to be passed
// to the JS array element operator, which will automatically call
// toString() on the object for us.)
return StringToInteger(cx, JSID_TO_STRING(val), result);
}
return false;
}
// Implicitly convert val to a size value, where the size value is represented
// by size_t but must also fit in a double.
static bool
jsidToSize(JSContext* cx, jsid val, bool allowString, size_t* result)
{
if (!jsidToBigInteger(cx, val, allowString, result))
return false;
// Also check that the result fits in a double.
return Convert<size_t>(double(*result)) == *result;
}
// Implicitly convert a size value to a Value, ensuring that the size_t value
// fits in a double.
static bool
SizeTojsval(JSContext* cx, size_t size, MutableHandleValue result)
{
if (Convert<size_t>(double(size)) != size) {
JS_ReportError(cx, "size overflow");
return false;
}
result.setNumber(double(size));
return true;
}
// Forcefully convert val to IntegerType when explicitly requested.
template<class IntegerType>
static bool
jsvalToIntegerExplicit(Value val, IntegerType* result)
{
JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact);
if (val.isDouble()) {
// Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
double d = val.toDouble();
*result = mozilla::IsFinite(d) ? IntegerType(d) : 0;
return true;
}
if (val.isObject()) {
// Convert Int64 and UInt64 values by C-style cast.
JSObject* obj = &val.toObject();
if (Int64::IsInt64(obj)) {
int64_t i = Int64Base::GetInt(obj);
*result = IntegerType(i);
return true;
}
if (UInt64::IsUInt64(obj)) {
uint64_t i = Int64Base::GetInt(obj);
*result = IntegerType(i);
return true;
}
}
return false;
}