mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-09-27 14:56:16 +00:00
536 lines
17 KiB
C++
536 lines
17 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
|
|
/* 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/. */
|
|
|
|
#ifndef ctypes_CTypes_h
|
|
#define ctypes_CTypes_h
|
|
|
|
#include "mozilla/UniquePtr.h"
|
|
#include "mozilla/Vector.h"
|
|
|
|
#include "ffi.h"
|
|
#include "jsalloc.h"
|
|
#include "jsprf.h"
|
|
#include "prlink.h"
|
|
|
|
#include "ctypes/typedefs.h"
|
|
#include "js/GCHashTable.h"
|
|
#include "js/Vector.h"
|
|
#include "vm/String.h"
|
|
|
|
namespace js {
|
|
namespace ctypes {
|
|
|
|
/*******************************************************************************
|
|
** Utility classes
|
|
*******************************************************************************/
|
|
|
|
// String and AutoString classes, based on Vector.
|
|
typedef Vector<char16_t, 0, SystemAllocPolicy> String;
|
|
typedef Vector<char16_t, 64, SystemAllocPolicy> AutoString;
|
|
typedef Vector<char, 0, SystemAllocPolicy> CString;
|
|
typedef Vector<char, 64, SystemAllocPolicy> AutoCString;
|
|
|
|
// Convenience functions to append, insert, and compare Strings.
|
|
template <class T, size_t N, class AP, size_t ArrayLength>
|
|
void
|
|
AppendString(mozilla::Vector<T, N, AP>& v, const char (&array)[ArrayLength])
|
|
{
|
|
// Don't include the trailing '\0'.
|
|
size_t alen = ArrayLength - 1;
|
|
size_t vlen = v.length();
|
|
if (!v.resize(vlen + alen))
|
|
return;
|
|
|
|
for (size_t i = 0; i < alen; ++i)
|
|
v[i + vlen] = array[i];
|
|
}
|
|
|
|
template <class T, size_t N, class AP>
|
|
void
|
|
AppendChars(mozilla::Vector<T, N, AP>& v, const char c, size_t count)
|
|
{
|
|
size_t vlen = v.length();
|
|
if (!v.resize(vlen + count))
|
|
return;
|
|
|
|
for (size_t i = 0; i < count; ++i)
|
|
v[i + vlen] = c;
|
|
}
|
|
|
|
template <class T, size_t N, class AP>
|
|
void
|
|
AppendUInt(mozilla::Vector<T, N, AP>& v, unsigned n)
|
|
{
|
|
char array[16];
|
|
size_t alen = JS_snprintf(array, 16, "%u", n);
|
|
size_t vlen = v.length();
|
|
if (!v.resize(vlen + alen))
|
|
return;
|
|
|
|
for (size_t i = 0; i < alen; ++i)
|
|
v[i + vlen] = array[i];
|
|
}
|
|
|
|
template <class T, size_t N, size_t M, class AP>
|
|
void
|
|
AppendString(mozilla::Vector<T, N, AP>& v, mozilla::Vector<T, M, AP>& w)
|
|
{
|
|
v.append(w.begin(), w.length());
|
|
}
|
|
|
|
template <size_t N, class AP>
|
|
void
|
|
AppendString(mozilla::Vector<char16_t, N, AP>& v, JSString* str)
|
|
{
|
|
MOZ_ASSERT(str);
|
|
JSLinearString* linear = str->ensureLinear(nullptr);
|
|
if (!linear)
|
|
return;
|
|
JS::AutoCheckCannotGC nogc;
|
|
if (linear->hasLatin1Chars())
|
|
v.append(linear->latin1Chars(nogc), linear->length());
|
|
else
|
|
v.append(linear->twoByteChars(nogc), linear->length());
|
|
}
|
|
|
|
template <size_t N, class AP>
|
|
void
|
|
AppendString(mozilla::Vector<char, N, AP>& v, JSString* str)
|
|
{
|
|
MOZ_ASSERT(str);
|
|
size_t vlen = v.length();
|
|
size_t alen = str->length();
|
|
if (!v.resize(vlen + alen))
|
|
return;
|
|
|
|
JSLinearString* linear = str->ensureLinear(nullptr);
|
|
if (!linear)
|
|
return;
|
|
|
|
JS::AutoCheckCannotGC nogc;
|
|
if (linear->hasLatin1Chars()) {
|
|
const Latin1Char* chars = linear->latin1Chars(nogc);
|
|
for (size_t i = 0; i < alen; ++i)
|
|
v[i + vlen] = char(chars[i]);
|
|
} else {
|
|
const char16_t* chars = linear->twoByteChars(nogc);
|
|
for (size_t i = 0; i < alen; ++i)
|
|
v[i + vlen] = char(chars[i]);
|
|
}
|
|
}
|
|
|
|
template <class T, size_t N, class AP, size_t ArrayLength>
|
|
void
|
|
PrependString(mozilla::Vector<T, N, AP>& v, const char (&array)[ArrayLength])
|
|
{
|
|
// Don't include the trailing '\0'.
|
|
size_t alen = ArrayLength - 1;
|
|
size_t vlen = v.length();
|
|
if (!v.resize(vlen + alen))
|
|
return;
|
|
|
|
// Move vector data forward. This is safe since we've already resized.
|
|
memmove(v.begin() + alen, v.begin(), vlen * sizeof(T));
|
|
|
|
// Copy data to insert.
|
|
for (size_t i = 0; i < alen; ++i)
|
|
v[i] = array[i];
|
|
}
|
|
|
|
template <size_t N, class AP>
|
|
void
|
|
PrependString(mozilla::Vector<char16_t, N, AP>& v, JSString* str)
|
|
{
|
|
MOZ_ASSERT(str);
|
|
size_t vlen = v.length();
|
|
size_t alen = str->length();
|
|
if (!v.resize(vlen + alen))
|
|
return;
|
|
|
|
JSLinearString* linear = str->ensureLinear(nullptr);
|
|
if (!linear)
|
|
return;
|
|
|
|
// Move vector data forward. This is safe since we've already resized.
|
|
memmove(v.begin() + alen, v.begin(), vlen * sizeof(char16_t));
|
|
|
|
// Copy data to insert.
|
|
JS::AutoCheckCannotGC nogc;
|
|
if (linear->hasLatin1Chars()) {
|
|
const Latin1Char* chars = linear->latin1Chars(nogc);
|
|
for (size_t i = 0; i < alen; i++)
|
|
v[i] = chars[i];
|
|
} else {
|
|
memcpy(v.begin(), linear->twoByteChars(nogc), alen * sizeof(char16_t));
|
|
}
|
|
}
|
|
|
|
template <typename CharT>
|
|
extern size_t
|
|
GetDeflatedUTF8StringLength(JSContext* maybecx, const CharT* chars,
|
|
size_t charsLength);
|
|
|
|
template <typename CharT>
|
|
bool
|
|
DeflateStringToUTF8Buffer(JSContext* maybecx, const CharT* src, size_t srclen,
|
|
char* dst, size_t* dstlenp);
|
|
|
|
|
|
/*******************************************************************************
|
|
** Function and struct API definitions
|
|
*******************************************************************************/
|
|
|
|
MOZ_ALWAYS_INLINE void
|
|
ASSERT_OK(bool ok)
|
|
{
|
|
MOZ_ASSERT(ok);
|
|
}
|
|
|
|
// for JS error reporting
|
|
enum ErrorNum {
|
|
#define MSG_DEF(name, count, exception, format) \
|
|
name,
|
|
#include "ctypes/ctypes.msg"
|
|
#undef MSG_DEF
|
|
CTYPESERR_LIMIT
|
|
};
|
|
|
|
/**
|
|
* ABI constants that specify the calling convention to use.
|
|
* ctypes.default_abi corresponds to the cdecl convention, and in almost all
|
|
* cases is the correct choice. ctypes.stdcall_abi is provided for calling
|
|
* stdcall functions on Win32, and implies stdcall symbol name decoration;
|
|
* ctypes.winapi_abi is just stdcall but without decoration.
|
|
*/
|
|
enum ABICode {
|
|
ABI_DEFAULT,
|
|
ABI_STDCALL,
|
|
ABI_WINAPI,
|
|
INVALID_ABI
|
|
};
|
|
|
|
enum TypeCode {
|
|
TYPE_void_t,
|
|
#define DEFINE_TYPE(name, type, ffiType) TYPE_##name,
|
|
CTYPES_FOR_EACH_TYPE(DEFINE_TYPE)
|
|
#undef DEFINE_TYPE
|
|
TYPE_pointer,
|
|
TYPE_function,
|
|
TYPE_array,
|
|
TYPE_struct
|
|
};
|
|
|
|
// Descriptor of one field in a StructType. The name of the field is stored
|
|
// as the key to the hash entry.
|
|
struct FieldInfo
|
|
{
|
|
JS::Heap<JSObject*> mType; // CType of the field
|
|
size_t mIndex; // index of the field in the struct (first is 0)
|
|
size_t mOffset; // offset of the field in the struct, in bytes
|
|
|
|
void trace(JSTracer* trc) {
|
|
JS_CallObjectTracer(trc, &mType, "fieldType");
|
|
}
|
|
};
|
|
|
|
struct UnbarrieredFieldInfo
|
|
{
|
|
JSObject* mType; // CType of the field
|
|
size_t mIndex; // index of the field in the struct (first is 0)
|
|
size_t mOffset; // offset of the field in the struct, in bytes
|
|
};
|
|
static_assert(sizeof(UnbarrieredFieldInfo) == sizeof(FieldInfo),
|
|
"UnbarrieredFieldInfo should be the same as FieldInfo but with unbarriered mType");
|
|
|
|
// Hash policy for FieldInfos.
|
|
struct FieldHashPolicy : DefaultHasher<JSFlatString*>
|
|
{
|
|
typedef JSFlatString* Key;
|
|
typedef Key Lookup;
|
|
|
|
template <typename CharT>
|
|
static uint32_t hash(const CharT* s, size_t n) {
|
|
uint32_t hash = 0;
|
|
for (; n > 0; s++, n--)
|
|
hash = hash * 33 + *s;
|
|
return hash;
|
|
}
|
|
|
|
static uint32_t hash(const Lookup& l) {
|
|
JS::AutoCheckCannotGC nogc;
|
|
return l->hasLatin1Chars()
|
|
? hash(l->latin1Chars(nogc), l->length())
|
|
: hash(l->twoByteChars(nogc), l->length());
|
|
}
|
|
|
|
static bool match(const Key& k, const Lookup& l) {
|
|
if (k == l)
|
|
return true;
|
|
|
|
if (k->length() != l->length())
|
|
return false;
|
|
|
|
return EqualChars(k, l);
|
|
}
|
|
};
|
|
|
|
using FieldInfoHash = GCHashMap<JSFlatString*, FieldInfo, FieldHashPolicy, SystemAllocPolicy>;
|
|
|
|
void
|
|
TraceFieldInfoHash(JSTracer* trc, FieldInfoHash* fields);
|
|
|
|
// Descriptor of ABI, return type, argument types, and variadicity for a
|
|
// FunctionType.
|
|
struct FunctionInfo
|
|
{
|
|
// Initialized in NewFunctionInfo when !mIsVariadic, but only later, in
|
|
// FunctionType::Call, when mIsVariadic. Not always consistent with
|
|
// mFFITypes, due to lazy initialization when mIsVariadic.
|
|
ffi_cif mCIF;
|
|
|
|
// Calling convention of the function. Convert to ffi_abi using GetABI
|
|
// and ObjectValue. Stored as a JSObject* for ease of tracing.
|
|
JS::Heap<JSObject*> mABI;
|
|
|
|
// The CType of the value returned by the function.
|
|
JS::Heap<JSObject*> mReturnType;
|
|
|
|
// A fixed array of known parameter types, excluding any variadic
|
|
// parameters (if mIsVariadic).
|
|
Vector<JS::Heap<JSObject*>, 0, SystemAllocPolicy> mArgTypes;
|
|
|
|
// A variable array of ffi_type*s corresponding to both known parameter
|
|
// types and dynamic (variadic) parameter types. Longer than mArgTypes
|
|
// only if mIsVariadic.
|
|
Vector<ffi_type*, 0, SystemAllocPolicy> mFFITypes;
|
|
|
|
// Flag indicating whether the function behaves like a C function with
|
|
// ... as the final formal parameter.
|
|
bool mIsVariadic;
|
|
};
|
|
|
|
// Parameters necessary for invoking a JS function from a C closure.
|
|
struct ClosureInfo
|
|
{
|
|
JSRuntime* rt;
|
|
JS::Heap<JSObject*> closureObj; // CClosure object
|
|
JS::Heap<JSObject*> typeObj; // FunctionType describing the C function
|
|
JS::Heap<JSObject*> thisObj; // 'this' object to use for the JS function call
|
|
JS::Heap<JSObject*> jsfnObj; // JS function
|
|
void* errResult; // Result that will be returned if the closure throws
|
|
ffi_closure* closure; // The C closure itself
|
|
|
|
// Anything conditionally freed in the destructor should be initialized to
|
|
// nullptr here.
|
|
explicit ClosureInfo(JSRuntime* runtime)
|
|
: rt(runtime)
|
|
, errResult(nullptr)
|
|
, closure(nullptr)
|
|
{}
|
|
|
|
~ClosureInfo() {
|
|
if (closure)
|
|
ffi_closure_free(closure);
|
|
js_free(errResult);
|
|
}
|
|
};
|
|
|
|
bool IsCTypesGlobal(HandleValue v);
|
|
bool IsCTypesGlobal(JSObject* obj);
|
|
|
|
const JSCTypesCallbacks* GetCallbacks(JSObject* obj);
|
|
|
|
/*******************************************************************************
|
|
** JSClass reserved slot definitions
|
|
*******************************************************************************/
|
|
|
|
enum CTypesGlobalSlot {
|
|
SLOT_CALLBACKS = 0, // pointer to JSCTypesCallbacks struct
|
|
SLOT_ERRNO = 1, // Value for latest |errno|
|
|
SLOT_LASTERROR = 2, // Value for latest |GetLastError|, used only with Windows
|
|
CTYPESGLOBAL_SLOTS
|
|
};
|
|
|
|
enum CABISlot {
|
|
SLOT_ABICODE = 0, // ABICode of the CABI object
|
|
CABI_SLOTS
|
|
};
|
|
|
|
enum CTypeProtoSlot {
|
|
SLOT_POINTERPROTO = 0, // ctypes.PointerType.prototype object
|
|
SLOT_ARRAYPROTO = 1, // ctypes.ArrayType.prototype object
|
|
SLOT_STRUCTPROTO = 2, // ctypes.StructType.prototype object
|
|
SLOT_FUNCTIONPROTO = 3, // ctypes.FunctionType.prototype object
|
|
SLOT_CDATAPROTO = 4, // ctypes.CData.prototype object
|
|
SLOT_POINTERDATAPROTO = 5, // common ancestor of all CData objects of PointerType
|
|
SLOT_ARRAYDATAPROTO = 6, // common ancestor of all CData objects of ArrayType
|
|
SLOT_STRUCTDATAPROTO = 7, // common ancestor of all CData objects of StructType
|
|
SLOT_FUNCTIONDATAPROTO = 8, // common ancestor of all CData objects of FunctionType
|
|
SLOT_INT64PROTO = 9, // ctypes.Int64.prototype object
|
|
SLOT_UINT64PROTO = 10, // ctypes.UInt64.prototype object
|
|
SLOT_CTYPES = 11, // ctypes object
|
|
SLOT_OURDATAPROTO = 12, // the data prototype corresponding to this object
|
|
CTYPEPROTO_SLOTS
|
|
};
|
|
|
|
enum CTypeSlot {
|
|
SLOT_PROTO = 0, // 'prototype' property of the CType object
|
|
SLOT_TYPECODE = 1, // TypeCode of the CType object
|
|
SLOT_FFITYPE = 2, // ffi_type representing the type
|
|
SLOT_NAME = 3, // name of the type
|
|
SLOT_SIZE = 4, // size of the type, in bytes
|
|
SLOT_ALIGN = 5, // alignment of the type, in bytes
|
|
SLOT_PTR = 6, // cached PointerType object for type.ptr
|
|
// Note that some of the slots below can overlap, since they're for
|
|
// mutually exclusive types.
|
|
SLOT_TARGET_T = 7, // (PointerTypes only) 'targetType' property
|
|
SLOT_ELEMENT_T = 7, // (ArrayTypes only) 'elementType' property
|
|
SLOT_LENGTH = 8, // (ArrayTypes only) 'length' property
|
|
SLOT_FIELDS = 7, // (StructTypes only) 'fields' property
|
|
SLOT_FIELDINFO = 8, // (StructTypes only) FieldInfoHash table
|
|
SLOT_FNINFO = 7, // (FunctionTypes only) FunctionInfo struct
|
|
SLOT_ARGS_T = 8, // (FunctionTypes only) 'argTypes' property (cached)
|
|
CTYPE_SLOTS
|
|
};
|
|
|
|
enum CDataSlot {
|
|
SLOT_CTYPE = 0, // CType object representing the underlying type
|
|
SLOT_REFERENT = 1, // JSObject this object must keep alive, if any
|
|
SLOT_DATA = 2, // pointer to a buffer containing the binary data
|
|
SLOT_OWNS = 3, // TrueValue() if this CData owns its own buffer
|
|
SLOT_FUNNAME = 4, // JSString representing the function name
|
|
CDATA_SLOTS
|
|
};
|
|
|
|
enum CClosureSlot {
|
|
SLOT_CLOSUREINFO = 0, // ClosureInfo struct
|
|
CCLOSURE_SLOTS
|
|
};
|
|
|
|
enum CDataFinalizerSlot {
|
|
// The type of the value (a CType JSObject).
|
|
// We hold it to permit ImplicitConvert and ToSource.
|
|
SLOT_DATAFINALIZER_VALTYPE = 0,
|
|
// The type of the function used at finalization (a CType JSObject).
|
|
// We hold it to permit |ToSource|.
|
|
SLOT_DATAFINALIZER_CODETYPE = 1,
|
|
CDATAFINALIZER_SLOTS
|
|
};
|
|
|
|
enum TypeCtorSlot {
|
|
SLOT_FN_CTORPROTO = 0 // ctypes.{Pointer,Array,Struct}Type.prototype
|
|
// JSFunction objects always get exactly two slots.
|
|
};
|
|
|
|
enum Int64Slot {
|
|
SLOT_INT64 = 0, // pointer to a 64-bit buffer containing the integer
|
|
INT64_SLOTS
|
|
};
|
|
|
|
enum Int64FunctionSlot {
|
|
SLOT_FN_INT64PROTO = 0 // ctypes.{Int64,UInt64}.prototype object
|
|
// JSFunction objects always get exactly two slots.
|
|
};
|
|
|
|
/*******************************************************************************
|
|
** Object API definitions
|
|
*******************************************************************************/
|
|
|
|
namespace CType {
|
|
JSObject* Create(JSContext* cx, HandleObject typeProto, HandleObject dataProto,
|
|
TypeCode type, JSString* name, Value size, Value align, ffi_type* ffiType);
|
|
|
|
JSObject* DefineBuiltin(JSContext* cx, HandleObject ctypesObj, const char* propName,
|
|
JSObject* typeProto, JSObject* dataProto, const char* name, TypeCode type,
|
|
Value size, Value align, ffi_type* ffiType);
|
|
|
|
bool IsCType(JSObject* obj);
|
|
bool IsCTypeProto(JSObject* obj);
|
|
TypeCode GetTypeCode(JSObject* typeObj);
|
|
bool TypesEqual(JSObject* t1, JSObject* t2);
|
|
size_t GetSize(JSObject* obj);
|
|
bool GetSafeSize(JSObject* obj, size_t* result);
|
|
bool IsSizeDefined(JSObject* obj);
|
|
size_t GetAlignment(JSObject* obj);
|
|
ffi_type* GetFFIType(JSContext* cx, JSObject* obj);
|
|
JSString* GetName(JSContext* cx, HandleObject obj);
|
|
JSObject* GetProtoFromCtor(JSObject* obj, CTypeProtoSlot slot);
|
|
JSObject* GetProtoFromType(JSContext* cx, JSObject* obj, CTypeProtoSlot slot);
|
|
const JSCTypesCallbacks* GetCallbacksFromType(JSObject* obj);
|
|
} // namespace CType
|
|
|
|
namespace PointerType {
|
|
JSObject* CreateInternal(JSContext* cx, HandleObject baseType);
|
|
|
|
JSObject* GetBaseType(JSObject* obj);
|
|
} // namespace PointerType
|
|
|
|
typedef mozilla::UniquePtr<ffi_type, JS::DeletePolicy<ffi_type>> UniquePtrFFIType;
|
|
|
|
namespace ArrayType {
|
|
JSObject* CreateInternal(JSContext* cx, HandleObject baseType, size_t length,
|
|
bool lengthDefined);
|
|
|
|
JSObject* GetBaseType(JSObject* obj);
|
|
size_t GetLength(JSObject* obj);
|
|
bool GetSafeLength(JSObject* obj, size_t* result);
|
|
UniquePtrFFIType BuildFFIType(JSContext* cx, JSObject* obj);
|
|
} // namespace ArrayType
|
|
|
|
namespace StructType {
|
|
bool DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj);
|
|
|
|
const FieldInfoHash* GetFieldInfo(JSObject* obj);
|
|
const FieldInfo* LookupField(JSContext* cx, JSObject* obj, JSFlatString* name);
|
|
JSObject* BuildFieldsArray(JSContext* cx, JSObject* obj);
|
|
UniquePtrFFIType BuildFFIType(JSContext* cx, JSObject* obj);
|
|
} // namespace StructType
|
|
|
|
namespace FunctionType {
|
|
JSObject* CreateInternal(JSContext* cx, HandleValue abi, HandleValue rtype,
|
|
const HandleValueArray& args);
|
|
|
|
JSObject* ConstructWithObject(JSContext* cx, JSObject* typeObj,
|
|
JSObject* refObj, PRFuncPtr fnptr, JSObject* result);
|
|
|
|
FunctionInfo* GetFunctionInfo(JSObject* obj);
|
|
void BuildSymbolName(JSString* name, JSObject* typeObj,
|
|
AutoCString& result);
|
|
} // namespace FunctionType
|
|
|
|
namespace CClosure {
|
|
JSObject* Create(JSContext* cx, HandleObject typeObj, HandleObject fnObj,
|
|
HandleObject thisObj, Value errVal, PRFuncPtr* fnptr);
|
|
} // namespace CClosure
|
|
|
|
namespace CData {
|
|
JSObject* Create(JSContext* cx, HandleObject typeObj, HandleObject refObj,
|
|
void* data, bool ownResult);
|
|
|
|
JSObject* GetCType(JSObject* dataObj);
|
|
void* GetData(JSObject* dataObj);
|
|
bool IsCData(JSObject* obj);
|
|
bool IsCData(HandleValue v);
|
|
bool IsCDataProto(JSObject* obj);
|
|
|
|
// Attached by JSAPI as the function 'ctypes.cast'
|
|
bool Cast(JSContext* cx, unsigned argc, Value* vp);
|
|
// Attached by JSAPI as the function 'ctypes.getRuntime'
|
|
bool GetRuntime(JSContext* cx, unsigned argc, Value* vp);
|
|
} // namespace CData
|
|
|
|
namespace Int64 {
|
|
bool IsInt64(JSObject* obj);
|
|
} // namespace Int64
|
|
|
|
namespace UInt64 {
|
|
bool IsUInt64(JSObject* obj);
|
|
} // namespace UInt64
|
|
|
|
} // namespace ctypes
|
|
} // namespace js
|
|
|
|
#endif /* ctypes_CTypes_h */
|