// Copyright 2019 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.

#ifndef V8_OBJECTS_FUNCTION_KIND_H_
#define V8_OBJECTS_FUNCTION_KIND_H_

#include "src/base/bounds.h"
#include "src/base/macros.h"

namespace v8 {
namespace internal {

enum class FunctionKind : uint8_t {
  // BEGIN constructable functions
  kNormalFunction,
  kModule,
  kAsyncModule,
  // BEGIN class constructors
  // BEGIN base constructors
  kBaseConstructor,
  // BEGIN default constructors
  kDefaultBaseConstructor,
  // END base constructors
  // BEGIN derived constructors
  kDefaultDerivedConstructor,
  // END default constructors
  kDerivedConstructor,
  // END derived constructors
  // END class constructors
  // END constructable functions.
  // BEGIN accessors
  kGetterFunction,
  kStaticGetterFunction,
  kSetterFunction,
  kStaticSetterFunction,
  // END accessors
  // BEGIN arrow functions
  kArrowFunction,
  // BEGIN async functions
  kAsyncArrowFunction,
  // END arrow functions
  kAsyncFunction,
  // BEGIN concise methods 1
  kAsyncConciseMethod,
  kStaticAsyncConciseMethod,
  // BEGIN generators
  kAsyncConciseGeneratorMethod,
  kStaticAsyncConciseGeneratorMethod,
  // END concise methods 1
  kAsyncGeneratorFunction,
  // END async functions
  kGeneratorFunction,
  // BEGIN concise methods 2
  kConciseGeneratorMethod,
  kStaticConciseGeneratorMethod,
  // END generators
  kConciseMethod,
  kStaticConciseMethod,
  kClassMembersInitializerFunction,
  kClassStaticInitializerFunction,
  // END concise methods 2
  kInvalid,

  kLastFunctionKind = kClassStaticInitializerFunction,
};

constexpr int kFunctionKindBitSize = 5;
STATIC_ASSERT(static_cast<int>(FunctionKind::kLastFunctionKind) <
              (1 << kFunctionKindBitSize));

inline bool IsArrowFunction(FunctionKind kind) {
  return base::IsInRange(kind, FunctionKind::kArrowFunction,
                         FunctionKind::kAsyncArrowFunction);
}

inline bool IsModule(FunctionKind kind) {
  return base::IsInRange(kind, FunctionKind::kModule,
                         FunctionKind::kAsyncModule);
}

inline bool IsAsyncModule(FunctionKind kind) {
  return kind == FunctionKind::kAsyncModule;
}

inline bool IsAsyncGeneratorFunction(FunctionKind kind) {
  return base::IsInRange(kind, FunctionKind::kAsyncConciseGeneratorMethod,
                         FunctionKind::kAsyncGeneratorFunction);
}

inline bool IsGeneratorFunction(FunctionKind kind) {
  return base::IsInRange(kind, FunctionKind::kAsyncConciseGeneratorMethod,
                         FunctionKind::kStaticConciseGeneratorMethod);
}

inline bool IsAsyncFunction(FunctionKind kind) {
  return base::IsInRange(kind, FunctionKind::kAsyncArrowFunction,
                         FunctionKind::kAsyncGeneratorFunction);
}

inline bool IsResumableFunction(FunctionKind kind) {
  return IsGeneratorFunction(kind) || IsAsyncFunction(kind) || IsModule(kind);
}

inline bool IsConciseMethod(FunctionKind kind) {
  return base::IsInRange(kind, FunctionKind::kAsyncConciseMethod,
                         FunctionKind::kStaticAsyncConciseGeneratorMethod) ||
         base::IsInRange(kind, FunctionKind::kConciseGeneratorMethod,
                         FunctionKind::kClassStaticInitializerFunction);
}

inline bool IsStrictFunctionWithoutPrototype(FunctionKind kind) {
  return base::IsInRange(kind, FunctionKind::kGetterFunction,
                         FunctionKind::kAsyncArrowFunction) ||
         base::IsInRange(kind, FunctionKind::kAsyncConciseMethod,
                         FunctionKind::kStaticAsyncConciseGeneratorMethod) ||
         base::IsInRange(kind, FunctionKind::kConciseGeneratorMethod,
                         FunctionKind::kClassStaticInitializerFunction);
}

inline bool IsGetterFunction(FunctionKind kind) {
  return base::IsInRange(kind, FunctionKind::kGetterFunction,
                         FunctionKind::kStaticGetterFunction);
}

inline bool IsSetterFunction(FunctionKind kind) {
  return base::IsInRange(kind, FunctionKind::kSetterFunction,
                         FunctionKind::kStaticSetterFunction);
}

inline bool IsAccessorFunction(FunctionKind kind) {
  return base::IsInRange(kind, FunctionKind::kGetterFunction,
                         FunctionKind::kStaticSetterFunction);
}

inline bool IsDefaultConstructor(FunctionKind kind) {
  return base::IsInRange(kind, FunctionKind::kDefaultBaseConstructor,
                         FunctionKind::kDefaultDerivedConstructor);
}

inline bool IsBaseConstructor(FunctionKind kind) {
  return base::IsInRange(kind, FunctionKind::kBaseConstructor,
                         FunctionKind::kDefaultBaseConstructor);
}

inline bool IsDerivedConstructor(FunctionKind kind) {
  return base::IsInRange(kind, FunctionKind::kDefaultDerivedConstructor,
                         FunctionKind::kDerivedConstructor);
}

inline bool IsClassConstructor(FunctionKind kind) {
  return base::IsInRange(kind, FunctionKind::kBaseConstructor,
                         FunctionKind::kDerivedConstructor);
}

inline bool IsClassMembersInitializerFunction(FunctionKind kind) {
  return base::IsInRange(kind, FunctionKind::kClassMembersInitializerFunction,
                         FunctionKind::kClassStaticInitializerFunction);
}

inline bool IsConstructable(FunctionKind kind) {
  return base::IsInRange(kind, FunctionKind::kNormalFunction,
                         FunctionKind::kDerivedConstructor);
}

inline bool IsStatic(FunctionKind kind) {
  switch (kind) {
    case FunctionKind::kStaticGetterFunction:
    case FunctionKind::kStaticSetterFunction:
    case FunctionKind::kStaticConciseMethod:
    case FunctionKind::kStaticConciseGeneratorMethod:
    case FunctionKind::kStaticAsyncConciseMethod:
    case FunctionKind::kStaticAsyncConciseGeneratorMethod:
    case FunctionKind::kClassStaticInitializerFunction:
      return true;
    default:
      return false;
  }
}

inline bool BindsSuper(FunctionKind kind) {
  return IsConciseMethod(kind) || IsAccessorFunction(kind) ||
         IsClassConstructor(kind);
}

inline bool IsAwaitAsIdentifierDisallowed(FunctionKind kind) {
  // 'await' is always disallowed as an identifier in module contexts. Callers
  // should short-circuit the module case instead of calling this.
  DCHECK(!IsModule(kind));
  return IsAsyncFunction(kind) ||
         kind == FunctionKind::kClassStaticInitializerFunction;
}

inline const char* FunctionKind2String(FunctionKind kind) {
  switch (kind) {
    case FunctionKind::kNormalFunction:
      return "NormalFunction";
    case FunctionKind::kArrowFunction:
      return "ArrowFunction";
    case FunctionKind::kGeneratorFunction:
      return "GeneratorFunction";
    case FunctionKind::kConciseMethod:
      return "ConciseMethod";
    case FunctionKind::kStaticConciseMethod:
      return "StaticConciseMethod";
    case FunctionKind::kDerivedConstructor:
      return "DerivedConstructor";
    case FunctionKind::kBaseConstructor:
      return "BaseConstructor";
    case FunctionKind::kGetterFunction:
      return "GetterFunction";
    case FunctionKind::kStaticGetterFunction:
      return "StaticGetterFunction";
    case FunctionKind::kSetterFunction:
      return "SetterFunction";
    case FunctionKind::kStaticSetterFunction:
      return "StaticSetterFunction";
    case FunctionKind::kAsyncFunction:
      return "AsyncFunction";
    case FunctionKind::kModule:
      return "Module";
    case FunctionKind::kAsyncModule:
      return "AsyncModule";
    case FunctionKind::kClassMembersInitializerFunction:
      return "ClassMembersInitializerFunction";
    case FunctionKind::kClassStaticInitializerFunction:
      return "ClassStaticInitializerFunction";
    case FunctionKind::kDefaultBaseConstructor:
      return "DefaultBaseConstructor";
    case FunctionKind::kDefaultDerivedConstructor:
      return "DefaultDerivedConstructor";
    case FunctionKind::kAsyncArrowFunction:
      return "AsyncArrowFunction";
    case FunctionKind::kAsyncConciseMethod:
      return "AsyncConciseMethod";
    case FunctionKind::kStaticAsyncConciseMethod:
      return "StaticAsyncConciseMethod";
    case FunctionKind::kConciseGeneratorMethod:
      return "ConciseGeneratorMethod";
    case FunctionKind::kStaticConciseGeneratorMethod:
      return "StaticConciseGeneratorMethod";
    case FunctionKind::kAsyncConciseGeneratorMethod:
      return "AsyncConciseGeneratorMethod";
    case FunctionKind::kStaticAsyncConciseGeneratorMethod:
      return "StaticAsyncConciseGeneratorMethod";
    case FunctionKind::kAsyncGeneratorFunction:
      return "AsyncGeneratorFunction";
    case FunctionKind::kInvalid:
      return "Invalid";
  }
  UNREACHABLE();
}

inline std::ostream& operator<<(std::ostream& os, FunctionKind kind) {
  return os << FunctionKind2String(kind);
}

}  // namespace internal
}  // namespace v8

#endif  // V8_OBJECTS_FUNCTION_KIND_H_