michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef ctypes_CTypes_h michael@0: #define ctypes_CTypes_h michael@0: michael@0: #include "ffi.h" michael@0: #include "jsalloc.h" michael@0: #include "prlink.h" michael@0: michael@0: #include "js/HashTable.h" michael@0: #include "js/Vector.h" michael@0: #include "vm/String.h" michael@0: michael@0: namespace js { michael@0: namespace ctypes { michael@0: michael@0: /******************************************************************************* michael@0: ** Utility classes michael@0: *******************************************************************************/ michael@0: michael@0: // Class that takes ownership of a pointer T*, and calls cx->delete_() or michael@0: // cx->array_delete() upon destruction. michael@0: template michael@0: class AutoPtr { michael@0: private: michael@0: typedef AutoPtr self_type; michael@0: michael@0: public: michael@0: AutoPtr() : mPtr(nullptr) { } michael@0: explicit AutoPtr(T* ptr) : mPtr(ptr) { } michael@0: ~AutoPtr() { js_delete(mPtr); } michael@0: michael@0: T* operator->() { return mPtr; } michael@0: bool operator!() { return mPtr == nullptr; } michael@0: T& operator[](size_t i) { return *(mPtr + i); } michael@0: // Note: we cannot safely provide an 'operator T*()', since this would allow michael@0: // the compiler to perform implicit conversion from one AutoPtr to another michael@0: // via the constructor AutoPtr(T*). michael@0: michael@0: T* get() { return mPtr; } michael@0: void set(T* other) { JS_ASSERT(mPtr == nullptr); mPtr = other; } michael@0: T* forget() { T* result = mPtr; mPtr = nullptr; return result; } michael@0: michael@0: self_type& operator=(T* rhs) { mPtr = rhs; return *this; } michael@0: michael@0: private: michael@0: // Do not allow copy construction or assignment from another AutoPtr. michael@0: AutoPtr(AutoPtr&); michael@0: self_type& operator=(AutoPtr& rhs); michael@0: michael@0: T* mPtr; michael@0: }; michael@0: michael@0: // Container class for Vector, using SystemAllocPolicy. michael@0: template michael@0: class Array : public Vector michael@0: { michael@0: static_assert(!mozilla::IsSame::value, michael@0: "use JS::AutoValueVector instead"); michael@0: }; michael@0: michael@0: // String and AutoString classes, based on Vector. michael@0: typedef Vector String; michael@0: typedef Vector AutoString; michael@0: typedef Vector CString; michael@0: typedef Vector AutoCString; michael@0: michael@0: // Convenience functions to append, insert, and compare Strings. michael@0: template michael@0: void michael@0: AppendString(Vector &v, const char (&array)[ArrayLength]) michael@0: { michael@0: // Don't include the trailing '\0'. michael@0: size_t alen = ArrayLength - 1; michael@0: size_t vlen = v.length(); michael@0: if (!v.resize(vlen + alen)) michael@0: return; michael@0: michael@0: for (size_t i = 0; i < alen; ++i) michael@0: v[i + vlen] = array[i]; michael@0: } michael@0: michael@0: template michael@0: void michael@0: AppendString(Vector &v, Vector &w) michael@0: { michael@0: v.append(w.begin(), w.length()); michael@0: } michael@0: michael@0: template michael@0: void michael@0: AppendString(Vector &v, JSString* str) michael@0: { michael@0: JS_ASSERT(str); michael@0: const jschar *chars = str->getChars(nullptr); michael@0: if (!chars) michael@0: return; michael@0: v.append(chars, str->length()); michael@0: } michael@0: michael@0: template michael@0: void michael@0: AppendString(Vector &v, JSString* str) michael@0: { michael@0: JS_ASSERT(str); michael@0: size_t vlen = v.length(); michael@0: size_t alen = str->length(); michael@0: if (!v.resize(vlen + alen)) michael@0: return; michael@0: michael@0: const jschar *chars = str->getChars(nullptr); michael@0: if (!chars) michael@0: return; michael@0: michael@0: for (size_t i = 0; i < alen; ++i) michael@0: v[i + vlen] = char(chars[i]); michael@0: } michael@0: michael@0: template michael@0: void michael@0: PrependString(Vector &v, const char (&array)[ArrayLength]) michael@0: { michael@0: // Don't include the trailing '\0'. michael@0: size_t alen = ArrayLength - 1; michael@0: size_t vlen = v.length(); michael@0: if (!v.resize(vlen + alen)) michael@0: return; michael@0: michael@0: // Move vector data forward. This is safe since we've already resized. michael@0: memmove(v.begin() + alen, v.begin(), vlen * sizeof(T)); michael@0: michael@0: // Copy data to insert. michael@0: for (size_t i = 0; i < alen; ++i) michael@0: v[i] = array[i]; michael@0: } michael@0: michael@0: template michael@0: void michael@0: PrependString(Vector &v, JSString* str) michael@0: { michael@0: JS_ASSERT(str); michael@0: size_t vlen = v.length(); michael@0: size_t alen = str->length(); michael@0: if (!v.resize(vlen + alen)) michael@0: return; michael@0: michael@0: const jschar *chars = str->getChars(nullptr); michael@0: if (!chars) michael@0: return; michael@0: michael@0: // Move vector data forward. This is safe since we've already resized. michael@0: memmove(v.begin() + alen, v.begin(), vlen * sizeof(jschar)); michael@0: michael@0: // Copy data to insert. michael@0: memcpy(v.begin(), chars, alen * sizeof(jschar)); michael@0: } michael@0: michael@0: extern size_t michael@0: GetDeflatedUTF8StringLength(JSContext *maybecx, const jschar *chars, michael@0: size_t charsLength); michael@0: michael@0: bool michael@0: DeflateStringToUTF8Buffer(JSContext *maybecx, const jschar *src, size_t srclen, michael@0: char *dst, size_t *dstlenp); michael@0: michael@0: michael@0: /******************************************************************************* michael@0: ** Function and struct API definitions michael@0: *******************************************************************************/ michael@0: michael@0: MOZ_ALWAYS_INLINE void michael@0: ASSERT_OK(bool ok) michael@0: { michael@0: JS_ASSERT(ok); michael@0: } michael@0: michael@0: // for JS error reporting michael@0: enum ErrorNum { michael@0: #define MSG_DEF(name, number, count, exception, format) \ michael@0: name = number, michael@0: #include "ctypes/ctypes.msg" michael@0: #undef MSG_DEF michael@0: CTYPESERR_LIMIT michael@0: }; michael@0: michael@0: /** michael@0: * ABI constants that specify the calling convention to use. michael@0: * ctypes.default_abi corresponds to the cdecl convention, and in almost all michael@0: * cases is the correct choice. ctypes.stdcall_abi is provided for calling michael@0: * stdcall functions on Win32, and implies stdcall symbol name decoration; michael@0: * ctypes.winapi_abi is just stdcall but without decoration. michael@0: */ michael@0: enum ABICode { michael@0: ABI_DEFAULT, michael@0: ABI_STDCALL, michael@0: ABI_WINAPI, michael@0: INVALID_ABI michael@0: }; michael@0: michael@0: enum TypeCode { michael@0: TYPE_void_t, michael@0: #define DEFINE_TYPE(name, type, ffiType) TYPE_##name, michael@0: #include "ctypes/typedefs.h" michael@0: TYPE_pointer, michael@0: TYPE_function, michael@0: TYPE_array, michael@0: TYPE_struct michael@0: }; michael@0: michael@0: // Descriptor of one field in a StructType. The name of the field is stored michael@0: // as the key to the hash entry. michael@0: struct FieldInfo michael@0: { michael@0: JS::Heap mType; // CType of the field michael@0: size_t mIndex; // index of the field in the struct (first is 0) michael@0: size_t mOffset; // offset of the field in the struct, in bytes michael@0: }; michael@0: michael@0: struct UnbarrieredFieldInfo michael@0: { michael@0: JSObject* mType; // CType of the field michael@0: size_t mIndex; // index of the field in the struct (first is 0) michael@0: size_t mOffset; // offset of the field in the struct, in bytes michael@0: }; michael@0: static_assert(sizeof(UnbarrieredFieldInfo) == sizeof(FieldInfo), michael@0: "UnbarrieredFieldInfo should be the same as FieldInfo but with unbarriered mType"); michael@0: michael@0: // Hash policy for FieldInfos. michael@0: struct FieldHashPolicy : DefaultHasher michael@0: { michael@0: typedef JSFlatString* Key; michael@0: typedef Key Lookup; michael@0: michael@0: static uint32_t hash(const Lookup &l) { michael@0: const jschar* s = l->chars(); michael@0: size_t n = l->length(); michael@0: uint32_t hash = 0; michael@0: for (; n > 0; s++, n--) michael@0: hash = hash * 33 + *s; michael@0: return hash; michael@0: } michael@0: michael@0: static bool match(const Key &k, const Lookup &l) { michael@0: if (k == l) michael@0: return true; michael@0: michael@0: if (k->length() != l->length()) michael@0: return false; michael@0: michael@0: return memcmp(k->chars(), l->chars(), k->length() * sizeof(jschar)) == 0; michael@0: } michael@0: }; michael@0: michael@0: typedef HashMap FieldInfoHash; michael@0: michael@0: // Descriptor of ABI, return type, argument types, and variadicity for a michael@0: // FunctionType. michael@0: struct FunctionInfo michael@0: { michael@0: // Initialized in NewFunctionInfo when !mIsVariadic, but only later, in michael@0: // FunctionType::Call, when mIsVariadic. Not always consistent with michael@0: // mFFITypes, due to lazy initialization when mIsVariadic. michael@0: ffi_cif mCIF; michael@0: michael@0: // Calling convention of the function. Convert to ffi_abi using GetABI michael@0: // and OBJECT_TO_JSVAL. Stored as a JSObject* for ease of tracing. michael@0: JS::Heap mABI; michael@0: michael@0: // The CType of the value returned by the function. michael@0: JS::Heap mReturnType; michael@0: michael@0: // A fixed array of known parameter types, excluding any variadic michael@0: // parameters (if mIsVariadic). michael@0: Array > mArgTypes; michael@0: michael@0: // A variable array of ffi_type*s corresponding to both known parameter michael@0: // types and dynamic (variadic) parameter types. Longer than mArgTypes michael@0: // only if mIsVariadic. michael@0: Array mFFITypes; michael@0: michael@0: // Flag indicating whether the function behaves like a C function with michael@0: // ... as the final formal parameter. michael@0: bool mIsVariadic; michael@0: }; michael@0: michael@0: // Parameters necessary for invoking a JS function from a C closure. michael@0: struct ClosureInfo michael@0: { michael@0: JSContext* cx; // JSContext to use michael@0: JSRuntime* rt; // Used in the destructor, where cx might have already michael@0: // been GCed. michael@0: JS::Heap closureObj; // CClosure object michael@0: JS::Heap typeObj; // FunctionType describing the C function michael@0: JS::Heap thisObj; // 'this' object to use for the JS function call michael@0: JS::Heap jsfnObj; // JS function michael@0: void* errResult; // Result that will be returned if the closure throws michael@0: ffi_closure* closure; // The C closure itself michael@0: michael@0: // Anything conditionally freed in the destructor should be initialized to michael@0: // nullptr here. michael@0: ClosureInfo(JSRuntime* runtime) michael@0: : rt(runtime) michael@0: , errResult(nullptr) michael@0: , closure(nullptr) michael@0: {} michael@0: michael@0: ~ClosureInfo() { michael@0: if (closure) michael@0: ffi_closure_free(closure); michael@0: js_free(errResult); michael@0: } michael@0: }; michael@0: michael@0: bool IsCTypesGlobal(HandleValue v); michael@0: bool IsCTypesGlobal(JSObject* obj); michael@0: michael@0: JSCTypesCallbacks* GetCallbacks(JSObject* obj); michael@0: michael@0: /******************************************************************************* michael@0: ** JSClass reserved slot definitions michael@0: *******************************************************************************/ michael@0: michael@0: enum CTypesGlobalSlot { michael@0: SLOT_CALLBACKS = 0, // pointer to JSCTypesCallbacks struct michael@0: SLOT_ERRNO = 1, // jsval for latest |errno| michael@0: SLOT_LASTERROR = 2, // jsval for latest |GetLastError|, used only with Windows michael@0: CTYPESGLOBAL_SLOTS michael@0: }; michael@0: michael@0: enum CABISlot { michael@0: SLOT_ABICODE = 0, // ABICode of the CABI object michael@0: CABI_SLOTS michael@0: }; michael@0: michael@0: enum CTypeProtoSlot { michael@0: SLOT_POINTERPROTO = 0, // ctypes.PointerType.prototype object michael@0: SLOT_ARRAYPROTO = 1, // ctypes.ArrayType.prototype object michael@0: SLOT_STRUCTPROTO = 2, // ctypes.StructType.prototype object michael@0: SLOT_FUNCTIONPROTO = 3, // ctypes.FunctionType.prototype object michael@0: SLOT_CDATAPROTO = 4, // ctypes.CData.prototype object michael@0: SLOT_POINTERDATAPROTO = 5, // common ancestor of all CData objects of PointerType michael@0: SLOT_ARRAYDATAPROTO = 6, // common ancestor of all CData objects of ArrayType michael@0: SLOT_STRUCTDATAPROTO = 7, // common ancestor of all CData objects of StructType michael@0: SLOT_FUNCTIONDATAPROTO = 8, // common ancestor of all CData objects of FunctionType michael@0: SLOT_INT64PROTO = 9, // ctypes.Int64.prototype object michael@0: SLOT_UINT64PROTO = 10, // ctypes.UInt64.prototype object michael@0: SLOT_CTYPES = 11, // ctypes object michael@0: SLOT_OURDATAPROTO = 12, // the data prototype corresponding to this object michael@0: CTYPEPROTO_SLOTS michael@0: }; michael@0: michael@0: enum CTypeSlot { michael@0: SLOT_PROTO = 0, // 'prototype' property of the CType object michael@0: SLOT_TYPECODE = 1, // TypeCode of the CType object michael@0: SLOT_FFITYPE = 2, // ffi_type representing the type michael@0: SLOT_NAME = 3, // name of the type michael@0: SLOT_SIZE = 4, // size of the type, in bytes michael@0: SLOT_ALIGN = 5, // alignment of the type, in bytes michael@0: SLOT_PTR = 6, // cached PointerType object for type.ptr michael@0: // Note that some of the slots below can overlap, since they're for michael@0: // mutually exclusive types. michael@0: SLOT_TARGET_T = 7, // (PointerTypes only) 'targetType' property michael@0: SLOT_ELEMENT_T = 7, // (ArrayTypes only) 'elementType' property michael@0: SLOT_LENGTH = 8, // (ArrayTypes only) 'length' property michael@0: SLOT_FIELDS = 7, // (StructTypes only) 'fields' property michael@0: SLOT_FIELDINFO = 8, // (StructTypes only) FieldInfoHash table michael@0: SLOT_FNINFO = 7, // (FunctionTypes only) FunctionInfo struct michael@0: SLOT_ARGS_T = 8, // (FunctionTypes only) 'argTypes' property (cached) michael@0: CTYPE_SLOTS michael@0: }; michael@0: michael@0: enum CDataSlot { michael@0: SLOT_CTYPE = 0, // CType object representing the underlying type michael@0: SLOT_REFERENT = 1, // JSObject this object must keep alive, if any michael@0: SLOT_DATA = 2, // pointer to a buffer containing the binary data michael@0: SLOT_OWNS = 3, // JSVAL_TRUE if this CData owns its own buffer michael@0: CDATA_SLOTS michael@0: }; michael@0: michael@0: enum CClosureSlot { michael@0: SLOT_CLOSUREINFO = 0, // ClosureInfo struct michael@0: CCLOSURE_SLOTS michael@0: }; michael@0: michael@0: enum CDataFinalizerSlot { michael@0: // The type of the value (a CType JSObject). michael@0: // We hold it to permit ImplicitConvert and ToSource. michael@0: SLOT_DATAFINALIZER_VALTYPE = 0, michael@0: // The type of the function used at finalization (a CType JSObject). michael@0: // We hold it to permit |ToSource|. michael@0: SLOT_DATAFINALIZER_CODETYPE = 1, michael@0: CDATAFINALIZER_SLOTS michael@0: }; michael@0: michael@0: enum TypeCtorSlot { michael@0: SLOT_FN_CTORPROTO = 0 // ctypes.{Pointer,Array,Struct}Type.prototype michael@0: // JSFunction objects always get exactly two slots. michael@0: }; michael@0: michael@0: enum Int64Slot { michael@0: SLOT_INT64 = 0, // pointer to a 64-bit buffer containing the integer michael@0: INT64_SLOTS michael@0: }; michael@0: michael@0: enum Int64FunctionSlot { michael@0: SLOT_FN_INT64PROTO = 0 // ctypes.{Int64,UInt64}.prototype object michael@0: // JSFunction objects always get exactly two slots. michael@0: }; michael@0: michael@0: /******************************************************************************* michael@0: ** Object API definitions michael@0: *******************************************************************************/ michael@0: michael@0: namespace CType { michael@0: JSObject* Create(JSContext* cx, HandleObject typeProto, HandleObject dataProto, michael@0: TypeCode type, JSString* name, jsval size, jsval align, ffi_type* ffiType); michael@0: michael@0: JSObject* DefineBuiltin(JSContext* cx, JSObject* parent, const char* propName, michael@0: JSObject* typeProto, JSObject* dataProto, const char* name, TypeCode type, michael@0: jsval size, jsval align, ffi_type* ffiType); michael@0: michael@0: bool IsCType(JSObject* obj); michael@0: bool IsCTypeProto(JSObject* obj); michael@0: TypeCode GetTypeCode(JSObject* typeObj); michael@0: bool TypesEqual(JSObject* t1, JSObject* t2); michael@0: size_t GetSize(JSObject* obj); michael@0: bool GetSafeSize(JSObject* obj, size_t* result); michael@0: bool IsSizeDefined(JSObject* obj); michael@0: size_t GetAlignment(JSObject* obj); michael@0: ffi_type* GetFFIType(JSContext* cx, JSObject* obj); michael@0: JSString* GetName(JSContext* cx, HandleObject obj); michael@0: JSObject* GetProtoFromCtor(JSObject* obj, CTypeProtoSlot slot); michael@0: JSObject* GetProtoFromType(JSContext* cx, JSObject* obj, CTypeProtoSlot slot); michael@0: JSCTypesCallbacks* GetCallbacksFromType(JSObject* obj); michael@0: } michael@0: michael@0: namespace PointerType { michael@0: JSObject* CreateInternal(JSContext* cx, HandleObject baseType); michael@0: michael@0: JSObject* GetBaseType(JSObject* obj); michael@0: } michael@0: michael@0: namespace ArrayType { michael@0: JSObject* CreateInternal(JSContext* cx, HandleObject baseType, size_t length, michael@0: bool lengthDefined); michael@0: michael@0: JSObject* GetBaseType(JSObject* obj); michael@0: size_t GetLength(JSObject* obj); michael@0: bool GetSafeLength(JSObject* obj, size_t* result); michael@0: ffi_type* BuildFFIType(JSContext* cx, JSObject* obj); michael@0: } michael@0: michael@0: namespace StructType { michael@0: bool DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj); michael@0: michael@0: const FieldInfoHash* GetFieldInfo(JSObject* obj); michael@0: const FieldInfo* LookupField(JSContext* cx, JSObject* obj, JSFlatString *name); michael@0: JSObject* BuildFieldsArray(JSContext* cx, JSObject* obj); michael@0: ffi_type* BuildFFIType(JSContext* cx, JSObject* obj); michael@0: } michael@0: michael@0: namespace FunctionType { michael@0: JSObject* CreateInternal(JSContext* cx, jsval abi, jsval rtype, michael@0: jsval* argtypes, unsigned arglen); michael@0: michael@0: JSObject* ConstructWithObject(JSContext* cx, JSObject* typeObj, michael@0: JSObject* refObj, PRFuncPtr fnptr, JSObject* result); michael@0: michael@0: FunctionInfo* GetFunctionInfo(JSObject* obj); michael@0: void BuildSymbolName(JSString* name, JSObject* typeObj, michael@0: AutoCString& result); michael@0: } michael@0: michael@0: namespace CClosure { michael@0: JSObject* Create(JSContext* cx, HandleObject typeObj, HandleObject fnObj, michael@0: HandleObject thisObj, jsval errVal, PRFuncPtr* fnptr); michael@0: } michael@0: michael@0: namespace CData { michael@0: JSObject* Create(JSContext* cx, HandleObject typeObj, HandleObject refObj, michael@0: void* data, bool ownResult); michael@0: michael@0: JSObject* GetCType(JSObject* dataObj); michael@0: void* GetData(JSObject* dataObj); michael@0: bool IsCData(JSObject* obj); michael@0: bool IsCData(HandleValue v); michael@0: bool IsCDataProto(JSObject* obj); michael@0: michael@0: // Attached by JSAPI as the function 'ctypes.cast' michael@0: bool Cast(JSContext* cx, unsigned argc, jsval* vp); michael@0: // Attached by JSAPI as the function 'ctypes.getRuntime' michael@0: bool GetRuntime(JSContext* cx, unsigned argc, jsval* vp); michael@0: } michael@0: michael@0: namespace Int64 { michael@0: bool IsInt64(JSObject* obj); michael@0: } michael@0: michael@0: namespace UInt64 { michael@0: bool IsUInt64(JSObject* obj); michael@0: } michael@0: michael@0: } michael@0: } michael@0: michael@0: #endif /* ctypes_CTypes_h */