// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//     * Neither the name of Google Inc. nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "elements-kind.h"

#include "api.h"
#include "elements.h"
#include "objects.h"

namespace v8 {
namespace internal {


const char* ElementsKindToString(ElementsKind kind) {
  ElementsAccessor* accessor = ElementsAccessor::ForKind(kind);
  return accessor->name();
}


void PrintElementsKind(FILE* out, ElementsKind kind) {
  PrintF(out, "%s", ElementsKindToString(kind));
}


ElementsKind GetInitialFastElementsKind() {
  if (FLAG_packed_arrays) {
    return FAST_SMI_ELEMENTS;
  } else {
    return FAST_HOLEY_SMI_ELEMENTS;
  }
}


struct InitializeFastElementsKindSequence {
  static void Construct(
      ElementsKind** fast_elements_kind_sequence_ptr) {
    ElementsKind* fast_elements_kind_sequence =
        new ElementsKind[kFastElementsKindCount];
    *fast_elements_kind_sequence_ptr = fast_elements_kind_sequence;
    STATIC_ASSERT(FAST_SMI_ELEMENTS == FIRST_FAST_ELEMENTS_KIND);
    fast_elements_kind_sequence[0] = FAST_SMI_ELEMENTS;
    fast_elements_kind_sequence[1] = FAST_HOLEY_SMI_ELEMENTS;
    fast_elements_kind_sequence[2] = FAST_DOUBLE_ELEMENTS;
    fast_elements_kind_sequence[3] = FAST_HOLEY_DOUBLE_ELEMENTS;
    fast_elements_kind_sequence[4] = FAST_ELEMENTS;
    fast_elements_kind_sequence[5] = FAST_HOLEY_ELEMENTS;
  }
};


static LazyInstance<ElementsKind*,
                    InitializeFastElementsKindSequence>::type
    fast_elements_kind_sequence = LAZY_INSTANCE_INITIALIZER;


ElementsKind GetFastElementsKindFromSequenceIndex(int sequence_number) {
  ASSERT(sequence_number >= 0 &&
         sequence_number < kFastElementsKindCount);
  return fast_elements_kind_sequence.Get()[sequence_number];
}


int GetSequenceIndexFromFastElementsKind(ElementsKind elements_kind) {
  for (int i = 0; i < kFastElementsKindCount; ++i) {
    if (fast_elements_kind_sequence.Get()[i] == elements_kind) {
      return i;
    }
  }
  UNREACHABLE();
  return 0;
}


ElementsKind GetNextMoreGeneralFastElementsKind(ElementsKind elements_kind,
                                                bool allow_only_packed) {
  ASSERT(IsFastElementsKind(elements_kind));
  ASSERT(elements_kind != TERMINAL_FAST_ELEMENTS_KIND);
  while (true) {
    int index =
        GetSequenceIndexFromFastElementsKind(elements_kind) + 1;
    elements_kind = GetFastElementsKindFromSequenceIndex(index);
    if (!IsFastHoleyElementsKind(elements_kind) || !allow_only_packed) {
      return elements_kind;
    }
  }
  UNREACHABLE();
  return TERMINAL_FAST_ELEMENTS_KIND;
}


bool IsMoreGeneralElementsKindTransition(ElementsKind from_kind,
                                         ElementsKind to_kind) {
  switch (from_kind) {
    case FAST_SMI_ELEMENTS:
      return to_kind != FAST_SMI_ELEMENTS;
    case FAST_HOLEY_SMI_ELEMENTS:
      return to_kind != FAST_SMI_ELEMENTS &&
          to_kind != FAST_HOLEY_SMI_ELEMENTS;
    case FAST_DOUBLE_ELEMENTS:
      return to_kind != FAST_SMI_ELEMENTS &&
          to_kind != FAST_HOLEY_SMI_ELEMENTS &&
          to_kind != FAST_DOUBLE_ELEMENTS;
    case FAST_HOLEY_DOUBLE_ELEMENTS:
      return to_kind == FAST_ELEMENTS ||
          to_kind == FAST_HOLEY_ELEMENTS;
    case FAST_ELEMENTS:
      return to_kind == FAST_HOLEY_ELEMENTS;
    case FAST_HOLEY_ELEMENTS:
      return false;
    default:
      return false;
  }
}


} }  // namespace v8::internal
