зеркало из https://github.com/mozilla/gecko-dev.git
Bug 554790 - Implement variadic ctypes functions. r=dwitte
This commit is contained in:
Родитель
53b4755d48
Коммит
53f422a393
|
@ -146,6 +146,8 @@ namespace FunctionType {
|
|||
static JSBool ReturnTypeGetter(JSContext* cx, JSObject* obj, jsval idval,
|
||||
jsval* vp);
|
||||
static JSBool ABIGetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp);
|
||||
static JSBool IsVariadicGetter(JSContext* cx, JSObject* obj, jsval idval,
|
||||
jsval* vp);
|
||||
}
|
||||
|
||||
namespace CClosure {
|
||||
|
@ -366,6 +368,7 @@ static JSPropertySpec sFunctionProps[] = {
|
|||
{ "argTypes", 0, CTYPESPROP_FLAGS, FunctionType::ArgTypesGetter, NULL },
|
||||
{ "returnType", 0, CTYPESPROP_FLAGS, FunctionType::ReturnTypeGetter, NULL },
|
||||
{ "abi", 0, CTYPESPROP_FLAGS, FunctionType::ABIGetter, NULL },
|
||||
{ "isVariadic", 0, CTYPESPROP_FLAGS, FunctionType::IsVariadicGetter, NULL },
|
||||
{ 0, 0, 0, NULL, NULL }
|
||||
};
|
||||
|
||||
|
@ -2017,9 +2020,12 @@ BuildTypeName(JSContext* cx, JSObject* typeObj)
|
|||
for (PRUint32 i = 0; i < fninfo->mArgTypes.Length(); ++i) {
|
||||
JSString* argName = CType::GetName(cx, fninfo->mArgTypes[i]);
|
||||
result.Append(GetString(argName));
|
||||
if (i != fninfo->mArgTypes.Length() - 1)
|
||||
if (i != fninfo->mArgTypes.Length() - 1 ||
|
||||
fninfo->mIsVariadic)
|
||||
result.Append(NS_LITERAL_STRING(", "));
|
||||
}
|
||||
if (fninfo->mIsVariadic)
|
||||
result.Append(NS_LITERAL_STRING("..."));
|
||||
result.Append(')');
|
||||
|
||||
// Set 'currentType' to the return type, and let the loop process it.
|
||||
|
@ -2110,9 +2116,12 @@ BuildTypeSource(JSContext* cx, JSObject* typeObj, bool makeShort)
|
|||
result.Append(NS_LITERAL_STRING(", ["));
|
||||
for (PRUint32 i = 0; i < fninfo->mArgTypes.Length(); ++i) {
|
||||
result.Append(BuildTypeSource(cx, fninfo->mArgTypes[i], true));
|
||||
if (i != fninfo->mArgTypes.Length() - 1)
|
||||
if (i != fninfo->mArgTypes.Length() - 1 ||
|
||||
fninfo->mIsVariadic)
|
||||
result.Append(NS_LITERAL_STRING(", "));
|
||||
}
|
||||
if (fninfo->mIsVariadic)
|
||||
result.Append(NS_LITERAL_STRING("\"...\""));
|
||||
result.Append(']');
|
||||
}
|
||||
|
||||
|
@ -2660,6 +2669,9 @@ CType::TypesEqual(JSContext* cx, JSObject* t1, JSObject* t2)
|
|||
if (f1->mArgTypes.Length() != f2->mArgTypes.Length())
|
||||
return false;
|
||||
|
||||
if (f1->mIsVariadic != f2->mIsVariadic)
|
||||
return false;
|
||||
|
||||
for (PRUint32 i = 0; i < f1->mArgTypes.Length(); ++i) {
|
||||
if (!TypesEqual(cx, f1->mArgTypes[i], f2->mArgTypes[i]))
|
||||
return false;
|
||||
|
@ -4189,7 +4201,7 @@ struct AutoValue
|
|||
};
|
||||
|
||||
static bool
|
||||
GetABI(JSContext* cx, jsval abiType, ffi_abi& result)
|
||||
GetABI(JSContext* cx, jsval abiType, ffi_abi* result)
|
||||
{
|
||||
if (JSVAL_IS_PRIMITIVE(abiType))
|
||||
return false;
|
||||
|
@ -4201,11 +4213,11 @@ GetABI(JSContext* cx, jsval abiType, ffi_abi& result)
|
|||
// C calling convention (cdecl) on each platform.
|
||||
switch (abi) {
|
||||
case ABI_DEFAULT:
|
||||
result = FFI_DEFAULT_ABI;
|
||||
*result = FFI_DEFAULT_ABI;
|
||||
return true;
|
||||
case ABI_STDCALL:
|
||||
#if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
|
||||
result = FFI_STDCALL;
|
||||
*result = FFI_STDCALL;
|
||||
return true;
|
||||
#endif
|
||||
case INVALID_ABI:
|
||||
|
@ -4270,6 +4282,52 @@ PrepareReturnType(JSContext* cx, jsval type)
|
|||
return result;
|
||||
}
|
||||
|
||||
static JS_ALWAYS_INLINE bool
|
||||
IsEllipsis(jsval v)
|
||||
{
|
||||
if (!JSVAL_IS_STRING(v))
|
||||
return false;
|
||||
JSString* str = JSVAL_TO_STRING(v);
|
||||
if (JS_GetStringLength(str) != 3)
|
||||
return false;
|
||||
jschar* chars = JS_GetStringChars(str), dot('.');
|
||||
return (chars[0] == dot &&
|
||||
chars[1] == dot &&
|
||||
chars[2] == dot);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
PrepareCIF(JSContext* cx,
|
||||
FunctionInfo* fninfo)
|
||||
{
|
||||
ffi_abi abi;
|
||||
if (!GetABI(cx, OBJECT_TO_JSVAL(fninfo->mABI), &abi)) {
|
||||
JS_ReportError(cx, "Invalid ABI specification");
|
||||
return false;
|
||||
}
|
||||
|
||||
ffi_status status =
|
||||
ffi_prep_cif(&fninfo->mCIF,
|
||||
abi,
|
||||
fninfo->mFFITypes.Length(),
|
||||
CType::GetFFIType(cx, fninfo->mReturnType),
|
||||
fninfo->mFFITypes.Elements());
|
||||
|
||||
switch (status) {
|
||||
case FFI_OK:
|
||||
return true;
|
||||
case FFI_BAD_ABI:
|
||||
JS_ReportError(cx, "Invalid ABI specification");
|
||||
return false;
|
||||
case FFI_BAD_TYPEDEF:
|
||||
JS_ReportError(cx, "Invalid type specification");
|
||||
return false;
|
||||
default:
|
||||
JS_ReportError(cx, "Unknown libffi error");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static FunctionInfo*
|
||||
NewFunctionInfo(JSContext* cx,
|
||||
jsval abiType,
|
||||
|
@ -4283,19 +4341,12 @@ NewFunctionInfo(JSContext* cx,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
// determine the ABI
|
||||
ffi_abi abi;
|
||||
if (!GetABI(cx, abiType, abi)) {
|
||||
JS_ReportError(cx, "Invalid ABI specification");
|
||||
return NULL;
|
||||
}
|
||||
fninfo->mABI = JSVAL_TO_OBJECT(abiType);
|
||||
|
||||
// prepare the result type
|
||||
fninfo->mReturnType = PrepareReturnType(cx, returnType);
|
||||
if (!fninfo->mReturnType)
|
||||
return NULL;
|
||||
ffi_type* rtype = CType::GetFFIType(cx, fninfo->mReturnType);
|
||||
|
||||
// prepare the argument types
|
||||
if (!fninfo->mArgTypes.SetCapacity(argLength) ||
|
||||
|
@ -4304,7 +4355,29 @@ NewFunctionInfo(JSContext* cx,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
fninfo->mIsVariadic = false;
|
||||
|
||||
for (PRUint32 i = 0; i < argLength; ++i) {
|
||||
if (IsEllipsis(argTypes[i])) {
|
||||
fninfo->mIsVariadic = true;
|
||||
if (i < 1) {
|
||||
JS_ReportError(cx, "\"...\" may not be the first and only parameter "
|
||||
"type of a variadic function declaration");
|
||||
return NULL;
|
||||
}
|
||||
if (i < argLength - 1) {
|
||||
JS_ReportError(cx, "\"...\" must be the last parameter type of a "
|
||||
"variadic function declaration");
|
||||
return NULL;
|
||||
}
|
||||
if (GetABICode(cx, fninfo->mABI) != ABI_DEFAULT) {
|
||||
JS_ReportError(cx, "Variadic functions must use the __cdecl calling "
|
||||
"convention");
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
JSObject* argType = PrepareType(cx, argTypes[i]);
|
||||
if (!argType)
|
||||
return NULL;
|
||||
|
@ -4313,21 +4386,14 @@ NewFunctionInfo(JSContext* cx,
|
|||
fninfo->mFFITypes.AppendElement(CType::GetFFIType(cx, argType));
|
||||
}
|
||||
|
||||
ffi_status status = ffi_prep_cif(&fninfo->mCIF, abi,
|
||||
fninfo->mFFITypes.Length(), rtype, fninfo->mFFITypes.Elements());
|
||||
switch (status) {
|
||||
case FFI_OK:
|
||||
if (fninfo->mIsVariadic)
|
||||
// wait to PrepareCIF until function is called
|
||||
return fninfo.forget();
|
||||
case FFI_BAD_ABI:
|
||||
JS_ReportError(cx, "Invalid ABI specification");
|
||||
|
||||
if (!PrepareCIF(cx, fninfo))
|
||||
return NULL;
|
||||
case FFI_BAD_TYPEDEF:
|
||||
JS_ReportError(cx, "Invalid type specification");
|
||||
return NULL;
|
||||
default:
|
||||
JS_ReportError(cx, "Unknown libffi error");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return fninfo.forget();
|
||||
}
|
||||
|
||||
JSBool
|
||||
|
@ -4453,6 +4519,11 @@ FunctionType::ConstructData(JSContext* cx,
|
|||
PRFuncPtr* data = static_cast<PRFuncPtr*>(CData::GetData(cx, result));
|
||||
|
||||
if (JSVAL_IS_OBJECT(arg) && JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(arg))) {
|
||||
FunctionInfo* fninfo = FunctionType::GetFunctionInfo(cx, obj);
|
||||
if (fninfo->mIsVariadic) {
|
||||
JS_ReportError(cx, "Can't declare a variadic callback function");
|
||||
return JS_FALSE;
|
||||
}
|
||||
// Construct from a JS function, and allow an optional 'this' argument.
|
||||
JSObject* thisObj = NULL;
|
||||
if (argc == 2) {
|
||||
|
@ -4493,6 +4564,35 @@ FunctionType::ConstructData(JSContext* cx,
|
|||
return JS_FALSE;
|
||||
}
|
||||
|
||||
typedef nsAutoTArray<AutoValue, 16> AutoValueAutoArray;
|
||||
|
||||
static JSBool
|
||||
ConvertArgument(JSContext* cx,
|
||||
jsval arg,
|
||||
JSObject* type,
|
||||
AutoValueAutoArray* values,
|
||||
AutoValueAutoArray* strings)
|
||||
{
|
||||
AutoValue* value = values->AppendElement();
|
||||
|
||||
if (!value->SizeToType(cx, type)) {
|
||||
JS_ReportAllocationOverflow(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool freePointer = false;
|
||||
if (!ImplicitConvert(cx, arg, type, value->mData, true, &freePointer))
|
||||
return false;
|
||||
|
||||
if (freePointer) {
|
||||
// ImplicitConvert converted a string for us, which we have to free.
|
||||
// Keep track of it.
|
||||
strings->AppendElement()->mData = *static_cast<char**>(value->mData);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
JSBool
|
||||
FunctionType::Call(JSContext* cx,
|
||||
JSObject* obj,
|
||||
|
@ -4514,8 +4614,10 @@ FunctionType::Call(JSContext* cx,
|
|||
}
|
||||
|
||||
FunctionInfo* fninfo = GetFunctionInfo(cx, typeObj);
|
||||
PRUint32 argcFixed = fninfo->mArgTypes.Length();
|
||||
|
||||
if (argc != fninfo->mArgTypes.Length()) {
|
||||
if ((!fninfo->mIsVariadic && argc != argcFixed) ||
|
||||
(fninfo->mIsVariadic && argc < argcFixed)) {
|
||||
JS_ReportError(cx, "Number of arguments does not match declaration");
|
||||
return false;
|
||||
}
|
||||
|
@ -4532,25 +4634,41 @@ FunctionType::Call(JSContext* cx,
|
|||
}
|
||||
|
||||
// prepare the values for each argument
|
||||
nsAutoTArray<AutoValue, 16> values;
|
||||
nsAutoTArray<AutoValue, 16> strings;
|
||||
for (PRUint32 i = 0; i < fninfo->mArgTypes.Length(); ++i) {
|
||||
AutoValue* value = values.AppendElement();
|
||||
bool freePointer = false;
|
||||
if (!value->SizeToType(cx, fninfo->mArgTypes[i])) {
|
||||
JS_ReportAllocationOverflow(cx);
|
||||
return false;
|
||||
}
|
||||
AutoValueAutoArray values;
|
||||
AutoValueAutoArray strings;
|
||||
|
||||
if (!ImplicitConvert(cx, argv[i], fninfo->mArgTypes[i], value->mData, true,
|
||||
&freePointer))
|
||||
for (PRUint32 i = 0; i < argcFixed; ++i)
|
||||
if (!ConvertArgument(cx, argv[i], fninfo->mArgTypes[i], &values, &strings))
|
||||
return false;
|
||||
|
||||
if (freePointer) {
|
||||
// ImplicitConvert converted a string for us, which we have to free.
|
||||
// Keep track of it.
|
||||
strings.AppendElement()->mData = *static_cast<char**>(value->mData);
|
||||
if (fninfo->mIsVariadic) {
|
||||
fninfo->mFFITypes.SetLength(argcFixed);
|
||||
ASSERT_OK(fninfo->mFFITypes.SetCapacity(argc));
|
||||
|
||||
JSObject* obj; // Could reuse obj instead of declaring a second
|
||||
JSObject* type; // JSObject*, but readability would suffer.
|
||||
|
||||
for (PRUint32 i = argcFixed; i < argc; ++i) {
|
||||
if (JSVAL_IS_PRIMITIVE(argv[i]) ||
|
||||
!CData::IsCData(cx, obj = JSVAL_TO_OBJECT(argv[i]))) {
|
||||
// Since we know nothing about the CTypes of the ... arguments,
|
||||
// they absolutely must be CData objects already.
|
||||
JS_ReportError(cx, "argument %d of type %s is not a CData object",
|
||||
i, JS_GetTypeName(cx, JS_TypeOfValue(cx, argv[i])));
|
||||
return false;
|
||||
}
|
||||
if (!(type = CData::GetCType(cx, obj)) ||
|
||||
!(type = PrepareType(cx, OBJECT_TO_JSVAL(type))) ||
|
||||
// Relying on ImplicitConvert only for the limited purpose of
|
||||
// converting one CType to another (e.g., T[] to T*).
|
||||
!ConvertArgument(cx, argv[i], type, &values, &strings)) {
|
||||
// These functions report their own errors.
|
||||
return false;
|
||||
}
|
||||
fninfo->mFFITypes.AppendElement(CType::GetFFIType(cx, type));
|
||||
}
|
||||
if (!PrepareCIF(cx, fninfo))
|
||||
return false;
|
||||
}
|
||||
|
||||
// initialize a pointer to an appropriate location, for storing the result
|
||||
|
@ -4590,13 +4708,21 @@ FunctionType::GetFunctionInfo(JSContext* cx, JSObject* obj)
|
|||
return static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot));
|
||||
}
|
||||
|
||||
JSBool
|
||||
FunctionType::ArgTypesGetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp)
|
||||
static JSBool
|
||||
CheckFunctionType(JSContext* cx, JSObject* obj)
|
||||
{
|
||||
if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_function) {
|
||||
JS_ReportError(cx, "not a FunctionType");
|
||||
return JS_FALSE;
|
||||
}
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSBool
|
||||
FunctionType::ArgTypesGetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp)
|
||||
{
|
||||
if (!CheckFunctionType(cx, obj))
|
||||
return JS_FALSE;
|
||||
|
||||
// Check if we have a cached argTypes array.
|
||||
ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_ARGS_T, vp));
|
||||
|
@ -4630,10 +4756,8 @@ FunctionType::ArgTypesGetter(JSContext* cx, JSObject* obj, jsval idval, jsval* v
|
|||
JSBool
|
||||
FunctionType::ReturnTypeGetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp)
|
||||
{
|
||||
if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_function) {
|
||||
JS_ReportError(cx, "not a FunctionType");
|
||||
if (!CheckFunctionType(cx, obj))
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
// Get the returnType object from the FunctionInfo.
|
||||
*vp = OBJECT_TO_JSVAL(GetFunctionInfo(cx, obj)->mReturnType);
|
||||
|
@ -4643,16 +4767,24 @@ FunctionType::ReturnTypeGetter(JSContext* cx, JSObject* obj, jsval idval, jsval*
|
|||
JSBool
|
||||
FunctionType::ABIGetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp)
|
||||
{
|
||||
if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_function) {
|
||||
JS_ReportError(cx, "not a FunctionType");
|
||||
if (!CheckFunctionType(cx, obj))
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
// Get the abi object from the FunctionInfo.
|
||||
*vp = OBJECT_TO_JSVAL(GetFunctionInfo(cx, obj)->mABI);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSBool
|
||||
FunctionType::IsVariadicGetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp)
|
||||
{
|
||||
if (!CheckFunctionType(cx, obj))
|
||||
return JS_FALSE;
|
||||
|
||||
*vp = BOOLEAN_TO_JSVAL(GetFunctionInfo(cx, obj)->mIsVariadic);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** CClosure implementation
|
||||
*******************************************************************************/
|
||||
|
@ -4671,6 +4803,7 @@ CClosure::Create(JSContext* cx,
|
|||
|
||||
// Get the FunctionInfo from the FunctionType.
|
||||
FunctionInfo* fninfo = FunctionType::GetFunctionInfo(cx, typeObj);
|
||||
JS_ASSERT(!fninfo->mIsVariadic);
|
||||
|
||||
nsAutoPtr<ClosureInfo> cinfo(new ClosureInfo());
|
||||
if (!cinfo) {
|
||||
|
|
|
@ -104,14 +104,34 @@ struct PropertySpec
|
|||
JSPropertyOp setter;
|
||||
};
|
||||
|
||||
// Descriptor of ABI, return type, and argument types for a FunctionType.
|
||||
// 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;
|
||||
JSObject* mABI;
|
||||
|
||||
// Calling convention of the function. Convert to ffi_abi using GetABI
|
||||
// and OBJECT_TO_JSVAL. Stored as a JSObject* for ease of tracing.
|
||||
JSObject* mABI;
|
||||
|
||||
// The CType of the value returned by the function.
|
||||
JSObject* mReturnType;
|
||||
nsTArray<JSObject*> mArgTypes;
|
||||
|
||||
// A fixed array of known parameter types, excluding any variadic
|
||||
// parameters (if mIsVariadic).
|
||||
nsTArray<JSObject*> 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.
|
||||
nsTArray<ffi_type*> 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.
|
||||
|
|
|
@ -94,7 +94,8 @@ let EXPORTED_SYMBOLS = [ "ctypes" ];
|
|||
* @returnType The return type of the function. Must be a type constant
|
||||
* from ctypes.
|
||||
* @argTypes Argument types. Must be a type constant (other than void_t)
|
||||
* from ctypes.
|
||||
* from ctypes, or the literal string "..." to denote a
|
||||
* variadic function.
|
||||
* @returns A function object.
|
||||
*
|
||||
* A function object can then be used to call the C function it represents
|
||||
|
@ -108,7 +109,9 @@ let EXPORTED_SYMBOLS = [ "ctypes" ];
|
|||
* Arguments will be checked against the types supplied at declaration, and
|
||||
* some attempt to convert values (e.g. boolean true/false to integer 0/1)
|
||||
* will be made. Otherwise, if types do not match, or conversion fails,
|
||||
* an exception will be thrown.
|
||||
* an exception will be thrown. Arguments passed as variadic parameters
|
||||
* must have an explicit ctypes type, since their types are not declared
|
||||
* in the signature.
|
||||
*/
|
||||
|
||||
// Initialize the ctypes object. You do not need to do this yourself.
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "nsCRTGlue.h"
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
void
|
||||
test_void_t_cdecl()
|
||||
|
@ -334,3 +335,67 @@ test_closure_cdecl(PRInt8 i, PRInt32 (NS_STDCALL *f)(PRInt8))
|
|||
}
|
||||
#endif /* defined(_WIN32) && !defined(__WIN64) */
|
||||
|
||||
template <typename T> struct PromotedTraits {
|
||||
typedef T type;
|
||||
};
|
||||
#define DECL_PROMOTED(FROM, TO) \
|
||||
template <> struct PromotedTraits<FROM> { \
|
||||
typedef TO type; \
|
||||
}
|
||||
DECL_PROMOTED(bool, int);
|
||||
DECL_PROMOTED(char, int);
|
||||
DECL_PROMOTED(short, int);
|
||||
|
||||
PRInt32
|
||||
test_sum_va_cdecl(PRUint8 n, ...)
|
||||
{
|
||||
va_list list;
|
||||
PRInt32 sum = 0;
|
||||
va_start(list, n);
|
||||
for (PRUint8 i = 0; i < n; ++i)
|
||||
sum += va_arg(list, PromotedTraits<PRInt32>::type);
|
||||
va_end(list);
|
||||
return sum;
|
||||
}
|
||||
|
||||
PRUint8
|
||||
test_count_true_va_cdecl(PRUint8 n, ...)
|
||||
{
|
||||
va_list list;
|
||||
PRUint8 count = 0;
|
||||
va_start(list, n);
|
||||
for (PRUint8 i = 0; i < n; ++i)
|
||||
if (va_arg(list, PromotedTraits<bool>::type))
|
||||
count += 1;
|
||||
va_end(list);
|
||||
return count;
|
||||
}
|
||||
|
||||
void
|
||||
test_add_char_short_int_va_cdecl(PRUint32* result, ...)
|
||||
{
|
||||
va_list list;
|
||||
va_start(list, result);
|
||||
*result += va_arg(list, PromotedTraits<char>::type);
|
||||
*result += va_arg(list, PromotedTraits<short>::type);
|
||||
*result += va_arg(list, PromotedTraits<int>::type);
|
||||
va_end(list);
|
||||
}
|
||||
|
||||
PRInt32*
|
||||
test_vector_add_va_cdecl(PRUint8 num_vecs,
|
||||
PRUint8 vec_len,
|
||||
PRInt32* result, ...)
|
||||
{
|
||||
va_list list;
|
||||
va_start(list, result);
|
||||
PRUint8 i;
|
||||
for (i = 0; i < vec_len; ++i)
|
||||
result[i] = 0;
|
||||
for (i = 0; i < num_vecs; ++i) {
|
||||
PRInt32* vec = va_arg(list, PRInt32*);
|
||||
for (PRUint8 j = 0; j < vec_len; ++j)
|
||||
result[j] += vec[j];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -195,5 +195,11 @@ NS_EXTERN_C
|
|||
|
||||
NS_EXPORT PRInt32 test_callme(PRInt8);
|
||||
NS_EXPORT void* test_getfn();
|
||||
}
|
||||
|
||||
EXPORT_CDECL(PRInt32) test_sum_va_cdecl(PRUint8 n, ...);
|
||||
EXPORT_CDECL(PRUint8) test_count_true_va_cdecl(PRUint8 n, ...);
|
||||
EXPORT_CDECL(void) test_add_char_short_int_va_cdecl(PRUint32* result, ...);
|
||||
EXPORT_CDECL(PRInt32*) test_vector_add_va_cdecl(PRUint8 num_vecs,
|
||||
PRUint8 vec_len,
|
||||
PRInt32* result, ...);
|
||||
}
|
||||
|
|
|
@ -53,8 +53,10 @@ function do_check_throws(f, type, stack)
|
|||
try {
|
||||
f();
|
||||
} catch (exc) {
|
||||
if (exc instanceof type)
|
||||
if (exc instanceof type) {
|
||||
do_check_true(true);
|
||||
return;
|
||||
}
|
||||
do_throw("expected " + type.name + " exception, caught " + exc, stack);
|
||||
}
|
||||
do_throw("expected " + type.name + " exception, none thrown", stack);
|
||||
|
@ -189,6 +191,7 @@ function run_test()
|
|||
run_struct_tests(library);
|
||||
run_function_tests(library);
|
||||
run_closure_tests(library);
|
||||
run_variadic_tests(library);
|
||||
|
||||
// test the string version of ctypes.open() as well
|
||||
let libpath = libfile.path;
|
||||
|
@ -1663,7 +1666,8 @@ function run_FunctionType_tests() {
|
|||
run_type_ctor_class_tests(ctypes.FunctionType,
|
||||
ctypes.FunctionType(ctypes.default_abi, ctypes.void_t),
|
||||
ctypes.FunctionType(ctypes.default_abi, ctypes.void_t, [ ctypes.int32_t ]),
|
||||
[ "abi", "returnType", "argTypes" ], undefined, undefined, undefined, undefined);
|
||||
[ "abi", "returnType", "argTypes", "isVariadic" ],
|
||||
undefined, undefined, undefined, undefined);
|
||||
|
||||
do_check_throws(function() { ctypes.FunctionType(); }, Error);
|
||||
do_check_throws(function() {
|
||||
|
@ -2111,6 +2115,94 @@ function run_single_closure_tests(library, abi, suffix)
|
|||
do_check_eq(test_closure(-52, closure2), -52 + thisobj.a);
|
||||
}
|
||||
|
||||
function run_variadic_tests(library) {
|
||||
let sum_va_type = ctypes.FunctionType(ctypes.default_abi,
|
||||
ctypes.int32_t,
|
||||
[ctypes.uint8_t, "..."]),
|
||||
sum_va = library.declare("test_sum_va_cdecl", ctypes.default_abi, ctypes.int32_t,
|
||||
ctypes.uint8_t, "...");
|
||||
|
||||
do_check_eq(sum_va_type.toSource(),
|
||||
'ctypes.FunctionType(ctypes.default_abi, ctypes.int32_t, [ctypes.uint8_t, "..."])');
|
||||
do_check_eq(sum_va.constructor.name, "int32_t(*)(uint8_t, ...)");
|
||||
do_check_true(sum_va.constructor.isVariadic);
|
||||
|
||||
do_check_eq(sum_va(3,
|
||||
ctypes.int32_t(1),
|
||||
ctypes.int32_t(2),
|
||||
ctypes.int32_t(3)),
|
||||
6);
|
||||
|
||||
do_check_throws(function() {
|
||||
ctypes.FunctionType(ctypes.default_abi, ctypes.bool,
|
||||
[ctypes.bool, "...", ctypes.bool]);
|
||||
}, Error);
|
||||
|
||||
do_check_throws(function() {
|
||||
ctypes.FunctionType(ctypes.default_abi, ctypes.bool, ["..."]);
|
||||
}, Error);
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef _WIN64
|
||||
do_check_throws(function() {
|
||||
ctypes.FunctionType(ctypes.stdcall_abi, ctypes.bool,
|
||||
[ctypes.bool, "..."]);
|
||||
}, Error);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
do_check_throws(function() {
|
||||
// No variadic closure callbacks allowed.
|
||||
sum_va_type(function(){});
|
||||
}, Error);
|
||||
|
||||
let count_true_va = library.declare("test_sum_va_cdecl", ctypes.default_abi, ctypes.uint8_t,
|
||||
ctypes.uint8_t, "...");
|
||||
do_check_eq(count_true_va(8,
|
||||
ctypes.bool(false),
|
||||
ctypes.bool(false),
|
||||
ctypes.bool(false),
|
||||
ctypes.bool(true),
|
||||
ctypes.bool(true),
|
||||
ctypes.bool(false),
|
||||
ctypes.bool(true),
|
||||
ctypes.bool(true)),
|
||||
4);
|
||||
|
||||
let add_char_short_int_va = library.declare("test_add_char_short_int_va_cdecl",
|
||||
ctypes.default_abi, ctypes.void_t,
|
||||
ctypes.uint32_t.ptr, "..."),
|
||||
result = ctypes.uint32_t(3);
|
||||
|
||||
add_char_short_int_va(result.address(),
|
||||
ctypes.char(5),
|
||||
ctypes.short(7),
|
||||
ctypes.uint32_t(11));
|
||||
|
||||
do_check_eq(result.value, 3 + 5 + 7 + 11);
|
||||
|
||||
let result = ctypes.int32_t.array(3)([1,1,1]),
|
||||
v1 = ctypes.int32_t.array(4)([1,2,3,5]),
|
||||
v2 = ctypes.int32_t.array(3)([7,11,13]),
|
||||
vector_add_va = library.declare("test_vector_add_va_cdecl",
|
||||
ctypes.default_abi, ctypes.int32_t.ptr,
|
||||
ctypes.uint8_t, ctypes.uint8_t, "..."),
|
||||
// Note that vector_add_va zeroes out result first.
|
||||
vec_sum = vector_add_va(2, 3, result, v1, v2);
|
||||
do_check_eq(vec_sum.contents, 8);
|
||||
do_check_eq(result[0], 8);
|
||||
do_check_eq(result[1], 13);
|
||||
do_check_eq(result[2], 16);
|
||||
|
||||
do_check_true(!!(sum_va_type().value = sum_va_type()));
|
||||
let sum_notva_type = ctypes.FunctionType(sum_va_type.abi,
|
||||
sum_va_type.returnType,
|
||||
[ctypes.uint8_t]);
|
||||
do_check_throws(function() {
|
||||
sum_va_type().value = sum_notva_type();
|
||||
}, Error);
|
||||
}
|
||||
|
||||
// bug 522360 - try loading system library without full path
|
||||
function run_load_system_library()
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче