// Copyright 2018 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/torque/type-oracle.h"
#include "src/base/optional.h"
#include "src/torque/type-visitor.h"
#include "src/torque/types.h"

namespace v8 {
namespace internal {
namespace torque {

DEFINE_CONTEXTUAL_VARIABLE(TypeOracle)

// static
const std::vector<std::unique_ptr<AggregateType>>&
TypeOracle::GetAggregateTypes() {
  return Get().aggregate_types_;
}

// static
const std::vector<std::unique_ptr<BitFieldStructType>>&
TypeOracle::GetBitFieldStructTypes() {
  return Get().bit_field_struct_types_;
}

// static
void TypeOracle::FinalizeAggregateTypes() {
  size_t current = 0;
  while (current != Get().aggregate_types_.size()) {
    auto& p = Get().aggregate_types_[current++];
    p->Finalize();
  }
}

// static
const Type* TypeOracle::GetGenericTypeInstance(GenericType* generic_type,
                                               TypeVector arg_types) {
  auto& params = generic_type->generic_parameters();

  if (params.size() != arg_types.size()) {
    ReportError("Generic struct takes ", params.size(), " parameters, but ",
                arg_types.size(), " were given");
  }

  if (auto specialization = generic_type->GetSpecialization(arg_types)) {
    return *specialization;
  } else {
    const Type* type = nullptr;
    // AddSpecialization can raise an error, which should be reported in the
    // scope of the code requesting the specialization, not the generic type's
    // parent scope, hence the following block.
    {
      v8::internal::torque::Scope* requester_scope = CurrentScope::Get();
      CurrentScope::Scope generic_scope(generic_type->ParentScope());
      type = TypeVisitor::ComputeType(generic_type->declaration(),
                                      {{generic_type, arg_types}},
                                      requester_scope);
    }
    generic_type->AddSpecialization(arg_types, type);
    return type;
  }
}

// static
Namespace* TypeOracle::CreateGenericTypeInstantiationNamespace() {
  Get().generic_type_instantiation_namespaces_.push_back(
      std::make_unique<Namespace>(GENERIC_TYPE_INSTANTIATION_NAMESPACE_STRING));
  return Get().generic_type_instantiation_namespaces_.back().get();
}

// static
std::vector<const ClassType*> TypeOracle::GetClasses() {
  std::vector<const ClassType*> result;
  for (const std::unique_ptr<AggregateType>& t : Get().aggregate_types_) {
    if (auto* class_type = ClassType::DynamicCast(t.get())) {
      result.push_back(class_type);
    }
  }
  return result;
}

base::Optional<const Type*> TypeOracle::MatchReferenceGeneric(
    const Type* reference_type, bool* is_const) {
  if (auto type = Type::MatchUnaryGeneric(reference_type,
                                          GetMutableReferenceGeneric())) {
    if (is_const) *is_const = false;
    return type;
  }
  if (auto type =
          Type::MatchUnaryGeneric(reference_type, GetConstReferenceGeneric())) {
    if (is_const) *is_const = true;
    return type;
  }
  return base::nullopt;
}

}  // namespace torque
}  // namespace internal
}  // namespace v8
