Bug 1478982 - Allow TypedObject fields to be flagged immutable. r=till

This allows internal clients (notably Wasm) to flag TO fields as
immutable; we need this both to provide immutability for fields that
are declared immutable in wasm structs, and to temporarily avoid the
need for type constraints on assignments to Ref-typed pointer fields.

--HG--
extra : rebase_source : 19d1b1bf81396ca305b699cda0277fd8e41f5fe9
extra : intermediate-source : d219c9587f920a0f5924dbdab3e8cf5dfecf3f75
extra : source : f1161dd31ac1cf6f050315d04b978b9d6c0c824a
This commit is contained in:
Lars T Hansen 2018-07-27 13:33:44 +02:00
parent 39bda38c95
commit 27be8bc412
8 changed files with 80 additions and 21 deletions

View File

@ -547,6 +547,7 @@ ArrayMetaTypeDescr::create(JSContext* cx,
obj->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(elementType->opaque()));
obj->initReservedSlot(JS_DESCR_SLOT_ARRAY_ELEM_TYPE, ObjectValue(*elementType));
obj->initReservedSlot(JS_DESCR_SLOT_ARRAY_LENGTH, Int32Value(length));
obj->initReservedSlot(JS_DESCR_SLOT_FLAGS, Int32Value(0));
RootedValue elementTypeVal(cx, ObjectValue(*elementType));
if (!DefineDataProperty(cx, obj, cx->names().elementType, elementTypeVal,
@ -767,6 +768,8 @@ StructMetaTypeDescr::create(JSContext* cx,
AutoValueVector fieldTypeObjs(cx); // Type descriptor of each field.
bool opaque = false; // Opacity of struct.
Vector<bool> fieldMutabilities(cx);
RootedValue fieldTypeVal(cx);
RootedId id(cx);
Rooted<TypeDescr*> fieldType(cx);
@ -796,6 +799,10 @@ StructMetaTypeDescr::create(JSContext* cx,
if (!fieldTypeObjs.append(ObjectValue(*fieldType)))
return nullptr;
// Along this path everything is mutable
if (!fieldMutabilities.append(true))
return nullptr;
// Struct is opaque if any field is opaque
if (fieldType->opaque())
opaque = true;
@ -805,7 +812,7 @@ StructMetaTypeDescr::create(JSContext* cx,
if (!structTypePrototype)
return nullptr;
return createFromArrays(cx, structTypePrototype, opaque, ids, fieldTypeObjs);
return createFromArrays(cx, structTypePrototype, opaque, ids, fieldTypeObjs, fieldMutabilities);
}
/* static */ StructTypeDescr*
@ -813,11 +820,13 @@ StructMetaTypeDescr::createFromArrays(JSContext* cx,
HandleObject structTypePrototype,
bool opaque,
AutoIdVector& ids,
AutoValueVector& fieldTypeObjs)
AutoValueVector& fieldTypeObjs,
Vector<bool>& fieldMutabilities)
{
StringBuffer stringBuffer(cx); // Canonical string repr
AutoValueVector fieldNames(cx); // Name of each field.
AutoValueVector fieldOffsets(cx); // Offset of each field field.
AutoValueVector fieldMuts(cx); // Mutability of each field.
RootedObject userFieldOffsets(cx); // User-exposed {f:offset} object
RootedObject userFieldTypes(cx); // User-exposed {f:descr} object.
Layout layout; // Field offsetter
@ -872,6 +881,9 @@ StructMetaTypeDescr::createFromArrays(JSContext* cx,
if (!fieldOffsets.append(Int32Value(offset.value())))
return nullptr;
if (!fieldMuts.append(BooleanValue(fieldMutabilities[i])))
return nullptr;
// userFieldOffsets[id] = offset
RootedValue offsetValue(cx, Int32Value(offset.value()));
if (!DefineDataProperty(cx, userFieldOffsets, id, offsetValue,
@ -908,6 +920,7 @@ StructMetaTypeDescr::createFromArrays(JSContext* cx,
descr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(AssertedCast<int32_t>(alignment)));
descr->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(totalSize.value()));
descr->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(opaque));
descr->initReservedSlot(JS_DESCR_SLOT_FLAGS, Int32Value(0));
// Construct for internal use an array with the name for each field.
{
@ -940,7 +953,19 @@ StructMetaTypeDescr::createFromArrays(JSContext* cx,
descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS, ObjectValue(*fieldOffsetsVec));
}
// Construct for internal use an array with the mutability for each field.
{
RootedObject fieldMutsVec(cx);
fieldMutsVec = NewDenseCopiedArray(cx, fieldMuts.length(),
fieldMuts.begin(), nullptr,
TenuredObject);
if (!fieldMutsVec)
return nullptr;
descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_MUTS, ObjectValue(*fieldMutsVec));
}
// Create data properties fieldOffsets and fieldTypes
// TODO: Probably also want to track mutability here, but not important yet.
if (!FreezeObject(cx, userFieldOffsets))
return nullptr;
if (!FreezeObject(cx, userFieldTypes))
@ -1041,6 +1066,14 @@ StructTypeDescr::fieldOffset(size_t index) const
return AssertedCast<size_t>(fieldOffsets.getDenseElement(index).toInt32());
}
bool
StructTypeDescr::fieldIsMutable(size_t index) const
{
ArrayObject& fieldMuts = fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_MUTS);
MOZ_ASSERT(index < fieldMuts.getDenseInitializedLength());
return fieldMuts.getDenseElement(index).toBoolean();
}
TypeDescr&
StructTypeDescr::fieldDescr(size_t index) const
{
@ -1133,6 +1166,7 @@ DefineSimpleTypeDescr(JSContext* cx,
descr->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(AssertedCast<int32_t>(T::size(type))));
descr->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(T::Opaque));
descr->initReservedSlot(JS_DESCR_SLOT_TYPE, Int32Value(int32_t(type)));
descr->initReservedSlot(JS_DESCR_SLOT_FLAGS, Int32Value(0));
if (!CreateUserSizeAndAlignmentProperties(cx, descr))
return false;
@ -1874,6 +1908,12 @@ TypedObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, Handl
if (!descr->fieldIndex(id, &fieldIndex))
break;
if (!descr->fieldIsMutable(fieldIndex)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_TYPEDOBJECT_SETTING_IMMUTABLE);
return false;
}
if (!receiver.isObject() || obj != &receiver.toObject())
return SetPropertyByDefining(cx, id, v, receiver, result);

View File

@ -417,7 +417,8 @@ class StructMetaTypeDescr : public NativeObject
HandleObject structTypePrototype,
bool opaque,
AutoIdVector& ids,
AutoValueVector& fieldTypeObjs);
AutoValueVector& fieldTypeObjs,
Vector<bool>& fieldMutabilities);
// Properties and methods to be installed on StructType.prototype,
// and hence inherited by all struct type objects:
@ -476,6 +477,9 @@ class StructTypeDescr : public ComplexTypeDescr
// Return the offset of the field at index `index`.
size_t fieldOffset(size_t index) const;
// Return the mutability of the field at index `index`.
bool fieldIsMutable(size_t index) const;
static bool call(JSContext* cx, unsigned argc, Value* vp);
private:

View File

@ -52,21 +52,23 @@
#define JS_DESCR_SLOT_TYPROTO 5 // Prototype for instances, if any
#define JS_DESCR_SLOT_ARRAYPROTO 6 // Lazily created prototype for arrays
#define JS_DESCR_SLOT_TRACE_LIST 7 // List of references for use in tracing
#define JS_DESCR_SLOT_FLAGS 8 // int32 bitvector of JS_DESCR_FLAG_*
// Slots on scalars, references
#define JS_DESCR_SLOT_TYPE 8 // Type code
#define JS_DESCR_SLOT_TYPE 9 // Type code
// Slots on array descriptors
#define JS_DESCR_SLOT_ARRAY_ELEM_TYPE 8
#define JS_DESCR_SLOT_ARRAY_LENGTH 9
#define JS_DESCR_SLOT_ARRAY_ELEM_TYPE 9
#define JS_DESCR_SLOT_ARRAY_LENGTH 10
// Slots on struct type objects
#define JS_DESCR_SLOT_STRUCT_FIELD_NAMES 8
#define JS_DESCR_SLOT_STRUCT_FIELD_TYPES 9
#define JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS 10
#define JS_DESCR_SLOT_STRUCT_FIELD_NAMES 9
#define JS_DESCR_SLOT_STRUCT_FIELD_TYPES 10
#define JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS 11
#define JS_DESCR_SLOT_STRUCT_FIELD_MUTS 12
// Maximum number of slots for any descriptor
#define JS_DESCR_SLOTS 11
#define JS_DESCR_SLOTS 13
// These constants are for use exclusively in JS code. In C++ code,
// prefer TypeRepresentation::Scalar etc, which allows you to

View File

@ -10574,7 +10574,8 @@ IonBuilder::getPropTryTypedObject(bool* emitted,
TypedObjectPrediction fieldPrediction;
size_t fieldOffset;
size_t fieldIndex;
if (!typedObjectHasField(obj, name, &fieldOffset, &fieldPrediction, &fieldIndex))
bool fieldMutable;
if (!typedObjectHasField(obj, name, &fieldOffset, &fieldPrediction, &fieldIndex, &fieldMutable))
return Ok();
switch (fieldPrediction.kind()) {
@ -11716,7 +11717,11 @@ IonBuilder::setPropTryTypedObject(bool* emitted, MDefinition* obj,
TypedObjectPrediction fieldPrediction;
size_t fieldOffset;
size_t fieldIndex;
if (!typedObjectHasField(obj, name, &fieldOffset, &fieldPrediction, &fieldIndex))
bool fieldMutable;
if (!typedObjectHasField(obj, name, &fieldOffset, &fieldPrediction, &fieldIndex, &fieldMutable))
return Ok();
if (!fieldMutable)
return Ok();
switch (fieldPrediction.kind()) {
@ -13486,7 +13491,8 @@ IonBuilder::typedObjectHasField(MDefinition* typedObj,
PropertyName* name,
size_t* fieldOffset,
TypedObjectPrediction* fieldPrediction,
size_t* fieldIndex)
size_t* fieldIndex,
bool* fieldMutable)
{
TypedObjectPrediction objPrediction = typedObjectPrediction(typedObj);
if (objPrediction.isUseless()) {
@ -13502,7 +13508,7 @@ IonBuilder::typedObjectHasField(MDefinition* typedObj,
// Determine the type/offset of the field `name`, if any.
if (!objPrediction.hasFieldNamed(NameToId(name), fieldOffset,
fieldPrediction, fieldIndex))
fieldPrediction, fieldIndex, fieldMutable))
{
trackOptimizationOutcome(TrackedOutcome::StructNoField);
return false;

View File

@ -357,7 +357,8 @@ class IonBuilder
PropertyName* name,
size_t* fieldOffset,
TypedObjectPrediction* fieldTypeReprs,
size_t* fieldIndex);
size_t* fieldIndex,
bool* fieldMutable);
MDefinition* loadTypedObjectType(MDefinition* value);
AbortReasonOr<Ok> loadTypedObjectData(MDefinition* typedObj,
MDefinition** owner,

View File

@ -256,7 +256,8 @@ TypedObjectPrediction::hasFieldNamedPrefix(const StructTypeDescr& descr,
jsid id,
size_t* fieldOffset,
TypedObjectPrediction* out,
size_t* index) const
size_t* index,
bool* isMutable) const
{
// Find the index of the field |id| if any.
if (!descr.fieldIndex(id, index))
@ -269,6 +270,7 @@ TypedObjectPrediction::hasFieldNamedPrefix(const StructTypeDescr& descr,
// Load the offset and type.
*fieldOffset = descr.fieldOffset(*index);
*out = TypedObjectPrediction(descr.fieldDescr(*index));
*isMutable = descr.fieldIsMutable(*index);
return true;
}
@ -276,7 +278,8 @@ bool
TypedObjectPrediction::hasFieldNamed(jsid id,
size_t* fieldOffset,
TypedObjectPrediction* fieldType,
size_t* fieldIndex) const
size_t* fieldIndex,
bool* fieldMutable) const
{
MOZ_ASSERT(kind() == type::Struct);
@ -288,12 +291,12 @@ TypedObjectPrediction::hasFieldNamed(jsid id,
case TypedObjectPrediction::Descr:
return hasFieldNamedPrefix(
descr().as<StructTypeDescr>(), ALL_FIELDS,
id, fieldOffset, fieldType, fieldIndex);
id, fieldOffset, fieldType, fieldIndex, fieldMutable);
case TypedObjectPrediction::Prefix:
return hasFieldNamedPrefix(
*prefix().descr, prefix().fields,
id, fieldOffset, fieldType, fieldIndex);
id, fieldOffset, fieldType, fieldIndex, fieldMutable);
default:
MOZ_CRASH("Bad prediction kind");

View File

@ -110,7 +110,8 @@ class TypedObjectPrediction {
jsid id,
size_t* fieldOffset,
TypedObjectPrediction* out,
size_t* index) const;
size_t* index,
bool* isMutable) const;
public:
@ -192,7 +193,8 @@ class TypedObjectPrediction {
bool hasFieldNamed(jsid id,
size_t* fieldOffset,
TypedObjectPrediction* fieldType,
size_t* fieldIndex) const;
size_t* fieldIndex,
bool* fieldMutable) const;
};
} // namespace jit

View File

@ -543,6 +543,7 @@ MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED, 0, JSEXN_TYPEERR, "handle unattache
MSG_DEF(JSMSG_TYPEDOBJECT_STRUCTTYPE_BAD_ARGS, 0, JSEXN_RANGEERR, "invalid field descriptor")
MSG_DEF(JSMSG_TYPEDOBJECT_STRUCTTYPE_NOT_CALLABLE, 0, JSEXN_TYPEERR, "not callable")
MSG_DEF(JSMSG_TYPEDOBJECT_TOO_BIG, 0, JSEXN_ERR, "Type is too large to allocate")
MSG_DEF(JSMSG_TYPEDOBJECT_SETTING_IMMUTABLE, 0, JSEXN_ERR, "setting immutable field")
// Array
MSG_DEF(JSMSG_TOO_LONG_ARRAY, 0, JSEXN_TYPEERR, "Too long array")