Commit a39971b5 authored by Jon Coppeard's avatar Jon Coppeard
Browse files

Bug 1765338 - Allow transplanting of nursery objects r=jandem

parent 87994c06
Loading
Loading
Loading
Loading
+13 −7
Original line number Diff line number Diff line
@@ -402,7 +402,7 @@ namespace detail {
struct ProxyReservedSlots {
  JS::Value slots[1];

  static inline int offsetOfPrivateSlot();
  static constexpr ptrdiff_t offsetOfPrivateSlot();

  static inline int offsetOfSlot(size_t slot) {
    return offsetof(ProxyReservedSlots, slots[0]) + slot * sizeof(JS::Value);
@@ -429,24 +429,30 @@ struct ProxyValueArray {
    reservedSlots.init(nreserved);
  }

  static size_t sizeOf(size_t nreserved) {
    return offsetOfReservedSlots() + nreserved * sizeof(JS::Value);
  }
  static MOZ_ALWAYS_INLINE ProxyValueArray* fromReservedSlots(
      ProxyReservedSlots* slots) {
    uintptr_t p = reinterpret_cast<uintptr_t>(slots);
    return reinterpret_cast<ProxyValueArray*>(p - offsetOfReservedSlots());
  }
  static size_t offsetOfReservedSlots() {
  static constexpr size_t offsetOfReservedSlots() {
    return offsetof(ProxyValueArray, reservedSlots);
  }

  static size_t allocCount(size_t nreserved) {
    static_assert(offsetOfReservedSlots() % sizeof(JS::Value) == 0);
    return offsetOfReservedSlots() / sizeof(JS::Value) + nreserved;
  }
  static size_t sizeOf(size_t nreserved) {
    return allocCount(nreserved) * sizeof(JS::Value);
  }

  ProxyValueArray(const ProxyValueArray&) = delete;
  void operator=(const ProxyValueArray&) = delete;
};

/* static */ inline int ProxyReservedSlots::offsetOfPrivateSlot() {
  return -int(ProxyValueArray::offsetOfReservedSlots()) +
/* static */
constexpr ptrdiff_t ProxyReservedSlots::offsetOfPrivateSlot() {
  return -ptrdiff_t(ProxyValueArray::offsetOfReservedSlots()) +
         offsetof(ProxyValueArray, privateSlot);
}

+2 −1
Original line number Diff line number Diff line
@@ -142,7 +142,8 @@ enum class GCAbortReason {
  _(ZoneAllocPolicy)                       \
  _(SharedArrayRawBuffer)                  \
  _(XDRBufferElements)                     \
  _(GlobalObjectData)
  _(GlobalObjectData)                      \
  _(ProxyExternalValueArray)

#define JS_FOR_EACH_MEMORY_USE(_)  \
  JS_FOR_EACH_PUBLIC_MEMORY_USE(_) \
+5 −1
Original line number Diff line number Diff line
@@ -118,6 +118,10 @@ static inline size_t GetGCKindBytes(AllocKind thingKind) {
  return sizeof(JSObject_Slots0) + GetGCKindSlots(thingKind) * sizeof(Value);
}

static inline bool CanUseBackgroundAllocKind(const JSClass* clasp) {
  return !clasp->hasFinalize() || (clasp->flags & JSCLASS_BACKGROUND_FINALIZE);
}

static inline bool CanChangeToBackgroundAllocKind(AllocKind kind,
                                                  const JSClass* clasp) {
  // If a foreground alloc kind is specified but the class has no finalizer or a
@@ -133,7 +137,7 @@ static inline bool CanChangeToBackgroundAllocKind(AllocKind kind,
    return false;  // This kind is already a background finalized kind.
  }

  return !clasp->hasFinalize() || (clasp->flags & JSCLASS_BACKGROUND_FINALIZE);
  return CanUseBackgroundAllocKind(clasp);
}

static inline AllocKind ForegroundToBackgroundAllocKind(AllocKind fgKind) {
+0 −3
Original line number Diff line number Diff line
@@ -755,9 +755,6 @@ void MemoryTracker::untrackGCMemory(Cell* cell, size_t nbytes, MemoryUse use) {
}

void MemoryTracker::swapGCMemory(Cell* a, Cell* b, MemoryUse use) {
  MOZ_ASSERT(a->isTenured());
  MOZ_ASSERT(b->isTenured());

  Key<Cell> ka{a, use};
  Key<Cell> kb{b, use};

+59 −26
Original line number Diff line number Diff line
@@ -33,7 +33,7 @@ struct ProxyConfig {
struct ObjectConfig {
  const JSClass* clasp;
  bool isNative;
  bool isPrototype;
  bool nurseryAllocated;
  union {
    NativeConfig native;
    ProxyConfig proxy;
@@ -61,17 +61,27 @@ static const uint32_t TestPropertyCounts[] = {0, 1, 2, 7, 8, 20};

static bool Verbose = false;

class DummyProxyHandler final : public ForwardingProxyHandler {
class TenuredProxyHandler final : public Wrapper {
 public:
  static const DummyProxyHandler singleton;
  static const char family;
  constexpr DummyProxyHandler() : ForwardingProxyHandler(&family) {}
  static const TenuredProxyHandler singleton;
  constexpr TenuredProxyHandler() : Wrapper(0) {}
  bool canNurseryAllocate() const override { return false; }
};

const DummyProxyHandler DummyProxyHandler::singleton;
const char DummyProxyHandler::family = 0;
const TenuredProxyHandler TenuredProxyHandler::singleton;

class NurseryProxyHandler final : public Wrapper {
 public:
  static const NurseryProxyHandler singleton;
  constexpr NurseryProxyHandler() : Wrapper(0) {}
  bool canNurseryAllocate() const override { return true; }
};

const NurseryProxyHandler NurseryProxyHandler::singleton;

BEGIN_TEST(testObjectSwap) {
  AutoLeaveZeal noZeal(cx);

  ObjectConfigVector objectConfigs = CreateObjectConfigs();

  for (const ObjectConfig& config1 : objectConfigs) {
@@ -85,7 +95,8 @@ BEGIN_TEST(testObjectSwap) {
      CHECK(obj2);

      if (Verbose) {
        fprintf(stderr, "Swap %p and %p\n", obj1.get(), obj2.get());
        fprintf(stderr, "Swap %p (%s) and %p (%s)\n", obj1.get(),
                GetLocation(obj1), obj2.get(), GetLocation(obj2));
      }

      {
@@ -121,6 +132,9 @@ ObjectConfigVector CreateObjectConfigs() {
    for (uint32_t propCount : TestPropertyCounts) {
      config.native.propCount = propCount;

      for (bool nurseryAllocated : {false, true}) {
        config.nurseryAllocated = nurseryAllocated;

        for (bool inDictionaryMode : {false, true}) {
          if (inDictionaryMode && propCount == 0) {
            continue;
@@ -131,6 +145,7 @@ ObjectConfigVector CreateObjectConfigs() {
        }
      }
    }
  }

  config.isNative = false;
  config.proxy = ProxyConfig{false};
@@ -138,15 +153,23 @@ ObjectConfigVector CreateObjectConfigs() {
  for (const JSClass& jsClass : TestProxyClasses) {
    config.clasp = &jsClass;

    for (bool inlineValues : {false, true}) {
    for (bool nurseryAllocated : {false, true}) {
      config.nurseryAllocated = nurseryAllocated;

      for (bool inlineValues : {true, false}) {
        config.proxy.inlineValues = inlineValues;
        MOZ_RELEASE_ASSERT(configs.append(config));
      }
    }
  }

  return configs;
}

const char* GetLocation(JSObject* obj) {
  return obj->isTenured() ? "tenured heap" : "nursery";
}

// Counter used to give slots and property names unique values.
uint32_t nextId = 0;

@@ -158,12 +181,14 @@ JSObject* CreateObject(const ObjectConfig& config, uint32_t* idOut) {
JSObject* CreateNativeObject(const ObjectConfig& config) {
  MOZ_ASSERT(config.isNative);

  RootedNativeObject obj(
      cx, NewBuiltinClassInstance(cx, config.clasp, TenuredObject));
  NewObjectKind kind = config.nurseryAllocated ? GenericObject : TenuredObject;
  RootedNativeObject obj(cx, NewBuiltinClassInstance(cx, config.clasp, kind));
  if (!obj) {
    return nullptr;
  }

  MOZ_RELEASE_ASSERT(IsInsideNursery(obj) == config.nurseryAllocated);

  for (uint32_t i = 0; i < JSCLASS_RESERVED_SLOTS(config.clasp); i++) {
    JS::SetReservedSlot(obj, i, Int32Value(nextId++));
  }
@@ -212,8 +237,14 @@ JSObject* CreateProxy(const ObjectConfig& config) {
  options.setClass(config.clasp);
  options.setLazyProto(true);

  RootedObject obj(cx, NewProxyObject(cx, &DummyProxyHandler::singleton, priv,
                                      nullptr, options));
  const Wrapper* handler;
  if (config.nurseryAllocated) {
    handler = &NurseryProxyHandler::singleton;
  } else {
    handler = &TenuredProxyHandler::singleton;
  }

  RootedObject obj(cx, NewProxyObject(cx, handler, priv, nullptr, options));
  if (!obj) {
    return nullptr;
  }
@@ -228,8 +259,10 @@ JSObject* CreateProxy(const ObjectConfig& config) {
  if (!config.proxy.inlineValues) {
    // To create a proxy with non-inline values we must swap the proxy with an
    // object with a different size.
    RootedObject dummy(
        cx, NewBuiltinClassInstance(cx, &TestDOMClasses[0], TenuredObject));
    NewObjectKind kind =
        config.nurseryAllocated ? GenericObject : TenuredObject;
    RootedObject dummy(cx,
                       NewBuiltinClassInstance(cx, &TestDOMClasses[0], kind));
    if (!dummy) {
      return nullptr;
    }
@@ -239,6 +272,7 @@ JSObject* CreateProxy(const ObjectConfig& config) {
    proxy = &dummy->as<ProxyObject>();
  }

  MOZ_RELEASE_ASSERT(IsInsideNursery(proxy) == config.nurseryAllocated);
  MOZ_RELEASE_ASSERT(proxy->usingInlineValueArray() ==
                     config.proxy.inlineValues);

@@ -255,14 +289,13 @@ bool CheckObject(HandleObject obj, const ObjectConfig& config, uint32_t id) {
    fprintf(stderr, "Check %p is a %s object with %u reserved slots", obj.get(),
            config.isNative ? "native" : "proxy", reservedSlots);
    if (config.isNative) {
      fprintf(stderr, ", %u properties and %s in dictionary mode",
      fprintf(stderr, ", %u properties and %s in dictionary mode\n",
              config.native.propCount,
              config.native.inDictionaryMode ? "is" : "is not");
    } else {
      fprintf(stderr, " with %s values",
      fprintf(stderr, " with %s values\n",
              config.proxy.inlineValues ? "inline" : "out-of-line");
    }
    fprintf(stderr, "\n");
  }

  if (!config.isNative) {
Loading