Bug 1479718 - Allow Wasm to prevent a TypedObject constructor from being invoked from JS. r=till
authorLars T Hansen <lhansen@mozilla.com>
Tue, 31 Jul 2018 10:45:55 +0200
changeset 430048 1cd14797e7cc6621f7bd08eef48a56b9483963a7
parent 430047 3e4eec1a2feee53119135913a33a2fa62c7b4ceb
child 430049 ea6577b8c72f4800808199a7ce00aea4513040a1
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
bugs1479718
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 1479718 - Allow Wasm to prevent a TypedObject constructor from being invoked from JS. r=till TypedObjects that map Wasm structs with fields that have Ref type are not yet constructible from JS because the type constraint can't be honored. So for now, make it possible for Wasm to flag such structs as unconstructible-from-JS.
js/src/builtin/TypedObject.cpp
js/src/builtin/TypedObject.h
js/src/builtin/TypedObjectConstants.h
js/src/js.msg
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -542,17 +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));
+    obj->initReservedSlot(JS_DESCR_SLOT_FLAGS, Int32Value(JS_DESCR_FLAG_ALLOW_CONSTRUCT));
 
     RootedValue elementTypeVal(cx, ObjectValue(*elementType));
     if (!DefineDataProperty(cx, obj, cx->names().elementType, elementTypeVal,
                             JSPROP_READONLY | JSPROP_PERMANENT))
     {
         return nullptr;
     }
 
@@ -807,23 +807,25 @@ StructMetaTypeDescr::create(JSContext* c
         if (fieldType->opaque())
             opaque = true;
     }
 
     RootedObject structTypePrototype(cx, GetPrototype(cx, metaTypeDescr));
     if (!structTypePrototype)
         return nullptr;
 
-    return createFromArrays(cx, structTypePrototype, opaque, ids, fieldTypeObjs, fieldMutabilities);
+    return createFromArrays(cx, structTypePrototype, opaque, /* allowConstruct= */ true, ids,
+                            fieldTypeObjs, fieldMutabilities);
 }
 
 /* static */ StructTypeDescr*
 StructMetaTypeDescr::createFromArrays(JSContext* cx,
                                       HandleObject structTypePrototype,
                                       bool opaque,
+                                      bool allowConstruct,
                                       AutoIdVector& ids,
                                       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.
@@ -915,17 +917,18 @@ 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));
+    descr->initReservedSlot(JS_DESCR_SLOT_FLAGS,
+                            Int32Value(allowConstruct ? JS_DESCR_FLAG_ALLOW_CONSTRUCT : 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)
@@ -2279,32 +2282,40 @@ TypedObject::construct(JSContext* cx, un
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     MOZ_ASSERT(args.callee().is<TypeDescr>());
     Rooted<TypeDescr*> callee(cx, &args.callee().as<TypeDescr>());
 
     MOZ_ASSERT(cx->realm() == callee->realm());
 
+    // Types created by Wasm may not be constructible from JS due to field types
+    // that are not expressible in the current TypedObject system.
+    if (callee->is<ComplexTypeDescr>() && !callee->as<ComplexTypeDescr>().allowConstruct()) {
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPEDOBJECT_NOT_CONSTRUCTIBLE);
+        return false;
+    }
+
     // Typed object constructors are overloaded in two ways:
     //
     //   new TypeObj()
     //   new TypeObj(data)
 
     // Zero argument constructor:
     if (args.length() == 0) {
         Rooted<TypedObject*> obj(cx, createZeroed(cx, callee));
         if (!obj)
             return false;
         args.rval().setObject(*obj);
         return true;
     }
 
     // Data constructor.
     if (args[0].isObject()) {
+
         // Create the typed object.
         Rooted<TypedObject*> obj(cx, createZeroed(cx, callee));
         if (!obj)
             return false;
 
         // Initialize from `arg`.
         if (!ConvertAndCopyTo(cx, obj, args[0]))
             return false;
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -323,16 +323,20 @@ class ReferenceTypeDescr : public Simple
 class ComplexTypeDescr : public TypeDescr
 {
   public:
     // Returns the prototype that instances of this type descriptor
     // will have.
     TypedProto& instancePrototype() const {
         return getReservedSlot(JS_DESCR_SLOT_TYPROTO).toObject().as<TypedProto>();
     }
+
+    bool allowConstruct() const {
+        return getReservedSlot(JS_DESCR_SLOT_FLAGS).toInt32() & JS_DESCR_FLAG_ALLOW_CONSTRUCT;
+    }
 };
 
 bool IsTypedObjectClass(const Class* clasp); // Defined below
 bool IsTypedObjectArray(JSObject& obj);
 
 MOZ_MUST_USE bool CreateUserSizeAndAlignmentProperties(JSContext* cx, HandleTypeDescr obj);
 
 class ArrayTypeDescr;
@@ -411,16 +415,17 @@ 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,
+                                             bool allowConstruct,
                                              AutoIdVector& ids,
                                              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[];
--- a/js/src/builtin/TypedObjectConstants.h
+++ b/js/src/builtin/TypedObjectConstants.h
@@ -65,16 +65,18 @@
 #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                   13
 
+#define JS_DESCR_FLAG_ALLOW_CONSTRUCT    1 // Allow structure to be constructed
+
 // 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/js.msg
+++ b/js/src/js.msg
@@ -539,16 +539,17 @@ MSG_DEF(JSMSG_NO_SUCH_SELF_HOSTED_PROP,1
 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")
+MSG_DEF(JSMSG_TYPEDOBJECT_NOT_CONSTRUCTIBLE, 0, JSEXN_TYPEERR, "not constructible")
 
 // 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")