Bug 560002 - Opaque struct and definition support in ctypes. Part 2: Remove opaque pointers. r=benjamn

This commit is contained in:
Dan Witte 2010-05-03 16:20:43 -07:00
parent 91a57c32a3
commit 70dd2657d6
4 changed files with 58 additions and 120 deletions

View File

@ -905,7 +905,7 @@ InitTypeClasses(JSContext* cx, JSObject* parent)
if (!typeObj) if (!typeObj)
return false; return false;
typeObj = PointerType::CreateInternal(cx, NULL, typeObj, NULL); typeObj = PointerType::CreateInternal(cx, typeObj);
if (!typeObj) if (!typeObj)
return false; return false;
if (!JS_DefineProperty(cx, parent, "voidptr_t", OBJECT_TO_JSVAL(typeObj), if (!JS_DefineProperty(cx, parent, "voidptr_t", OBJECT_TO_JSVAL(typeObj),
@ -1703,20 +1703,18 @@ ImplicitConvert(JSContext* cx,
} }
#include "typedefs.h" #include "typedefs.h"
case TYPE_pointer: { case TYPE_pointer: {
JSObject* baseType = PointerType::GetBaseType(cx, targetType);
if (JSVAL_IS_NULL(val)) { if (JSVAL_IS_NULL(val)) {
// Convert to a null pointer. // Convert to a null pointer.
*static_cast<void**>(buffer) = NULL; *static_cast<void**>(buffer) = NULL;
break; break;
} }
JSObject* baseType = PointerType::GetBaseType(cx, targetType);
if (sourceData) { if (sourceData) {
// First, determine if the targetType is ctypes.void_t.ptr. // First, determine if the targetType is ctypes.void_t.ptr.
TypeCode sourceCode = CType::GetTypeCode(cx, sourceType); TypeCode sourceCode = CType::GetTypeCode(cx, sourceType);
void* sourceBuffer = CData::GetData(cx, sourceData); void* sourceBuffer = CData::GetData(cx, sourceData);
bool voidptrTarget = baseType && bool voidptrTarget = CType::GetTypeCode(cx, baseType) == TYPE_void_t;
CType::GetTypeCode(cx, baseType) == TYPE_void_t;
if (sourceCode == TYPE_pointer && voidptrTarget) { if (sourceCode == TYPE_pointer && voidptrTarget) {
// Autoconvert if targetType is ctypes.voidptr_t. // Autoconvert if targetType is ctypes.voidptr_t.
@ -1733,7 +1731,7 @@ ImplicitConvert(JSContext* cx,
} }
} }
} else if (isArgument && baseType && JSVAL_IS_STRING(val)) { } else if (isArgument && JSVAL_IS_STRING(val)) {
// Convert the string for the ffi call. This requires allocating space // Convert the string for the ffi call. This requires allocating space
// which the caller assumes ownership of. // which the caller assumes ownership of.
// TODO: Extend this so we can safely convert strings at other times also. // TODO: Extend this so we can safely convert strings at other times also.
@ -2037,23 +2035,15 @@ BuildTypeName(JSContext* cx, JSObject* typeObj)
// pointers on the left and arrays on the right. An excellent description // pointers on the left and arrays on the right. An excellent description
// of the rules for building C type declarations can be found at: // of the rules for building C type declarations can be found at:
// http://unixwiz.net/techtips/reading-cdecl.html // http://unixwiz.net/techtips/reading-cdecl.html
JSObject* currentType = typeObj; TypeCode prevGrouping = CType::GetTypeCode(cx, typeObj), currentGrouping;
JSObject* nextType;
TypeCode prevGrouping = CType::GetTypeCode(cx, currentType), currentGrouping;
while (1) { while (1) {
currentGrouping = CType::GetTypeCode(cx, currentType); currentGrouping = CType::GetTypeCode(cx, typeObj);
switch (currentGrouping) { switch (currentGrouping) {
case TYPE_pointer: { case TYPE_pointer: {
nextType = PointerType::GetBaseType(cx, currentType);
if (!nextType) {
// Opaque pointer type. Use the type's name as the base type.
break;
}
// Pointer types go on the left. // Pointer types go on the left.
PrependString(result, "*"); PrependString(result, "*");
currentType = nextType; typeObj = PointerType::GetBaseType(cx, typeObj);
prevGrouping = currentGrouping; prevGrouping = currentGrouping;
continue; continue;
} }
@ -2067,17 +2057,17 @@ BuildTypeName(JSContext* cx, JSObject* typeObj)
// Array types go on the right. // Array types go on the right.
AppendString(result, "["); AppendString(result, "[");
size_t length; size_t length;
if (ArrayType::GetSafeLength(cx, currentType, &length)) if (ArrayType::GetSafeLength(cx, typeObj, &length))
IntegerToString(length, 10, result); IntegerToString(length, 10, result);
AppendString(result, "]"); AppendString(result, "]");
currentType = ArrayType::GetBaseType(cx, currentType); typeObj = ArrayType::GetBaseType(cx, typeObj);
prevGrouping = currentGrouping; prevGrouping = currentGrouping;
continue; continue;
} }
case TYPE_function: { case TYPE_function: {
FunctionInfo* fninfo = FunctionType::GetFunctionInfo(cx, currentType); FunctionInfo* fninfo = FunctionType::GetFunctionInfo(cx, typeObj);
// Add in the calling convention, if it's not cdecl. // Add in the calling convention, if it's not cdecl.
if (GetABICode(cx, fninfo->mABI) == ABI_STDCALL) if (GetABICode(cx, fninfo->mABI) == ABI_STDCALL)
@ -2100,10 +2090,10 @@ BuildTypeName(JSContext* cx, JSObject* typeObj)
AppendString(result, "..."); AppendString(result, "...");
AppendString(result, ")"); AppendString(result, ")");
// Set 'currentType' to the return type, and let the loop process it. // Set 'typeObj' to the return type, and let the loop process it.
// 'prevGrouping' doesn't matter here, because functions cannot return // 'prevGrouping' doesn't matter here, because functions cannot return
// arrays -- thus the parenthetical rules don't get tickled. // arrays -- thus the parenthetical rules don't get tickled.
currentType = fninfo->mReturnType; typeObj = fninfo->mReturnType;
continue; continue;
} }
default: default:
@ -2114,7 +2104,7 @@ BuildTypeName(JSContext* cx, JSObject* typeObj)
} }
// Stick the base type and derived type parts together. // Stick the base type and derived type parts together.
JSString* baseName = CType::GetName(cx, currentType); JSString* baseName = CType::GetName(cx, typeObj);
PrependString(result, baseName); PrependString(result, baseName);
return NewUCString(cx, result); return NewUCString(cx, result);
} }
@ -2146,14 +2136,6 @@ BuildTypeSource(JSContext* cx,
} }
case TYPE_pointer: { case TYPE_pointer: {
JSObject* baseType = PointerType::GetBaseType(cx, typeObj); JSObject* baseType = PointerType::GetBaseType(cx, typeObj);
if (!baseType) {
// Opaque pointer type. Use the type's name.
AppendString(result, "ctypes.PointerType(\"");
JSString* baseName = CType::GetName(cx, typeObj);
AppendString(result, baseName);
AppendString(result, "\")");
break;
}
// Specialcase ctypes.voidptr_t. // Specialcase ctypes.voidptr_t.
if (CType::GetTypeCode(cx, baseType) == TYPE_void_t) { if (CType::GetTypeCode(cx, baseType) == TYPE_void_t) {
@ -2729,18 +2711,9 @@ CType::TypesEqual(JSContext* cx, JSObject* t1, JSObject* t2)
// Determine whether the types require shallow or deep comparison. // Determine whether the types require shallow or deep comparison.
switch (c1) { switch (c1) {
case TYPE_pointer: { case TYPE_pointer: {
// Compare base types.
JSObject* b1 = PointerType::GetBaseType(cx, t1); JSObject* b1 = PointerType::GetBaseType(cx, t1);
JSObject* b2 = PointerType::GetBaseType(cx, t2); JSObject* b2 = PointerType::GetBaseType(cx, t2);
if (!b1 || !b2) {
// One or both pointers are opaque.
// If both are opaque, compare names.
JSString* n1 = GetName(cx, t1);
JSString* n2 = GetName(cx, t2);
return b1 == b2 && JS_CompareStrings(n1, n2) == 0;
}
// Compare base types.
return TypesEqual(cx, b1, b2); return TypesEqual(cx, b1, b2);
} }
case TYPE_function: { case TYPE_function: {
@ -2957,7 +2930,7 @@ CType::PtrGetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp)
return JS_FALSE; return JS_FALSE;
} }
JSObject* pointerType = PointerType::CreateInternal(cx, NULL, obj, NULL); JSObject* pointerType = PointerType::CreateInternal(cx, obj);
if (!pointerType) if (!pointerType)
return JS_FALSE; return JS_FALSE;
@ -3081,23 +3054,12 @@ PointerType::Create(JSContext* cx, uintN argc, jsval* vp)
} }
jsval arg = JS_ARGV(cx, vp)[0]; jsval arg = JS_ARGV(cx, vp)[0];
JSObject* baseType = NULL; if (JSVAL_IS_PRIMITIVE(arg) || !CType::IsCType(cx, JSVAL_TO_OBJECT(arg))) {
JSString* name = NULL; JS_ReportError(cx, "first argument must be a CType");
if (!JSVAL_IS_PRIMITIVE(arg) &&
CType::IsCType(cx, JSVAL_TO_OBJECT(arg))) {
baseType = JSVAL_TO_OBJECT(arg);
} else if (JSVAL_IS_STRING(arg)) {
// Construct an opaque pointer type from a string.
name = JSVAL_TO_STRING(arg);
} else {
JS_ReportError(cx, "first argument must be a CType or a string");
return JS_FALSE; return JS_FALSE;
} }
JSObject* callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)); JSObject* result = CreateInternal(cx, JSVAL_TO_OBJECT(arg));
JSObject* result = CreateInternal(cx, callee, baseType, name);
if (!result) if (!result)
return JS_FALSE; return JS_FALSE;
@ -3106,37 +3068,24 @@ PointerType::Create(JSContext* cx, uintN argc, jsval* vp)
} }
JSObject* JSObject*
PointerType::CreateInternal(JSContext* cx, PointerType::CreateInternal(JSContext* cx, JSObject* baseType)
JSObject* ctor,
JSObject* baseType,
JSString* name)
{ {
JS_ASSERT(ctor || baseType); // check if we have a cached PointerType on our base CType.
JS_ASSERT((baseType && !name) || (!baseType && name)); jsval slot;
ASSERT_OK(JS_GetReservedSlot(cx, baseType, SLOT_PTR, &slot));
if (baseType) { if (!JSVAL_IS_VOID(slot))
// check if we have a cached PointerType on our base CType. return JSVAL_TO_OBJECT(slot);
jsval slot;
ASSERT_OK(JS_GetReservedSlot(cx, baseType, SLOT_PTR, &slot));
if (!JSVAL_IS_VOID(slot))
return JSVAL_TO_OBJECT(slot);
}
// Get ctypes.PointerType.prototype and the common prototype for CData objects // Get ctypes.PointerType.prototype and the common prototype for CData objects
// of this type, either from ctor or the baseType, whichever was provided. // of this type.
JSObject* typeProto; JSObject* typeProto;
JSObject* dataProto; JSObject* dataProto;
if (ctor) { typeProto = CType::GetProtoFromType(cx, baseType, SLOT_POINTERPROTO);
typeProto = CType::GetProtoFromCtor(cx, ctor, SLOT_POINTERPROTO); dataProto = CType::GetProtoFromType(cx, baseType, SLOT_POINTERDATAPROTO);
dataProto = CType::GetProtoFromCtor(cx, ctor, SLOT_POINTERDATAPROTO);
} else {
typeProto = CType::GetProtoFromType(cx, baseType, SLOT_POINTERPROTO);
dataProto = CType::GetProtoFromType(cx, baseType, SLOT_POINTERDATAPROTO);
}
// Create a new CType object with the common properties and slots. // Create a new CType object with the common properties and slots.
JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_pointer, JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_pointer,
name, INT_TO_JSVAL(sizeof(void*)), NULL, INT_TO_JSVAL(sizeof(void*)),
INT_TO_JSVAL(ffi_type_pointer.alignment), INT_TO_JSVAL(ffi_type_pointer.alignment),
&ffi_type_pointer); &ffi_type_pointer);
if (!typeObj) if (!typeObj)
@ -3147,17 +3096,15 @@ PointerType::CreateInternal(JSContext* cx,
if (!JS_SetReservedSlot(cx, typeObj, SLOT_TARGET_T, OBJECT_TO_JSVAL(baseType))) if (!JS_SetReservedSlot(cx, typeObj, SLOT_TARGET_T, OBJECT_TO_JSVAL(baseType)))
return NULL; return NULL;
if (baseType) { // Determine the name of the PointerType.
// Determine the name of the PointerType, since it wasn't supplied. JSString* nameStr = BuildTypeName(cx, typeObj);
JSString* nameStr = BuildTypeName(cx, typeObj); if (!nameStr ||
if (!nameStr || !JS_SetReservedSlot(cx, typeObj, SLOT_NAME, STRING_TO_JSVAL(nameStr)))
!JS_SetReservedSlot(cx, typeObj, SLOT_NAME, STRING_TO_JSVAL(nameStr))) return NULL;
return NULL;
// Finally, cache our newly-created PointerType on our pointed-to CType. // Finally, cache our newly-created PointerType on our pointed-to CType.
if (!JS_SetReservedSlot(cx, baseType, SLOT_PTR, OBJECT_TO_JSVAL(typeObj))) if (!JS_SetReservedSlot(cx, baseType, SLOT_PTR, OBJECT_TO_JSVAL(typeObj)))
return NULL; return NULL;
}
return typeObj; return typeObj;
} }
@ -3192,7 +3139,7 @@ PointerType::ConstructData(JSContext* cx,
if (argc >= 1) { if (argc >= 1) {
JSObject* baseObj = PointerType::GetBaseType(cx, obj); JSObject* baseObj = PointerType::GetBaseType(cx, obj);
if (baseObj && CType::GetTypeCode(cx, baseObj) == TYPE_function && if (CType::GetTypeCode(cx, baseObj) == TYPE_function &&
JSVAL_IS_OBJECT(argv[0]) && JSVAL_IS_OBJECT(argv[0]) &&
JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(argv[0]))) { JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(argv[0]))) {
// Construct a FunctionType.ptr from a JS function, and allow an // Construct a FunctionType.ptr from a JS function, and allow an
@ -3227,6 +3174,7 @@ PointerType::GetBaseType(JSContext* cx, JSObject* obj)
jsval type; jsval type;
ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_TARGET_T, &type)); ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_TARGET_T, &type));
JS_ASSERT(!JSVAL_IS_NULL(type));
return JSVAL_TO_OBJECT(type); return JSVAL_TO_OBJECT(type);
} }
@ -3289,11 +3237,6 @@ PointerType::ContentsGetter(JSContext* cx,
} }
JSObject* baseType = GetBaseType(cx, typeObj); JSObject* baseType = GetBaseType(cx, typeObj);
if (!baseType) {
JS_ReportError(cx, "cannot get contents of an opaque pointer type");
return JS_FALSE;
}
if (!CType::IsSizeDefined(cx, baseType)) { if (!CType::IsSizeDefined(cx, baseType)) {
JS_ReportError(cx, "cannot get contents of undefined size"); JS_ReportError(cx, "cannot get contents of undefined size");
return JS_FALSE; return JS_FALSE;
@ -3332,13 +3275,8 @@ PointerType::ContentsSetter(JSContext* cx,
} }
JSObject* baseType = GetBaseType(cx, typeObj); JSObject* baseType = GetBaseType(cx, typeObj);
if (!baseType) {
JS_ReportError(cx, "cannot set contents of an opaque pointer type");
return JS_FALSE;
}
if (!CType::IsSizeDefined(cx, baseType)) { if (!CType::IsSizeDefined(cx, baseType)) {
JS_ReportError(cx, "cannot get contents of undefined size"); JS_ReportError(cx, "cannot set contents of undefined size");
return JS_FALSE; return JS_FALSE;
} }
@ -3586,6 +3524,7 @@ ArrayType::GetBaseType(JSContext* cx, JSObject* obj)
jsval type; jsval type;
ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_ELEMENT_T, &type)); ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_ELEMENT_T, &type));
JS_ASSERT(!JSVAL_IS_NULL(type));
return JSVAL_TO_OBJECT(type); return JSVAL_TO_OBJECT(type);
} }
@ -3756,7 +3695,7 @@ ArrayType::AddressOfElement(JSContext* cx, uintN argc, jsval *vp)
} }
JSObject* baseType = GetBaseType(cx, typeObj); JSObject* baseType = GetBaseType(cx, typeObj);
JSObject* pointerType = PointerType::CreateInternal(cx, NULL, baseType, NULL); JSObject* pointerType = PointerType::CreateInternal(cx, baseType);
if (!pointerType) if (!pointerType)
return JS_FALSE; return JS_FALSE;
js::AutoValueRooter root(cx, pointerType); js::AutoValueRooter root(cx, pointerType);
@ -4332,7 +4271,7 @@ StructType::AddressOfField(JSContext* cx, uintN argc, jsval *vp)
return JS_FALSE; return JS_FALSE;
JSObject* baseType = field->mType; JSObject* baseType = field->mType;
JSObject* pointerType = PointerType::CreateInternal(cx, NULL, baseType, NULL); JSObject* pointerType = PointerType::CreateInternal(cx, baseType);
if (!pointerType) if (!pointerType)
return JS_FALSE; return JS_FALSE;
js::AutoValueRooter root(cx, pointerType); js::AutoValueRooter root(cx, pointerType);
@ -4418,7 +4357,7 @@ PrepareType(JSContext* cx, jsval type)
// convert array argument types to pointers, just like C. // convert array argument types to pointers, just like C.
// ImplicitConvert will do the same, when passing an array as data. // ImplicitConvert will do the same, when passing an array as data.
JSObject* baseType = ArrayType::GetBaseType(cx, result); JSObject* baseType = ArrayType::GetBaseType(cx, result);
result = PointerType::CreateInternal(cx, NULL, baseType, NULL); result = PointerType::CreateInternal(cx, baseType);
if (!result) if (!result)
return NULL; return NULL;
@ -4762,9 +4701,13 @@ FunctionType::Call(JSContext* cx,
} }
JSObject* typeObj = CData::GetCType(cx, obj); JSObject* typeObj = CData::GetCType(cx, obj);
if (CType::GetTypeCode(cx, typeObj) != TYPE_pointer || if (CType::GetTypeCode(cx, typeObj) != TYPE_pointer) {
!(typeObj = PointerType::GetBaseType(cx, typeObj)) || JS_ReportError(cx, "not a FunctionType.ptr");
CType::GetTypeCode(cx, typeObj) != TYPE_function) { return false;
}
typeObj = PointerType::GetBaseType(cx, typeObj);
if (CType::GetTypeCode(cx, typeObj) != TYPE_function) {
JS_ReportError(cx, "not a FunctionType.ptr"); JS_ReportError(cx, "not a FunctionType.ptr");
return false; return false;
} }
@ -5347,7 +5290,7 @@ CData::Address(JSContext* cx, uintN argc, jsval *vp)
} }
JSObject* typeObj = CData::GetCType(cx, obj); JSObject* typeObj = CData::GetCType(cx, obj);
JSObject* pointerType = PointerType::CreateInternal(cx, NULL, typeObj, NULL); JSObject* pointerType = PointerType::CreateInternal(cx, typeObj);
if (!pointerType) if (!pointerType)
return JS_FALSE; return JS_FALSE;
js::AutoValueRooter root(cx, pointerType); js::AutoValueRooter root(cx, pointerType);
@ -5434,11 +5377,6 @@ CData::ReadString(JSContext* cx, uintN argc, jsval *vp)
switch (typeCode) { switch (typeCode) {
case TYPE_pointer: case TYPE_pointer:
baseType = PointerType::GetBaseType(cx, typeObj); baseType = PointerType::GetBaseType(cx, typeObj);
if (!baseType) {
JS_ReportError(cx, "cannot read contents of pointer to opaque type");
return JS_FALSE;
}
data = *static_cast<void**>(GetData(cx, obj)); data = *static_cast<void**>(GetData(cx, obj));
if (data == NULL) { if (data == NULL) {
JS_ReportError(cx, "cannot read contents of null pointer"); JS_ReportError(cx, "cannot read contents of null pointer");

View File

@ -405,8 +405,7 @@ namespace CType {
} }
namespace PointerType { namespace PointerType {
JSObject* CreateInternal(JSContext* cx, JSObject* ctor, JSObject* baseType, JSObject* CreateInternal(JSContext* cx, JSObject* baseType);
JSString* name);
JSObject* GetBaseType(JSContext* cx, JSObject* obj); JSObject* GetBaseType(JSContext* cx, JSObject* obj);
} }

View File

@ -253,7 +253,7 @@ Library::Declare(JSContext* cx, uintN argc, jsval* vp)
root.setObject(typeObj); root.setObject(typeObj);
// Make a function pointer type. // Make a function pointer type.
typeObj = PointerType::CreateInternal(cx, NULL, typeObj, NULL); typeObj = PointerType::CreateInternal(cx, typeObj);
if (!typeObj) if (!typeObj)
return JS_FALSE; return JS_FALSE;
root.setObject(typeObj); root.setObject(typeObj);

View File

@ -1633,13 +1633,14 @@ function ptrValue(p) {
function run_PointerType_tests() { function run_PointerType_tests() {
run_type_ctor_class_tests(ctypes.PointerType, run_type_ctor_class_tests(ctypes.PointerType,
ctypes.PointerType(ctypes.int32_t), ctypes.PointerType("FILE"), ctypes.PointerType(ctypes.int32_t), ctypes.PointerType(ctypes.int64_t),
[ "targetType" ], [], [ "contents" ], [ "isNull" ], []); [ "targetType" ], [], [ "contents" ], [ "isNull" ], []);
do_check_throws(function() { ctypes.PointerType(); }, Error); do_check_throws(function() { ctypes.PointerType(); }, Error);
do_check_throws(function() { ctypes.PointerType(ctypes.int32_t, 5); }, Error); do_check_throws(function() { ctypes.PointerType(ctypes.int32_t, 5); }, Error);
do_check_throws(function() { ctypes.PointerType(null); }, Error); do_check_throws(function() { ctypes.PointerType(null); }, Error);
do_check_throws(function() { ctypes.PointerType(ctypes.int32_t()); }, Error); do_check_throws(function() { ctypes.PointerType(ctypes.int32_t()); }, Error);
do_check_throws(function() { ctypes.PointerType("void"); }, Error);
let name = "g_t"; let name = "g_t";
let g_t = ctypes.StructType(name, [{ a: ctypes.int32_t }, { b: ctypes.double }]); let g_t = ctypes.StructType(name, [{ a: ctypes.int32_t }, { b: ctypes.double }]);
@ -1675,15 +1676,15 @@ function run_PointerType_tests() {
do_check_throws(function() { p.value = 5; }, Error); do_check_throws(function() { p.value = 5; }, Error);
// Test opaque pointers. // Test opaque pointers.
let f_t = ctypes.PointerType("FILE*"); let f_t = ctypes.StructType("FILE").ptr;
do_check_eq(f_t.name, "FILE*"); do_check_eq(f_t.name, "FILE*");
do_check_eq(f_t.toSource(), "ctypes.PointerType(\"FILE*\")"); do_check_eq(f_t.toSource(), 'ctypes.StructType("FILE").ptr');
let f = new f_t(); let f = new f_t();
do_check_throws(function() { f.contents; }, Error); do_check_throws(function() { f.contents; }, Error);
do_check_throws(function() { f.contents = 0; }, Error); do_check_throws(function() { f.contents = 0; }, Error);
let f = f_t(5); let f = f_t(5);
do_check_throws(function() { f.contents = 0; }, Error); do_check_throws(function() { f.contents = 0; }, Error);
do_check_eq(f.toSource(), "ctypes.PointerType(\"FILE*\")(ctypes.UInt64(\"0x5\"))"); do_check_eq(f.toSource(), 'FILE.ptr(ctypes.UInt64("0x5"))');
do_check_throws(function() { f_t(p); }, Error); do_check_throws(function() { f_t(p); }, Error);
do_check_throws(function() { f.value = p; }, Error); do_check_throws(function() { f.value = p; }, Error);