From e7cac15e9849b2855ccb581083b7b5b0d581f865 Mon Sep 17 00:00:00 2001 From: Dan Witte Date: Mon, 3 May 2010 16:49:53 -0700 Subject: [PATCH] Bug 551982 - Generate t.name and t.fields lazily. Part 4: hashify StructType fields. r=benjamn --- js/src/ctypes/CTypes.cpp | 212 +++++++++++++++++++++------------------ js/src/ctypes/CTypes.h | 44 ++++++-- 2 files changed, 147 insertions(+), 109 deletions(-) diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index 9d983726be1..1a8c21e402f 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -1909,7 +1909,7 @@ ImplicitConvert(JSContext* cx, return false; } - FieldInfo* field = StructType::LookupField(cx, targetType, + const FieldInfo* field = StructType::LookupField(cx, targetType, fieldVal.value()); if (!field) return false; @@ -1927,8 +1927,8 @@ ImplicitConvert(JSContext* cx, ++i; } - Array* fields = StructType::GetFieldInfo(cx, targetType); - if (i != fields->length()) { + const FieldInfoHash* fields = StructType::GetFieldInfo(cx, targetType); + if (i != fields->count()) { JS_ReportError(cx, "missing fields"); return false; } @@ -2223,15 +2223,23 @@ BuildTypeSource(JSContext* cx, AppendString(result, ", ["); - Array* fields = StructType::GetFieldInfo(cx, typeObj); - for (size_t i = 0; i < fields->length(); ++i) { - FieldInfo* field = fields->begin() + i; + const FieldInfoHash* fields = StructType::GetFieldInfo(cx, typeObj); + size_t length = fields->count(); + Array fieldsArray; + if (!fieldsArray.resize(length)) + break; + + for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) + fieldsArray[r.front().value.mIndex] = &r.front(); + + for (size_t i = 0; i < length; ++i) { + const FieldInfoHash::Entry* entry = fieldsArray[i]; AppendString(result, "{ \""); - AppendString(result, field->mName); + AppendString(result, entry->key); AppendString(result, "\": "); - BuildTypeSource(cx, field->mType, true, result); + BuildTypeSource(cx, entry->value.mType, true, result); AppendString(result, " }"); - if (i != fields->length() - 1) + if (i != length - 1) AppendString(result, ", "); } @@ -2366,21 +2374,29 @@ BuildDataSource(JSContext* cx, // Serialize each field of the struct recursively. Each field must // be able to ImplicitConvert successfully. - Array* fields = StructType::GetFieldInfo(cx, typeObj); - for (size_t i = 0; i < fields->length(); ++i) { - FieldInfo* field = fields->begin() + i; - char* fieldData = static_cast(data) + field->mOffset; + const FieldInfoHash* fields = StructType::GetFieldInfo(cx, typeObj); + size_t length = fields->count(); + Array fieldsArray; + if (!fieldsArray.resize(length)) + return false; + + for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) + fieldsArray[r.front().value.mIndex] = &r.front(); + + for (size_t i = 0; i < length; ++i) { + const FieldInfoHash::Entry* entry = fieldsArray[i]; if (isImplicit) { AppendString(result, "\""); - AppendString(result, field->mName); + AppendString(result, entry->key); AppendString(result, "\": "); } - if (!BuildDataSource(cx, field->mType, fieldData, true, result)) + char* fieldData = static_cast(data) + entry->value.mOffset; + if (!BuildDataSource(cx, entry->value.mType, fieldData, true, result)) return false; - if (i + 1 != fields->length()) + if (i + 1 != length) AppendString(result, ", "); } @@ -2603,7 +2619,7 @@ CType::Finalize(JSContext* cx, JSObject* obj) ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FIELDINFO, &slot)); if (!JSVAL_IS_VOID(slot)) { void* info = JSVAL_TO_PRIVATE(slot); - delete static_cast*>(info); + delete static_cast(info); } // Fall through. @@ -2659,11 +2675,11 @@ CType::Trace(JSTracer* trc, JSObject* obj) if (JSVAL_IS_VOID(slot)) return; - Array* fields = - static_cast*>(JSVAL_TO_PRIVATE(slot)); - for (size_t i = 0; i < fields->length(); ++i) { - FieldInfo* field = fields->begin() + i; - JS_CALL_TRACER(trc, field->mType, JSTRACE_OBJECT, "field"); + FieldInfoHash* fields = + static_cast(JSVAL_TO_PRIVATE(slot)); + for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) { + JS_CALL_TRACER(trc, r.front().key, JSTRACE_STRING, "fieldName"); + JS_CALL_TRACER(trc, r.front().value.mType, JSTRACE_OBJECT, "fieldType"); } break; @@ -3776,66 +3792,63 @@ ArrayType::AddressOfElement(JSContext* cx, uintN argc, jsval *vp) *******************************************************************************/ // For a struct field descriptor 'val' of the form { name : type }, extract -// 'name' and 'type', and populate 'field' with the information. -static JSBool -ExtractStructField(JSContext* cx, jsval val, FieldInfo* field) +// 'name' and 'type'. +static JSString* +ExtractStructField(JSContext* cx, jsval val, JSObject** typeObj) { if (JSVAL_IS_PRIMITIVE(val)) { JS_ReportError(cx, "struct field descriptors require a valid name and type"); - return false; + return NULL; } JSObject* obj = JSVAL_TO_OBJECT(val); JSObject* iter = JS_NewPropertyIterator(cx, obj); if (!iter) - return false; + return NULL; js::AutoValueRooter iterroot(cx, iter); jsid id; if (!JS_NextProperty(cx, iter, &id)) - return false; + return NULL; js::AutoValueRooter nameVal(cx); if (!JS_IdToValue(cx, id, nameVal.addr())) - return false; + return NULL; if (!JSVAL_IS_STRING(nameVal.value())) { JS_ReportError(cx, "struct field descriptors require a valid name and type"); - return false; + return NULL; } + JSString* name = JSVAL_TO_STRING(nameVal.value()); // make sure we have one, and only one, property if (!JS_NextProperty(cx, iter, &id)) - return false; + return NULL; if (!JSVAL_IS_VOID(id)) { JS_ReportError(cx, "struct field descriptors must contain one property"); - return false; + return NULL; } - JSString* name = JSVAL_TO_STRING(nameVal.value()); - JS_ASSERT(field->mName.length() == 0); - AppendString(field->mName, name); - js::AutoValueRooter propVal(cx); if (!JS_GetUCProperty(cx, obj, name->chars(), name->length(), propVal.addr())) - return false; + return NULL; if (JSVAL_IS_PRIMITIVE(propVal.value()) || !CType::IsCType(cx, JSVAL_TO_OBJECT(propVal.value()))) { JS_ReportError(cx, "struct field descriptors require a valid name and type"); - return false; + return NULL; } // Undefined size or zero size struct members are illegal. // (Zero-size arrays are legal as struct members in C++, but libffi will // choke on a zero-size struct, so we disallow them.) - field->mType = JSVAL_TO_OBJECT(propVal.value()); + *typeObj = JSVAL_TO_OBJECT(propVal.value()); size_t size; - if (!CType::GetSafeSize(cx, field->mType, &size) || size == 0) { + if (!CType::GetSafeSize(cx, *typeObj, &size) || size == 0) { JS_ReportError(cx, "struct field types must have defined and nonzero size"); - return false; + return NULL; } - return true; + return name; } // For a struct field with 'name' and 'type', add an element to field @@ -3844,7 +3857,7 @@ static JSBool AddFieldToArray(JSContext* cx, JSObject* arrayObj, jsval* element, - const String& name, + JSString* name, JSObject* typeObj) { JSObject* fieldObj = JS_NewObject(cx, NULL, NULL, arrayObj); @@ -3854,7 +3867,7 @@ AddFieldToArray(JSContext* cx, *element = OBJECT_TO_JSVAL(fieldObj); if (!JS_DefineUCProperty(cx, fieldObj, - name.begin(), name.length(), + name->chars(), name->length(), OBJECT_TO_JSVAL(typeObj), NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) return false; @@ -3930,13 +3943,20 @@ StructType::DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT)) return JS_FALSE; - // Create an array of FieldInfo objects to stash on the type object. - AutoPtr< Array > fields(new Array); - if (!fields || !fields->resize(len)) { + // Create a hash of FieldInfo objects to stash on the type object. + FieldInfoHash* fields(new FieldInfoHash); + if (!fields || !fields->init(len)) { + delete fields; JS_ReportOutOfMemory(cx); return JS_FALSE; } + // Stash the FieldInfo hash in a reserved slot now, for GC safety of its + // constituents. + if (!JS_SetReservedSlot(cx, typeObj, SLOT_FIELDINFO, + PRIVATE_TO_JSVAL(fields))) + return JS_FALSE; + // Process the field types. size_t structSize, structAlign; if (len != 0) { @@ -3948,28 +3968,31 @@ StructType::DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj if (!JS_GetElement(cx, fieldsObj, i, item.addr())) return JS_FALSE; - FieldInfo* info = fields->begin() + i; - if (!ExtractStructField(cx, item.value(), info)) + JSObject* fieldType; + JSString* name = ExtractStructField(cx, item.value(), &fieldType); + if (!name) return JS_FALSE; - // Make sure each field name is unique. - for (size_t j = 0; j < i; ++j) { - FieldInfo* field = fields->begin() + j; - if (StringsEqual(field->mName, info->mName)) { - JS_ReportError(cx, "struct fields must have unique names"); - return JS_FALSE; - } + // Make sure each field name is unique, and add it to the hash. + FieldInfoHash::AddPtr entryPtr = fields->lookupForAdd(name); + if (entryPtr) { + JS_ReportError(cx, "struct fields must have unique names"); + return JS_FALSE; } + ASSERT_OK(fields->add(entryPtr, name, FieldInfo())); + FieldInfo& info = entryPtr->value; + info.mType = fieldType; + info.mIndex = i; // Add the field to the StructType's 'prototype' property. if (!JS_DefineUCProperty(cx, prototype, - info->mName.begin(), info->mName.length(), JSVAL_VOID, + name->chars(), name->length(), JSVAL_VOID, StructType::FieldGetter, StructType::FieldSetter, JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_PERMANENT)) return JS_FALSE; - size_t fieldSize = CType::GetSize(cx, info->mType); - size_t fieldAlign = CType::GetAlignment(cx, info->mType); + size_t fieldSize = CType::GetSize(cx, fieldType); + size_t fieldAlign = CType::GetAlignment(cx, fieldType); size_t fieldOffset = Align(structSize, fieldAlign); // Check for overflow. Since we hold invariant that fieldSize % fieldAlign // be zero, we can safely check fieldOffset + fieldSize without first @@ -3978,7 +4001,7 @@ StructType::DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj JS_ReportError(cx, "size overflow"); return JS_FALSE; } - info->mOffset = fieldOffset; + info.mOffset = fieldOffset; structSize = fieldOffset + fieldSize; if (fieldAlign > structAlign) @@ -4012,12 +4035,6 @@ StructType::DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj !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; } @@ -4028,8 +4045,8 @@ StructType::BuildFFIType(JSContext* cx, JSObject* 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(); + const FieldInfoHash* fields = GetFieldInfo(cx, obj); + size_t len = fields->count(); size_t structSize = CType::GetSize(cx, obj); size_t structAlign = CType::GetAlignment(cx, obj); @@ -4050,11 +4067,12 @@ StructType::BuildFFIType(JSContext* cx, JSObject* obj) } 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]) + for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) { + const FieldInfoHash::Entry& entry = r.front(); + ffi_type* fieldType = CType::GetFFIType(cx, entry.value.mType); + if (!fieldType) return NULL; + elements[entry.value.mIndex] = fieldType; } } else { @@ -4154,7 +4172,7 @@ StructType::ConstructData(JSContext* cx, return JS_TRUE; char* buffer = static_cast(CData::GetData(cx, result)); - Array* fields = GetFieldInfo(cx, obj); + const FieldInfoHash* fields = GetFieldInfo(cx, obj); if (argc == 1) { // There are two possible interpretations of the argument: @@ -4169,7 +4187,7 @@ StructType::ConstructData(JSContext* cx, if (ExplicitConvert(cx, argv[0], obj, buffer)) return JS_TRUE; - if (fields->length() != 1) + if (fields->count() != 1) return JS_FALSE; // If ExplicitConvert failed, and there is no pending exception, then assume @@ -4186,10 +4204,11 @@ StructType::ConstructData(JSContext* cx, // We have a type constructor of the form 'ctypes.StructType(a, b, c, ...)'. // ImplicitConvert each field. - if (argc == fields->length()) { - for (size_t i = 0; i < fields->length(); ++i) { - FieldInfo* field = fields->begin() + i; - if (!ImplicitConvert(cx, argv[i], field->mType, buffer + field->mOffset, + if (argc == fields->count()) { + for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) { + const FieldInfo& field = r.front().value; + if (!ImplicitConvert(cx, argv[field.mIndex], field.mType, + buffer + field.mOffset, false, NULL)) return JS_FALSE; } @@ -4198,11 +4217,11 @@ StructType::ConstructData(JSContext* cx, } JS_ReportError(cx, "constructor takes 0, 1, or %u arguments", - fields->length()); + fields->count()); return JS_FALSE; } -Array* +const FieldInfoHash* StructType::GetFieldInfo(JSContext* cx, JSObject* obj) { JS_ASSERT(CType::IsCType(cx, obj)); @@ -4212,23 +4231,19 @@ StructType::GetFieldInfo(JSContext* cx, JSObject* obj) ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FIELDINFO, &slot)); JS_ASSERT(!JSVAL_IS_VOID(slot) && JSVAL_TO_PRIVATE(slot)); - return static_cast*>(JSVAL_TO_PRIVATE(slot)); + return static_cast(JSVAL_TO_PRIVATE(slot)); } -FieldInfo* +const FieldInfo* StructType::LookupField(JSContext* cx, JSObject* obj, jsval idval) { JS_ASSERT(CType::IsCType(cx, obj)); JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_struct); - Array* fields = GetFieldInfo(cx, obj); - JSString* name = JSVAL_TO_STRING(idval); - for (size_t i = 0; i < fields->length(); ++i) { - FieldInfo* field = fields->begin() + i; - if (StringsEqual(field->mName, name)) - return field; - } + FieldInfoHash::Ptr ptr = GetFieldInfo(cx, obj)->lookup(name); + if (ptr) + return &ptr->value; const char* bytes = JS_GetStringBytesZ(cx, name); if (!bytes) @@ -4245,8 +4260,8 @@ StructType::BuildFieldsArray(JSContext* cx, JSObject* 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(); + const FieldInfoHash* fields = GetFieldInfo(cx, obj); + size_t len = fields->count(); // Prepare a new array for the 'fields' property of the StructType. jsval* fieldsVec; @@ -4257,12 +4272,11 @@ StructType::BuildFieldsArray(JSContext* cx, JSObject* obj) js::AutoValueRooter root(cx, fieldsProp); JS_ASSERT(len == 0 || fieldsVec); - for (size_t i = 0; i < len; ++i) { - FieldInfo* field = fields->begin() + i; - + for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) { + const FieldInfoHash::Entry& entry = r.front(); // Add the field descriptor to the array. - if (!AddFieldToArray(cx, fieldsProp, &fieldsVec[i], - field->mName, field->mType)) + if (!AddFieldToArray(cx, fieldsProp, &fieldsVec[entry.value.mIndex], + entry.key, entry.value.mType)) return NULL; } @@ -4317,7 +4331,7 @@ StructType::FieldGetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp) return JS_FALSE; } - FieldInfo* field = LookupField(cx, typeObj, idval); + const FieldInfo* field = LookupField(cx, typeObj, idval); if (!field) return JS_FALSE; @@ -4339,7 +4353,7 @@ StructType::FieldSetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp) return JS_FALSE; } - FieldInfo* field = LookupField(cx, typeObj, idval); + const FieldInfo* field = LookupField(cx, typeObj, idval); if (!field) return JS_FALSE; @@ -4369,7 +4383,7 @@ StructType::AddressOfField(JSContext* cx, uintN argc, jsval *vp) return JS_FALSE; } - FieldInfo* field = LookupField(cx, typeObj, JS_ARGV(cx, vp)[0]); + const FieldInfo* field = LookupField(cx, typeObj, JS_ARGV(cx, vp)[0]); if (!field) return JS_FALSE; diff --git a/js/src/ctypes/CTypes.h b/js/src/ctypes/CTypes.h index ae1f16bc0ef..9db5db2710a 100644 --- a/js/src/ctypes/CTypes.h +++ b/js/src/ctypes/CTypes.h @@ -41,6 +41,7 @@ #include "jscntxt.h" #include "jsapi.h" +#include "jshashtable.h" #include "prlink.h" #include "ffi.h" @@ -237,20 +238,43 @@ enum TypeCode { TYPE_struct }; +// Descriptor of one field in a StructType. The name of the field is stored +// as the key to the hash entry. struct FieldInfo { - // We need to provide a copy constructor because of Vector. - FieldInfo() {} - FieldInfo(const FieldInfo& other) - { - JS_NOT_REACHED("shouldn't be copy constructing FieldInfo"); + JSObject* mType; // CType of the field + size_t mIndex; // index of the field in the struct (first is 0) + size_t mOffset; // offset of the field in the struct, in bytes +}; + +// Hash policy for FieldInfos. +struct FieldHashPolicy +{ + typedef JSString* Key; + typedef Key Lookup; + + static uint32 hash(const Lookup &l) { + const jschar* s = l->chars(); + size_t n = l->length(); + uint32 hash = 0; + for (; n > 0; s++, n--) + hash = hash * 33 + *s; + return hash; } - String mName; - JSObject* mType; - size_t mOffset; + static JSBool match(const Key &k, const Lookup &l) { + if (k == l) + return true; + + if (k->length() != l->length()) + return false; + + return memcmp(k->chars(), l->chars(), k->length() * sizeof(jschar)) == 0; + } }; +typedef HashMap FieldInfoHash; + // Descriptor of ABI, return type, argument types, and variadicity for a // FunctionType. struct FunctionInfo @@ -423,8 +447,8 @@ namespace ArrayType { namespace StructType { JSBool DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj); - Array* GetFieldInfo(JSContext* cx, JSObject* obj); - FieldInfo* LookupField(JSContext* cx, JSObject* obj, jsval idval); + const FieldInfoHash* GetFieldInfo(JSContext* cx, JSObject* obj); + const FieldInfo* LookupField(JSContext* cx, JSObject* obj, jsval idval); JSObject* BuildFieldsArray(JSContext* cx, JSObject* obj); ffi_type* BuildFFIType(JSContext* cx, JSObject* obj); }