summaryrefslogtreecommitdiffstats
path: root/js/src/ctypes/CTypes.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/ctypes/CTypes.h')
-rw-r--r--js/src/ctypes/CTypes.h599
1 files changed, 599 insertions, 0 deletions
diff --git a/js/src/ctypes/CTypes.h b/js/src/ctypes/CTypes.h
new file mode 100644
index 0000000000..665aed537e
--- /dev/null
+++ b/js/src/ctypes/CTypes.h
@@ -0,0 +1,599 @@
+/* -*- 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/Sprintf.h"
+#include "mozilla/Vector.h"
+
+#include "ffi.h"
+#include "prlink.h"
+
+#include "ctypes/typedefs.h"
+#include "gc/ZoneAllocator.h"
+#include "js/AllocPolicy.h"
+#include "js/GCHashTable.h"
+#include "js/UniquePtr.h"
+#include "js/Vector.h"
+#include "vm/JSObject.h"
+#include "vm/StringType.h"
+
+namespace JS {
+struct CTypesCallbacks;
+} // namespace JS
+
+namespace js {
+namespace ctypes {
+
+/*******************************************************************************
+** Utility classes
+*******************************************************************************/
+
+// CTypes builds a number of strings. StringBuilder allows repeated appending
+// with a single error check at the end. Only the Vector methods required for
+// building the string are exposed.
+
+template <class CharT, size_t N>
+class StringBuilder {
+ Vector<CharT, N, SystemAllocPolicy> v;
+
+ // Have any (OOM) errors been encountered while constructing this string?
+ bool errored{false};
+
+#ifdef DEBUG
+ // Have we finished building this string?
+ bool finished{false};
+
+ // Did we check for errors?
+ mutable bool checked{false};
+#endif
+
+ public:
+ explicit operator bool() const {
+#ifdef DEBUG
+ checked = true;
+#endif
+ return !errored;
+ }
+
+ // Handle the result of modifying the string, by remembering the persistent
+ // errored status.
+ bool handle(bool result) {
+ MOZ_ASSERT(!finished);
+ if (!result) {
+ errored = true;
+ }
+ return result;
+ }
+
+ bool resize(size_t n) { return handle(v.resize(n)); }
+
+ CharT& operator[](size_t index) { return v[index]; }
+ const CharT& operator[](size_t index) const { return v[index]; }
+ size_t length() const { return v.length(); }
+
+ template <typename U>
+ [[nodiscard]] bool append(U&& u) {
+ return handle(v.append(u));
+ }
+
+ template <typename U>
+ [[nodiscard]] bool append(const U* begin, const U* end) {
+ return handle(v.append(begin, end));
+ }
+
+ template <typename U>
+ [[nodiscard]] bool append(const U* begin, size_t len) {
+ return handle(v.append(begin, len));
+ }
+
+ CharT* begin() {
+ MOZ_ASSERT(!finished);
+ return v.begin();
+ }
+
+ // finish() produces the results of the string building, and is required as
+ // the last thing before the string contents are used. The StringBuilder must
+ // be checked for errors before calling this, however.
+ Vector<CharT, N, SystemAllocPolicy>&& finish() {
+ MOZ_ASSERT(!errored);
+ MOZ_ASSERT(!finished);
+ MOZ_ASSERT(checked);
+#ifdef DEBUG
+ finished = true;
+#endif
+ return std::move(v);
+ }
+};
+
+// Note that these strings do not have any inline storage, because we use move
+// constructors to pass the data around and inline storage would necessitate
+// copying.
+typedef StringBuilder<char16_t, 0> AutoString;
+typedef StringBuilder<char, 0> AutoCString;
+
+typedef Vector<char16_t, 0, SystemAllocPolicy> AutoStringChars;
+typedef Vector<char, 0, SystemAllocPolicy> AutoCStringChars;
+
+// Convenience functions to append, insert, and compare Strings.
+template <class T, size_t N, size_t ArrayLength>
+void AppendString(JSContext* cx, StringBuilder<T, N>& 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>
+void AppendChars(StringBuilder<T, N>& 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>
+void AppendUInt(StringBuilder<T, N>& v, unsigned n) {
+ char array[16];
+ size_t alen = SprintfLiteral(array, "%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(JSContext* cx, StringBuilder<T, N>& v,
+ mozilla::Vector<T, M, AP>& w) {
+ if (!v.append(w.begin(), w.length())) {
+ return;
+ }
+}
+
+template <size_t N>
+void AppendString(JSContext* cx, StringBuilder<char16_t, N>& v, JSString* str) {
+ MOZ_ASSERT(str);
+ JSLinearString* linear = str->ensureLinear(cx);
+ if (!linear) {
+ return;
+ }
+ JS::AutoCheckCannotGC nogc;
+ if (linear->hasLatin1Chars()) {
+ if (!v.append(linear->latin1Chars(nogc), linear->length())) {
+ return;
+ }
+ } else {
+ if (!v.append(linear->twoByteChars(nogc), linear->length())) {
+ return;
+ }
+ }
+}
+
+template <size_t N>
+void AppendString(JSContext* cx, StringBuilder<char, N>& 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(cx);
+ 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, size_t ArrayLength>
+void PrependString(JSContext* cx, StringBuilder<T, N>& 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>
+void PrependString(JSContext* cx, StringBuilder<char16_t, N>& 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(cx);
+ 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.
+ CopyChars(v.begin(), *linear);
+}
+
+[[nodiscard]] bool ReportErrorIfUnpairedSurrogatePresent(JSContext* cx,
+ JSLinearString* str);
+
+[[nodiscard]] JSObject* GetThisObject(JSContext* cx, const CallArgs& args,
+ const char* msg);
+
+/*******************************************************************************
+** Function and struct API definitions
+*******************************************************************************/
+
+// 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_THISCALL,
+ 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 {
+ HeapPtr<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) { TraceEdge(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 {
+ using Key = JSLinearString*;
+ using Lookup = Key;
+
+ static HashNumber hash(const Lookup& l) { return js::HashStringChars(l); }
+
+ static bool match(const Key& k, const Lookup& l) {
+ return js::EqualStrings(k, l);
+ }
+};
+
+using FieldInfoHash = GCHashMap<js::HeapPtr<JSLinearString*>, FieldInfo,
+ FieldHashPolicy, CellAllocPolicy>;
+
+// Descriptor of ABI, return type, argument types, and variadicity for a
+// FunctionType.
+struct FunctionInfo {
+ explicit FunctionInfo(JS::Zone* zone) : mArgTypes(zone), mFFITypes(zone) {}
+
+ // 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.
+ HeapPtr<JSObject*> mABI;
+
+ // The CType of the value returned by the function.
+ HeapPtr<JSObject*> mReturnType;
+
+ // A fixed array of known parameter types, excluding any variadic
+ // parameters (if mIsVariadic).
+ GCVector<HeapPtr<JSObject*>, 0, CellAllocPolicy> 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, CellAllocPolicy> 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 {
+ JSContext* cx;
+ HeapPtr<JSObject*> closureObj; // CClosure object
+ HeapPtr<JSObject*> typeObj; // FunctionType describing the C function
+ HeapPtr<JSObject*> thisObj; // 'this' object to use for the JS function call
+ HeapPtr<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(JSContext* context)
+ : cx(context), errResult(nullptr), closure(nullptr) {}
+
+ ~ClosureInfo() {
+ if (closure) {
+ ffi_closure_free(closure);
+ }
+ js_free(errResult);
+ }
+};
+
+bool IsCTypesGlobal(HandleValue v);
+bool IsCTypesGlobal(JSObject* obj);
+
+const JS::CTypesCallbacks* GetCallbacks(JSObject* obj);
+
+/*******************************************************************************
+** JSClass reserved slot definitions
+*******************************************************************************/
+
+enum CTypesGlobalSlot {
+ SLOT_CALLBACKS = 0, // pointer to JS::CTypesCallbacks 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 {
+ // PrivateValue storing CDataFinalizer::Private* pointer or UndefinedValue.
+ SLOT_DATAFINALIZER_PRIVATE = 0,
+ // The type of the value (a CType JSObject).
+ // We hold it to permit ImplicitConvert and ToSource.
+ SLOT_DATAFINALIZER_VALTYPE = 1,
+ // The type of the function used at finalization (a CType JSObject).
+ // We hold it to permit |ToSource|.
+ SLOT_DATAFINALIZER_CODETYPE = 2,
+ 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, HandleValue size,
+ HandleValue align, ffi_type* ffiType);
+
+JSObject* DefineBuiltin(JSContext* cx, HandleObject ctypesObj,
+ const char* propName, JSObject* typeProto,
+ JSObject* dataProto, const char* name, TypeCode type,
+ HandleValue size, HandleValue 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);
+[[nodiscard]] 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 JS::CTypesCallbacks* GetCallbacksFromType(JSObject* obj);
+} // namespace CType
+
+namespace PointerType {
+JSObject* CreateInternal(JSContext* cx, HandleObject baseType);
+
+JSObject* GetBaseType(JSObject* obj);
+} // namespace PointerType
+
+using UniquePtrFFIType = UniquePtr<ffi_type>;
+
+namespace ArrayType {
+JSObject* CreateInternal(JSContext* cx, HandleObject baseType, size_t length,
+ bool lengthDefined);
+
+JSObject* GetBaseType(JSObject* obj);
+size_t GetLength(JSObject* obj);
+[[nodiscard]] bool GetSafeLength(JSObject* obj, size_t* result);
+UniquePtrFFIType BuildFFIType(JSContext* cx, JSObject* obj);
+} // namespace ArrayType
+
+namespace StructType {
+[[nodiscard]] bool DefineInternal(JSContext* cx, JSObject* typeObj,
+ JSObject* fieldsObj);
+
+const FieldInfoHash* GetFieldInfo(JSObject* obj);
+const FieldInfo* LookupField(JSContext* cx, JSObject* obj,
+ JSLinearString* 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(JSContext* cx, JSString* name, JSObject* typeObj,
+ AutoCString& result);
+} // namespace FunctionType
+
+namespace CClosure {
+JSObject* Create(JSContext* cx, HandleObject typeObj, HandleObject fnObj,
+ HandleObject thisObj, HandleValue 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 IsCDataMaybeUnwrap(MutableHandleObject obj);
+bool IsCData(HandleValue v);
+bool IsCDataProto(JSObject* obj);
+
+// Attached by JSAPI as the function 'ctypes.cast'
+[[nodiscard]] bool Cast(JSContext* cx, unsigned argc, Value* vp);
+// Attached by JSAPI as the function 'ctypes.getRuntime'
+[[nodiscard]] 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 */