Bug 558949 - Make ctypes functions FunctionType.ptr's. r=benjamn

This commit is contained in:
Dan Witte 2010-04-20 17:37:35 -07:00
Родитель ba38fac269
Коммит 14ec83f753
3 изменённых файлов: 156 добавлений и 144 удалений

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

@ -146,8 +146,8 @@ namespace StructType {
namespace FunctionType {
static JSBool Create(JSContext* cx, uintN argc, jsval* vp);
static JSBool ConstructData(JSContext* cx, JSObject* obj, uintN argc,
jsval* argv, jsval* rval);
static JSBool ConstructData(JSContext* cx, JSObject* typeObj,
JSObject* dataObj, JSObject* fnObj, JSObject* thisObj);
static JSBool Call(JSContext* cx, JSObject* obj, uintN argc, jsval* argv,
jsval* rval);
@ -1579,7 +1579,6 @@ ConvertToJS(JSContext* cx,
break;
}
case TYPE_pointer:
case TYPE_function:
case TYPE_array:
case TYPE_struct: {
// We're about to create a new CData object to return. If the caller doesn't
@ -1596,6 +1595,8 @@ ConvertToJS(JSContext* cx,
*result = OBJECT_TO_JSVAL(obj);
break;
}
case TYPE_function:
JS_NOT_REACHED("cannot return a FunctionType");
}
return true;
@ -1774,14 +1775,6 @@ ImplicitConvert(JSContext* cx,
}
return TypeError(cx, "pointer", val);
}
case TYPE_function: {
if (JSVAL_IS_NULL(val)) {
// Convert to a null function pointer.
*static_cast<void**>(buffer) = NULL;
break;
}
return TypeError(cx, "function", val);
}
case TYPE_array: {
JSObject* baseType = ArrayType::GetBaseType(cx, targetType);
size_t targetLength = ArrayType::GetLength(cx, targetType);
@ -1937,6 +1930,7 @@ ImplicitConvert(JSContext* cx,
return TypeError(cx, "struct", val);
}
case TYPE_void_t:
case TYPE_function:
JS_NOT_REACHED("invalid type");
return false;
}
@ -1999,14 +1993,6 @@ ExplicitConvert(JSContext* cx, jsval val, JSObject* targetType, void* buffer)
*static_cast<uintptr_t*>(buffer) = result;
break;
}
case TYPE_function: {
// Convert a number, Int64 object, or UInt64 object to a function pointer.
uintptr_t result;
if (!jsvalToPtrExplicit(cx, val, &result))
return TypeError(cx, "function", val);
*static_cast<uintptr_t*>(buffer) = result;
break;
}
case TYPE_float32_t:
case TYPE_float64_t:
case TYPE_float:
@ -2017,6 +2003,7 @@ ExplicitConvert(JSContext* cx, jsval val, JSObject* targetType, void* buffer)
JS_SetPendingException(cx, ex.value());
return false;
case TYPE_void_t:
case TYPE_function:
JS_NOT_REACHED("invalid type");
return false;
}
@ -2080,9 +2067,6 @@ BuildTypeName(JSContext* cx, JSObject* typeObj)
case TYPE_function: {
FunctionInfo* fninfo = FunctionType::GetFunctionInfo(cx, currentType);
// Function pointer goes on the left.
PrependString(result, "*");
// Add in the calling convention, if it's not cdecl.
if (GetABICode(cx, fninfo->mABI) == ABI_STDCALL)
PrependString(result, "__stdcall ");
@ -2105,8 +2089,9 @@ BuildTypeName(JSContext* cx, JSObject* typeObj)
AppendString(result, ")");
// Set 'currentType' to the return type, and let the loop process it.
// 'prevGrouping' doesn't matter here, because functions cannot return
// arrays -- thus the parenthetical rules don't get tickled.
currentType = fninfo->mReturnType;
prevGrouping = currentGrouping;
continue;
}
default:
@ -2452,10 +2437,11 @@ CType::ConstructData(JSContext* cx,
case TYPE_void_t:
JS_ReportError(cx, "cannot construct from void_t");
return JS_FALSE;
case TYPE_function:
JS_ReportError(cx, "cannot construct from FunctionType; use FunctionType.ptr instead");
return JS_FALSE;
case TYPE_pointer:
return PointerType::ConstructData(cx, obj, argc, argv, rval);
case TYPE_function:
return FunctionType::ConstructData(cx, obj, argc, argv, rval);
case TYPE_array:
return ArrayType::ConstructData(cx, obj, argc, argv, rval);
case TYPE_struct:
@ -3179,8 +3165,8 @@ PointerType::ConstructData(JSContext* cx,
return JS_FALSE;
}
if (argc > 1) {
JS_ReportError(cx, "constructor takes zero or one argument");
if (argc > 2) {
JS_ReportError(cx, "constructor takes 0, 1, or 2 arguments");
return JS_FALSE;
}
@ -3190,12 +3176,39 @@ PointerType::ConstructData(JSContext* cx,
*rval = OBJECT_TO_JSVAL(result);
if (argc == 1) {
if (!ExplicitConvert(cx, argv[0], obj, CData::GetData(cx, result)))
return JS_FALSE;
if (argc == 0) {
// Construct a null pointer.
return JS_TRUE;
}
return JS_TRUE;
if (argc >= 1) {
JSObject* baseObj = PointerType::GetBaseType(cx, obj);
if (baseObj && CType::GetTypeCode(cx, baseObj) == TYPE_function &&
JSVAL_IS_OBJECT(argv[0]) &&
JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(argv[0]))) {
// Construct a FunctionType.ptr from a JS function, and allow an
// optional 'this' argument.
JSObject* thisObj = NULL;
if (argc == 2) {
if (JSVAL_IS_OBJECT(argv[1])) {
thisObj = JSVAL_TO_OBJECT(argv[1]);
} else if (!JS_ValueToObject(cx, argv[1], &thisObj)) {
return JS_FALSE;
}
}
JSObject* fnObj = JSVAL_TO_OBJECT(argv[0]);
return FunctionType::ConstructData(cx, baseObj, result, fnObj, thisObj);
}
if (argc == 2) {
JS_ReportError(cx, "first argument must be a function");
return JS_FALSE;
}
}
// Construct from a raw pointer value.
return ExplicitConvert(cx, argv[0], obj, CData::GetData(cx, result));
}
JSObject*
@ -4336,9 +4349,9 @@ PrepareType(JSContext* cx, jsval type)
if (!result)
return NULL;
} else if (typeCode == TYPE_void_t) {
// disallow void argument types
JS_ReportError(cx, "Cannot have void argument type");
} else if (typeCode == TYPE_void_t || typeCode == TYPE_function) {
// disallow void or function argument types
JS_ReportError(cx, "Cannot have void or function argument type");
return NULL;
}
@ -4360,9 +4373,9 @@ PrepareReturnType(JSContext* cx, jsval type)
JSObject* result = JSVAL_TO_OBJECT(type);
TypeCode typeCode = CType::GetTypeCode(cx, result);
// Arrays can never be return types.
if (typeCode == TYPE_array) {
JS_ReportError(cx, "Result type cannot be an array");
// Arrays and functions can never be return types.
if (typeCode == TYPE_array || typeCode == TYPE_function) {
JS_ReportError(cx, "Return type cannot be an array or function");
return NULL;
}
@ -4431,6 +4444,11 @@ NewFunctionInfo(JSContext* cx,
return NULL;
}
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
@ -4557,10 +4575,9 @@ FunctionType::CreateInternal(JSContext* cx,
SLOT_FUNCTIONDATAPROTO);
// Create a new CType object with the common properties and slots.
// We use ffi_type_void here in its capacity as "a type of undefined size".
JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_function,
NULL, INT_TO_JSVAL(sizeof(void*)),
INT_TO_JSVAL(ffi_type_pointer.alignment),
&ffi_type_pointer, NULL);
NULL, JSVAL_VOID, JSVAL_VOID, &ffi_type_void, NULL);
if (!typeObj)
return NULL;
js::AutoValueRooter root(cx, typeObj);
@ -4582,75 +4599,38 @@ FunctionType::CreateInternal(JSContext* cx,
JSBool
FunctionType::ConstructData(JSContext* cx,
JSObject* obj,
uintN argc,
jsval* argv,
jsval* rval)
JSObject* typeObj,
JSObject* dataObj,
JSObject* fnObj,
JSObject* thisObj)
{
if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_function) {
JS_ReportError(cx, "not a FunctionType");
JS_ASSERT(CType::GetTypeCode(cx, typeObj) == TYPE_function);
PRFuncPtr* data = static_cast<PRFuncPtr*>(CData::GetData(cx, dataObj));
FunctionInfo* fninfo = FunctionType::GetFunctionInfo(cx, typeObj);
if (fninfo->mIsVariadic) {
JS_ReportError(cx, "Can't declare a variadic callback function");
return JS_FALSE;
}
JSObject* result = CData::Create(cx, obj, NULL, NULL, true);
if (!result)
JSObject* closureObj = CClosure::Create(cx, typeObj, fnObj, thisObj, data);
if (!closureObj)
return JS_FALSE;
js::AutoValueRooter root(cx, closureObj);
// Set the closure object as the referent of the new CData object.
if (!JS_SetReservedSlot(cx, dataObj, SLOT_REFERENT,
OBJECT_TO_JSVAL(closureObj)))
return JS_FALSE;
*rval = OBJECT_TO_JSVAL(result);
if (argc == 0) {
// Construct a null pointer.
return JS_TRUE;
}
if (argc == 1 || argc == 2) {
jsval arg = argv[0];
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) {
if (JSVAL_IS_OBJECT(argv[1])) {
thisObj = JSVAL_TO_OBJECT(argv[1]);
} else if (!JS_ValueToObject(cx, argv[1], &thisObj)) {
return JS_FALSE;
}
}
JSObject* fnObj = JSVAL_TO_OBJECT(arg);
JSObject* closureObj = CClosure::Create(cx, obj, fnObj, thisObj, data);
if (!closureObj)
return JS_FALSE;
js::AutoValueRooter root(cx, closureObj);
// Set the closure object as the referent of the new CData object.
if (!JS_SetReservedSlot(cx, result, SLOT_REFERENT,
OBJECT_TO_JSVAL(closureObj)))
return JS_FALSE;
// Seal the CData object, to prevent modification of the function pointer.
// This permanently associates this object with the closure, and avoids
// having to do things like reset SLOT_REFERENT when someone tries to
// change the pointer value.
// XXX This will need to change when bug 541212 is fixed -- CData::ValueSetter
// could be called on a sealed object.
return JS_SealObject(cx, result, JS_FALSE);
}
if (argc == 1) {
// Construct from a raw pointer value.
return ExplicitConvert(cx, arg, obj, data);
}
}
JS_ReportError(cx, "constructor takes 0, 1, or 2 arguments");
return JS_FALSE;
// Seal the CData object, to prevent modification of the function pointer.
// This permanently associates this object with the closure, and avoids
// having to do things like reset SLOT_REFERENT when someone tries to
// change the pointer value.
// XXX This will need to change when bug 541212 is fixed -- CData::ValueSetter
// could be called on a sealed object.
return JS_SealObject(cx, dataObj, JS_FALSE);
}
typedef Array<AutoValue, 16> AutoValueAutoArray;
@ -4699,8 +4679,10 @@ FunctionType::Call(JSContext* cx,
}
JSObject* typeObj = CData::GetCType(cx, obj);
if (CType::GetTypeCode(cx, typeObj) != TYPE_function) {
JS_ReportError(cx, "not a FunctionType");
if (CType::GetTypeCode(cx, typeObj) != TYPE_pointer ||
!(typeObj = PointerType::GetBaseType(cx, typeObj)) ||
CType::GetTypeCode(cx, typeObj) != TYPE_function) {
JS_ReportError(cx, "not a FunctionType.ptr");
return false;
}

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

@ -252,16 +252,26 @@ Library::Declare(JSContext* cx, uintN argc, jsval* vp)
return JS_FALSE;
root.setObject(typeObj);
// Make a function pointer type.
typeObj = PointerType::CreateInternal(cx, NULL, typeObj, NULL);
if (!typeObj)
return JS_FALSE;
root.setObject(typeObj);
} else {
// Case 2).
if (JSVAL_IS_PRIMITIVE(argv[1]) ||
!CType::IsCType(cx, JSVAL_TO_OBJECT(argv[1]))) {
JS_ReportError(cx, "second argument must be a type");
!CType::IsCType(cx, JSVAL_TO_OBJECT(argv[1])) ||
!CType::IsSizeDefined(cx, JSVAL_TO_OBJECT(argv[1]))) {
JS_ReportError(cx, "second argument must be a type of defined size");
return JS_FALSE;
}
typeObj = JSVAL_TO_OBJECT(argv[1]);
isFunction = CType::GetTypeCode(cx, typeObj) == TYPE_function;
if (CType::GetTypeCode(cx, typeObj) == TYPE_pointer) {
JSObject* baseType = PointerType::GetBaseType(cx, typeObj);
isFunction = baseType && CType::GetTypeCode(cx, baseType) == TYPE_function;
}
}
void* data;

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

@ -1334,12 +1334,14 @@ function run_type_ctor_class_tests(c, t, t2, props, fns, instanceProps, instance
do_check_throws(function() { t.prototype[p]; }, Error);
// Test that an instance 'd' of 't' is a CData.
let d = t();
do_check_class(d, "CData");
do_check_true(d.__parent__ === ctypes);
do_check_true(d.__proto__ === t.prototype);
do_check_true(d instanceof t);
do_check_true(d.constructor === t);
if (t.__proto__ != ctypes.FunctionType.prototype) {
let d = t();
do_check_class(d, "CData");
do_check_true(d.__parent__ === ctypes);
do_check_true(d.__proto__ === t.prototype);
do_check_true(d instanceof t);
do_check_true(d.constructor === t);
}
}
function run_StructType_tests() {
@ -1692,16 +1694,19 @@ function run_FunctionType_tests() {
ctypes.FunctionType(ctypes.default_abi, ctypes.void_t, null);
}, Error);
do_check_throws(function() {
ctypes.FunctionType(ctypes.default_abi, ctypes.void_t());
ctypes.FunctionType(ctypes.default_abi, ctypes.int32_t());
}, Error);
do_check_throws(function() {
ctypes.FunctionType(ctypes.void_t, ctypes.void_t);
}, Error);
let g_t = ctypes.StructType("g_t", [{ a: ctypes.int32_t }, { b: ctypes.double }]);
let g = g_t(1, 2);
let f_t = ctypes.FunctionType(ctypes.default_abi, g_t);
let name = "g_t(*)()";
let name = "g_t()()";
do_check_eq(f_t.name, name);
do_check_eq(f_t.size, ctypes.uintptr_t.size);
do_check_eq(f_t.size, undefined);
do_check_true(f_t.abi === ctypes.default_abi);
do_check_true(f_t.returnType === g_t);
do_check_true(f_t.argTypes.length == 0);
@ -1710,13 +1715,25 @@ function run_FunctionType_tests() {
do_check_eq(f_t.toSource(),
"ctypes.FunctionType(ctypes.default_abi, g_t)");
let fp_t = f_t.ptr;
name = "g_t(*)()";
do_check_eq(fp_t.name, name);
do_check_eq(fp_t.size, ctypes.uintptr_t.size);
do_check_eq(fp_t.toString(), "type " + name);
do_check_eq(fp_t.toSource(),
"ctypes.FunctionType(ctypes.default_abi, g_t).ptr");
// Check that constructing a FunctionType CData directly throws.
do_check_throws(function() { f_t(); }, Error);
// Test ExplicitConvert.
let f = f_t();
let f = fp_t();
do_check_throws(function() { f.value; }, Error);
do_check_eq(ptrValue(f), 0);
f = f_t(5);
f = fp_t(5);
do_check_eq(ptrValue(f), 5);
f = f_t(ctypes.UInt64(10));
f = fp_t(ctypes.UInt64(10));
do_check_eq(ptrValue(f), 10);
// Test ImplicitConvert.
@ -1724,43 +1741,45 @@ function run_FunctionType_tests() {
do_check_eq(ptrValue(f), 0);
do_check_throws(function() { f.value = 5; }, Error);
do_check_eq(f.toSource(),
'ctypes.FunctionType(ctypes.default_abi, g_t)(ctypes.UInt64("0x0"))');
'ctypes.FunctionType(ctypes.default_abi, g_t).ptr(ctypes.UInt64("0x0"))');
// Test ExplicitConvert from a function pointer of different type.
let f2_t = ctypes.FunctionType(ctypes.default_abi, g_t, [ ctypes.int32_t ]);
let f2 = f2_t();
do_check_throws(function() { f_t(f2); }, Error);
let f2 = f2_t.ptr();
do_check_throws(function() { fp_t(f2); }, Error);
do_check_throws(function() { f.value = f2; }, Error);
do_check_throws(function() { f2.value = f; }, Error);
// Test that converting to a voidptr_t throws.
do_check_throws(function() { ctypes.voidptr_t(f2); }, Error);
// Test that converting to a voidptr_t works, but not the other way.
let v = ctypes.voidptr_t(f2);
do_check_eq(v.toSource(), 'ctypes.voidptr_t(ctypes.UInt64("0x0"))');
do_check_throws(function() { f2_t.ptr(v); }, TypeError);
// Test some more complex names.
do_check_eq(f_t.array().name, "g_t(*[])()");
do_check_eq(f_t.array().ptr.name, "g_t(*(*)[])()");
do_check_eq(fp_t.array().name, "g_t(*[])()");
do_check_eq(fp_t.array().ptr.name, "g_t(*(*)[])()");
let f3_t = ctypes.FunctionType(ctypes.default_abi,
ctypes.char.ptr.array().ptr).ptr.array(8).array();
ctypes.char.ptr.array().ptr).ptr.ptr.array(8).array();
do_check_eq(f3_t.name, "char*(*(**[][8])())[]");
#ifdef _WIN32
#ifndef _WIN64
f3_t = ctypes.FunctionType(ctypes.stdcall_abi,
ctypes.char.ptr.array().ptr).ptr.array(8).array();
do_check_eq(f3_t.name, "char*(__stdcall *(**[][8])())[]");
do_check_eq(f3_t.ptr.name, "char*(__stdcall *(**[][8])())[]");
#endif
#endif
let f4_t = ctypes.FunctionType(ctypes.default_abi,
ctypes.char.ptr.array().ptr, [ ctypes.int32_t, f_t ]);
ctypes.char.ptr.array().ptr, [ ctypes.int32_t, fp_t ]);
do_check_true(f4_t.argTypes.length == 2);
do_check_true(f4_t.argTypes[0] === ctypes.int32_t);
do_check_true(f4_t.argTypes[1] === f_t);
do_check_true(f4_t.argTypes[1] === fp_t);
do_check_throws(function() { f4_t.argTypes.z = 0; }, Error);
do_check_throws(function() { f4_t.argTypes[0] = 0; }, Error);
let t4_t = f4_t.ptr.array(8).array();
let t4_t = f4_t.ptr.ptr.array(8).array();
do_check_eq(t4_t.name, "char*(*(**[][8])(int32_t, g_t(*)()))[]");
}
@ -1908,7 +1927,7 @@ function run_cast_tests() {
let g_t = ctypes.StructType("g_t", [{ a: ctypes.int32_t }, { b: ctypes.double }]);
let a_t = ctypes.ArrayType(g_t, 4);
let p_t = ctypes.PointerType(g_t);
let f_t = ctypes.FunctionType(ctypes.default_abi, ctypes.void_t);
let f_t = ctypes.FunctionType(ctypes.default_abi, ctypes.void_t).ptr;
let a = a_t();
a[0] = { a: 5, b: 7.5 };
@ -2064,7 +2083,7 @@ function run_function_tests(library)
let test_ansi_len = library.declare("test_ansi_len", ctypes.default_abi,
ctypes.int32_t, ctypes.char.ptr);
let fn_t = ctypes.FunctionType(ctypes.default_abi, ctypes.int32_t,
[ ctypes.char.ptr ]);
[ ctypes.char.ptr ]).ptr;
let test_fnptr = library.declare("test_fnptr", ctypes.default_abi, fn_t);
@ -2076,11 +2095,12 @@ function run_function_tests(library)
// Test that we can call ptr().
do_check_eq(ptr("function pointers rule!"), 23);
// Test that library.declare() returns data of type FunctionType, and that
// Test that library.declare() returns data of type FunctionType.ptr, and that
// it is immutable.
do_check_true(test_ansi_len.constructor.__proto__ === ctypes.FunctionType.prototype);
do_check_true(test_ansi_len.constructor.targetType.__proto__ ===
ctypes.FunctionType.prototype);
do_check_eq(test_ansi_len.constructor.toSource(),
"ctypes.FunctionType(ctypes.default_abi, ctypes.int32_t, [ctypes.char.ptr])");
"ctypes.FunctionType(ctypes.default_abi, ctypes.int32_t, [ctypes.char.ptr]).ptr");
do_check_throws(function() { test_ansi_len.value = null; }, Error);
do_check_eq(ptrValue(test_ansi_len), ptrValue(ptr));
@ -2116,7 +2136,7 @@ function run_single_closure_tests(library, abi, suffix)
do_check_eq(closure_fn.call(thisobj, 7), 7 + thisobj.a);
// Construct a closure, and call it ourselves.
let fn_t = ctypes.FunctionType(abi, ctypes.int32_t, [ ctypes.int8_t ]);
let fn_t = ctypes.FunctionType(abi, ctypes.int32_t, [ ctypes.int8_t ]).ptr;
let closure = fn_t(closure_fn);
do_check_eq(closure(-17), -17 + b);
@ -2134,14 +2154,14 @@ function run_single_closure_tests(library, abi, suffix)
function run_variadic_tests(library) {
let sum_va_type = ctypes.FunctionType(ctypes.default_abi,
ctypes.int32_t,
[ctypes.uint8_t, "..."]),
[ctypes.uint8_t, "..."]).ptr,
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, "..."])');
'ctypes.FunctionType(ctypes.default_abi, ctypes.int32_t, [ctypes.uint8_t, "..."]).ptr');
do_check_eq(sum_va.constructor.name, "int32_t(*)(uint8_t, ...)");
do_check_true(sum_va.constructor.isVariadic);
do_check_true(sum_va.constructor.targetType.isVariadic);
do_check_eq(sum_va(3,
ctypes.int32_t(1),
@ -2211,9 +2231,9 @@ function run_variadic_tests(library) {
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]);
let sum_notva_type = ctypes.FunctionType(sum_va_type.targetType.abi,
sum_va_type.targetType.returnType,
[ctypes.uint8_t]).ptr;
do_check_throws(function() {
sum_va_type().value = sum_notva_type();
}, Error);