From 9cecf41fe6c0e30e37502189e1a79b68a9f11bc1 Mon Sep 17 00:00:00 2001 From: Dan Witte Date: Mon, 3 May 2010 16:26:50 -0700 Subject: [PATCH] Bug 551982 - Generate t.name and t.fields lazily. Part 3: lazy ffi_type. r=benjamn --- js/src/ctypes/CTypes.cpp | 239 +++++++++++------- js/src/ctypes/CTypes.h | 2 + .../ctypes/tests/unit/test_jsctypes.js.in | 60 ++--- 3 files changed, 175 insertions(+), 126 deletions(-) diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index d8a93170591..9d983726be1 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -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(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(JSVAL_TO_PRIVATE(slot)); - JS_ASSERT(result); - return result; + if (!JSVAL_IS_VOID(slot)) { + return static_cast(JSVAL_TO_PRIVATE(slot)); + } + + AutoPtr 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(*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 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 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 > fields(new Array); if (!fields || !fields->resize(len)) { @@ -3910,17 +3937,11 @@ StructType::DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj return JS_FALSE; } - AutoPtr::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* fields = GetFieldInfo(cx, obj); + size_t len = fields->length(); + + size_t structSize = CType::GetSize(cx, obj); + size_t structAlign = CType::GetAlignment(cx, obj); + + AutoPtr ffiType(new ffi_type); + if (!ffiType) { + JS_ReportOutOfMemory(cx); + return NULL; + } + ffiType->type = FFI_TYPE_STRUCT; + + AutoPtr::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; diff --git a/js/src/ctypes/CTypes.h b/js/src/ctypes/CTypes.h index 205cca33ee1..ae1f16bc0ef 100644 --- a/js/src/ctypes/CTypes.h +++ b/js/src/ctypes/CTypes.h @@ -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* 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 { diff --git a/toolkit/components/ctypes/tests/unit/test_jsctypes.js.in b/toolkit/components/ctypes/tests/unit/test_jsctypes.js.in index 6d1e4298473..32bf924163f 100644 --- a/toolkit/components/ctypes/tests/unit/test_jsctypes.js.in +++ b/toolkit/components/ctypes/tests/unit/test_jsctypes.js.in @@ -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); }