Bug 551982 - Generate t.name and t.fields lazily. Part 4: hashify StructType fields. r=benjamn

This commit is contained in:
Dan Witte 2010-05-03 16:49:53 -07:00
parent 7124fa12fa
commit e7cac15e98
2 changed files with 147 additions and 109 deletions

View File

@ -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<FieldInfo>* 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<FieldInfo>* 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<const FieldInfoHash::Entry*, 64> 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<FieldInfo>* fields = StructType::GetFieldInfo(cx, typeObj);
for (size_t i = 0; i < fields->length(); ++i) {
FieldInfo* field = fields->begin() + i;
char* fieldData = static_cast<char*>(data) + field->mOffset;
const FieldInfoHash* fields = StructType::GetFieldInfo(cx, typeObj);
size_t length = fields->count();
Array<const FieldInfoHash::Entry*, 64> 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<char*>(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<Array<FieldInfo>*>(info);
delete static_cast<FieldInfoHash*>(info);
}
// Fall through.
@ -2659,11 +2675,11 @@ CType::Trace(JSTracer* trc, JSObject* obj)
if (JSVAL_IS_VOID(slot))
return;
Array<FieldInfo>* fields =
static_cast<Array<FieldInfo>*>(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<FieldInfoHash*>(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<FieldInfo> > fields(new Array<FieldInfo>);
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<FieldInfo>* 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<char*>(CData::GetData(cx, result));
Array<FieldInfo>* 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<FieldInfo>*
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<Array<FieldInfo>*>(JSVAL_TO_PRIVATE(slot));
return static_cast<const FieldInfoHash*>(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<FieldInfo>* 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<FieldInfo>* 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;

View File

@ -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<JSString*, FieldInfo, FieldHashPolicy, SystemAllocPolicy> 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<FieldInfo>* 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);
}