/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * 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/. */

/*
 * GC-internal helper functions for getting the AllocKind used to allocate a
 * JSObject and related information.
 */

#ifndef gc_ObjectKind_inl_h
#define gc_ObjectKind_inl_h

#include "vm/NativeObject.h"

namespace js {
namespace gc {

static inline bool CanBeFinalizedInBackground(AllocKind kind,
                                              const Class* clasp) {
  MOZ_ASSERT(IsObjectAllocKind(kind));
  /* If the class has no finalizer or a finalizer that is safe to call on
   * a different thread, we change the alloc kind. For example,
   * AllocKind::OBJECT0 calls the finalizer on the active thread,
   * AllocKind::OBJECT0_BACKGROUND calls the finalizer on the gcHelperThread.
   * IsBackgroundFinalized is called to prevent recursively incrementing
   * the alloc kind; kind may already be a background finalize kind.
   */
  return (
      !IsBackgroundFinalized(kind) &&
      (!clasp->hasFinalize() || (clasp->flags & JSCLASS_BACKGROUND_FINALIZE)));
}

static inline AllocKind GetBackgroundAllocKind(AllocKind kind) {
  MOZ_ASSERT(!IsBackgroundFinalized(kind));
  MOZ_ASSERT(IsObjectAllocKind(kind));
  return AllocKind(size_t(kind) + 1);
}

/* Capacity for slotsToThingKind */
const size_t SLOTS_TO_THING_KIND_LIMIT = 17;

extern const AllocKind slotsToThingKind[];

/* Get the best kind to use when making an object with the given slot count. */
static inline AllocKind GetGCObjectKind(size_t numSlots) {
  if (numSlots >= SLOTS_TO_THING_KIND_LIMIT) return AllocKind::OBJECT16;
  return slotsToThingKind[numSlots];
}

static inline AllocKind GetGCObjectKind(const Class* clasp) {
  if (clasp == FunctionClassPtr) return AllocKind::FUNCTION;

  MOZ_ASSERT(!clasp->isProxy(), "Proxies should use GetProxyGCObjectKind");

  uint32_t nslots = JSCLASS_RESERVED_SLOTS(clasp);
  if (clasp->flags & JSCLASS_HAS_PRIVATE) nslots++;
  return GetGCObjectKind(nslots);
}

/* As for GetGCObjectKind, but for dense array allocation. */
static inline AllocKind GetGCArrayKind(size_t numElements) {
  /*
   * Dense arrays can use their fixed slots to hold their elements array
   * (less two Values worth of ObjectElements header), but if more than the
   * maximum number of fixed slots is needed then the fixed slots will be
   * unused.
   */
  JS_STATIC_ASSERT(ObjectElements::VALUES_PER_HEADER == 2);
  if (numElements > NativeObject::MAX_DENSE_ELEMENTS_COUNT ||
      numElements + ObjectElements::VALUES_PER_HEADER >=
          SLOTS_TO_THING_KIND_LIMIT) {
    return AllocKind::OBJECT2;
  }
  return slotsToThingKind[numElements + ObjectElements::VALUES_PER_HEADER];
}

static inline AllocKind GetGCObjectFixedSlotsKind(size_t numFixedSlots) {
  MOZ_ASSERT(numFixedSlots < SLOTS_TO_THING_KIND_LIMIT);
  return slotsToThingKind[numFixedSlots];
}

// Get the best kind to use when allocating an object that needs a specific
// number of bytes.
static inline AllocKind GetGCObjectKindForBytes(size_t nbytes) {
  MOZ_ASSERT(nbytes <= JSObject::MAX_BYTE_SIZE);

  if (nbytes <= sizeof(NativeObject)) return AllocKind::OBJECT0;
  nbytes -= sizeof(NativeObject);

  size_t dataSlots = AlignBytes(nbytes, sizeof(Value)) / sizeof(Value);
  MOZ_ASSERT(nbytes <= dataSlots * sizeof(Value));
  return GetGCObjectKind(dataSlots);
}

/* Get the number of fixed slots and initial capacity associated with a kind. */
static inline size_t GetGCKindSlots(AllocKind thingKind) {
  /* Using a switch in hopes that thingKind will usually be a compile-time
   * constant. */
  switch (thingKind) {
    case AllocKind::FUNCTION:
    case AllocKind::OBJECT0:
    case AllocKind::OBJECT0_BACKGROUND:
      return 0;
    case AllocKind::FUNCTION_EXTENDED:
    case AllocKind::OBJECT2:
    case AllocKind::OBJECT2_BACKGROUND:
      return 2;
    case AllocKind::OBJECT4:
    case AllocKind::OBJECT4_BACKGROUND:
      return 4;
    case AllocKind::OBJECT8:
    case AllocKind::OBJECT8_BACKGROUND:
      return 8;
    case AllocKind::OBJECT12:
    case AllocKind::OBJECT12_BACKGROUND:
      return 12;
    case AllocKind::OBJECT16:
    case AllocKind::OBJECT16_BACKGROUND:
      return 16;
    default:
      MOZ_CRASH("Bad object alloc kind");
  }
}

static inline size_t GetGCKindSlots(AllocKind thingKind, const Class* clasp) {
  size_t nslots = GetGCKindSlots(thingKind);

  /* An object's private data uses the space taken by its last fixed slot. */
  if (clasp->flags & JSCLASS_HAS_PRIVATE) {
    MOZ_ASSERT(nslots > 0);
    nslots--;
  }

  /*
   * Functions have a larger alloc kind than AllocKind::OBJECT to reserve
   * space for the extra fields in JSFunction, but have no fixed slots.
   */
  if (clasp == FunctionClassPtr) nslots = 0;

  return nslots;
}

static inline size_t GetGCKindBytes(AllocKind thingKind) {
  return sizeof(JSObject_Slots0) + GetGCKindSlots(thingKind) * sizeof(Value);
}

}  // namespace gc
}  // namespace js

#endif  // gc_ObjectKind_inl_h
