// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/ast/ast-function-literal-id-reindexer.h"
#include "src/objects/objects-inl.h"

#include "src/ast/ast.h"

namespace v8 {
namespace internal {

AstFunctionLiteralIdReindexer::AstFunctionLiteralIdReindexer(size_t stack_limit,
                                                             int delta)
    : AstTraversalVisitor(stack_limit), delta_(delta) {}

AstFunctionLiteralIdReindexer::~AstFunctionLiteralIdReindexer() = default;

void AstFunctionLiteralIdReindexer::Reindex(Expression* pattern) {
#ifdef DEBUG
  visited_.clear();
#endif
  Visit(pattern);
  CheckVisited(pattern);
}

void AstFunctionLiteralIdReindexer::VisitFunctionLiteral(FunctionLiteral* lit) {
  // Make sure we're not already in the visited set.
  DCHECK(visited_.insert(lit).second);

  AstTraversalVisitor::VisitFunctionLiteral(lit);
  lit->set_function_literal_id(lit->function_literal_id() + delta_);
}

void AstFunctionLiteralIdReindexer::VisitClassLiteral(ClassLiteral* expr) {
  // Manually visit the class literal so that we can change the property walk.
  // This should be kept in-sync with AstTraversalVisitor::VisitClassLiteral.

  if (expr->extends() != nullptr) {
    Visit(expr->extends());
  }
  Visit(expr->constructor());
  if (expr->static_fields_initializer() != nullptr) {
    Visit(expr->static_fields_initializer());
  }
  if (expr->instance_members_initializer_function() != nullptr) {
    Visit(expr->instance_members_initializer_function());
  }
  ZonePtrList<ClassLiteral::Property>* private_members =
      expr->private_members();
  for (int i = 0; i < private_members->length(); ++i) {
    ClassLiteralProperty* prop = private_members->at(i);

    // Private fields have their key and value present in
    // instance_members_initializer_function, so they will
    // already have been visited.
    if (prop->value()->IsFunctionLiteral()) {
      Visit(prop->value());
    } else {
      CheckVisited(prop->value());
    }
  }
  ZonePtrList<ClassLiteral::Property>* props = expr->public_members();
  for (int i = 0; i < props->length(); ++i) {
    ClassLiteralProperty* prop = props->at(i);

    // Public fields with computed names have their key
    // and value present in instance_members_initializer_function, so they will
    // already have been visited.
    if (prop->is_computed_name() && !prop->value()->IsFunctionLiteral()) {
      if (!prop->key()->IsLiteral()) {
        CheckVisited(prop->key());
      }
      CheckVisited(prop->value());
    } else {
      if (!prop->key()->IsLiteral()) {
        Visit(prop->key());
      }
      Visit(prop->value());
    }
  }
}

#ifdef DEBUG
namespace {

class AstFunctionLiteralIdReindexChecker final
    : public AstTraversalVisitor<AstFunctionLiteralIdReindexChecker> {
 public:
  AstFunctionLiteralIdReindexChecker(size_t stack_limit,
                                     const std::set<FunctionLiteral*>* visited)
      : AstTraversalVisitor(stack_limit), visited_(visited) {}

  void VisitFunctionLiteral(FunctionLiteral* lit) {
    // TODO(leszeks): It would be nice to print the unvisited function literal
    // here, but that requires more advanced DCHECK support with formatting.
    DCHECK(visited_->find(lit) != visited_->end());
  }

 private:
  const std::set<FunctionLiteral*>* visited_;
};

}  // namespace

void AstFunctionLiteralIdReindexer::CheckVisited(Expression* expr) {
  AstFunctionLiteralIdReindexChecker(stack_limit(), &visited_).Visit(expr);
}
#endif

}  // namespace internal
}  // namespace v8
