Bug 554790 - Implement variadic ctypes functions. r=dwitte

This commit is contained in:
Ben Newman 2010-04-01 10:47:37 -07:00
Родитель 53b4755d48
Коммит 53f422a393
6 изменённых файлов: 376 добавлений и 57 удалений

Просмотреть файл

@ -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()
{