/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#include "hermes/VM/Callable.h"

#include "hermes/VM/ArrayLike.h"
#include "hermes/VM/BuildMetadata.h"
#include "hermes/VM/JSDataView.h"
#include "hermes/VM/JSDate.h"
#include "hermes/VM/JSError.h"
#include "hermes/VM/JSMapImpl.h"
#include "hermes/VM/JSNativeFunctions.h"
#include "hermes/VM/JSProxy.h"
#include "hermes/VM/JSRegExp.h"
#include "hermes/VM/JSTypedArray.h"
#include "hermes/VM/JSWeakMapImpl.h"
#include "hermes/VM/PrimitiveBox.h"
#include "hermes/VM/PropertyAccessor.h"
#include "hermes/VM/SmallXString.h"
#include "hermes/VM/StackFrame-inline.h"
#include "hermes/VM/StringPrimitive.h"

#include "llvh/ADT/ArrayRef.h"

namespace hermes {
namespace vm {

//===----------------------------------------------------------------------===//
// class Environment

const VTable Environment::vt{CellKind::EnvironmentKind, 0};

void EnvironmentBuildMeta(const GCCell *cell, Metadata::Builder &mb) {
  const auto *self = static_cast<const Environment *>(cell);
  mb.setVTable(&Environment::vt);
  mb.addField("parentEnvironment", &self->parentEnvironment_);
  mb.addArray(self->getSlots(), &self->size_, sizeof(GCHermesValue));
}

//===----------------------------------------------------------------------===//
// class Callable

void CallableBuildMeta(const GCCell *cell, Metadata::Builder &mb) {
  mb.addJSObjectOverlapSlots(JSObject::numOverlapSlots<Callable>());
  ObjectBuildMeta(cell, mb);
  const auto *self = static_cast<const Callable *>(cell);
  mb.setVTable(&Callable::vt.base);
  mb.addField("environment", &self->environment_);
}

std::string Callable::_snapshotNameImpl(GCCell *cell, GC *gc) {
  auto *const self = reinterpret_cast<Callable *>(cell);
  return self->getNameIfExists(gc->getPointerBase());
}

CallResult<PseudoHandle<JSObject>> Callable::_newObjectImpl(
    Handle<Callable> /*selfHandle*/,
    Runtime *runtime,
    Handle<JSObject> parentHandle) {
  return JSObject::create(runtime, parentHandle);
}

void Callable::defineLazyProperties(Handle<Callable> fn, Runtime *runtime) {
  // lazy functions can be Bound or JS Functions.
  if (auto jsFun = Handle<JSFunction>::dyn_vmcast(fn)) {
    const CodeBlock *codeBlock = jsFun->getCodeBlock();
    // Create empty object for prototype.
    auto prototypeParent = vmisa<JSGeneratorFunction>(*jsFun)
        ? Handle<JSObject>::vmcast(&runtime->generatorPrototype)
        : Handle<JSObject>::vmcast(&runtime->objectPrototype);

    // According to ES12 26.7.4, AsyncFunction instances do not have a
    // 'prototype' property, hence we need to set an null handle here.
    auto prototypeObjectHandle = vmisa<JSAsyncFunction>(*jsFun)
        ? Runtime::makeNullHandle<JSObject>()
        : runtime->makeHandle(JSObject::create(runtime, prototypeParent));

    auto cr = Callable::defineNameLengthAndPrototype(
        fn,
        runtime,
        codeBlock->getNameMayAllocate(),
        codeBlock->getParamCount() - 1,
        prototypeObjectHandle,
        Callable::WritablePrototype::Yes,
        codeBlock->isStrictMode());
    assert(
        cr != ExecutionStatus::EXCEPTION && "failed to define length and name");
    (void)cr;
  } else if (vmisa<BoundFunction>(fn.get())) {
    Handle<BoundFunction> boundfn = Handle<BoundFunction>::vmcast(fn);
    Handle<Callable> target = runtime->makeHandle(boundfn->getTarget(runtime));
    unsigned int argsWithThis = boundfn->getArgCountWithThis(runtime);

    auto res = BoundFunction::initializeLengthAndName(
        boundfn, runtime, target, argsWithThis == 0 ? 0 : argsWithThis - 1);
    assert(
        res != ExecutionStatus::EXCEPTION &&
        "failed to define length and name of bound function");
    (void)res;
  } else {
    // no other kind of function can be lazy currently
    assert(false && "invalid lazy function");
  }
}

ExecutionStatus Callable::defineNameLengthAndPrototype(
    Handle<Callable> selfHandle,
    Runtime *runtime,
    SymbolID name,
    unsigned paramCount,
    Handle<JSObject> prototypeObjectHandle,
    WritablePrototype writablePrototype,
    bool strictMode) {
  PropertyFlags pf;
  pf.clear();
  pf.enumerable = 0;
  pf.writable = 0;
  pf.configurable = 1;

  GCScope scope{runtime, "defineNameLengthAndPrototype"};

  namespace P = Predefined;
/// Adds a property to the object in \p OBJ_HANDLE.  \p SYMBOL provides its name
/// as a \c Predefined enum value, and its value is  rooted in \p HANDLE.  If
/// property definition fails, the exceptional execution status will be
/// propagated to the outer function.
#define DEFINE_PROP(OBJ_HANDLE, SYMBOL, HANDLE)                            \
  do {                                                                     \
    auto status = JSObject::defineNewOwnProperty(                          \
        OBJ_HANDLE, runtime, Predefined::getSymbolID(SYMBOL), pf, HANDLE); \
    if (LLVM_UNLIKELY(status == ExecutionStatus::EXCEPTION)) {             \
      return ExecutionStatus::EXCEPTION;                                   \
    }                                                                      \
  } while (false)

  assert(name.isValid() && "A name must always be provided");

  // Length is the number of formal arguments.
  // 10.2.9 SetFunctionLength is performed during 10.2.3 OrdinaryFunctionCreate.
  auto lengthHandle =
      runtime->makeHandle(HermesValue::encodeDoubleValue(paramCount));
  DEFINE_PROP(selfHandle, P::length, lengthHandle);

  // Define the name.
  // 10.2.8 SetFunctionName is performed after 10.2.3 OrdinaryFunctionCreate.
  auto nameHandle =
      runtime->makeHandle(runtime->getStringPrimFromSymbolID(name));
  DEFINE_PROP(selfHandle, P::name, nameHandle);

  if (strictMode) {
    // Define .callee and .arguments properties: throw always in strict mode.
    auto accessor =
        Handle<PropertyAccessor>::vmcast(&runtime->throwTypeErrorAccessor);

    pf.clear();
    pf.enumerable = 0;
    pf.configurable = 0;
    pf.accessor = 1;

    DEFINE_PROP(selfHandle, P::caller, accessor);
    DEFINE_PROP(selfHandle, P::arguments, accessor);
  }

  if (prototypeObjectHandle) {
    // Set its 'prototype' property.
    pf.clear();
    pf.enumerable = 0;
    /// System constructors have read-only prototypes.
    pf.writable = (uint8_t)writablePrototype;
    pf.configurable = 0;
    DEFINE_PROP(selfHandle, P::prototype, prototypeObjectHandle);

    if (LLVM_LIKELY(!vmisa<JSGeneratorFunction>(*selfHandle))) {
      // Set the 'constructor' property in the prototype object.
      // This must not be set for GeneratorFunctions, because
      // prototypes must not point back to their constructors.
      // See the diagram: ES9.0 25.2 (GeneratorFunction objects).
      pf.clear();
      pf.enumerable = 0;
      pf.writable = 1;
      pf.configurable = 1;
      DEFINE_PROP(prototypeObjectHandle, P::constructor, selfHandle);
    }
  }

  return ExecutionStatus::RETURNED;

#undef DEFINE_PROP
}

/// Execute this function with no arguments. This is just a convenience
/// helper method; it actually invokes the interpreter recursively.
CallResult<PseudoHandle<>> Callable::executeCall0(
    Handle<Callable> selfHandle,
    Runtime *runtime,
    Handle<> thisArgHandle,
    bool construct) {
  ScopedNativeCallFrame newFrame{
      runtime,
      0,
      selfHandle.getHermesValue(),
      construct ? selfHandle.getHermesValue()
                : HermesValue::encodeUndefinedValue(),
      *thisArgHandle};
  if (LLVM_UNLIKELY(newFrame.overflowed()))
    return runtime->raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
  return call(selfHandle, runtime);
}

/// Execute this function with one argument. This is just a convenience
/// helper method; it actually invokes the interpreter recursively.
CallResult<PseudoHandle<>> Callable::executeCall1(
    Handle<Callable> selfHandle,
    Runtime *runtime,
    Handle<> thisArgHandle,
    HermesValue param1,
    bool construct) {
  ScopedNativeCallFrame newFrame{
      runtime,
      1,
      selfHandle.getHermesValue(),
      construct ? selfHandle.getHermesValue()
                : HermesValue::encodeUndefinedValue(),
      *thisArgHandle};
  if (LLVM_UNLIKELY(newFrame.overflowed()))
    return runtime->raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
  newFrame->getArgRef(0) = param1;
  return call(selfHandle, runtime);
}

/// Execute this function with two arguments. This is just a convenience
/// helper method; it actually invokes the interpreter recursively.
CallResult<PseudoHandle<>> Callable::executeCall2(
    Handle<Callable> selfHandle,
    Runtime *runtime,
    Handle<> thisArgHandle,
    HermesValue param1,
    HermesValue param2,
    bool construct) {
  ScopedNativeCallFrame newFrame{
      runtime,
      2,
      selfHandle.getHermesValue(),
      construct ? selfHandle.getHermesValue()
                : HermesValue::encodeUndefinedValue(),
      *thisArgHandle};
  if (LLVM_UNLIKELY(newFrame.overflowed()))
    return runtime->raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
  newFrame->getArgRef(0) = param1;
  newFrame->getArgRef(1) = param2;
  return call(selfHandle, runtime);
}

/// Execute this function with three arguments. This is just a convenience
/// helper method; it actually invokes the interpreter recursively.
CallResult<PseudoHandle<>> Callable::executeCall3(
    Handle<Callable> selfHandle,
    Runtime *runtime,
    Handle<> thisArgHandle,
    HermesValue param1,
    HermesValue param2,
    HermesValue param3,
    bool construct) {
  ScopedNativeCallFrame newFrame{
      runtime,
      3,
      selfHandle.getHermesValue(),
      construct ? selfHandle.getHermesValue()
                : HermesValue::encodeUndefinedValue(),
      *thisArgHandle};
  if (LLVM_UNLIKELY(newFrame.overflowed()))
    return runtime->raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
  newFrame->getArgRef(0) = param1;
  newFrame->getArgRef(1) = param2;
  newFrame->getArgRef(2) = param3;
  return call(selfHandle, runtime);
}

/// Execute this function with four arguments. This is just a convenience
/// helper method; it actually invokes the interpreter recursively.
CallResult<PseudoHandle<>> Callable::executeCall4(
    Handle<Callable> selfHandle,
    Runtime *runtime,
    Handle<> thisArgHandle,
    HermesValue param1,
    HermesValue param2,
    HermesValue param3,
    HermesValue param4,
    bool construct) {
  ScopedNativeCallFrame newFrame{
      runtime,
      4,
      selfHandle.getHermesValue(),
      construct ? selfHandle.getHermesValue()
                : HermesValue::encodeUndefinedValue(),
      *thisArgHandle};
  if (LLVM_UNLIKELY(newFrame.overflowed()))
    return runtime->raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
  newFrame->getArgRef(0) = param1;
  newFrame->getArgRef(1) = param2;
  newFrame->getArgRef(2) = param3;
  newFrame->getArgRef(3) = param4;
  return call(selfHandle, runtime);
}

CallResult<PseudoHandle<>> Callable::executeCall(
    Handle<Callable> selfHandle,
    Runtime *runtime,
    Handle<> newTarget,
    Handle<> thisArgument,
    Handle<JSObject> arrayLike) {
  CallResult<uint64_t> nRes = getArrayLikeLength(arrayLike, runtime);
  if (LLVM_UNLIKELY(nRes == ExecutionStatus::EXCEPTION)) {
    return ExecutionStatus::EXCEPTION;
  }
  if (*nRes > UINT32_MAX) {
    return runtime->raiseRangeError("Too many arguments for apply");
  }
  uint32_t n = static_cast<uint32_t>(*nRes);
  ScopedNativeCallFrame newFrame{
      runtime, n, selfHandle.getHermesValue(), *newTarget, *thisArgument};
  if (LLVM_UNLIKELY(newFrame.overflowed()))
    return runtime->raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);

  // Initialize the arguments to undefined because we might allocate and cause
  // a gc while populating them.
  // TODO: look into doing this lazily.
  newFrame.fillArguments(n, HermesValue::encodeUndefinedValue());

  if (LLVM_UNLIKELY(
          createListFromArrayLike(
              arrayLike,
              runtime,
              n,
              [&newFrame](Runtime *, uint64_t index, PseudoHandle<> value) {
                newFrame->getArgRef(index) = value.getHermesValue();
                return ExecutionStatus::RETURNED;
              }) == ExecutionStatus::EXCEPTION)) {
    return ExecutionStatus::EXCEPTION;
  }

  return Callable::call(selfHandle, runtime);
}

CallResult<PseudoHandle<>> Callable::executeConstruct0(
    Handle<Callable> selfHandle,
    Runtime *runtime) {
  auto thisVal = Callable::createThisForConstruct(selfHandle, runtime);
  if (LLVM_UNLIKELY(thisVal == ExecutionStatus::EXCEPTION)) {
    return ExecutionStatus::EXCEPTION;
  }
  auto thisValHandle = runtime->makeHandle<JSObject>(std::move(thisVal->get()));
  auto result = executeCall0(selfHandle, runtime, thisValHandle, true);
  if (LLVM_UNLIKELY(result == ExecutionStatus::EXCEPTION)) {
    return ExecutionStatus::EXCEPTION;
  }
  return (*result)->isObject() ? std::move(result)
                               : CallResult<PseudoHandle<>>(createPseudoHandle(
                                     thisValHandle.getHermesValue()));
}

CallResult<PseudoHandle<>> Callable::executeConstruct1(
    Handle<Callable> selfHandle,
    Runtime *runtime,
    Handle<> param1) {
  auto thisVal = Callable::createThisForConstruct(selfHandle, runtime);
  if (LLVM_UNLIKELY(thisVal == ExecutionStatus::EXCEPTION)) {
    return ExecutionStatus::EXCEPTION;
  }
  auto thisValHandle = runtime->makeHandle<JSObject>(std::move(thisVal->get()));
  CallResult<PseudoHandle<>> result =
      executeCall1(selfHandle, runtime, thisValHandle, *param1, true);
  if (LLVM_UNLIKELY(result == ExecutionStatus::EXCEPTION)) {
    return ExecutionStatus::EXCEPTION;
  }
  return (*result)->isObject() ? std::move(result)
                               : CallResult<PseudoHandle<>>(createPseudoHandle(
                                     thisValHandle.getHermesValue()));
}

CallResult<PseudoHandle<JSObject>> Callable::createThisForConstruct(
    Handle<Callable> selfHandle,
    Runtime *runtime) {
  CallResult<PseudoHandle<>> prototypeProp = JSObject::getNamed_RJS(
      selfHandle, runtime, Predefined::getSymbolID(Predefined::prototype));
  if (LLVM_UNLIKELY(prototypeProp == ExecutionStatus::EXCEPTION)) {
    return ExecutionStatus::EXCEPTION;
  }
  Handle<JSObject> prototype = vmisa<JSObject>(prototypeProp->get())
      ? runtime->makeHandle<JSObject>(prototypeProp->get())
      : Handle<JSObject>::vmcast(&runtime->objectPrototype);
  return Callable::newObject(selfHandle, runtime, prototype);
}

CallResult<double> Callable::extractOwnLengthProperty_RJS(
    Handle<Callable> selfHandle,
    Runtime *runtime) {
  CallResult<PseudoHandle<>> propRes{
      createPseudoHandle(HermesValue::encodeUndefinedValue())};
  NamedPropertyDescriptor desc;
  if (JSObject::getOwnNamedDescriptor(
          selfHandle,
          runtime,
          Predefined::getSymbolID(Predefined::length),
          desc)) {
    propRes = JSObject::getNamedPropertyValue_RJS(
        selfHandle, runtime, selfHandle, desc);
  } else if (selfHandle->isProxyObject()) {
    ComputedPropertyDescriptor desc;
    CallResult<bool> hasLength = JSProxy::getOwnProperty(
        selfHandle,
        runtime,
        runtime->getPredefinedStringHandle(Predefined::length),
        desc,
        nullptr);
    if (LLVM_UNLIKELY(hasLength == ExecutionStatus::EXCEPTION)) {
      return ExecutionStatus::EXCEPTION;
    }
    if (*hasLength) {
      propRes = JSProxy::getNamed(
          selfHandle,
          runtime,
          Predefined::getSymbolID(Predefined::length),
          selfHandle);
    }
  }

  {
    NoAllocScope naScope{runtime};

    if (propRes == ExecutionStatus::EXCEPTION) {
      return ExecutionStatus::EXCEPTION;
    }

    if (!(*propRes)->isNumber()) {
      return 0.0;
    }
  }

  auto intRes =
      toIntegerOrInfinity(runtime, runtime->makeHandle(std::move(*propRes)));
  if (intRes == ExecutionStatus::EXCEPTION) {
    return ExecutionStatus::EXCEPTION;
  }

  return intRes->getNumber();
}

//===----------------------------------------------------------------------===//
// class BoundFunction

const CallableVTable BoundFunction::vt{
    {
        VTable(
            CellKind::BoundFunctionKind,
            cellSize<BoundFunction>(),
            nullptr,
            nullptr,
            nullptr,
            nullptr,
            VTable::HeapSnapshotMetadata{
                HeapSnapshot::NodeType::Closure,
                BoundFunction::_snapshotNameImpl,
                BoundFunction::_snapshotAddEdgesImpl,
                nullptr,
                nullptr}),
        BoundFunction::_getOwnIndexedRangeImpl,
        BoundFunction::_haveOwnIndexedImpl,
        BoundFunction::_getOwnIndexedPropertyFlagsImpl,
        BoundFunction::_getOwnIndexedImpl,
        BoundFunction::_setOwnIndexedImpl,
        BoundFunction::_deleteOwnIndexedImpl,
        BoundFunction::_checkAllOwnIndexedImpl,
    },
    BoundFunction::_newObjectImpl,
    BoundFunction::_callImpl};

void BoundFunctionBuildMeta(const GCCell *cell, Metadata::Builder &mb) {
  mb.addJSObjectOverlapSlots(JSObject::numOverlapSlots<BoundFunction>());
  CallableBuildMeta(cell, mb);
  const auto *self = static_cast<const BoundFunction *>(cell);
  mb.setVTable(&BoundFunction::vt.base.base);
  mb.addField("target", &self->target_);
  mb.addField("argStorage", &self->argStorage_);
}

CallResult<HermesValue> BoundFunction::create(
    Runtime *runtime,
    Handle<Callable> target,
    unsigned argCountWithThis,
    ConstArgIterator argsWithThis) {
  unsigned argCount = argCountWithThis > 0 ? argCountWithThis - 1 : 0;

  auto arrRes = ArrayStorage::create(runtime, argCount + 1);
  if (LLVM_UNLIKELY(arrRes == ExecutionStatus::EXCEPTION)) {
    return ExecutionStatus::EXCEPTION;
  }
  auto argStorageHandle = runtime->makeHandle<ArrayStorage>(*arrRes);

  auto *cell = runtime->makeAFixed<BoundFunction>(
      runtime,
      Handle<JSObject>::vmcast(&runtime->functionPrototype),
      runtime->getHiddenClassForPrototype(
          runtime->functionPrototypeRawPtr, numOverlapSlots<BoundFunction>()),
      target,
      argStorageHandle);
  auto selfHandle = JSObjectInit::initToHandle(runtime, cell);

  // Copy the arguments. If we don't have any, we must at least initialize
  // 'this' to 'undefined'.
  MutableHandle<ArrayStorage> handle(
      runtime, selfHandle->argStorage_.get(runtime));

  // In case the storage was trimmed, make sure it has enough capacity.
  ArrayStorage::ensureCapacity(handle, runtime, argCount + 1);

  if (argCountWithThis) {
    for (unsigned i = 0; i != argCountWithThis; ++i) {
      ArrayStorage::push_back(handle, runtime, Handle<>(&argsWithThis[i]));
    }
  } else {
    // Don't need to worry about resizing since it was created with a capacity
    // of at least 1.
    ArrayStorage::push_back(handle, runtime, Runtime::getUndefinedValue());
  }
  // Update the storage pointer in case push_back() needed to reallocate.
  selfHandle->argStorage_.set(runtime, *handle, &runtime->getHeap());

  if (target->isLazy()) {
    // If the target is lazy we can make the bound function lazy.
    // If the target is NOT lazy, it might have getter/setters on length that
    // throws and we also need to throw.
    selfHandle->flags_.lazyObject = 1;
  } else {
    if (initializeLengthAndName(selfHandle, runtime, target, argCount) ==
        ExecutionStatus::EXCEPTION) {
      return ExecutionStatus::EXCEPTION;
    }
  }
  return selfHandle.getHermesValue();
}

ExecutionStatus BoundFunction::initializeLengthAndName(
    Handle<Callable> selfHandle,
    Runtime *runtime,
    Handle<Callable> target,
    unsigned argCount) {
  if (LLVM_UNLIKELY(target->isLazy())) {
    Callable::initializeLazyObject(runtime, target);
  }

  // Extract target.length.
  auto targetLength = Callable::extractOwnLengthProperty_RJS(target, runtime);
  if (targetLength == ExecutionStatus::EXCEPTION)
    return ExecutionStatus::EXCEPTION;

  // Define .length
  PropertyFlags pf{};
  pf.enumerable = 0;
  pf.writable = 0;
  pf.configurable = 1;

  // Length is the number of formal arguments.
  auto length = runtime->makeHandle(HermesValue::encodeNumberValue(
      argCount >= *targetLength ? 0.0 : *targetLength - argCount));
  if (LLVM_UNLIKELY(
          JSObject::defineNewOwnProperty(
              selfHandle,
              runtime,
              Predefined::getSymbolID(Predefined::length),
              pf,
              length) == ExecutionStatus::EXCEPTION)) {
    return ExecutionStatus::EXCEPTION;
  }

  // Set the name by prepending "bound ".
  auto propRes = JSObject::getNamed_RJS(
      target, runtime, Predefined::getSymbolID(Predefined::name));
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
    return ExecutionStatus::EXCEPTION;
  }
  auto nameHandle = (*propRes)->isString()
      ? runtime->makeHandle<StringPrimitive>(propRes->getHermesValue())
      : runtime->getPredefinedStringHandle(Predefined::emptyString);
  auto nameView = StringPrimitive::createStringView(runtime, nameHandle);
  llvh::SmallU16String<32> boundName{"bound "};
  boundName.append(nameView.begin(), nameView.end());
  // Share name strings for repeatedly bound functions by using the
  // identifier table. If a new symbol is created, it will disappear
  // after the name string dies, since nothing else refers to it.
  auto &identifierTable = runtime->getIdentifierTable();
  auto boundNameSym = identifierTable.getSymbolHandle(runtime, boundName);
  if (LLVM_UNLIKELY(boundNameSym == ExecutionStatus::EXCEPTION)) {
    return ExecutionStatus::EXCEPTION;
  }
  Handle<StringPrimitive> boundNameHandle(
      runtime, identifierTable.getStringPrim(runtime, **boundNameSym));
  DefinePropertyFlags dpf = DefinePropertyFlags::getDefaultNewPropertyFlags();
  dpf.writable = 0;
  dpf.enumerable = 0;

  if (LLVM_UNLIKELY(
          JSObject::defineOwnProperty(
              selfHandle,
              runtime,
              Predefined::getSymbolID(Predefined::name),
              dpf,
              boundNameHandle) == ExecutionStatus::EXCEPTION)) {
    return ExecutionStatus::EXCEPTION;
  }

  // Define .callee and .arguments properties: throw always in bound functions.
  auto accessor =
      Handle<PropertyAccessor>::vmcast(&runtime->throwTypeErrorAccessor);

  pf.clear();
  pf.enumerable = 0;
  pf.configurable = 0;
  pf.accessor = 1;

  if (LLVM_UNLIKELY(
          JSObject::defineNewOwnProperty(
              selfHandle,
              runtime,
              Predefined::getSymbolID(Predefined::caller),
              pf,
              accessor) == ExecutionStatus::EXCEPTION)) {
    return ExecutionStatus::EXCEPTION;
  }

  if (LLVM_UNLIKELY(
          JSObject::defineNewOwnProperty(
              selfHandle,
              runtime,
              Predefined::getSymbolID(Predefined::arguments),
              pf,
              accessor) == ExecutionStatus::EXCEPTION)) {
    return ExecutionStatus::EXCEPTION;
  }

  return ExecutionStatus::RETURNED;
}

CallResult<PseudoHandle<JSObject>> BoundFunction::_newObjectImpl(
    Handle<Callable> selfHandle,
    Runtime *runtime,
    Handle<JSObject>) {
  auto *self = vmcast<BoundFunction>(*selfHandle);

  // If it is a chain of bound functions, skip directly to the end.
  while (auto *targetAsBound =
             dyn_vmcast<BoundFunction>(self->getTarget(runtime)))
    self = targetAsBound;

  auto targetHandle = runtime->makeHandle(self->getTarget(runtime));

  // We must duplicate the [[Construct]] functionality here.

  // Obtain "target.prototype".
  auto propRes = JSObject::getNamed_RJS(
      targetHandle, runtime, Predefined::getSymbolID(Predefined::prototype));
  if (propRes == ExecutionStatus::EXCEPTION)
    return ExecutionStatus::EXCEPTION;
  auto prototype = runtime->makeHandle(std::move(*propRes));

  // If target.prototype is an object, use it, otherwise use the standard
  // object prototype.
  return targetHandle->getVT()->newObject(
      targetHandle,
      runtime,
      prototype->isObject()
          ? Handle<JSObject>::vmcast(prototype)
          : Handle<JSObject>::vmcast(&runtime->objectPrototype));
}

CallResult<PseudoHandle<>> BoundFunction::_boundCall(
    BoundFunction *self,
    const Inst *ip,
    Runtime *runtime) {
  ScopedNativeDepthTracker depthTracker{runtime};
  if (LLVM_UNLIKELY(depthTracker.overflowed())) {
    return runtime->raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
  }

  CallResult<PseudoHandle<>> res{ExecutionStatus::EXCEPTION};
  StackFramePtr originalCalleeFrame = StackFramePtr(runtime->getStackPointer());
  // Save the original newTarget since we will overwrite it.
  HermesValue originalNewTarget = originalCalleeFrame.getNewTargetRef();
  // Save the original arg count since we will lose it.
  auto originalArgCount = originalCalleeFrame.getArgCount();
  // Keep track of the total arg count.
  auto totalArgCount = originalArgCount;

  auto callerFrame = runtime->getCurrentFrame();
  // We must preserve the "thisArg" passed to us by the caller because it is in
  // a register that is not supposed to be modified by a call. Copy it to the
  // scratch register in the caller's frame.
  // Note that since there is only one scratch reg, we must process all chained
  // bound calls in one go (which is more efficient anyway).
  callerFrame.getScratchRef() = originalCalleeFrame.getThisArgRef();

  // Pop the stack down to the first argument, erasing the call frame - we don't
  // need the call frame since we will build a new one.
  runtime->popToSavedStackPointer(&originalCalleeFrame.getThisArgRef());

  // Loop, copying the bound arguments of all chained bound functions.
  for (;;) {
    auto boundArgCount = self->getArgCountWithThis(runtime) - 1;
    totalArgCount += boundArgCount;

    // Check if we have enough stack for the arguments and the frame metadata.
    if (LLVM_UNLIKELY(!runtime->checkAvailableStack(
            StackFrameLayout::callerOutgoingRegisters(boundArgCount)))) {
      // Oops, we ran out of stack in the middle of calling a bound function.
      // Restore everything and bail.

      // We can't "pop" the stack pointer to an arbitrary value, which may be
      // higher than the current pointer. So, first we pop everything that we
      // may have pushed, then allocate the correct amount to get back to the
      // initial state.
      runtime->popToSavedStackPointer(&originalCalleeFrame.getThisArgRef());
      runtime->allocUninitializedStack(
          StackFrameLayout::CallerExtraRegistersAtEnd + 1);
      assert(
          runtime->getStackPointer() == originalCalleeFrame.ptr() &&
          "Stack wasn't restored properly");

      res = runtime->raiseStackOverflow(
          Runtime::StackOverflowKind::JSRegisterStack);
      goto bail;
    }

    // Allocate space only for the arguments for now.
    auto *stack = runtime->allocUninitializedStack(boundArgCount);

    // Copy the bound arguments (but not the bound "this").
    std::uninitialized_copy_n(
        self->getArgsWithThis(runtime) + 1, boundArgCount, ArgIterator(stack));

    // Loop while the target is another bound function.
    auto *targetAsBound = dyn_vmcast<BoundFunction>(self->getTarget(runtime));
    if (!targetAsBound)
      break;
    self = targetAsBound;
  }

  // Block scope for non-trivial variables to avoid complaints from "goto".
  {
    // Allocate space for "thisArg" and the frame metdata following the outgoing
    // registers. Note that we already checked earlier that we have enough
    // stack.
    auto *stack = runtime->allocUninitializedStack(
        StackFrameLayout::CallerExtraRegistersAtEnd + 1);

    // Initialize the new frame metadata.
    auto newCalleeFrame = StackFramePtr::initFrame(
        stack,
        runtime->getCurrentFrame(),
        ip,
        nullptr,
        totalArgCount,
        HermesValue::encodeObjectValue(self->getTarget(runtime)),
        originalNewTarget);
    // Initialize "thisArg". When constructing we must use the original 'this',
    // not the bound one.
    newCalleeFrame.getThisArgRef() = !originalNewTarget.isUndefined()
        ? static_cast<HermesValue>(callerFrame.getScratchRef())
        : self->getArgsWithThis(runtime)[0];

    res =
        Callable::call(newCalleeFrame.getCalleeClosureHandleUnsafe(), runtime);

    assert(
        runtime->getCurrentFrame() == callerFrame &&
        "caller frame not restored");

    // Restore the original stack level.
    runtime->popToSavedStackPointer(originalCalleeFrame.ptr());
  }

bail:
  // We must restore the original call frame. There is no need to restore
  // all the fields to their previous values, just the registers which are not
  // supposed to be modified by a call.
  StackFramePtr::initFrame(
      originalCalleeFrame.ptr(),
      StackFramePtr{},
      ip,
      nullptr,
      0,
      HermesValue::encodeEmptyValue(),
      HermesValue::encodeEmptyValue());

  // Restore "thisArg" and clear the scratch register to avoid a leak.
  originalCalleeFrame.getThisArgRef() = callerFrame.getScratchRef();
  callerFrame.getScratchRef() = HermesValue::encodeUndefinedValue();

  return res;
}

CallResult<PseudoHandle<>> BoundFunction::_callImpl(
    Handle<Callable> selfHandle,
    Runtime *runtime) {
  // Pass `nullptr` as the IP because this function is never called
  // from the interpreter, which should use `_boundCall` directly.
  return _boundCall(vmcast<BoundFunction>(selfHandle.get()), nullptr, runtime);
}

//===----------------------------------------------------------------------===//
// class NativeFunction

const CallableVTable NativeFunction::vt{
    {
        VTable(
            CellKind::NativeFunctionKind,
            cellSize<NativeFunction>(),
            nullptr,
            nullptr,
            nullptr,
            nullptr,
            VTable::HeapSnapshotMetadata{
                HeapSnapshot::NodeType::Closure,
                NativeFunction::_snapshotNameImpl,
                NativeFunction::_snapshotAddEdgesImpl,
                nullptr,
                nullptr}),
        NativeFunction::_getOwnIndexedRangeImpl,
        NativeFunction::_haveOwnIndexedImpl,
        NativeFunction::_getOwnIndexedPropertyFlagsImpl,
        NativeFunction::_getOwnIndexedImpl,
        NativeFunction::_setOwnIndexedImpl,
        NativeFunction::_deleteOwnIndexedImpl,
        NativeFunction::_checkAllOwnIndexedImpl,
    },
    NativeFunction::_newObjectImpl,
    NativeFunction::_callImpl};

void NativeFunctionBuildMeta(const GCCell *cell, Metadata::Builder &mb) {
  mb.addJSObjectOverlapSlots(JSObject::numOverlapSlots<NativeFunction>());
  CallableBuildMeta(cell, mb);
  mb.setVTable(&NativeFunction::vt.base.base);
}

std::string NativeFunction::_snapshotNameImpl(GCCell *cell, GC *gc) {
  auto *const self = reinterpret_cast<NativeFunction *>(cell);
  return getFunctionName(self->functionPtr_);
}

Handle<NativeFunction> NativeFunction::create(
    Runtime *runtime,
    Handle<JSObject> parentHandle,
    void *context,
    NativeFunctionPtr functionPtr,
    SymbolID name,
    unsigned paramCount,
    Handle<JSObject> prototypeObjectHandle,
    unsigned additionalSlotCount) {
  size_t reservedSlots =
      numOverlapSlots<NativeFunction>() + additionalSlotCount;
  auto *cell = runtime->makeAFixed<NativeFunction>(
      runtime,
      &vt.base.base,
      parentHandle,
      runtime->getHiddenClassForPrototype(*parentHandle, reservedSlots),
      context,
      functionPtr);
  auto selfHandle = JSObjectInit::initToHandle(runtime, cell);

  // Allocate a propStorage if the number of additional slots requires it.
  runtime->ignoreAllocationFailure(
      JSObject::allocatePropStorage(selfHandle, runtime, reservedSlots));

  auto st = defineNameLengthAndPrototype(
      selfHandle,
      runtime,
      name,
      paramCount,
      prototypeObjectHandle,
      Callable::WritablePrototype::Yes,
      false);
  (void)st;
  assert(
      st != ExecutionStatus::EXCEPTION && "defineLengthAndPrototype() failed");

  return selfHandle;
}

Handle<NativeFunction> NativeFunction::create(
    Runtime *runtime,
    Handle<JSObject> parentHandle,
    Handle<Environment> parentEnvHandle,
    void *context,
    NativeFunctionPtr functionPtr,
    SymbolID name,
    unsigned paramCount,
    Handle<JSObject> prototypeObjectHandle,
    unsigned additionalSlotCount) {
  auto *cell = runtime->makeAFixed<NativeFunction>(
      runtime,
      &vt.base.base,
      parentHandle,
      runtime->getHiddenClassForPrototype(
          *parentHandle,
          numOverlapSlots<NativeFunction>() + additionalSlotCount),
      parentEnvHandle,
      context,
      functionPtr);
  auto selfHandle = JSObjectInit::initToHandle(runtime, cell);

  auto st = defineNameLengthAndPrototype(
      selfHandle,
      runtime,
      name,
      paramCount,
      prototypeObjectHandle,
      Callable::WritablePrototype::Yes,
      false);
  (void)st;
  assert(
      st != ExecutionStatus::EXCEPTION && "defineLengthAndPrototype() failed");

  return selfHandle;
}

CallResult<PseudoHandle<>> NativeFunction::_callImpl(
    Handle<Callable> selfHandle,
    Runtime *runtime) {
  return _nativeCall(vmcast<NativeFunction>(selfHandle.get()), runtime);
}

CallResult<PseudoHandle<JSObject>> NativeFunction::_newObjectImpl(
    Handle<Callable>,
    Runtime *runtime,
    Handle<JSObject>) {
  return runtime->raiseTypeError(
      "This function cannot be used as a constructor.");
}

//===----------------------------------------------------------------------===//
// class NativeConstructor

template <class From>
static CallResult<PseudoHandle<JSObject>> toCallResultPseudoHandleJSObject(
    PseudoHandle<From> &&other) {
  return PseudoHandle<JSObject>{std::move(other)};
}

template <class From>
static CallResult<PseudoHandle<JSObject>> toCallResultPseudoHandleJSObject(
    CallResult<PseudoHandle<From>> &&other) {
  return std::move(other);
}

template <class From>
static CallResult<PseudoHandle<JSObject>> toCallResultPseudoHandleJSObject(
    CallResult<Handle<From>> other) {
  if (LLVM_UNLIKELY(other == ExecutionStatus::EXCEPTION)) {
    return ExecutionStatus::EXCEPTION;
  }
  return PseudoHandle<JSObject>{*other};
}

template <class From>
static CallResult<PseudoHandle<JSObject>> toCallResultPseudoHandleJSObject(
    Handle<From> other) {
  return PseudoHandle<JSObject>{other};
}

template <class NativeClass>
CallResult<PseudoHandle<JSObject>> NativeConstructor::creatorFunction(
    Runtime *runtime,
    Handle<JSObject> prototype,
    void *) {
  return toCallResultPseudoHandleJSObject(
      NativeClass::create(runtime, prototype));
}

#define NATIVE_CONSTRUCTOR(fun)                    \
  template CallResult<PseudoHandle<JSObject>> fun( \
      Runtime *, Handle<JSObject>, void *);
#include "hermes/VM/NativeFunctions.def"
#undef NATIVE_CONSTRUCTOR

const CallableVTable NativeConstructor::vt{
    {
        VTable(
            CellKind::NativeConstructorKind,
            cellSize<NativeConstructor>(),
            nullptr,
            nullptr,
            nullptr,
            nullptr,
            VTable::HeapSnapshotMetadata{
                HeapSnapshot::NodeType::Closure,
                NativeConstructor::_snapshotNameImpl,
                NativeConstructor::_snapshotAddEdgesImpl,
                nullptr,
                nullptr}),
        NativeConstructor::_getOwnIndexedRangeImpl,
        NativeConstructor::_haveOwnIndexedImpl,
        NativeConstructor::_getOwnIndexedPropertyFlagsImpl,
        NativeConstructor::_getOwnIndexedImpl,
        NativeConstructor::_setOwnIndexedImpl,
        NativeConstructor::_deleteOwnIndexedImpl,
        NativeConstructor::_checkAllOwnIndexedImpl,
    },
    NativeConstructor::_newObjectImpl,
    NativeConstructor::_callImpl};

void NativeConstructorBuildMeta(const GCCell *cell, Metadata::Builder &mb) {
  mb.addJSObjectOverlapSlots(JSObject::numOverlapSlots<NativeConstructor>());
  NativeFunctionBuildMeta(cell, mb);
  mb.setVTable(&NativeConstructor::vt.base.base);
}

#ifndef NDEBUG
CallResult<PseudoHandle<>> NativeConstructor::_callImpl(
    Handle<Callable> selfHandle,
    Runtime *runtime) {
  StackFramePtr newFrame{runtime->getStackPointer()};

  if (newFrame.isConstructorCall()) {
    auto consHandle = Handle<NativeConstructor>::vmcast(selfHandle);
    assert(
        consHandle->targetKind_ ==
            vmcast<JSObject>(newFrame.getThisArgRef())->getKind() &&
        "call(construct=true) called without the correct 'this' value");
  }
  return NativeFunction::_callImpl(selfHandle, runtime);
}
#endif

//===----------------------------------------------------------------------===//
// class JSFunction

const CallableVTable JSFunction::vt{
    {
        VTable(
            CellKind::FunctionKind,
            cellSize<JSFunction>(),
            nullptr,
            nullptr,
            nullptr,
            nullptr,
            VTable::HeapSnapshotMetadata{
                HeapSnapshot::NodeType::Closure,
                JSFunction::_snapshotNameImpl,
                JSFunction::_snapshotAddEdgesImpl,
                nullptr,
                JSFunction::_snapshotAddLocationsImpl}),
        JSFunction::_getOwnIndexedRangeImpl,
        JSFunction::_haveOwnIndexedImpl,
        JSFunction::_getOwnIndexedPropertyFlagsImpl,
        JSFunction::_getOwnIndexedImpl,
        JSFunction::_setOwnIndexedImpl,
        JSFunction::_deleteOwnIndexedImpl,
        JSFunction::_checkAllOwnIndexedImpl,
    },
    JSFunction::_newObjectImpl,
    JSFunction::_callImpl};

void FunctionBuildMeta(const GCCell *cell, Metadata::Builder &mb) {
  mb.addJSObjectOverlapSlots(JSObject::numOverlapSlots<JSFunction>());
  CallableBuildMeta(cell, mb);
  const auto *self = static_cast<const JSFunction *>(cell);
  mb.addField("domain", &self->domain_);
  mb.setVTable(&JSFunction::vt.base.base);
}

PseudoHandle<JSFunction> JSFunction::create(
    Runtime *runtime,
    Handle<Domain> domain,
    Handle<JSObject> parentHandle,
    Handle<Environment> envHandle,
    CodeBlock *codeBlock) {
  auto *cell = runtime->makeAFixed<JSFunction, kHasFinalizer>(
      runtime,
      domain,
      parentHandle,
      runtime->getHiddenClassForPrototype(
          *parentHandle, numOverlapSlots<JSFunction>()),
      envHandle,
      codeBlock);
  auto self = JSObjectInit::initToPseudoHandle(runtime, cell);
  self->flags_.lazyObject = 1;
  return self;
}

void JSFunction::addLocationToSnapshot(
    HeapSnapshot &snap,
    HeapSnapshot::NodeID id) const {
  if (auto location = codeBlock_->getSourceLocation()) {
    snap.addLocation(
        id,
        codeBlock_->getRuntimeModule()->getScriptID(),
        location->line,
        location->column);
  }
}

CallResult<PseudoHandle<>> JSFunction::_callImpl(
    Handle<Callable> selfHandle,
    Runtime *runtime) {
  auto *self = vmcast<JSFunction>(selfHandle.get());
  CallResult<HermesValue> result{ExecutionStatus::EXCEPTION};
  result = runtime->interpretFunction(self->getCodeBlock());
  if (LLVM_UNLIKELY(result == ExecutionStatus::EXCEPTION)) {
    return ExecutionStatus::EXCEPTION;
  }
  return createPseudoHandle(*result);
}

std::string JSFunction::_snapshotNameImpl(GCCell *cell, GC *gc) {
  auto *const self = vmcast<JSFunction>(cell);
  std::string funcName = Callable::_snapshotNameImpl(self, gc);
  if (!funcName.empty()) {
    return funcName;
  }
  return self->codeBlock_->getNameString(gc->getCallbacks());
}

void JSFunction::_snapshotAddEdgesImpl(
    GCCell *cell,
    GC *gc,
    HeapSnapshot &snap) {
  auto *const self = vmcast<JSFunction>(cell);
  // Add the super type's edges too.
  Callable::_snapshotAddEdgesImpl(self, gc, snap);

  // A node for the code block will be added as part of the RuntimeModule.
  snap.addNamedEdge(
      HeapSnapshot::EdgeType::Shortcut,
      "codeBlock",
      gc->getIDTracker().getNativeID(self->codeBlock_));
}

void JSFunction::_snapshotAddLocationsImpl(
    GCCell *cell,
    GC *gc,
    HeapSnapshot &snap) {
  auto *const self = vmcast<JSFunction>(cell);
  self->addLocationToSnapshot(snap, gc->getObjectID(self));
}

//===----------------------------------------------------------------------===//
// class JSAsyncFunction

const CallableVTable JSAsyncFunction::vt{
    {
        VTable(
            CellKind::AsyncFunctionKind,
            cellSize<JSAsyncFunction>(),
            nullptr,
            nullptr,
            nullptr,
            nullptr,
            VTable::HeapSnapshotMetadata{
                HeapSnapshot::NodeType::Closure,
                JSAsyncFunction::_snapshotNameImpl,
                JSAsyncFunction::_snapshotAddEdgesImpl,
                nullptr,
                nullptr}),
        JSAsyncFunction::_getOwnIndexedRangeImpl,
        JSAsyncFunction::_haveOwnIndexedImpl,
        JSAsyncFunction::_getOwnIndexedPropertyFlagsImpl,
        JSAsyncFunction::_getOwnIndexedImpl,
        JSAsyncFunction::_setOwnIndexedImpl,
        JSAsyncFunction::_deleteOwnIndexedImpl,
        JSAsyncFunction::_checkAllOwnIndexedImpl,
    },
    JSAsyncFunction::_newObjectImpl,
    JSAsyncFunction::_callImpl};

void AsyncFunctionBuildMeta(const GCCell *cell, Metadata::Builder &mb) {
  mb.addJSObjectOverlapSlots(JSObject::numOverlapSlots<JSAsyncFunction>());
  FunctionBuildMeta(cell, mb);
  mb.setVTable(&JSAsyncFunction::vt.base.base);
}

PseudoHandle<JSAsyncFunction> JSAsyncFunction::create(
    Runtime *runtime,
    Handle<Domain> domain,
    Handle<JSObject> parentHandle,
    Handle<Environment> envHandle,
    CodeBlock *codeBlock) {
  auto *cell = runtime->makeAFixed<JSAsyncFunction, kHasFinalizer>(
      runtime,
      domain,
      parentHandle,
      runtime->getHiddenClassForPrototype(
          *parentHandle, numOverlapSlots<JSAsyncFunction>()),
      envHandle,
      codeBlock);
  auto self = JSObjectInit::initToPseudoHandle(runtime, cell);
  self->flags_.lazyObject = 1;
  return self;
}

//===----------------------------------------------------------------------===//
// class JSGeneratorFunction

const CallableVTable JSGeneratorFunction::vt{
    {
        VTable(
            CellKind::GeneratorFunctionKind,
            cellSize<JSGeneratorFunction>(),
            nullptr,
            nullptr,
            nullptr,
            nullptr,
            VTable::HeapSnapshotMetadata{
                HeapSnapshot::NodeType::Closure,
                JSGeneratorFunction::_snapshotNameImpl,
                JSGeneratorFunction::_snapshotAddEdgesImpl,
                nullptr,
                nullptr}),
        JSGeneratorFunction::_getOwnIndexedRangeImpl,
        JSGeneratorFunction::_haveOwnIndexedImpl,
        JSGeneratorFunction::_getOwnIndexedPropertyFlagsImpl,
        JSGeneratorFunction::_getOwnIndexedImpl,
        JSGeneratorFunction::_setOwnIndexedImpl,
        JSGeneratorFunction::_deleteOwnIndexedImpl,
        JSGeneratorFunction::_checkAllOwnIndexedImpl,
    },
    JSGeneratorFunction::_newObjectImpl,
    JSGeneratorFunction::_callImpl};

void GeneratorFunctionBuildMeta(const GCCell *cell, Metadata::Builder &mb) {
  mb.addJSObjectOverlapSlots(JSObject::numOverlapSlots<JSGeneratorFunction>());
  FunctionBuildMeta(cell, mb);
  mb.setVTable(&JSGeneratorFunction::vt.base.base);
}

PseudoHandle<JSGeneratorFunction> JSGeneratorFunction::create(
    Runtime *runtime,
    Handle<Domain> domain,
    Handle<JSObject> parentHandle,
    Handle<Environment> envHandle,
    CodeBlock *codeBlock) {
  auto *cell = runtime->makeAFixed<JSGeneratorFunction, kHasFinalizer>(
      runtime,
      domain,
      parentHandle,
      runtime->getHiddenClassForPrototype(
          *parentHandle, numOverlapSlots<JSGeneratorFunction>()),
      envHandle,
      codeBlock);
  auto self = JSObjectInit::initToPseudoHandle(runtime, cell);
  self->flags_.lazyObject = 1;
  return self;
}

//===----------------------------------------------------------------------===//
// class GeneratorInnerFunction

const CallableVTable GeneratorInnerFunction::vt{
    {
        VTable(
            CellKind::GeneratorInnerFunctionKind,
            cellSize<GeneratorInnerFunction>(),
            nullptr,
            nullptr,
            nullptr,
            nullptr,
            VTable::HeapSnapshotMetadata{
                HeapSnapshot::NodeType::Closure,
                GeneratorInnerFunction::_snapshotNameImpl,
                GeneratorInnerFunction::_snapshotAddEdgesImpl,
                nullptr,
                nullptr}),
        GeneratorInnerFunction::_getOwnIndexedRangeImpl,
        GeneratorInnerFunction::_haveOwnIndexedImpl,
        GeneratorInnerFunction::_getOwnIndexedPropertyFlagsImpl,
        GeneratorInnerFunction::_getOwnIndexedImpl,
        GeneratorInnerFunction::_setOwnIndexedImpl,
        GeneratorInnerFunction::_deleteOwnIndexedImpl,
        GeneratorInnerFunction::_checkAllOwnIndexedImpl,
    },
    GeneratorInnerFunction::_newObjectImpl,
    GeneratorInnerFunction::_callImpl};

void GeneratorInnerFunctionBuildMeta(
    const GCCell *cell,
    Metadata::Builder &mb) {
  mb.addJSObjectOverlapSlots(
      JSObject::numOverlapSlots<GeneratorInnerFunction>());
  FunctionBuildMeta(cell, mb);
  const auto *self = static_cast<const GeneratorInnerFunction *>(cell);
  mb.setVTable(&GeneratorInnerFunction::vt.base.base);
  mb.addField("savedContext", &self->savedContext_);
  mb.addField("result", &self->result_);
}

CallResult<Handle<GeneratorInnerFunction>> GeneratorInnerFunction::create(
    Runtime *runtime,
    Handle<Domain> domain,
    Handle<JSObject> parentHandle,
    Handle<Environment> envHandle,
    CodeBlock *codeBlock,
    NativeArgs args) {
  auto *cell = runtime->makeAFixed<GeneratorInnerFunction>(
      runtime,
      domain,
      parentHandle,
      runtime->getHiddenClassForPrototype(
          *parentHandle, numOverlapSlots<GeneratorInnerFunction>()),
      envHandle,
      codeBlock,
      args.getArgCount());
  auto self = JSObjectInit::initToHandle(runtime, cell);

  const uint32_t ctxSize = getContextSize(codeBlock, args.getArgCount());

  auto ctxRes = ArrayStorage::create(runtime, ctxSize, ctxSize);
  if (LLVM_UNLIKELY(ctxRes == ExecutionStatus::EXCEPTION)) {
    return ExecutionStatus::EXCEPTION;
  }
  auto ctx = runtime->makeHandle<ArrayStorage>(*ctxRes);

  // Set "this" as the first element.
  ctx->set(0, args.getThisArg(), &runtime->getHeap());

  // Set the rest of the arguments.
  // Argument i goes in slot i+1 to account for the "this".
  for (uint32_t i = 0, e = args.getArgCount(); i < e; ++i) {
    ctx->set(i + 1, args.getArg(i), &runtime->getHeap());
  }

  self->savedContext_.set(runtime, ctx.get(), &runtime->getHeap());

  return self;
}

/// Call the callable with arguments already on the stack.
CallResult<PseudoHandle<>> GeneratorInnerFunction::callInnerFunction(
    Handle<GeneratorInnerFunction> selfHandle,
    Runtime *runtime,
    Handle<> arg,
    Action action) {
  auto self = Handle<GeneratorInnerFunction>::vmcast(selfHandle);

  SmallHermesValue shv =
      SmallHermesValue::encodeHermesValue(arg.getHermesValue(), runtime);
  self->result_.set(shv, &runtime->getHeap());
  self->action_ = action;

  auto ctx = runtime->makeMutableHandle(selfHandle->savedContext_);
  // Account for the `this` argument stored as the first element of ctx.
  const uint32_t argCount = self->argCount_;
  // Generators cannot be used as constructors, so newTarget is always
  // undefined.
  HermesValue newTarget = HermesValue::encodeUndefinedValue();
  ScopedNativeCallFrame frame{
      runtime,
      argCount, // Account for `this`.
      selfHandle.getHermesValue(),
      newTarget,
      ctx->at(0)};
  if (LLVM_UNLIKELY(frame.overflowed()))
    return runtime->raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
  for (ArrayStorage::size_type i = 0, e = argCount; i < e; ++i) {
    frame->getArgRef(i) = ctx->at(i + 1);
  }

  // Force lazy compilation immediately in order to size the context properly.
  // We're about to call the function anyway, so this doesn't reduce laziness.
  // Note that this will do nothing after the very first time a lazy function
  // is called, so we only resize before we save any registers at all.
  if (LLVM_UNLIKELY(selfHandle->getCodeBlock()->isLazy())) {
    selfHandle->getCodeBlock()->lazyCompile(runtime);
    if (LLVM_UNLIKELY(
            ArrayStorage::resize(
                ctx,
                runtime,
                getContextSize(
                    selfHandle->getCodeBlock(), selfHandle->argCount_)) ==
            ExecutionStatus::EXCEPTION)) {
      return ExecutionStatus::EXCEPTION;
    }
    selfHandle->savedContext_.set(runtime, ctx.get(), &runtime->getHeap());
  }

  return JSFunction::_callImpl(selfHandle, runtime);
}

void GeneratorInnerFunction::restoreStack(Runtime *runtime) {
  const uint32_t frameOffset = getFrameOffsetInContext();
  const uint32_t frameSize = getFrameSizeInContext(runtime);
  // Start at the lower end of the range to be copied.
  PinnedHermesValue *dst = runtime->getCurrentFrame().ptr();
  assert(
      dst + frameSize <= runtime->getStackPointer() &&
      "writing off the end of the stack");
  const GCHermesValue *src =
      savedContext_.getNonNull(runtime)->data() + frameOffset;
  GCHermesValueUtil::copyToPinned(src, src + frameSize, dst);
}

void GeneratorInnerFunction::saveStack(Runtime *runtime) {
  const uint32_t frameOffset = getFrameOffsetInContext();
  const uint32_t frameSize = getFrameSizeInContext(runtime);
  // Start at the lower end of the range to be copied.
  PinnedHermesValue *first = runtime->getCurrentFrame().ptr();
  assert(
      first + frameSize <= runtime->getStackPointer() &&
      "reading off the end of the stack");
  // Use GCHermesValue::copy to ensure write barriers are executed.
  GCHermesValue::copy(
      first,
      first + frameSize,
      savedContext_.getNonNull(runtime)->data() + frameOffset,
      &runtime->getHeap());
}

} // namespace vm
} // namespace hermes
