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

View File

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

View File

@ -52,21 +52,23 @@
#define JS_DESCR_SLOT_TYPROTO 5 // Prototype for instances, if any #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_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_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 // Slots on scalars, references
#define JS_DESCR_SLOT_TYPE 8 // Type code #define JS_DESCR_SLOT_TYPE 9 // Type code
// Slots on array descriptors // Slots on array descriptors
#define JS_DESCR_SLOT_ARRAY_ELEM_TYPE 8 #define JS_DESCR_SLOT_ARRAY_ELEM_TYPE 9
#define JS_DESCR_SLOT_ARRAY_LENGTH 9 #define JS_DESCR_SLOT_ARRAY_LENGTH 10
// Slots on struct type objects // Slots on struct type objects
#define JS_DESCR_SLOT_STRUCT_FIELD_NAMES 8 #define JS_DESCR_SLOT_STRUCT_FIELD_NAMES 9
#define JS_DESCR_SLOT_STRUCT_FIELD_TYPES 9 #define JS_DESCR_SLOT_STRUCT_FIELD_TYPES 10
#define JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS 10 #define JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS 11
#define JS_DESCR_SLOT_STRUCT_FIELD_MUTS 12
// Maximum number of slots for any descriptor // 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, // These constants are for use exclusively in JS code. In C++ code,
// prefer TypeRepresentation::Scalar etc, which allows you to // prefer TypeRepresentation::Scalar etc, which allows you to

View File

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

View File

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

View File

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

View File

@ -110,7 +110,8 @@ class TypedObjectPrediction {
jsid id, jsid id,
size_t* fieldOffset, size_t* fieldOffset,
TypedObjectPrediction* out, TypedObjectPrediction* out,
size_t* index) const; size_t* index,
bool* isMutable) const;
public: public:
@ -192,7 +193,8 @@ class TypedObjectPrediction {
bool hasFieldNamed(jsid id, bool hasFieldNamed(jsid id,
size_t* fieldOffset, size_t* fieldOffset,
TypedObjectPrediction* fieldType, TypedObjectPrediction* fieldType,
size_t* fieldIndex) const; size_t* fieldIndex,
bool* fieldMutable) const;
}; };
} // namespace jit } // 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_BAD_ARGS, 0, JSEXN_RANGEERR, "invalid field descriptor")
MSG_DEF(JSMSG_TYPEDOBJECT_STRUCTTYPE_NOT_CALLABLE, 0, JSEXN_TYPEERR, "not callable") 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_TOO_BIG, 0, JSEXN_ERR, "Type is too large to allocate")
MSG_DEF(JSMSG_TYPEDOBJECT_SETTING_IMMUTABLE, 0, JSEXN_ERR, "setting immutable field")
// Array // Array
MSG_DEF(JSMSG_TOO_LONG_ARRAY, 0, JSEXN_TYPEERR, "Too long array") MSG_DEF(JSMSG_TOO_LONG_ARRAY, 0, JSEXN_TYPEERR, "Too long array")