/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "vm/BuiltinObjectKind.h"

#include "jspubtd.h"

#include "frontend/ParserAtom.h"
#include "vm/GlobalObject.h"

using namespace js;

static JSProtoKey ToProtoKey(BuiltinObjectKind kind) {
  switch (kind) {
    case BuiltinObjectKind::Array:
      return JSProto_Array;
    case BuiltinObjectKind::Map:
      return JSProto_Map;
    case BuiltinObjectKind::Promise:
      return JSProto_Promise;
    case BuiltinObjectKind::RegExp:
      return JSProto_RegExp;
    case BuiltinObjectKind::Set:
      return JSProto_Set;
    case BuiltinObjectKind::Symbol:
      return JSProto_Symbol;

    case BuiltinObjectKind::FunctionPrototype:
      return JSProto_Function;
    case BuiltinObjectKind::IteratorPrototype:
      return JSProto_Iterator;

    case BuiltinObjectKind::None:
      break;
  }
  MOZ_CRASH("Unexpected builtin object kind");
}

static bool IsPrototype(BuiltinObjectKind kind) {
  switch (kind) {
    case BuiltinObjectKind::Array:
    case BuiltinObjectKind::Map:
    case BuiltinObjectKind::Promise:
    case BuiltinObjectKind::RegExp:
    case BuiltinObjectKind::Set:
    case BuiltinObjectKind::Symbol:
      return false;

    case BuiltinObjectKind::FunctionPrototype:
    case BuiltinObjectKind::IteratorPrototype:
      return true;

    case BuiltinObjectKind::None:
      break;
  }
  MOZ_CRASH("Unexpected builtin object kind");
}

BuiltinObjectKind js::BuiltinConstructorForName(
    frontend::TaggedParserAtomIndex name) {
  if (name == frontend::TaggedParserAtomIndex::WellKnown::Array()) {
    return BuiltinObjectKind::Array;
  }
  if (name == frontend::TaggedParserAtomIndex::WellKnown::Map()) {
    return BuiltinObjectKind::Map;
  }
  if (name == frontend::TaggedParserAtomIndex::WellKnown::Promise()) {
    return BuiltinObjectKind::Promise;
  }
  if (name == frontend::TaggedParserAtomIndex::WellKnown::RegExp()) {
    return BuiltinObjectKind::RegExp;
  }
  if (name == frontend::TaggedParserAtomIndex::WellKnown::Set()) {
    return BuiltinObjectKind::Set;
  }
  if (name == frontend::TaggedParserAtomIndex::WellKnown::Symbol()) {
    return BuiltinObjectKind::Symbol;
  }
  return BuiltinObjectKind::None;
}

BuiltinObjectKind js::BuiltinPrototypeForName(
    frontend::TaggedParserAtomIndex name) {
  if (name == frontend::TaggedParserAtomIndex::WellKnown::Function()) {
    return BuiltinObjectKind::FunctionPrototype;
  }
  if (name == frontend::TaggedParserAtomIndex::WellKnown::Iterator()) {
    return BuiltinObjectKind::IteratorPrototype;
  }
  return BuiltinObjectKind::None;
}

JSObject* js::MaybeGetBuiltinObject(GlobalObject* global,
                                    BuiltinObjectKind kind) {
  JSProtoKey key = ToProtoKey(kind);
  if (IsPrototype(kind)) {
    return global->maybeGetPrototype(key);
  }
  return global->maybeGetConstructor(key);
}

JSObject* js::GetOrCreateBuiltinObject(JSContext* cx, BuiltinObjectKind kind) {
  JSProtoKey key = ToProtoKey(kind);
  if (IsPrototype(kind)) {
    return GlobalObject::getOrCreatePrototype(cx, key);
  }
  return GlobalObject::getOrCreateConstructor(cx, key);
}

const char* js::BuiltinObjectName(BuiltinObjectKind kind) {
  switch (kind) {
    case BuiltinObjectKind::Array:
      return "Array";
    case BuiltinObjectKind::Map:
      return "Map";
    case BuiltinObjectKind::Promise:
      return "Promise";
    case BuiltinObjectKind::RegExp:
      return "RegExp";
    case BuiltinObjectKind::Set:
      return "Set";
    case BuiltinObjectKind::Symbol:
      return "Symbol";

    case BuiltinObjectKind::FunctionPrototype:
      return "Function.prototype";
    case BuiltinObjectKind::IteratorPrototype:
      return "Iterator.prototype";

    case BuiltinObjectKind::None:
      break;
  }
  MOZ_CRASH("Unexpected builtin object kind");
}
