Bug 1461948 - Generalize and abstract TypedObject field layout algorithm. r=till
authorLars T Hansen <lhansen@mozilla.com>
Wed, 16 May 2018 09:44:14 +0200
changeset 808738 c8434494445c6b5403c292226498a78b3d428331
parent 808737 e6cd4f48162d22ef3608391bbf16e7509e39de11
child 808739 1d069afa41fa40ba8c5835dbd83f1218c46b83e7
push id113477
push userbmo:cmanchester@mozilla.com
push dateWed, 20 Jun 2018 15:54:34 +0000
reviewerstill
bugs1461948
milestone62.0a1
Bug 1461948 - Generalize and abstract TypedObject field layout algorithm. r=till The algorithm is used by code that builds the TypeDesc, but also needed earlier by the Wasm parser to build the compilation environment. So abstract the algorithm and expose it.
js/src/builtin/TypedObject.cpp
js/src/builtin/TypedObject.h
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -761,16 +761,54 @@ const JSFunctionSpec StructMetaTypeDescr
 const JSPropertySpec StructMetaTypeDescr::typedObjectProperties[] = {
     JS_PS_END
 };
 
 const JSFunctionSpec StructMetaTypeDescr::typedObjectMethods[] = {
     JS_FS_END
 };
 
+CheckedInt32
+StructMetaTypeDescr::Layout::addField(int32_t fieldAlignment, int32_t fieldSize)
+{
+    // Alignment of the struct is the max of the alignment of its fields.
+    structAlignment = js::Max(structAlignment, fieldAlignment);
+
+    // Align the pointer.
+    CheckedInt32 offset = RoundUpToAlignment(sizeSoFar, fieldAlignment);
+    if (!offset.isValid())
+        return offset;
+
+    // Allocate space.
+    sizeSoFar = offset + fieldSize;
+    if (!sizeSoFar.isValid())
+        return sizeSoFar;
+
+    return offset;
+}
+
+CheckedInt32
+StructMetaTypeDescr::Layout::addScalar(Scalar::Type type) {
+    return addField(ScalarTypeDescr::alignment(type),
+                    ScalarTypeDescr::size(type));
+}
+
+CheckedInt32
+StructMetaTypeDescr::Layout::addReference(ReferenceTypeDescr::Type type) {
+    return addField(ReferenceTypeDescr::alignment(type),
+                    ReferenceTypeDescr::size(type));
+}
+
+CheckedInt32
+StructMetaTypeDescr::Layout::close(int32_t *alignment) {
+    if (alignment)
+        *alignment = structAlignment;
+    return RoundUpToAlignment(sizeSoFar, structAlignment);
+}
+
 /* static */ JSObject*
 StructMetaTypeDescr::create(JSContext* cx,
                             HandleObject metaTypeDescr,
                             HandleObject fields)
 {
     // Obtain names of fields, which are the own properties of `fields`
     AutoIdVector ids(cx);
     if (!GetPropertyKeys(cx, fields, JSITER_OWNONLY | JSITER_SYMBOLS, &ids))
@@ -830,18 +868,17 @@ StructMetaTypeDescr::createFromArrays(JS
                                       AutoIdVector& ids,
                                       AutoValueVector& fieldTypeObjs)
 {
     StringBuffer stringBuffer(cx);     // Canonical string repr
     AutoValueVector fieldNames(cx);    // Name of each field.
     AutoValueVector fieldOffsets(cx);  // Offset of each field field.
     RootedObject userFieldOffsets(cx); // User-exposed {f:offset} object
     RootedObject userFieldTypes(cx);   // User-exposed {f:descr} object.
-    CheckedInt32 sizeSoFar(0);         // Size of struct thus far.
-    uint32_t alignment = 1;            // Alignment of struct.
+    Layout layout;                     // Field offsetter
 
     userFieldOffsets = NewBuiltinClassInstance<PlainObject>(cx, TenuredObject);
     if (!userFieldOffsets)
         return nullptr;
 
     userFieldTypes = NewBuiltinClassInstance<PlainObject>(cx, TenuredObject);
     if (!userFieldTypes)
         return nullptr;
@@ -874,56 +911,44 @@ StructMetaTypeDescr::createFromArrays(JS
             return nullptr;
         if (!stringBuffer.append(JSID_TO_ATOM(id)))
             return nullptr;
         if (!stringBuffer.append(": "))
             return nullptr;
         if (!stringBuffer.append(&fieldType->stringRepr()))
             return nullptr;
 
-        // Offset of this field is the current total size adjusted for
-        // the field's alignment.
-        CheckedInt32 offset = RoundUpToAlignment(sizeSoFar, fieldType->alignment());
+        CheckedInt32 offset = layout.addField(fieldType->alignment(), fieldType->size());
         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;
 
         // userFieldOffsets[id] = offset
         RootedValue offsetValue(cx, Int32Value(offset.value()));
         if (!DefineDataProperty(cx, userFieldOffsets, id, offsetValue,
                                 JSPROP_READONLY | JSPROP_PERMANENT))
         {
             return nullptr;
         }
-
-        // Add space for this field to the total struct size.
-        sizeSoFar = offset + fieldType->size();
-        if (!sizeSoFar.isValid()) {
-            JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPEDOBJECT_TOO_BIG);
-            return nullptr;
-        }
-
-        // Alignment of the struct is the max of the alignment of its fields.
-        alignment = js::Max(alignment, fieldType->alignment());
     }
 
     // Complete string representation.
     if (!stringBuffer.append("})"))
         return nullptr;
 
     RootedAtom stringRepr(cx, stringBuffer.finishAtom());
     if (!stringRepr)
         return nullptr;
 
-    // Adjust the total size to be a multiple of the final alignment.
-    CheckedInt32 totalSize = RoundUpToAlignment(sizeSoFar, alignment);
+    int32_t alignment;
+    CheckedInt32 totalSize = layout.close(&alignment);
     if (!totalSize.isValid()) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPEDOBJECT_TOO_BIG);
         return nullptr;
     }
 
     // Now create the resulting type descriptor.
 
     Rooted<StructTypeDescr*> descr(cx);
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -2,16 +2,18 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef builtin_TypedObject_h
 #define builtin_TypedObject_h
 
+#include "mozilla/CheckedInt.h"
+
 #include "builtin/TypedObjectConstants.h"
 #include "gc/WeakMap.h"
 #include "js/Conversions.h"
 #include "vm/ArrayBufferObject.h"
 #include "vm/JSObject.h"
 #include "vm/ShapedObject.h"
 
 /*
@@ -452,16 +454,37 @@ class StructMetaTypeDescr : public Nativ
     // Properties and methods to be installed on StructType.prototype.prototype,
     // and hence inherited by all struct *typed* objects:
     static const JSPropertySpec typedObjectProperties[];
     static const JSFunctionSpec typedObjectMethods[];
 
     // This is the function that gets called when the user
     // does `new StructType(...)`. It produces a struct type object.
     static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);
+
+    class Layout
+    {
+        // Can call addField() directly.
+        friend class StructMetaTypeDescr;
+
+        mozilla::CheckedInt32 sizeSoFar = 0;
+        int32_t structAlignment = 1;
+
+        mozilla::CheckedInt32 addField(int32_t fieldAlignment, int32_t fieldSize);
+
+      public:
+        // The field adders return the offset of the the field.
+        mozilla::CheckedInt32 addScalar(Scalar::Type type);
+        mozilla::CheckedInt32 addReference(ReferenceTypeDescr::Type type);
+
+        // The close method rounds up the structure size to the appropriate
+        // alignment and returns that size.  If `alignment` is not NULL then
+        // return the structure alignment through that pointer.
+        mozilla::CheckedInt32 close(int32_t* alignment = nullptr);
+    };
 };
 
 class StructTypeDescr : public ComplexTypeDescr
 {
   public:
     static const Class class_;
 
     // Returns the number of fields defined in this struct.