Bug 1478982 - Allow TypedObject fields to be flagged immutable. r=till
authorLars T Hansen <lhansen@mozilla.com>
Fri, 27 Jul 2018 13:33:44 +0200
changeset 430047 3e4eec1a2feee53119135913a33a2fa62c7b4ceb
parent 430046 3858eb122b9fb0868140e7c553ec99a49cc82611
child 430048 1cd14797e7cc6621f7bd08eef48a56b9483963a7
push id34381
push userdluca@mozilla.com
push dateFri, 03 Aug 2018 22:01:59 +0000
treeherdermozilla-central@ff0d31843793 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill
bugs1478982
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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.
js/src/builtin/TypedObject.cpp
js/src/builtin/TypedObject.h
js/src/builtin/TypedObjectConstants.h
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/TypedObjectPrediction.cpp
js/src/jit/TypedObjectPrediction.h
js/src/js.msg
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -542,16 +542,17 @@ ArrayMetaTypeDescr::create(JSContext* cx
 
     obj->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(ArrayTypeDescr::Kind));
     obj->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr));
     obj->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(elementType->alignment()));
     obj->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(size));
     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,
                             JSPROP_READONLY | JSPROP_PERMANENT))
     {
         return nullptr;
     }
 
@@ -762,16 +763,18 @@ StructMetaTypeDescr::create(JSContext* c
         return nullptr;
 
     // Iterate through each field. Collect values for the various
     // vectors below and also track total size and alignment. Be wary
     // of overflow!
     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);
 
     for (unsigned int i = 0; i < ids.length(); i++) {
         id = ids[i];
 
         // Check that all the property names are non-numeric strings.
@@ -791,38 +794,44 @@ StructMetaTypeDescr::create(JSContext* c
             ReportCannotConvertTo(cx, fieldTypeVal, "StructType field specifier");
             return nullptr;
         }
 
         // Collect field type object
         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;
     }
 
     RootedObject structTypePrototype(cx, GetPrototype(cx, metaTypeDescr));
     if (!structTypePrototype)
         return nullptr;
 
-    return createFromArrays(cx, structTypePrototype, opaque, ids, fieldTypeObjs);
+    return createFromArrays(cx, structTypePrototype, opaque, ids, fieldTypeObjs, fieldMutabilities);
 }
 
 /* static */ StructTypeDescr*
 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
 
     userFieldOffsets = NewBuiltinClassInstance<PlainObject>(cx, TenuredObject);
     if (!userFieldOffsets)
         return nullptr;
 
@@ -867,16 +876,19 @@ StructMetaTypeDescr::createFromArrays(JS
         if (!offset.isValid()) {
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPEDOBJECT_TOO_BIG);
             return nullptr;
         }
         MOZ_ASSERT(offset.value() >= 0);
         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,
                                 JSPROP_READONLY | JSPROP_PERMANENT))
         {
             return nullptr;
         }
     }
@@ -903,16 +915,17 @@ StructMetaTypeDescr::createFromArrays(JS
     if (!descr)
         return nullptr;
 
     descr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(type::Struct));
     descr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr));
     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.
     {
         RootedObject fieldNamesVec(cx);
         fieldNamesVec = NewDenseCopiedArray(cx, fieldNames.length(),
                                             fieldNames.begin(), nullptr,
                                             TenuredObject);
         if (!fieldNamesVec)
@@ -935,17 +948,29 @@ StructMetaTypeDescr::createFromArrays(JS
         fieldOffsetsVec = NewDenseCopiedArray(cx, fieldOffsets.length(),
                                               fieldOffsets.begin(), nullptr,
                                               TenuredObject);
         if (!fieldOffsetsVec)
             return nullptr;
         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))
         return nullptr;
     RootedValue userFieldOffsetsValue(cx, ObjectValue(*userFieldOffsets));
     if (!DefineDataProperty(cx, descr, cx->names().fieldOffsets, userFieldOffsetsValue,
                             JSPROP_READONLY | JSPROP_PERMANENT))
     {
@@ -1036,16 +1061,24 @@ StructTypeDescr::fieldName(size_t index)
 size_t
 StructTypeDescr::fieldOffset(size_t index) const
 {
     ArrayObject& fieldOffsets = fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS);
     MOZ_ASSERT(index < fieldOffsets.getDenseInitializedLength());
     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
 {
     ArrayObject& fieldDescrs = fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_TYPES);
     MOZ_ASSERT(index < fieldDescrs.getDenseInitializedLength());
     return fieldDescrs.getDenseElement(index).toObject().as<TypeDescr>();
 }
 
@@ -1128,16 +1161,17 @@ DefineSimpleTypeDescr(JSContext* cx,
         return false;
 
     descr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(T::Kind));
     descr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(className));
     descr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(T::alignment(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_TYPE, Int32Value(int32_t(type)));
+    descr->initReservedSlot(JS_DESCR_SLOT_FLAGS, Int32Value(0));
 
     if (!CreateUserSizeAndAlignmentProperties(cx, descr))
         return false;
 
     if (!JS_DefineFunctions(cx, descr, T::typeObjectMethods))
         return false;
 
     // Create the typed prototype for the scalar type. This winds up
@@ -1869,16 +1903,22 @@ TypedObject::obj_setProperty(JSContext* 
 
       case type::Struct: {
         Rooted<StructTypeDescr*> descr(cx, &typedObj->typeDescr().as<StructTypeDescr>());
 
         size_t fieldIndex;
         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);
 
         size_t offset = descr->fieldOffset(fieldIndex);
         Rooted<TypeDescr*> fieldType(cx, &descr->fieldDescr(fieldIndex));
         RootedAtom fieldName(cx, &descr->fieldName(fieldIndex));
         if (!ConvertAndCopyTo(cx, fieldType, typedObj, offset, fieldName, v))
             return false;
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -412,17 +412,18 @@ class StructMetaTypeDescr : public Nativ
   public:
     // The prototype cannot be null.
     // The names in `ids` must all be non-numeric.
     // The type objects in `fieldTypeObjs` must all be TypeDescr objects.
     static StructTypeDescr* createFromArrays(JSContext* cx,
                                              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:
     static const JSPropertySpec typeObjectProperties[];
     static const JSFunctionSpec typeObjectMethods[];
 
     // Properties and methods to be installed on StructType.prototype.prototype,
     // and hence inherited by all struct *typed* objects:
@@ -471,16 +472,19 @@ class StructTypeDescr : public ComplexTy
     JSAtom& fieldName(size_t index) const;
 
     // Return the type descr of the field at index `index`.
     TypeDescr& fieldDescr(size_t index) const;
 
     // 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:
     ArrayObject& fieldInfoObject(size_t slot) const {
         return getReservedSlot(slot).toObject().as<ArrayObject>();
     }
 };
 
--- a/js/src/builtin/TypedObjectConstants.h
+++ b/js/src/builtin/TypedObjectConstants.h
@@ -47,31 +47,33 @@
 #define JS_DESCR_SLOT_KIND               0  // Atomized string representation
 #define JS_DESCR_SLOT_STRING_REPR        1  // Atomized string representation
 #define JS_DESCR_SLOT_ALIGNMENT          2  // Alignment in bytes
 #define JS_DESCR_SLOT_SIZE               3  // Size in bytes, else 0
 #define JS_DESCR_SLOT_OPAQUE             4  // Atomized string representation
 #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
 // write a switch which will receive a warning if you omit a case.
 #define JS_TYPEREPR_SCALAR_KIND         1
 #define JS_TYPEREPR_REFERENCE_KIND      2
 #define JS_TYPEREPR_STRUCT_KIND         3
 #define JS_TYPEREPR_ARRAY_KIND          4
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -10569,17 +10569,18 @@ IonBuilder::getPropTryNotDefined(bool* e
 AbortReasonOr<Ok>
 IonBuilder::getPropTryTypedObject(bool* emitted,
                                   MDefinition* obj,
                                   PropertyName* name)
 {
     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()) {
       case type::Struct:
       case type::Array:
         return getPropTryComplexPropOfTypedObject(emitted,
                                                   obj,
                                                   fieldOffset,
@@ -11711,17 +11712,21 @@ IonBuilder::setPropTryCommonDOMSetter(bo
 
 AbortReasonOr<Ok>
 IonBuilder::setPropTryTypedObject(bool* emitted, MDefinition* obj,
                                   PropertyName* name, MDefinition* value)
 {
     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()) {
       case type::Reference:
         return setPropTryReferencePropOfTypedObject(emitted, obj, fieldOffset,
                                                     value, fieldPrediction, name);
 
       case type::Scalar:
@@ -13481,33 +13486,34 @@ IonBuilder::loadTypedObjectElements(MDef
 // set `objTypes` of the field owner. If a field is found, returns true
 // and sets *fieldOffset, *fieldPrediction, and *fieldIndex. Returns false
 // otherwise. Infallible.
 bool
 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()) {
         trackOptimizationOutcome(TrackedOutcome::AccessNotTypedObject);
         return false;
     }
 
     // Must be accessing a struct.
     if (objPrediction.kind() != type::Struct) {
         trackOptimizationOutcome(TrackedOutcome::NotStruct);
         return false;
     }
 
     // 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;
     }
 
     return true;
 }
 
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -352,17 +352,18 @@ class IonBuilder
 
     // binary data lookup helpers.
     TypedObjectPrediction typedObjectPrediction(MDefinition* typedObj);
     TypedObjectPrediction typedObjectPrediction(TemporaryTypeSet* types);
     bool typedObjectHasField(MDefinition* typedObj,
                              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,
                                           LinearSum* ownerOffset);
     AbortReasonOr<Ok> loadTypedObjectElements(MDefinition* typedObj,
                                               const LinearSum& byteOffset,
                                               uint32_t scale,
                                               MDefinition** ownerElements,
--- a/js/src/jit/TypedObjectPrediction.cpp
+++ b/js/src/jit/TypedObjectPrediction.cpp
@@ -251,51 +251,54 @@ TypedObjectPrediction::arrayElementType(
 }
 
 bool
 TypedObjectPrediction::hasFieldNamedPrefix(const StructTypeDescr& descr,
                                            size_t fieldCount,
                                            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))
         return false;
 
     // Check whether the index falls within our known safe prefix.
     if (*index >= fieldCount)
         return false;
 
     // Load the offset and type.
     *fieldOffset = descr.fieldOffset(*index);
     *out = TypedObjectPrediction(descr.fieldDescr(*index));
+    *isMutable = descr.fieldIsMutable(*index);
     return true;
 }
 
 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);
 
     switch (predictionKind()) {
       case TypedObjectPrediction::Empty:
       case TypedObjectPrediction::Inconsistent:
         return false;
 
       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");
     }
 }
--- a/js/src/jit/TypedObjectPrediction.h
+++ b/js/src/jit/TypedObjectPrediction.h
@@ -105,17 +105,18 @@ class TypedObjectPrediction {
     template<typename T>
     typename T::Type extractType() const;
 
     bool hasFieldNamedPrefix(const StructTypeDescr& descr,
                              size_t fieldCount,
                              jsid id,
                              size_t* fieldOffset,
                              TypedObjectPrediction* out,
-                             size_t* index) const;
+                             size_t* index,
+                             bool* isMutable) const;
 
   public:
 
     ///////////////////////////////////////////////////////////////////////////
     // Constructing a prediction. Generally, you start with an empty
     // prediction and invoke addDescr() repeatedly.
 
     TypedObjectPrediction()
@@ -187,15 +188,16 @@ class TypedObjectPrediction {
 
     // Returns true if the predicted type includes a field named |id|
     // and sets |*fieldOffset|, |*fieldType|, and |*fieldIndex| with
     // the offset (in bytes), type, and index of the field
     // respectively.  Otherwise returns false.
     bool hasFieldNamed(jsid id,
                        size_t* fieldOffset,
                        TypedObjectPrediction* fieldType,
-                       size_t* fieldIndex) const;
+                       size_t* fieldIndex,
+                       bool* fieldMutable) const;
 };
 
 } // namespace jit
 } // namespace js
 
 #endif
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -538,16 +538,17 @@ MSG_DEF(JSMSG_NO_SUCH_SELF_HOSTED_PROP,1
 // Typed object
 MSG_DEF(JSMSG_INVALID_PROTOTYPE,       0, JSEXN_TYPEERR, "prototype field is not an object")
 MSG_DEF(JSMSG_TYPEDOBJECT_BAD_ARGS,    0, JSEXN_TYPEERR, "invalid arguments")
 MSG_DEF(JSMSG_TYPEDOBJECT_BINARYARRAY_BAD_INDEX, 0, JSEXN_RANGEERR, "invalid or out-of-range index")
 MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED, 0, JSEXN_TYPEERR, "handle unattached")
 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")
 
 // Typed array
 MSG_DEF(JSMSG_BAD_INDEX,               0, JSEXN_RANGEERR, "invalid or out-of-range index")
 MSG_DEF(JSMSG_NON_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected ArrayBuffer, but species constructor returned non-ArrayBuffer")
 MSG_DEF(JSMSG_SAME_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected different ArrayBuffer, but species constructor returned same ArrayBuffer")