// Copyright 2006-2008 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 <stdlib.h>

#include "v8.h"

#include "scopeinfo.h"
#include "scopes.h"

namespace v8 {
namespace internal {


static int CompareLocal(Variable* const* v, Variable* const* w) {
  Slot* s = (*v)->slot();
  Slot* t = (*w)->slot();
  // We may have rewritten parameters (that are in the arguments object)
  // and which may have a NULL slot... - find a better solution...
  int x = (s != NULL ? s->index() : 0);
  int y = (t != NULL ? t->index() : 0);
  // Consider sorting them according to type as well?
  return x - y;
}


template<class Allocator>
ScopeInfo<Allocator>::ScopeInfo(Scope* scope)
    : function_name_(Factory::empty_symbol()),
      calls_eval_(scope->calls_eval()),
      parameters_(scope->num_parameters()),
      stack_slots_(scope->num_stack_slots()),
      context_slots_(scope->num_heap_slots()),
      context_modes_(scope->num_heap_slots()) {
  // Add parameters.
  for (int i = 0; i < scope->num_parameters(); i++) {
    ASSERT(parameters_.length() == i);
    parameters_.Add(scope->parameter(i)->name());
  }

  // Add stack locals and collect heap locals.
  // We are assuming that the locals' slots are allocated in
  // increasing order, so we can simply add them to the
  // ScopeInfo lists. However, due to usage analysis, this is
  // not true for context-allocated locals: Some of them
  // may be parameters which are allocated before the
  // non-parameter locals. When the non-parameter locals are
  // sorted according to usage, the allocated slot indices may
  // not be in increasing order with the variable list anymore.
  // Thus, we first collect the context-allocated locals, and then
  // sort them by context slot index before adding them to the
  // ScopeInfo list.
  List<Variable*, Allocator> locals(32);  // 32 is a wild guess
  ASSERT(locals.is_empty());
  scope->CollectUsedVariables(&locals);
  locals.Sort(&CompareLocal);

  List<Variable*, Allocator> heap_locals(locals.length());
  for (int i = 0; i < locals.length(); i++) {
    Variable* var = locals[i];
    if (var->var_uses()->is_used()) {
      Slot* slot = var->slot();
      if (slot != NULL) {
        switch (slot->type()) {
          case Slot::PARAMETER:
            // explicitly added to parameters_ above - ignore
            break;

          case Slot::LOCAL:
            ASSERT(stack_slots_.length() == slot->index());
            stack_slots_.Add(var->name());
            break;

          case Slot::CONTEXT:
            heap_locals.Add(var);
            break;

          case Slot::LOOKUP:
            // This is currently not used.
            UNREACHABLE();
            break;
        }
      }
    }
  }

  // Add heap locals.
  if (scope->num_heap_slots() > 0) {
    // Add user-defined slots.
    for (int i = 0; i < heap_locals.length(); i++) {
      ASSERT(heap_locals[i]->slot()->index() - Context::MIN_CONTEXT_SLOTS ==
             context_slots_.length());
      ASSERT(heap_locals[i]->slot()->index() - Context::MIN_CONTEXT_SLOTS ==
             context_modes_.length());
      context_slots_.Add(heap_locals[i]->name());
      context_modes_.Add(heap_locals[i]->mode());
    }

  } else {
    ASSERT(heap_locals.length() == 0);
  }

  // Add the function context slot, if present.
  // For now, this must happen at the very end because of the
  // ordering of the scope info slots and the respective slot indices.
  if (scope->is_function_scope()) {
    Variable* var = scope->function();
    if (var != NULL &&
        var->var_uses()->is_used() &&
        var->slot()->type() == Slot::CONTEXT) {
      function_name_ = var->name();
      // Note that we must not find the function name in the context slot
      // list - instead it must be handled separately in the
      // Contexts::Lookup() function. Thus record an empty symbol here so we
      // get the correct number of context slots.
      ASSERT(var->slot()->index() - Context::MIN_CONTEXT_SLOTS ==
             context_slots_.length());
      ASSERT(var->slot()->index() - Context::MIN_CONTEXT_SLOTS ==
             context_modes_.length());
      context_slots_.Add(Factory::empty_symbol());
      context_modes_.Add(Variable::INTERNAL);
    }
  }
}


// Encoding format in the Code object:
//
// - function name
//
// - number of variables in the context object (smi) (= function context
//   slot index + 1)
// - list of pairs (name, Var mode) of context-allocated variables (starting
//   with context slot 0)
// - NULL (sentinel)
//
// - number of parameters (smi)
// - list of parameter names (starting with parameter 0 first)
// - NULL (sentinel)
//
// - number of variables on the stack (smi)
// - list of names of stack-allocated variables (starting with stack slot 0)
// - NULL (sentinel)

// The ScopeInfo representation could be simplified and the ScopeInfo
// re-implemented (with almost the same interface). Here is a
// suggestion for the new format:
//
// - have a single list with all variable names (parameters, stack locals,
//   context locals), followed by a list of non-Object* values containing
//   the variables information (what kind, index, attributes)
// - searching the linear list of names is fast and yields an index into the
//   list if the variable name is found
// - that list index is then used to find the variable information in the
//   subsequent list
// - the list entries don't have to be in any particular order, so all the
//   current sorting business can go away
// - the ScopeInfo lookup routines can be reduced to perhaps a single lookup
//   which returns all information at once
// - when gathering the information from a Scope, we only need to iterate
//   through the local variables (parameters and context info is already
//   present)


static inline Object** ReadInt(Object** p, int* x) {
  *x = (reinterpret_cast<Smi*>(*p++))->value();
  return p;
}


static inline Object** ReadBool(Object** p, bool* x) {
  *x = (reinterpret_cast<Smi*>(*p++))->value() != 0;
  return p;
}


static inline Object** ReadSymbol(Object** p, Handle<String>* s) {
  *s = Handle<String>(reinterpret_cast<String*>(*p++));
  return p;
}


static inline Object** ReadSentinel(Object** p) {
  ASSERT(*p == NULL);
  return p + 1;
}


template <class Allocator>
static Object** ReadList(Object** p, List<Handle<String>, Allocator >* list) {
  ASSERT(list->is_empty());
  int n;
  p = ReadInt(p, &n);
  while (n-- > 0) {
    Handle<String> s;
    p = ReadSymbol(p, &s);
    list->Add(s);
  }
  return ReadSentinel(p);
}


template <class Allocator>
static Object** ReadList(Object** p,
                         List<Handle<String>, Allocator>* list,
                         List<Variable::Mode, Allocator>* modes) {
  ASSERT(list->is_empty());
  int n;
  p = ReadInt(p, &n);
  while (n-- > 0) {
    Handle<String> s;
    int m;
    p = ReadSymbol(p, &s);
    p = ReadInt(p, &m);
    list->Add(s);
    modes->Add(static_cast<Variable::Mode>(m));
  }
  return ReadSentinel(p);
}


template<class Allocator>
ScopeInfo<Allocator>::ScopeInfo(Code* code)
  : function_name_(Factory::empty_symbol()),
    parameters_(4),
    stack_slots_(8),
    context_slots_(8),
    context_modes_(8) {
  if (code == NULL || code->sinfo_size() == 0) return;

  Object** p0 = &Memory::Object_at(code->sinfo_start());
  Object** p = p0;
  p = ReadSymbol(p, &function_name_);
  p = ReadBool(p, &calls_eval_);
  p = ReadList<Allocator>(p, &context_slots_, &context_modes_);
  p = ReadList<Allocator>(p, &parameters_);
  p = ReadList<Allocator>(p, &stack_slots_);
  ASSERT((p - p0) * kPointerSize == code->sinfo_size());
}


static inline Object** WriteInt(Object** p, int x) {
  *p++ = Smi::FromInt(x);
  return p;
}


static inline Object** WriteBool(Object** p, bool b) {
  *p++ = Smi::FromInt(b ? 1 : 0);
  return p;
}


static inline Object** WriteSymbol(Object** p, Handle<String> s) {
  *p++ = *s;
  return p;
}


static inline Object** WriteSentinel(Object** p) {
  *p++ = NULL;
  return p;
}


template <class Allocator>
static Object** WriteList(Object** p, List<Handle<String>, Allocator >* list) {
  const int n = list->length();
  p = WriteInt(p, n);
  for (int i = 0; i < n; i++) {
    p = WriteSymbol(p, list->at(i));
  }
  return WriteSentinel(p);
}


template <class Allocator>
static Object** WriteList(Object** p,
                          List<Handle<String>, Allocator>* list,
                          List<Variable::Mode, Allocator>* modes) {
  const int n = list->length();
  p = WriteInt(p, n);
  for (int i = 0; i < n; i++) {
    p = WriteSymbol(p, list->at(i));
    p = WriteInt(p, modes->at(i));
  }
  return WriteSentinel(p);
}


template<class Allocator>
int ScopeInfo<Allocator>::Serialize(Code* code) {
  // function name, calls eval, length & sentinel for 3 tables:
  const int extra_slots = 1 + 1 + 2 * 3;
  int size = (extra_slots +
              context_slots_.length() * 2 +
              parameters_.length() +
              stack_slots_.length()) * kPointerSize;

  if (code != NULL) {
    CHECK(code->sinfo_size() == size);
    Object** p0 = &Memory::Object_at(code->sinfo_start());
    Object** p = p0;
    p = WriteSymbol(p, function_name_);
    p = WriteBool(p, calls_eval_);
    p = WriteList(p, &context_slots_, &context_modes_);
    p = WriteList(p, &parameters_);
    p = WriteList(p, &stack_slots_);
    ASSERT((p - p0) * kPointerSize == size);
  }

  return size;
}


template<class Allocator>
void ScopeInfo<Allocator>::IterateScopeInfo(Code* code, ObjectVisitor* v) {
  Object** start = &Memory::Object_at(code->sinfo_start());
  Object** end = &Memory::Object_at(code->sinfo_start() + code->sinfo_size());
  v->VisitPointers(start, end);
}


static Object** ContextEntriesAddr(Code* code) {
  ASSERT(code->sinfo_size() > 0);
  // +2 for function name and calls eval:
  return &Memory::Object_at(code->sinfo_start()) + 2;
}


static Object** ParameterEntriesAddr(Code* code) {
  ASSERT(code->sinfo_size() > 0);
  Object** p = ContextEntriesAddr(code);
  int n;  // number of context slots;
  p = ReadInt(p, &n);
  return p + n*2 + 1;  // *2 for pairs, +1 for sentinel
}


static Object** StackSlotEntriesAddr(Code* code) {
  ASSERT(code->sinfo_size() > 0);
  Object** p = ParameterEntriesAddr(code);
  int n;  // number of parameter slots;
  p = ReadInt(p, &n);
  return p + n + 1;  // +1 for sentinel
}


template<class Allocator>
bool ScopeInfo<Allocator>::CallsEval(Code* code) {
  if (code->sinfo_size() > 0) {
    // +1 for function name:
    Object** p = &Memory::Object_at(code->sinfo_start()) + 1;
    bool calls_eval;
    p = ReadBool(p, &calls_eval);
    return calls_eval;
  }
  return true;
}


template<class Allocator>
int ScopeInfo<Allocator>::NumberOfStackSlots(Code* code) {
  if (code->sinfo_size() > 0) {
    Object** p = StackSlotEntriesAddr(code);
    int n;  // number of stack slots;
    ReadInt(p, &n);
    return n;
  }
  return 0;
}


template<class Allocator>
int ScopeInfo<Allocator>::NumberOfContextSlots(Code* code) {
  if (code->sinfo_size() > 0) {
    Object** p = ContextEntriesAddr(code);
    int n;  // number of context slots;
    ReadInt(p, &n);
    return n + Context::MIN_CONTEXT_SLOTS;
  }
  return 0;
}


template<class Allocator>
int ScopeInfo<Allocator>::StackSlotIndex(Code* code, String* name) {
  ASSERT(name->IsSymbol());
  if (code->sinfo_size() > 0) {
    // Loop below depends on the NULL sentinel after the stack slot names.
    ASSERT(NumberOfStackSlots(code) > 0 ||
           *(StackSlotEntriesAddr(code) + 1) == NULL);
    // slots start after length entry
    Object** p0 = StackSlotEntriesAddr(code) + 1;
    Object** p = p0;
    while (*p != NULL) {
      if (*p == name) return static_cast<int>(p - p0);
      p++;
    }
  }
  return -1;
}


template<class Allocator>
int ScopeInfo<Allocator>::ContextSlotIndex(Code* code,
                                           String* name,
                                           Variable::Mode* mode) {
  ASSERT(name->IsSymbol());
  int result = ContextSlotCache::Lookup(code, name, mode);
  if (result != ContextSlotCache::kNotFound) return result;
  if (code->sinfo_size() > 0) {
    // Loop below depends on the NULL sentinel after the context slot names.
    ASSERT(NumberOfContextSlots(code) >= Context::MIN_CONTEXT_SLOTS ||
           *(ContextEntriesAddr(code) + 1) == NULL);

    // slots start after length entry
    Object** p0 = ContextEntriesAddr(code) + 1;
    Object** p = p0;
    // contexts may have no variable slots (in the presence of eval()).
    while (*p != NULL) {
      if (*p == name) {
        ASSERT(((p - p0) & 1) == 0);
        int v;
        ReadInt(p + 1, &v);
        Variable::Mode mode_value = static_cast<Variable::Mode>(v);
        if (mode != NULL) *mode = mode_value;
        result = static_cast<int>((p - p0) >> 1) + Context::MIN_CONTEXT_SLOTS;
        ContextSlotCache::Update(code, name, mode_value, result);
        return result;
      }
      p += 2;
    }
  }
  ContextSlotCache::Update(code, name, Variable::INTERNAL, -1);
  return -1;
}


template<class Allocator>
int ScopeInfo<Allocator>::ParameterIndex(Code* code, String* name) {
  ASSERT(name->IsSymbol());
  if (code->sinfo_size() > 0) {
    // We must read parameters from the end since for
    // multiply declared parameters the value of the
    // last declaration of that parameter is used
    // inside a function (and thus we need to look
    // at the last index). Was bug# 1110337.
    //
    // Eventually, we should only register such parameters
    // once, with corresponding index. This requires a new
    // implementation of the ScopeInfo code. See also other
    // comments in this file regarding this.
    Object** p = ParameterEntriesAddr(code);
    int n;  // number of parameters
    Object** p0 = ReadInt(p, &n);
    p = p0 + n;
    while (p > p0) {
      p--;
      if (*p == name) return static_cast<int>(p - p0);
    }
  }
  return -1;
}


template<class Allocator>
int ScopeInfo<Allocator>::FunctionContextSlotIndex(Code* code, String* name) {
  ASSERT(name->IsSymbol());
  if (code->sinfo_size() > 0) {
    Object** p = &Memory::Object_at(code->sinfo_start());
    if (*p == name) {
      p = ContextEntriesAddr(code);
      int n;  // number of context slots
      ReadInt(p, &n);
      ASSERT(n != 0);
      // The function context slot is the last entry.
      return n + Context::MIN_CONTEXT_SLOTS - 1;
    }
  }
  return -1;
}


template<class Allocator>
Handle<String> ScopeInfo<Allocator>::LocalName(int i) const {
  // A local variable can be allocated either on the stack or in the context.
  // For variables allocated in the context they are always preceded by the
  // number Context::MIN_CONTEXT_SLOTS number of fixed allocated slots in the
  // context.
  if (i < number_of_stack_slots()) {
    return stack_slot_name(i);
  } else {
    return context_slot_name(i - number_of_stack_slots() +
                             Context::MIN_CONTEXT_SLOTS);
  }
}


template<class Allocator>
int ScopeInfo<Allocator>::NumberOfLocals() const {
  int number_of_locals = number_of_stack_slots();
  if (number_of_context_slots() > 0) {
    ASSERT(number_of_context_slots() >= Context::MIN_CONTEXT_SLOTS);
    number_of_locals += number_of_context_slots() - Context::MIN_CONTEXT_SLOTS;
  }
  return number_of_locals;
}


int ContextSlotCache::Hash(Code* code, String* name) {
  // Uses only lower 32 bits if pointers are larger.
  uintptr_t addr_hash =
      static_cast<uint32_t>(reinterpret_cast<uintptr_t>(code)) >> 2;
  return static_cast<int>((addr_hash ^ name->Hash()) % kLength);
}


int ContextSlotCache::Lookup(Code* code,
                             String* name,
                             Variable::Mode* mode) {
  int index = Hash(code, name);
  Key& key = keys_[index];
  if ((key.code == code) && key.name->Equals(name)) {
    Value result(values_[index]);
    if (mode != NULL) *mode = result.mode();
    return result.index() + kNotFound;
  }
  return kNotFound;
}


void ContextSlotCache::Update(Code* code,
                              String* name,
                              Variable::Mode mode,
                              int slot_index) {
  String* symbol;
  ASSERT(slot_index > kNotFound);
  if (Heap::LookupSymbolIfExists(name, &symbol)) {
    int index = Hash(code, symbol);
    Key& key = keys_[index];
    key.code = code;
    key.name = symbol;
    // Please note value only takes a uint as index.
    values_[index] = Value(mode, slot_index - kNotFound).raw();
#ifdef DEBUG
    ValidateEntry(code, name, mode, slot_index);
#endif
  }
}


void ContextSlotCache::Clear() {
  for (int index = 0; index < kLength; index++) keys_[index].code = NULL;
}


ContextSlotCache::Key ContextSlotCache::keys_[ContextSlotCache::kLength];


uint32_t ContextSlotCache::values_[ContextSlotCache::kLength];


#ifdef DEBUG

void ContextSlotCache::ValidateEntry(Code* code,
                                     String* name,
                                     Variable::Mode mode,
                                     int slot_index) {
  String* symbol;
  if (Heap::LookupSymbolIfExists(name, &symbol)) {
    int index = Hash(code, name);
    Key& key = keys_[index];
    ASSERT(key.code == code);
    ASSERT(key.name->Equals(name));
    Value result(values_[index]);
    ASSERT(result.mode() == mode);
    ASSERT(result.index() + kNotFound == slot_index);
  }
}


template <class Allocator>
static void PrintList(const char* list_name,
                      int nof_internal_slots,
                      List<Handle<String>, Allocator>& list) {
  if (list.length() > 0) {
    PrintF("\n  // %s\n", list_name);
    if (nof_internal_slots > 0) {
      PrintF("  %2d - %2d [internal slots]\n", 0 , nof_internal_slots - 1);
    }
    for (int i = 0; i < list.length(); i++) {
      PrintF("  %2d ", i + nof_internal_slots);
      list[i]->ShortPrint();
      PrintF("\n");
    }
  }
}


template<class Allocator>
void ScopeInfo<Allocator>::Print() {
  PrintF("ScopeInfo ");
  if (function_name_->length() > 0)
    function_name_->ShortPrint();
  else
    PrintF("/* no function name */");
  PrintF("{");

  PrintList<Allocator>("parameters", 0, parameters_);
  PrintList<Allocator>("stack slots", 0, stack_slots_);
  PrintList<Allocator>("context slots", Context::MIN_CONTEXT_SLOTS,
                       context_slots_);

  PrintF("}\n");
}
#endif  // DEBUG


// Make sure the classes get instantiated by the template system.
template class ScopeInfo<FreeStoreAllocationPolicy>;
template class ScopeInfo<PreallocatedStorage>;
template class ScopeInfo<ZoneListAllocationPolicy>;

} }  // namespace v8::internal