From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- js/src/ctypes/CTypes.h | 599 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 599 insertions(+) create mode 100644 js/src/ctypes/CTypes.h (limited to 'js/src/ctypes/CTypes.h') 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 StringBuilder { + Vector 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 + [[nodiscard]] bool append(U&& u) { + return handle(v.append(u)); + } + + template + [[nodiscard]] bool append(const U* begin, const U* end) { + return handle(v.append(begin, end)); + } + + template + [[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&& 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 AutoString; +typedef StringBuilder AutoCString; + +typedef Vector AutoStringChars; +typedef Vector AutoCStringChars; + +// Convenience functions to append, insert, and compare Strings. +template +void AppendString(JSContext* cx, StringBuilder& 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 +void AppendChars(StringBuilder& 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 +void AppendUInt(StringBuilder& 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 +void AppendString(JSContext* cx, StringBuilder& v, + mozilla::Vector& w) { + if (!v.append(w.begin(), w.length())) { + return; + } +} + +template +void AppendString(JSContext* cx, StringBuilder& 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 +void AppendString(JSContext* cx, StringBuilder& 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 +void PrependString(JSContext* cx, StringBuilder& 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 +void PrependString(JSContext* cx, StringBuilder& 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 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, 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 mABI; + + // The CType of the value returned by the function. + HeapPtr mReturnType; + + // A fixed array of known parameter types, excluding any variadic + // parameters (if mIsVariadic). + GCVector, 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 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 closureObj; // CClosure object + HeapPtr typeObj; // FunctionType describing the C function + HeapPtr thisObj; // 'this' object to use for the JS function call + HeapPtr 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; + +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 */ -- cgit v1.2.3