/* -*- 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 String; typedef Vector AutoString; typedef Vector CString; typedef Vector AutoCString; // Convenience functions to append, insert, and compare Strings. template void AppendString(mozilla::Vector& 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(mozilla::Vector& 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(mozilla::Vector& 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 void AppendString(mozilla::Vector& v, mozilla::Vector& w) { v.append(w.begin(), w.length()); } template void AppendString(mozilla::Vector& 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 void AppendString(mozilla::Vector& 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 void PrependString(mozilla::Vector& 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(mozilla::Vector& 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 extern size_t GetDeflatedUTF8StringLength(JSContext* maybecx, const CharT* chars, size_t charsLength); template 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 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 { typedef JSFlatString* Key; typedef Key Lookup; template 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; 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 mABI; // The CType of the value returned by the function. JS::Heap mReturnType; // A fixed array of known parameter types, excluding any variadic // parameters (if mIsVariadic). Vector, 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 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 closureObj; // CClosure object JS::Heap typeObj; // FunctionType describing the C function JS::Heap thisObj; // 'this' object to use for the JS function call JS::Heap 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> 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 */