Source code

Revision control

Other Tools

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* 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/Sprintf.h"
#include "mozilla/TextUtils.h"
#include "mozilla/Unused.h"
#include "mozilla/Vector.h"
#include "mozilla/WrappingOperations.h"
#if defined(XP_UNIX)
# include <errno.h>
#endif
#if defined(XP_WIN)
# include <float.h>
#endif
#if defined(SOLARIS)
# include <ieeefp.h>
#endif
#include <limits>
#include <math.h>
#include <stdint.h>
#ifdef HAVE_SSIZE_T
# include <sys/types.h>
#endif
#include <type_traits>
#include "jsexn.h"
#include "jsnum.h"
#include "builtin/TypedObject.h"
#include "ctypes/Library.h"
#include "gc/FreeOp.h"
#include "gc/Policy.h"
#include "jit/AtomicOperations.h"
#include "js/Array.h" // JS::GetArrayLength, JS::IsArrayObject, JS::NewArrayObject
#include "js/ArrayBuffer.h" // JS::{IsArrayBufferObject,GetArrayBufferData,GetArrayBuffer{ByteLength,Data}}
#include "js/CharacterEncoding.h"
#include "js/PropertySpec.h"
#include "js/SharedArrayBuffer.h" // JS::{GetSharedArrayBuffer{ByteLength,Data},IsSharedArrayBufferObject}
#include "js/StableStringChars.h"
#include "js/UniquePtr.h"
#include "js/Utility.h"
#include "js/Vector.h"
#include "util/Text.h"
#include "util/Unicode.h"
#include "util/Windows.h"
#include "vm/JSContext.h"
#include "vm/JSFunction.h"
#include "vm/JSObject-inl.h"
using std::numeric_limits;
using mozilla::IsAsciiAlpha;
using mozilla::IsAsciiDigit;
using JS::AutoCheckCannotGC;
using JS::AutoStableStringChars;
namespace js::ctypes {
static bool HasUnpairedSurrogate(const char16_t* chars, size_t nchars,
char16_t* unpaired) {
for (const char16_t* end = chars + nchars; chars != end; chars++) {
char16_t c = *chars;
if (unicode::IsSurrogate(c)) {
chars++;
if (unicode::IsTrailSurrogate(c) || chars == end) {
*unpaired = c;
return true;
}
char16_t c2 = *chars;
if (!unicode::IsTrailSurrogate(c2)) {
*unpaired = c;
return true;
}
}
}
return false;
}
bool ReportErrorIfUnpairedSurrogatePresent(JSContext* cx, JSLinearString* str) {
if (str->hasLatin1Chars()) {
return true;
}
char16_t unpaired;
{
JS::AutoCheckCannotGC nogc;
if (!HasUnpairedSurrogate(str->twoByteChars(nogc), str->length(),
&unpaired)) {
return true;
}
}
char buffer[10];
SprintfLiteral(buffer, "0x%x", unpaired);
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_BAD_SURROGATE_CHAR, buffer);
return false;
}
/*******************************************************************************
** 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,
const char* name);
} // 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, bool* handled);
static bool Setter(JSContext* cx, HandleObject obj, HandleId idval,
HandleValue v, ObjectOpResult& result, bool* handled);
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, HandleValue 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 ReadString(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);
} // 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 JSClassOps sCTypeProtoClassOps = {
nullptr, // addProperty
nullptr, // delProperty
nullptr, // enumerate
nullptr, // newEnumerate
nullptr, // resolve
nullptr, // mayResolve
nullptr, // finalize
ConstructAbstract, // call
nullptr, // hasInstance
ConstructAbstract, // construct
nullptr, // trace
};
static const JSClass sCTypeProtoClass = {
"CType", JSCLASS_HAS_RESERVED_SLOTS(CTYPEPROTO_SLOTS),
&sCTypeProtoClassOps};
// 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 JSClassOps sCTypeClassOps = {
nullptr, // addProperty
nullptr, // delProperty
nullptr, // enumerate
nullptr, // newEnumerate
nullptr, // resolve
nullptr, // mayResolve
CType::Finalize, // finalize
CType::ConstructData, // call
CType::HasInstance, // hasInstance
CType::ConstructData, // construct
CType::Trace, // trace
};
static const JSClass sCTypeClass = {
"CType",
JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS) | JSCLASS_FOREGROUND_FINALIZE,
&sCTypeClassOps};
static const JSClassOps sCDataClassOps = {
nullptr, // addProperty
nullptr, // delProperty
nullptr, // enumerate
nullptr, // newEnumerate
nullptr, // resolve
nullptr, // mayResolve
CData::Finalize, // finalize
FunctionType::Call, // call
nullptr, // hasInstance
FunctionType::Call, // construct
nullptr, // trace
};
static const JSClass sCDataClass = {
"CData",
JSCLASS_HAS_RESERVED_SLOTS(CDATA_SLOTS) | JSCLASS_FOREGROUND_FINALIZE,
&sCDataClassOps};
static const JSClassOps sCClosureClassOps = {
nullptr, // addProperty
nullptr, // delProperty
nullptr, // enumerate
nullptr, // newEnumerate
nullptr, // resolve
nullptr, // mayResolve
CClosure::Finalize, // finalize
nullptr, // call
nullptr, // hasInstance
nullptr, // construct
CClosure::Trace, // trace
};
static const JSClass sCClosureClass = {
"CClosure",
JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS) | JSCLASS_FOREGROUND_FINALIZE,
&sCClosureClassOps};
/*
* 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 JSClassOps sCDataFinalizerClassOps = {
nullptr, // addProperty
nullptr, // delProperty
nullptr, // enumerate
nullptr, // newEnumerate
nullptr, // resolve
nullptr, // mayResolve
CDataFinalizer::Finalize, // finalize
nullptr, // call
nullptr, // hasInstance
nullptr, // construct
nullptr, // trace
};
static const JSClass sCDataFinalizerClass = {
"CDataFinalizer",
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(CDATAFINALIZER_SLOTS) |
JSCLASS_FOREGROUND_FINALIZE,
&sCDataFinalizerClassOps};
#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", CDataFinalizer::Methods::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 JSClassOps sInt64ClassOps = {
nullptr, // addProperty
nullptr, // delProperty
nullptr, // enumerate
nullptr, // newEnumerate
nullptr, // resolve
nullptr, // mayResolve
Int64Base::Finalize, // finalize
nullptr, // call
nullptr, // hasInstance
nullptr, // construct
nullptr, // trace
};
static const JSClass sInt64Class = {
"Int64",
JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS) | JSCLASS_FOREGROUND_FINALIZE,
&sInt64ClassOps};
static const JSClass sUInt64Class = {
"UInt64",
JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS) | JSCLASS_FOREGROUND_FINALIZE,
&sInt64ClassOps};
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};
// Wrapper for arrays, to intercept indexed gets/sets.
class CDataArrayProxyHandler : public ForwardingProxyHandler {
public:
static const CDataArrayProxyHandler singleton;
static const char family;
constexpr CDataArrayProxyHandler() : ForwardingProxyHandler(&family) {}
bool get(JSContext* cx, HandleObject proxy, HandleValue receiver, HandleId id,
MutableHandleValue vp) const override;
bool set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v,
HandleValue receiver, ObjectOpResult& result) const override;
};
const CDataArrayProxyHandler CDataArrayProxyHandler::singleton;
const char CDataArrayProxyHandler::family = 0;
bool CDataArrayProxyHandler::get(JSContext* cx, HandleObject proxy,
HandleValue receiver, HandleId id,
MutableHandleValue vp) const {
RootedObject target(cx, proxy->as<ProxyObject>().target());
bool handled = false;
if (!ArrayType::Getter(cx, target, id, vp, &handled)) {
return false;
}
if (handled) {
return true;
}
return ForwardingProxyHandler::get(cx, proxy, receiver, id, vp);
}
bool CDataArrayProxyHandler::set(JSContext* cx, HandleObject proxy, HandleId id,
HandleValue v, HandleValue receiver,
ObjectOpResult& result) const {
RootedObject target(cx, proxy->as<ProxyObject>().target());
bool handled = false;
if (!ArrayType::Setter(cx, target, id, v, result, &handled)) {
return false;
}
if (handled) {
return true;
}
return ForwardingProxyHandler::set(cx, proxy, id, v, receiver, result);
}
static JSObject* MaybeUnwrapArrayWrapper(JSObject* obj) {
if (IsProxy(obj) &&
obj->as<ProxyObject>().handler() == &CDataArrayProxyHandler::singleton) {
return obj->as<ProxyObject>().target();
}
return obj;
}
static MOZ_ALWAYS_INLINE JSString* NewUCString(JSContext* cx,
const AutoStringChars&& 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) \
{#name, 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 JS::UniqueChars EncodeUTF8(JSContext* cx, AutoString& str) {
RootedString string(cx, NewUCString(cx, str.finish()));
if (!string) {
return nullptr;
}
return JS_EncodeStringToUTF8(cx, string);
}
static const char* CTypesToSourceForError(JSContext* cx, HandleValue val,
JS::UniqueChars& bytes) {
if (val.isObject()) {
RootedObject obj(cx, &val.toObject());
if (CType::IsCType(obj) || CData::IsCDataMaybeUnwrap(&obj)) {
RootedValue v(cx, ObjectValue(*obj));
RootedString str(cx, JS_ValueToSource(cx, v));
bytes = JS_EncodeStringToUTF8(cx, str);
return bytes.get();
}
}
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(cx, source, #name); \
break;
CTYPES_FOR_EACH_TYPE(BUILD_SOURCE)
#undef BUILD_SOURCE
case TYPE_void_t:
AppendString(cx, 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(cx, source, "struct ");
AppendString(cx, 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(cx, source, " ");
if (nameStr) {
MOZ_ASSERT(ptrCount == 0);
AppendString(cx, source, nameStr);
} else if (ptrCount) {
AppendString(cx, source, "(");
AppendChars(source, '*', ptrCount);
AppendString(cx, source, ")");
}
AppendString(cx, 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(cx, source, ", ");
}
}
if (fninfo->mIsVariadic) {
AppendString(cx, source, "...");
}
}
AppendString(cx, 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(cx, source, "<<error converting function to string>>");
return;
}
AppendString(cx, 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(cx, source, " at argument ");
AppendUInt(source, argIndex + 1);
AppendString(cx, source, " of ");
BuildFunctionTypeSource(cx, funObj, source);
break;
}
case ConversionType::Finalizer:
MOZ_ASSERT(funObj);
AppendString(cx, source, " at argument 1 of ");
BuildFunctionTypeSource(cx, funObj, source);
break;
case ConversionType::Return:
MOZ_ASSERT(funObj);
AppendString(cx, source, " at the return value of ");
BuildFunctionTypeSource(cx, funObj, source);
break;
default:
MOZ_ASSERT(!funObj);
break;
}
}
static JSLinearString* 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 JS::UniqueChars TypeSourceForError(JSContext* cx, JSObject* typeObj) {
AutoString source;
BuildTypeSource(cx, typeObj, true, source);
if (!source) {
return nullptr;
}
return EncodeUTF8(cx, source);
}
static JS::UniqueChars FunctionTypeSourceForError(JSContext* cx,
HandleObject funObj) {
AutoString funSource;
BuildFunctionTypeSource(cx, funObj, funSource);
if (!funSource) {
return nullptr;
}
return EncodeUTF8(cx, funSource);
}
static JS::UniqueChars ConversionPositionForError(JSContext* cx,
ConversionType convType,
HandleObject funObj,
unsigned argIndex) {
AutoString posSource;
BuildConversionPosition(cx, convType, funObj, argIndex, posSource);
if (!posSource) {
return nullptr;
}
return EncodeUTF8(cx, posSource);
}
class IndexCString final {
char indexStr[21]; // space for UINT64_MAX plus terminating null
static_assert(sizeof(size_t) <= 8, "index array too small");
public:
explicit IndexCString(size_t index) {
SprintfLiteral(indexStr, "%zu", index);
}
const char* get() const { return indexStr; }
};
static bool ConvError(JSContext* cx, const char* expectedStr,
HandleValue actual, ConversionType convType,
HandleObject funObj = nullptr, unsigned argIndex = 0,
HandleObject arrObj = nullptr, unsigned arrIndex = 0) {
JS::UniqueChars 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);
IndexCString indexStr(arrIndex);
JS::UniqueChars arrStr = TypeSourceForError(cx, arrObj);
if (!arrStr) {
return false;
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
CTYPESMSG_CONV_ERROR_ARRAY, valStr,
indexStr.get(), arrStr.get());
break;
}
case TYPE_struct: {
RootedString name(cx, GetFieldName(arrObj, arrIndex));
MOZ_ASSERT(name);
JS::UniqueChars nameStr = JS_EncodeStringToUTF8(cx, name);
if (!nameStr) {
return false;
}
JS::UniqueChars structStr = TypeSourceForError(cx, arrObj);
if (!structStr) {
return false;
}
JS::UniqueChars posStr;
if (funObj) {
posStr = ConversionPositionForError(cx, convType, funObj, argIndex);
if (!posStr) {
return false;
}
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
CTYPESMSG_CONV_ERROR_STRUCT, valStr,
nameStr.get(), expectedStr, structStr.get(),
(posStr ? posStr.get() : ""));
break;
}
default:
MOZ_CRASH("invalid arrObj value");
}
return false;
}
switch (convType) {
case ConversionType::Argument: {
MOZ_ASSERT(funObj);
IndexCString indexStr(argIndex + 1);
JS::UniqueChars funStr = FunctionTypeSourceForError(cx, funObj);
if (!funStr) {
return false;
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
CTYPESMSG_CONV_ERROR_ARG, valStr, indexStr.get(),
funStr.get());
break;
}
case ConversionType::Finalizer: {
MOZ_ASSERT(funObj);
JS::UniqueChars funStr = FunctionTypeSourceForError(cx, funObj);
if (!funStr) {
return false;
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
CTYPESMSG_CONV_ERROR_FIN, valStr, funStr.get());
break;
}
case ConversionType::Return: {
MOZ_ASSERT(funObj);
JS::UniqueChars funStr = FunctionTypeSourceForError(cx, funObj);
if (!funStr) {
return false;
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
CTYPESMSG_CONV_ERROR_RET, valStr, funStr.get());
break;
}
case ConversionType::Setter:
case ConversionType::Construct:
MOZ_ASSERT(!funObj);
JS_ReportErrorNumberUTF8(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));
JS::UniqueChars expectedStr = TypeSourceForError(cx, expectedType);
if (!expectedStr) {
return false;
}
return ConvError(cx, expectedStr.get(), actual, convType, funObj, argIndex,
arrObj, arrIndex);
}
static bool ArgumentConvError(JSContext* cx, HandleValue actual,
const char* funStr, unsigned argIndex) {
MOZ_ASSERT(JS::StringIsASCII(funStr));
JS::UniqueChars valBytes;
const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
if (!valStr) {
return false;
}
IndexCString indexStr(argIndex + 1);
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
CTYPESMSG_CONV_ERROR_ARG, valStr, indexStr.get(),
funStr);
return false;
}
static bool ArgumentLengthError(JSContext* cx, const char* fun,
const char* count, const char* s) {
JS_ReportErrorNumberUTF8(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));
JS::UniqueChars valBytes;
const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
if (!valStr) {
return false;
}
IndexCString expectedLengthStr(expectedLength);
IndexCString actualLengthStr(actualLength);
JS::UniqueChars arrStr = TypeSourceForError(cx, arrObj);
if (!arrStr) {
return false;
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
CTYPESMSG_ARRAY_MISMATCH, valStr, arrStr.get(),
expectedLengthStr.get(), actualLengthStr.get());
return false;
}
static bool ArrayLengthOverflow(JSContext* cx, unsigned expectedLength,
HandleObject arrObj, unsigned actualLength,
HandleValue actual, ConversionType convType) {
MOZ_ASSERT(arrObj && CType::IsCType(arrObj));
JS::UniqueChars valBytes;
const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
if (!valStr) {
return false;
}
IndexCString expectedLengthStr(expectedLength);
IndexCString actualLengthStr(actualLength);
JS::UniqueChars arrStr = TypeSourceForError(cx, arrObj);
if (!arrStr) {
return false;
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
CTYPESMSG_ARRAY_OVERFLOW, valStr, arrStr.get(),
expectedLengthStr.get(), actualLengthStr.get());
return false;
}
static bool ArgumentRangeMismatch(JSContext* cx, const char* func,
const char* range) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
CTYPESMSG_ARG_RANGE_MISMATCH, func, range);
return false;
}
static bool ArgumentTypeMismatch(JSContext* cx, const char* arg,
const char* func, const char* type) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
CTYPESMSG_ARG_TYPE_MISMATCH, arg, func, type);
return false;
}
static bool CannotConstructError(JSContext* cx, const char* type) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
CTYPESMSG_CANNOT_CONSTRUCT, type);
return false;
}
static bool DuplicateFieldError(JSContext* cx, Handle<JSLinearString*> name) {
JS::UniqueChars nameStr = JS_EncodeStringToUTF8(cx, name);
if (!nameStr) {
return false;
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
CTYPESMSG_DUPLICATE_FIELD, nameStr.get());
return false;
}
static bool EmptyFinalizerCallError(JSContext* cx, const char* funName) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
CTYPESMSG_EMPTY_FIN_CALL, funName);
return false;
}
static bool EmptyFinalizerError(JSContext* cx, ConversionType convType,
HandleObject funObj = nullptr,
unsigned argIndex = 0) {
JS::UniqueChars posStr;
if (funObj) {
posStr = ConversionPositionForError(cx, convType, funObj, argIndex);
if (!posStr) {
return false;
}
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, CTYPESMSG_EMPTY_FIN,
(posStr ? posStr.get() : ""));
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));
JS::UniqueChars valBytes;
const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
if (!valStr) {
return false;
}
JS::UniqueChars structStr = TypeSourceForError(cx, structObj);
if (!structStr) {
return false;
}
IndexCString expectedCountStr(expectedCount);
IndexCString actualCountStr(actualCount);
JS::UniqueChars posStr;
if (funObj) {
posStr = ConversionPositionForError(cx, convType, funObj, argIndex);
if (!posStr) {
return false;
}
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
CTYPESMSG_FIELD_MISMATCH, valStr, structStr.get(),
expectedCountStr.get(), actualCountStr.get(),
(posStr ? posStr.get() : ""));
return false;
}
static bool FieldDescriptorCountError(JSContext* cx, HandleValue typeVal,
size_t length) {
JS::UniqueChars valBytes;
const char* valStr = CTypesToSourceForError(cx, typeVal, valBytes);
if (!valStr) {
return false;
}
IndexCString lengthStr(length);
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
CTYPESMSG_FIELD_DESC_COUNT, valStr, lengthStr.get());
return false;
}
static bool FieldDescriptorNameError(JSContext* cx, HandleId id) {
JS::UniqueChars idBytes;
RootedValue idVal(cx, IdToValue(id));
const char* propStr = CTypesToSourceForError(cx, idVal, idBytes);
if (!propStr) {
return false;
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
CTYPESMSG_FIELD_DESC_NAME, propStr);
return false;
}
static bool FieldDescriptorSizeError(JSContext* cx, HandleObject typeObj,
HandleId id) {
RootedValue typeVal(cx, ObjectValue(*typeObj));
JS::UniqueChars typeBytes;
const char* typeStr = CTypesToSourceForError(cx, typeVal, typeBytes);
if (!typeStr) {
return false;
}
RootedString idStr(cx, IdToString(cx, id));
JS::UniqueChars propStr = JS_EncodeStringToUTF8(cx, idStr);
if (!propStr) {
return false;
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
CTYPESMSG_FIELD_DESC_SIZE, typeStr, propStr.get());
return false;
}
static bool FieldDescriptorNameTypeError(JSContext* cx, HandleValue typeVal) {
JS::UniqueChars valBytes;
const char* valStr = CTypesToSourceForError(cx, typeVal, valBytes);
if (!valStr) {
return false;
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
CTYPESMSG_FIELD_DESC_NAMETYPE, valStr);
return false;
}
static bool FieldDescriptorTypeError(JSContext* cx, HandleValue poroVal,
HandleId id) {
JS::UniqueChars typeBytes;
const char* typeStr = CTypesToSourceForError(cx, poroVal, typeBytes);
if (!typeStr) {
return false;
}
RootedString idStr(cx, IdToString(cx, id));
JS::UniqueChars propStr = JS_EncodeStringToUTF8(cx, idStr);
if (!propStr) {
return false;
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
CTYPESMSG_FIELD_DESC_TYPE, typeStr, propStr.get());
return false;
}
static bool FieldMissingError(JSContext* cx, JSObject* typeObj,
JSLinearString* name_) {
JS::UniqueChars typeBytes;
RootedString name(cx, name_);
RootedValue typeVal(cx, ObjectValue(*typeObj));
const char* typeStr = CTypesToSourceForError(cx, typeVal, typeBytes);
if (!typeStr) {
return false;
}
JS::UniqueChars nameStr = JS_EncodeStringToUTF8(cx, name);
if (!nameStr) {
return false;
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
CTYPESMSG_FIELD_MISSING, typeStr, nameStr.get());
return false;
}
static bool FinalizerSizeError(JSContext* cx, HandleObject funObj,
HandleValue actual) {
MOZ_ASSERT(CType::IsCType(funObj));
JS::UniqueChars valBytes;
const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
if (!valStr) {
return false;
}
JS::UniqueChars funStr = FunctionTypeSourceForError(cx, funObj);
if (!funStr) {
return false;
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
CTYPESMSG_FIN_SIZE_ERROR, funStr.get(), valStr);
return false;
}
static bool FunctionArgumentLengthMismatch(
JSContext* cx, unsigned expectedCount, unsigned actualCount,
HandleObject funObj, HandleObject typeObj, bool isVariadic) {
JS::UniqueChars funStr;
Value slot = JS_GetReservedSlot(funObj, SLOT_REFERENT);
if (!slot.isUndefined() && Library::IsLibrary(&slot.toObject())) {
funStr = FunctionTypeSourceForError(cx, funObj);
} else {
funStr = FunctionTypeSourceForError(cx, typeObj);
}
if (!funStr) {
return false;
}
IndexCString expectedCountStr(expectedCount);
IndexCString actualCountStr(actualCount);
const char* variadicStr = isVariadic ? " or more" : "";
JS_ReportErrorNumberUTF8(
cx, GetErrorMessage, nullptr, CTYPESMSG_ARG_COUNT_MISMATCH, funStr.get(),
expectedCountStr.get(), variadicStr, actualCountStr.get());
return false;
}
static bool FunctionArgumentTypeError(JSContext* cx, uint32_t index,
HandleValue typeVal, const char* reason) {
MOZ_ASSERT(JS::StringIsASCII(reason));
JS::UniqueChars valBytes;
const char* valStr = CTypesToSourceForError(cx, typeVal, valBytes);
if (!valStr) {
return false;
}
IndexCString indexStr(index + 1);
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
CTYPESMSG_ARG_TYPE_ERROR, indexStr.get(), reason,
valStr);
return false;
}
static bool FunctionReturnTypeError(JSContext* cx, HandleValue type,
const char* reason) {
MOZ_ASSERT(JS::StringIsASCII(reason));
JS::UniqueChars valBytes;
const char* valStr = CTypesToSourceForError(cx, type, valBytes);
if (!valStr) {
return false;
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
CTYPESMSG_RET_TYPE_ERROR, reason, valStr);
return false;
}
static bool IncompatibleCallee(JSContext* cx, const char* funName,
HandleObject actualObj) {
MOZ_ASSERT(JS::StringIsASCII(funName));
JS::UniqueChars valBytes;
RootedValue val(cx, ObjectValue(*actualObj));
const char* valStr = CTypesToSourceForError(cx, val, valBytes);
if (!valStr) {
return false;
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
CTYPESMSG_INCOMPATIBLE_CALLEE, funName, valStr);
return false;
}
static bool IncompatibleThisProto(JSContext* cx, const char* funName,
const char* actualType) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
CTYPESMSG_INCOMPATIBLE_THIS, funName, actualType);
return false;
}
static bool IncompatibleThisProto(JSContext* cx, const char* funName,
HandleValue actualVal) {
MOZ_ASSERT(JS::StringIsASCII(funName));
JS::UniqueChars valBytes;
const char* valStr = CTypesToSourceForError(cx, actualVal, valBytes);
if (!valStr) {
return false;
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
CTYPESMSG_INCOMPATIBLE_THIS_VAL, funName,
"incompatible object", valStr);
return false;
}
static bool IncompatibleThisType(JSContext* cx, const char* funName,
const char* actualType) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
CTYPESMSG_INCOMPATIBLE_THIS_TYPE, funName,
actualType);
return false;
}
static bool IncompatibleThisType(JSContext* cx, const char* funName,
const char* actualType,
HandleValue actualVal) {
MOZ_ASSERT(JS::StringIsASCII(funName));
MOZ_ASSERT(JS::StringIsASCII(actualType));
JS::UniqueChars valBytes;
const char* valStr = CTypesToSourceForError(cx, actualVal, valBytes);
if (!valStr) {
return false;
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
CTYPESMSG_INCOMPATIBLE_THIS_VAL, funName, actualType,
valStr);
return false;
}
static bool InvalidIndexError(JSContext* cx, HandleValue val) {
JS::UniqueChars idBytes;
const char* indexStr = CTypesToSourceForError(cx, val, idBytes);
if (!indexStr) {
return false;
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
CTYPESMSG_INVALID_INDEX, indexStr);
return false;
}
static bool InvalidIndexError(JSContext* cx, HandleId id) {
RootedValue idVal(cx, IdToValue(id));
return InvalidIndexError(cx, idVal);
}
static bool InvalidIndexRangeError(JSContext* cx, size_t index, size_t length) {
IndexCString indexStr(index);
IndexCString lengthStr(length);
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
CTYPESMSG_INVALID_RANGE, indexStr.get(),
lengthStr.get());
return false;
}
static bool NonPrimitiveError(JSContext* cx, HandleObject typeObj) {
MOZ_ASSERT(CType::IsCType(typeObj));
JS::UniqueChars typeStr = TypeSourceForError(cx, typeObj);
if (!typeStr) {
return false;
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
CTYPESMSG_NON_PRIMITIVE, typeStr.get());
return false;
}
static bool NonStringBaseError(JSContext* cx, HandleValue thisVal) {
JS::UniqueChars valBytes;
const char* valStr = CTypesToSourceForError(cx, thisVal, valBytes);
if (!valStr) {
return false;
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
CTYPESMSG_NON_STRING_BASE, valStr);
return false;
}
static bool NullPointerError(JSContext* cx, const char* action,
HandleObject obj) {
MOZ_ASSERT(JS::StringIsASCII(action));
JS::UniqueChars valBytes;
RootedValue val(cx, ObjectValue(*obj));
const char* valStr = CTypesToSourceForError(cx, val, valBytes);
if (!valStr) {
return false;
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, CTYPESMSG_NULL_POINTER,
action, valStr);
return false;
}
static bool PropNameNonStringError(JSContext* cx, HandleId id,
HandleValue actual, ConversionType convType,
HandleObject funObj = nullptr,
unsigned argIndex = 0) {
JS::UniqueChars valBytes;
const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
if (!valStr) {
return false;
}
JS::UniqueChars idBytes;
RootedValue idVal(cx, IdToValue(id));
const char* propStr = CTypesToSourceForError(cx, idVal, idBytes);
if (!propStr) {
return false;
}
JS::UniqueChars posStr;
if (funObj) {
posStr = ConversionPositionForError(cx, convType, funObj, argIndex);
if (!posStr) {
return false;
}
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
CTYPESMSG_PROP_NONSTRING, propStr, valStr,
(posStr ? posStr.get() : ""));
return false;
}
static bool SizeOverflow(JSContext* cx, const char* name, const char* limit) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
CTYPESMSG_SIZE_OVERFLOW, name, limit);
return false;
}
static bool TypeError(JSContext* cx, const char* expected, HandleValue actual) {
MOZ_ASSERT(JS::StringIsASCII(expected));
JS::UniqueChars bytes;
const char* src = CTypesToSourceForError(cx, actual, bytes);
if (!src) {
return false;
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, CTYPESMSG_TYPE_ERROR,
expected, src);
return false;
}
static bool TypeOverflow(JSContext* cx, const char* expected,
HandleValue actual) {
MOZ_ASSERT(JS::StringIsASCII(expected));
JS::UniqueChars valBytes;
const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
if (!valStr) {
return false;
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
CTYPESMSG_TYPE_OVERFLOW, valStr, expected);
return false;
}
static bool UndefinedSizeCastError(JSContext* cx, HandleObject targetTypeObj) {
JS::UniqueChars targetTypeStr = TypeSourceForError(cx, targetTypeObj);
if (!targetTypeStr) {
return false;
}
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
CTYPESMSG_UNDEFINED_SIZE_CAST, targetTypeStr.get());
return false;
}
static bool SizeMismatchCastError(JSContext* cx, HandleObject sourceTypeObj,
HandleObject targetTypeObj, size_t sourceSize,
size_t targetSize) {
JS::UniqueChars sourceTypeStr = TypeSourceForError(cx, sourceTypeObj);
if (!sourceTypeStr) {
return false;
}
JS::UniqueChars targetTypeStr = TypeSourceForError(cx, targetTypeObj);
if (!targetTypeStr) {
return false;
}