Bug 551982 - Generate t.name and t.fields lazily. Part 3: lazy ffi_type. r=benjamn

This commit is contained in:
Dan Witte 2010-05-03 16:26:50 -07:00
parent 5667b860bf
commit 2a0914b781
3 changed files with 175 additions and 126 deletions

View File

@ -2516,7 +2516,7 @@ CType::Create(JSContext* cx,
// Set up the reserved slots.
if (!JS_SetReservedSlot(cx, typeObj, SLOT_TYPECODE, INT_TO_JSVAL(type)) ||
!JS_SetReservedSlot(cx, typeObj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(ffiType)) ||
(ffiType && !JS_SetReservedSlot(cx, typeObj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(ffiType))) ||
(name && !JS_SetReservedSlot(cx, typeObj, SLOT_NAME, STRING_TO_JSVAL(name))) ||
!JS_SetReservedSlot(cx, typeObj, SLOT_SIZE, size) ||
!JS_SetReservedSlot(cx, typeObj, SLOT_ALIGN, align))
@ -2611,7 +2611,7 @@ CType::Finalize(JSContext* cx, JSObject* obj)
// Free the ffi_type info.
jsval slot;
ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FFITYPE, &slot));
if (!JSVAL_IS_VOID(slot) && JSVAL_TO_PRIVATE(slot)) {
if (!JSVAL_IS_VOID(slot)) {
ffi_type* ffiType = static_cast<ffi_type*>(JSVAL_TO_PRIVATE(slot));
delete[] ffiType->elements;
delete ffiType;
@ -2849,9 +2849,29 @@ CType::GetFFIType(JSContext* cx, JSObject* obj)
jsval slot;
ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FFITYPE, &slot));
ffi_type* result = static_cast<ffi_type*>(JSVAL_TO_PRIVATE(slot));
JS_ASSERT(result);
return result;
if (!JSVAL_IS_VOID(slot)) {
return static_cast<ffi_type*>(JSVAL_TO_PRIVATE(slot));
}
AutoPtr<ffi_type> result;
switch (CType::GetTypeCode(cx, obj)) {
case TYPE_array:
result = ArrayType::BuildFFIType(cx, obj);
break;
case TYPE_struct:
result = StructType::BuildFFIType(cx, obj);
break;
default:
JS_NOT_REACHED("simple types must have an ffi_type");
}
if (!result ||
!JS_SetReservedSlot(cx, obj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(result.get())))
return NULL;
return result.forget();
}
JSString*
@ -3365,9 +3385,6 @@ ArrayType::CreateInternal(JSContext* cx,
return NULL;
}
ffi_type* ffiType = NULL;
size_t align = CType::GetAlignment(cx, baseType);
jsval sizeVal = JSVAL_VOID;
jsval lengthVal = JSVAL_VOID;
if (lengthDefined) {
@ -3380,39 +3397,13 @@ ArrayType::CreateInternal(JSContext* cx,
if (!SizeTojsval(cx, size, &sizeVal) ||
!SizeTojsval(cx, length, &lengthVal))
return NULL;
// Create an ffi_type to represent the array. This is necessary for the case
// where the array is part of a struct. Since libffi has no intrinsic
// support for array types, we approximate it by creating a struct type
// with elements of type 'baseType' and with appropriate size and alignment
// values. It would be nice to not do all the work of setting up 'elements',
// but some libffi platforms currently require that it be meaningful. I'm
// looking at you, x86_64.
ffiType = new ffi_type;
if (!ffiType) {
JS_ReportOutOfMemory(cx);
return NULL;
}
ffiType->type = FFI_TYPE_STRUCT;
ffiType->size = size;
ffiType->alignment = align;
ffiType->elements = new ffi_type*[length + 1];
if (!ffiType->elements) {
delete ffiType;
JS_ReportAllocationOverflow(cx);
return NULL;
}
ffi_type* ffiBaseType = CType::GetFFIType(cx, baseType);
for (size_t i = 0; i < length; ++i)
ffiType->elements[i] = ffiBaseType;
ffiType->elements[length] = NULL;
}
size_t align = CType::GetAlignment(cx, baseType);
// Create a new CType object with the common properties and slots.
JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_array, NULL,
sizeVal, INT_TO_JSVAL(align), ffiType);
sizeVal, INT_TO_JSVAL(align), NULL);
if (!typeObj)
return NULL;
js::AutoValueRooter root(cx, typeObj);
@ -3585,6 +3576,49 @@ ArrayType::GetLength(JSContext* cx, JSObject* obj)
return Convert<size_t>(*JSVAL_TO_DOUBLE(length));
}
ffi_type*
ArrayType::BuildFFIType(JSContext* cx, JSObject* obj)
{
JS_ASSERT(CType::IsCType(cx, obj));
JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_array);
JS_ASSERT(CType::IsSizeDefined(cx, obj));
JSObject* baseType = ArrayType::GetBaseType(cx, obj);
ffi_type* ffiBaseType = CType::GetFFIType(cx, baseType);
if (!ffiBaseType)
return NULL;
size_t length = ArrayType::GetLength(cx, obj);
// Create an ffi_type to represent the array. This is necessary for the case
// where the array is part of a struct. Since libffi has no intrinsic
// support for array types, we approximate it by creating a struct type
// with elements of type 'baseType' and with appropriate size and alignment
// values. It would be nice to not do all the work of setting up 'elements',
// but some libffi platforms currently require that it be meaningful. I'm
// looking at you, x86_64.
AutoPtr<ffi_type> ffiType(new ffi_type);
if (!ffiType) {
JS_ReportOutOfMemory(cx);
return NULL;
}
ffiType->type = FFI_TYPE_STRUCT;
ffiType->size = CType::GetSize(cx, obj);
ffiType->alignment = CType::GetAlignment(cx, obj);
ffiType->elements = new ffi_type*[length + 1];
if (!ffiType->elements) {
JS_ReportAllocationOverflow(cx);
return NULL;
}
for (size_t i = 0; i < length; ++i)
ffiType->elements[i] = ffiBaseType;
ffiType->elements[length] = NULL;
return ffiType.forget();
}
JSBool
ArrayType::ElementTypeGetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp)
{
@ -3778,7 +3812,7 @@ ExtractStructField(JSContext* cx, jsval val, FieldInfo* field)
}
JSString* name = JSVAL_TO_STRING(nameVal.value());
field->mName.clear();
JS_ASSERT(field->mName.length() == 0);
AppendString(field->mName, name);
js::AutoValueRooter propVal(cx);
@ -3896,13 +3930,6 @@ StructType::DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj
NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT))
return JS_FALSE;
AutoPtr<ffi_type> ffiType(new ffi_type);
if (!ffiType) {
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
ffiType->type = FFI_TYPE_STRUCT;
// Create an array of FieldInfo objects to stash on the type object.
AutoPtr< Array<FieldInfo> > fields(new Array<FieldInfo>);
if (!fields || !fields->resize(len)) {
@ -3910,17 +3937,11 @@ StructType::DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj
return JS_FALSE;
}
AutoPtr<ffi_type*>::Array elements;
// Process the field types and fill in the ffi_type fields.
size_t structSize = 0, structAlign = 0;
// Process the field types.
size_t structSize, structAlign;
if (len != 0) {
elements = new ffi_type*[len + 1];
if (!elements) {
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
elements[len] = NULL;
structSize = 0;
structAlign = 0;
for (jsuint i = 0; i < len; ++i) {
js::AutoValueRooter item(cx);
@ -3947,8 +3968,6 @@ StructType::DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj
JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_PERMANENT))
return JS_FALSE;
elements[i] = CType::GetFFIType(cx, info->mType);
size_t fieldSize = CType::GetSize(cx, info->mType);
size_t fieldAlign = CType::GetAlignment(cx, info->mType);
size_t fieldOffset = Align(structSize, fieldAlign);
@ -3981,10 +4000,71 @@ StructType::DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj
// no getters or setters, and will be initialized to zero.
structSize = 1;
structAlign = 1;
}
jsval sizeVal;
if (!SizeTojsval(cx, structSize, &sizeVal))
return JS_FALSE;
if (!JS_SetReservedSlot(cx, typeObj, SLOT_SIZE, sizeVal) ||
!JS_SetReservedSlot(cx, typeObj, SLOT_ALIGN, INT_TO_JSVAL(structAlign)) ||
//!JS_SealObject(cx, prototype, JS_FALSE) || // XXX fixme - see bug 541212!
!JS_SetReservedSlot(cx, typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype)))
return JS_FALSE;
// Stash the FieldInfo array in a reserved slot.
if (!JS_SetReservedSlot(cx, typeObj, SLOT_FIELDINFO,
PRIVATE_TO_JSVAL(fields.get())))
return JS_FALSE;
fields.forget();
return JS_TRUE;
}
ffi_type*
StructType::BuildFFIType(JSContext* cx, JSObject* obj)
{
JS_ASSERT(CType::IsCType(cx, obj));
JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_struct);
JS_ASSERT(CType::IsSizeDefined(cx, obj));
Array<FieldInfo>* fields = GetFieldInfo(cx, obj);
size_t len = fields->length();
size_t structSize = CType::GetSize(cx, obj);
size_t structAlign = CType::GetAlignment(cx, obj);
AutoPtr<ffi_type> ffiType(new ffi_type);
if (!ffiType) {
JS_ReportOutOfMemory(cx);
return NULL;
}
ffiType->type = FFI_TYPE_STRUCT;
AutoPtr<ffi_type*>::Array elements;
if (len != 0) {
elements = new ffi_type*[len + 1];
if (!elements) {
JS_ReportOutOfMemory(cx);
return NULL;
}
elements[len] = NULL;
for (size_t i = 0; i < len; ++i) {
FieldInfo* info = fields->begin() + i;
elements[i] = CType::GetFFIType(cx, info->mType);
if (!elements[i])
return NULL;
}
} else {
// Represent an empty struct as having a size of 1 byte, just like C++.
JS_ASSERT(structSize == 1);
JS_ASSERT(structAlign == 1);
elements = new ffi_type*[2];
if (!elements) {
JS_ReportOutOfMemory(cx);
return JS_FALSE;
return NULL;
}
elements[0] = &ffi_type_uint8;
elements[1] = NULL;
@ -4012,30 +4092,8 @@ StructType::DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj
ffiType->alignment = structAlign;
#endif
jsval sizeVal;
if (!SizeTojsval(cx, structSize, &sizeVal))
return JS_FALSE;
// Set up the reserved slots.
if (!JS_SetReservedSlot(cx, typeObj, SLOT_FFITYPE,
PRIVATE_TO_JSVAL(ffiType.get())))
return JS_FALSE;
ffiType.forget();
elements.forget();
if (!JS_SetReservedSlot(cx, typeObj, SLOT_SIZE, sizeVal) ||
!JS_SetReservedSlot(cx, typeObj, SLOT_ALIGN, INT_TO_JSVAL(structAlign)) ||
//!JS_SealObject(cx, prototype, JS_FALSE) || // XXX fixme - see bug 541212!
!JS_SetReservedSlot(cx, typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype)))
return JS_FALSE;
// Stash the FieldInfo array in a reserved slot.
if (!JS_SetReservedSlot(cx, typeObj, SLOT_FIELDINFO,
PRIVATE_TO_JSVAL(fields.get())))
return JS_FALSE;
fields.forget();
return JS_TRUE;
return ffiType.forget();
}
JSBool
@ -4476,11 +4534,15 @@ PrepareCIF(JSContext* cx,
return false;
}
ffi_type* rtype = CType::GetFFIType(cx, fninfo->mReturnType);
if (!rtype)
return false;
ffi_status status =
ffi_prep_cif(&fninfo->mCIF,
abi,
fninfo->mFFITypes.length(),
CType::GetFFIType(cx, fninfo->mReturnType),
rtype,
fninfo->mFFITypes.begin());
switch (status) {
@ -4557,8 +4619,12 @@ NewFunctionInfo(JSContext* cx,
if (!argType)
return NULL;
ffi_type* ffiType = CType::GetFFIType(cx, argType);
if (!ffiType)
return NULL;
fninfo->mArgTypes.append(argType);
fninfo->mFFITypes.append(CType::GetFFIType(cx, argType));
fninfo->mFFITypes.append(ffiType);
}
if (fninfo->mIsVariadic)
@ -4642,9 +4708,8 @@ 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, JSVAL_VOID, JSVAL_VOID, &ffi_type_void);
NULL, JSVAL_VOID, JSVAL_VOID, NULL);
if (!typeObj)
return NULL;
js::AutoValueRooter root(cx, typeObj);
@ -4805,11 +4870,11 @@ FunctionType::Call(JSContext* cx,
!(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[i], &strings)) {
!ConvertArgument(cx, argv[i], type, &values[i], &strings) ||
!(fninfo->mFFITypes[i] = CType::GetFFIType(cx, type))) {
// These functions report their own errors.
return false;
}
fninfo->mFFITypes[i] = CType::GetFFIType(cx, type);
}
if (!PrepareCIF(cx, fninfo))
return false;

View File

@ -417,6 +417,7 @@ namespace ArrayType {
JSObject* GetBaseType(JSContext* cx, JSObject* obj);
size_t GetLength(JSContext* cx, JSObject* obj);
bool GetSafeLength(JSContext* cx, JSObject* obj, size_t* result);
ffi_type* BuildFFIType(JSContext* cx, JSObject* obj);
}
namespace StructType {
@ -425,6 +426,7 @@ namespace StructType {
Array<FieldInfo>* GetFieldInfo(JSContext* cx, JSObject* obj);
FieldInfo* LookupField(JSContext* cx, JSObject* obj, jsval idval);
JSObject* BuildFieldsArray(JSContext* cx, JSObject* obj);
ffi_type* BuildFFIType(JSContext* cx, JSObject* obj);
}
namespace FunctionType {

View File

@ -1454,69 +1454,57 @@ function run_StructType_tests() {
do_check_throws(function() { t_t.fields[4].e = 0; }, Error);
// Check that struct size bounds work, and that large, but not illegal, sizes
// are OK. This gets tricky. The type constructor needs to allocate 'n'
// fields for the type descriptor, so we have to build up structs of structs
// to avoid trying to allocate an insane size.
// are OK.
if (ctypes.size_t.size == 4) {
// Test 1: overflow struct size + field padding + field size.
let large_t = ctypes.int8_t;
while (large_t.size != 0xffffffff)
large_t = ctypes.StructType("large_t",
[{"a": large_t.array(2)}, {"b": ctypes.int8_t}]);
let large_t = ctypes.StructType("large_t",
[{"a": ctypes.int8_t.array(0xffffffff)}]);
do_check_eq(large_t.size, 0xffffffff);
do_check_throws(function() {
ctypes.StructType("large_t", [{"a": large_t}, {"b": ctypes.int8_t}]);
}, Error);
// Test 2: overflow struct size + struct tail padding.
// To do this, we use a struct with maximum size and alignment 2.
large_t = ctypes.int16_t;
while (large_t.size != 0xfffffffe)
large_t = ctypes.StructType("large_t",
[{"a": large_t.array(2)}, {"b": ctypes.int16_t}]);
large_t = ctypes.StructType("large_t",
[{"a": ctypes.int16_t.array(0xfffffffe / 2)}]);
do_check_eq(large_t.size, 0xfffffffe);
do_check_throws(function() {
ctypes.StructType("large_t", [{"a": large_t}, {"b": ctypes.int8_t}]);
}, Error);
} else {
// Test 1: overflow struct size when converting from size_t to jsdouble.
let small_t = ctypes.int8_t.array(0x800);
let large_t = small_t;
while (large_t.size != 0xfffffffffffff800)
large_t = ctypes.StructType("large_t",
[{"a": large_t.array(2)}, {"b": small_t}]);
let large_t = ctypes.StructType("large_t",
[{"a": ctypes.int8_t.array(0xfffffffffffff800)}]);
do_check_eq(large_t.size, 0xfffffffffffff800);
do_check_throws(function() {
ctypes.StructType("large_t", [{"a": large_t}, {"b": ctypes.int8_t}]);
}, Error);
small_t = ctypes.int8_t.array(0x400);
let small_t = ctypes.int8_t.array(0x400);
do_check_throws(function() {
ctypes.StructType("large_t", [{"a": large_t}, {"b": small_t}]);
}, Error);
large_t = ctypes.int8_t;
while (large_t.size != 0x1fffffffffffff)
large_t = ctypes.StructType("large_t",
[{"a": large_t.array(2)}, {"b": ctypes.int8_t}]);
large_t = ctypes.StructType("large_t",
[{"a": ctypes.int8_t.array(0x1fffffffffffff)}]);
do_check_eq(large_t.size, 0x1fffffffffffff);
do_check_throws(function() {
ctypes.StructType("large_t", [{"a": large_t.array(2)}, {"b": ctypes.int8_t}]);
}, Error);
// Test 2: overflow struct size + field padding + field size.
large_t = ctypes.int8_t.array(0xfffffffffffff800);
small_t = ctypes.int8_t.array(0x800);
large_t = small_t;
while (large_t.size != 0xfffffffffffff800)
large_t = ctypes.StructType("large_t",
[{"a": large_t.array(2)}, {"b": small_t}]);
do_check_throws(function() {
ctypes.StructType("large_t", [{"a": large_t}, {"b": small_t}]);
}, Error);
// Test 3: overflow struct size + struct tail padding.
// To do this, we use a struct with maximum size and alignment 2.
small_t = ctypes.int16_t.array(0x800);
large_t = small_t;
while (large_t.size != 0xfffffffffffff000)
large_t = ctypes.StructType("large_t",
[{"a": large_t.array(2)}, {"b": small_t}]);
large_t = ctypes.StructType("large_t",
[{"a": ctypes.int16_t.array(0xfffffffffffff000 / 2)}]);
do_check_eq(large_t.size, 0xfffffffffffff000);
small_t = ctypes.int8_t.array(0xfff);
do_check_throws(function() {
ctypes.StructType("large_t", [{"a": large_t}, {"b": small_t}]);
@ -1896,9 +1884,7 @@ function run_ArrayType_tests() {
do_check_eq(b_t.length, 0xffff);
// Check that array size bounds work, and that large, but not illegal, sizes
// are OK. This gets tricky. The type constructor needs to allocate 'n'
// elements for the type descriptor, so we have to build up arrays of arrays
// to avoid trying to allocate an insane size.
// are OK.
if (ctypes.size_t.size == 4) {
do_check_throws(function() {
ctypes.ArrayType(ctypes.int8_t, 0x100000000);
@ -1907,9 +1893,7 @@ function run_ArrayType_tests() {
ctypes.ArrayType(ctypes.int16_t, 0x80000000);
}, Error);
let large_t = ctypes.int8_t;
while (large_t.size != 0x80000000)
large_t = large_t.array(2);
let large_t = ctypes.int8_t.array(0x80000000);
do_check_throws(function() { large_t.array(2); }, Error);
} else {
@ -1920,9 +1904,7 @@ function run_ArrayType_tests() {
ctypes.ArrayType(ctypes.int16_t, ctypes.UInt64("0x8000000000000000"));
}, Error);
let large_t = ctypes.int8_t;
while (large_t.size != 0x8000000000000000)
large_t = large_t.array(2);
let large_t = ctypes.int8_t.array(0x8000000000000000);
do_check_throws(function() { large_t.array(2); }, Error);
}