Commit ef5dad8c authored by Jan de Mooij's avatar Jan de Mooij
Browse files

Bug 1812055 - Add fast paths for the most common cases to...

Bug 1812055 - Add fast paths for the most common cases to JSStructuredCloneReader::readObjectField. r=sfink

On typical workloads, these fast paths for plain objects and array objects handle
at least 97% of cases. This is much faster than the generic `DefineDataProperty` code.

Differential Revision: https://phabricator.services.mozilla.com/D167685
parent 4c45bb09
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -63,6 +63,25 @@ namespace js {
  return aobj;
}

inline DenseElementResult ArrayObject::addDenseElementNoLengthChange(
    JSContext* cx, uint32_t index, const Value& val) {
  MOZ_ASSERT(isExtensible());

  // Only support the `index < length` case so that we don't have to increase
  // the array's .length value below.
  if (index >= length() || containsDenseElement(index) || isIndexed()) {
    return DenseElementResult::Incomplete;
  }

  DenseElementResult res = ensureDenseElements(cx, index, 1);
  if (MOZ_UNLIKELY(res != DenseElementResult::Success)) {
    return res;
  }

  initDenseElement(index, val);
  return DenseElementResult::Success;
}

}  // namespace js

#endif  // vm_ArrayObject_inl_h
+9 −0
Original line number Diff line number Diff line
@@ -41,6 +41,15 @@ class ArrayObject : public NativeObject {
    getElementsHeader()->length = length;
  }

  // Try to add a new dense element to this array. The array must be extensible.
  //
  // Returns DenseElementResult::Incomplete if `index >= length`, if the array
  // has sparse elements, if we're adding a sparse element, or if the array
  // already contains a dense element at this index.
  inline DenseElementResult addDenseElementNoLengthChange(JSContext* cx,
                                                          uint32_t index,
                                                          const Value& val);

  // Make an array object with the specified initial state.
  static MOZ_ALWAYS_INLINE ArrayObject* create(
      JSContext* cx, gc::AllocKind kind, gc::InitialHeap heap,
+26 −3
Original line number Diff line number Diff line
@@ -66,11 +66,13 @@
#include "vm/TypedArrayObject.h"
#include "wasm/WasmJS.h"

#include "vm/ArrayObject-inl.h"
#include "vm/Compartment-inl.h"
#include "vm/ErrorObject-inl.h"
#include "vm/InlineCharBuffer-inl.h"
#include "vm/JSContext-inl.h"
#include "vm/JSObject-inl.h"
#include "vm/NativeObject-inl.h"
#include "vm/ObjectOperations-inl.h"
#include "vm/Realm-inl.h"

@@ -3672,11 +3674,32 @@ bool JSStructuredCloneReader::readObjectField(HandleObject obj,
    return false;
  }

  if (!DefineDataProperty(context(), obj, id, val)) {
    return false;
  // Fast path for adding a new property to a plain object. The property names
  // we see here should be unique, but we check for duplicates to guard against
  // corrupt or malicious data.
  if (id.isString() && obj->is<PlainObject>() &&
      MOZ_LIKELY(!obj->as<PlainObject>().contains(context(), id))) {
    return AddDataPropertyToPlainObject(context(), obj.as<PlainObject>(), id,
                                        val);
  }

  // Fast path for adding an array element. The index shouldn't exceed the
  // array's length, but we check for this in `addDenseElementNoLengthChange` to
  // guard against corrupt or malicious data.
  if (id.isInt() && obj->is<ArrayObject>()) {
    ArrayObject* arr = &obj->as<ArrayObject>();
    switch (arr->addDenseElementNoLengthChange(context(), id.toInt(), val)) {
      case DenseElementResult::Failure:
        return false;
      case DenseElementResult::Success:
        return true;
      case DenseElementResult::Incomplete:
        // Fall-through to slow path.
        break;
    }
  }

  return DefineDataProperty(context(), obj, id, val);
}

// Perform the whole recursive reading procedure.