diff --git a/js/ctypes/CTypes.cpp b/js/ctypes/CTypes.cpp index 65a2b1e624e3..a1ec2a082ed6 100644 --- a/js/ctypes/CTypes.cpp +++ b/js/ctypes/CTypes.cpp @@ -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(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 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(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 values; - nsAutoTArray 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(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(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 cinfo(new ClosureInfo()); if (!cinfo) { diff --git a/js/ctypes/CTypes.h b/js/ctypes/CTypes.h index e6fc2417f2c9..7215b56514cf 100644 --- a/js/ctypes/CTypes.h +++ b/js/ctypes/CTypes.h @@ -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 mArgTypes; + + // A fixed array of known parameter types, excluding any variadic + // parameters (if mIsVariadic). + nsTArray 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 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. diff --git a/js/ctypes/ctypes.jsm b/js/ctypes/ctypes.jsm index 285c91dfa5e1..d6e82db9780c 100644 --- a/js/ctypes/ctypes.jsm +++ b/js/ctypes/ctypes.jsm @@ -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. diff --git a/js/ctypes/tests/jsctypes-test.cpp b/js/ctypes/tests/jsctypes-test.cpp index 52f5d6d4ae1d..63d1032b9593 100644 --- a/js/ctypes/tests/jsctypes-test.cpp +++ b/js/ctypes/tests/jsctypes-test.cpp @@ -42,6 +42,7 @@ #include "nsCRTGlue.h" #include #include +#include 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 struct PromotedTraits { + typedef T type; +}; +#define DECL_PROMOTED(FROM, TO) \ + template <> struct PromotedTraits { \ + 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::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::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::type); + *result += va_arg(list, PromotedTraits::type); + *result += va_arg(list, PromotedTraits::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; +} diff --git a/js/ctypes/tests/jsctypes-test.h b/js/ctypes/tests/jsctypes-test.h index b3ad8c4f986f..1ba322fa1b7a 100644 --- a/js/ctypes/tests/jsctypes-test.h +++ b/js/ctypes/tests/jsctypes-test.h @@ -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, ...); +} diff --git a/js/ctypes/tests/unit/test_jsctypes.js.in b/js/ctypes/tests/unit/test_jsctypes.js.in index ed08e057678b..8813c58e8535 100644 --- a/js/ctypes/tests/unit/test_jsctypes.js.in +++ b/js/ctypes/tests/unit/test_jsctypes.js.in @@ -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() {