/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * 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/. */

#ifndef vm_Interpreter_h
#define vm_Interpreter_h

/*
 * JS interpreter interface.
 */

#include "jspubtd.h"

#include "vm/BuiltinObjectKind.h"
#include "vm/CheckIsObjectKind.h"  // CheckIsObjectKind
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
#  include "vm/ErrorObject.h"
#  include "vm/UsingHint.h"
#endif
#include "vm/Stack.h"

namespace js {

class WithScope;
class EnvironmentIter;
class PlainObject;

/*
 * Convert null/undefined |thisv| into the global lexical's |this| object, and
 * replace other primitives with boxed versions.
 */
extern JSObject* BoxNonStrictThis(JSContext* cx, HandleValue thisv);

extern bool GetFunctionThis(JSContext* cx, AbstractFramePtr frame,
                            MutableHandleValue res);

extern void GetNonSyntacticGlobalThis(JSContext* cx, HandleObject envChain,
                                      MutableHandleValue res);

/*
 * numToSkip is the number of stack values the expression decompiler should skip
 * before it reaches |v|. If it's -1, the decompiler will search the stack.
 */
extern bool ReportIsNotFunction(JSContext* cx, HandleValue v, int numToSkip,
                                MaybeConstruct construct = NO_CONSTRUCT);

/* See ReportIsNotFunction comment for the meaning of numToSkip. */
extern JSObject* ValueToCallable(JSContext* cx, HandleValue v,
                                 int numToSkip = -1,
                                 MaybeConstruct construct = NO_CONSTRUCT);

// Reasons why a call could be performed, for passing onto the debugger's
// `onNativeCall` hook.
// `onNativeCall` hook disabled all JITs, and this needs to be handled only in
// the interpreter.
enum class CallReason {
  Call,
  // callContentFunction or constructContentFunction in self-hosted JS.
  CallContent,
  // Function.prototype.call or Function.prototype.apply.
  FunCall,
  Getter,
  Setter,
};

/*
 * Call or construct arguments that are stored in rooted memory.
 *
 * NOTE: Any necessary |GetThisValue| computation must have been performed on
 *       |args.thisv()|, likely by the interpreter when pushing |this| onto the
 *       stack.  If you're not sure whether |GetThisValue| processing has been
 *       performed, use |Invoke|.
 */
extern bool InternalCallOrConstruct(JSContext* cx, const CallArgs& args,
                                    MaybeConstruct construct,
                                    CallReason reason = CallReason::Call);

/*
 * These helpers take care of the infinite-recursion check necessary for
 * getter/setter calls.
 */
extern bool CallGetter(JSContext* cx, HandleValue thisv, HandleValue getter,
                       MutableHandleValue rval);

extern bool CallSetter(JSContext* cx, HandleValue thisv, HandleValue setter,
                       HandleValue rval);

// ES7 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93
// 7.3.12 Call(F, V, argumentsList).
// All parameters are required, hopefully forcing callers to be careful not to
// (say) blindly pass callee as |newTarget| when a different value should have
// been passed.  Behavior is unspecified if any element of |args| isn't
// initialized.
//
// |rval| is written to *only* after |fval| and |thisv| have been consumed, so
// |rval| *may* alias either argument.
extern bool Call(JSContext* cx, HandleValue fval, HandleValue thisv,
                 const AnyInvokeArgs& args, MutableHandleValue rval,
                 CallReason reason = CallReason::Call);

inline bool Call(JSContext* cx, HandleValue fval, HandleValue thisv,
                 MutableHandleValue rval) {
  FixedInvokeArgs<0> args(cx);
  return Call(cx, fval, thisv, args, rval);
}

inline bool Call(JSContext* cx, HandleValue fval, JSObject* thisObj,
                 MutableHandleValue rval) {
  RootedValue thisv(cx, ObjectOrNullValue(thisObj));
  FixedInvokeArgs<0> args(cx);
  return Call(cx, fval, thisv, args, rval);
}

inline bool Call(JSContext* cx, HandleValue fval, HandleValue thisv,
                 HandleValue arg0, MutableHandleValue rval) {
  FixedInvokeArgs<1> args(cx);
  args[0].set(arg0);
  return Call(cx, fval, thisv, args, rval);
}

inline bool Call(JSContext* cx, HandleValue fval, JSObject* thisObj,
                 HandleValue arg0, MutableHandleValue rval) {
  RootedValue thisv(cx, ObjectOrNullValue(thisObj));
  FixedInvokeArgs<1> args(cx);
  args[0].set(arg0);
  return Call(cx, fval, thisv, args, rval);
}

inline bool Call(JSContext* cx, HandleValue fval, HandleValue thisv,
                 HandleValue arg0, HandleValue arg1, MutableHandleValue rval) {
  FixedInvokeArgs<2> args(cx);
  args[0].set(arg0);
  args[1].set(arg1);
  return Call(cx, fval, thisv, args, rval);
}

inline bool Call(JSContext* cx, HandleValue fval, JSObject* thisObj,
                 HandleValue arg0, HandleValue arg1, MutableHandleValue rval) {
  RootedValue thisv(cx, ObjectOrNullValue(thisObj));
  FixedInvokeArgs<2> args(cx);
  args[0].set(arg0);
  args[1].set(arg1);
  return Call(cx, fval, thisv, args, rval);
}

inline bool Call(JSContext* cx, HandleValue fval, JSObject* thisObj,
                 HandleValue arg0, HandleValue arg1, HandleValue arg2,
                 MutableHandleValue rval) {
  RootedValue thisv(cx, ObjectOrNullValue(thisObj));
  FixedInvokeArgs<3> args(cx);
  args[0].set(arg0);
  args[1].set(arg1);
  args[2].set(arg2);
  return Call(cx, fval, thisv, args, rval);
}

// Perform the above Call() operation using the given arguments.  Similar to
// ConstructFromStack() below, this handles |!IsCallable(args.calleev())|.
//
// This internal operation is intended only for use with arguments known to be
// on the JS stack, or at least in carefully-rooted memory. The vast majority of
// potential users should instead use InvokeArgs in concert with Call().
extern bool CallFromStack(JSContext* cx, const CallArgs& args,
                          CallReason reason = CallReason::Call);

// ES6 7.3.13 Construct(F, argumentsList, newTarget).  All parameters are
// required, hopefully forcing callers to be careful not to (say) blindly pass
// callee as |newTarget| when a different value should have been passed.
// Behavior is unspecified if any element of |args| isn't initialized.
//
// |rval| is written to *only* after |fval| and |newTarget| have been consumed,
// so |rval| *may* alias either argument.
//
// NOTE: As with the ES6 spec operation, it's the caller's responsibility to
//       ensure |fval| and |newTarget| are both |IsConstructor|.
extern bool Construct(JSContext* cx, HandleValue fval,
                      const AnyConstructArgs& args, HandleValue newTarget,
                      MutableHandleObject objp);

// Check that in the given |args|, which must be |args.isConstructing()|, that
// |IsConstructor(args.callee())|. If this is not the case, throw a TypeError.
// Otherwise, the user must ensure that, additionally,
// |IsConstructor(args.newTarget())|. (If |args| comes directly from the
// interpreter stack, as set up by JSOp::New, this comes for free.) Then perform
// a Construct() operation using |args|.
//
// This internal operation is intended only for use with arguments known to be
// on the JS stack, or at least in carefully-rooted memory. The vast majority of
// potential users should instead use ConstructArgs in concert with Construct().
extern bool ConstructFromStack(JSContext* cx, const CallArgs& args,
                               CallReason reason = CallReason::Call);

// Call Construct(fval, args, newTarget), but use the given |thisv| as |this|
// during construction of |fval|.
//
// |rval| is written to *only* after |fval|, |thisv|, and |newTarget| have been
// consumed, so |rval| *may* alias any of these arguments.
//
// This method exists only for very rare cases where a |this| was created
// caller-side for construction of |fval|: basically only for JITs using
// |CreateThis|.  If that's not you, use Construct()!
extern bool InternalConstructWithProvidedThis(JSContext* cx, HandleValue fval,
                                              HandleValue thisv,
                                              const AnyConstructArgs& args,
                                              HandleValue newTarget,
                                              MutableHandleValue rval);

/*
 * Executes a script with the given envChain. To support debugging, the
 * evalInFrame parameter can point to an arbitrary frame in the context's call
 * stack to simulate executing an eval in that frame.
 */
extern bool ExecuteKernel(JSContext* cx, HandleScript script,
                          HandleObject envChainArg,
                          AbstractFramePtr evalInFrame,
                          MutableHandleValue result);

/* Execute a script with the given envChain as global code. */
extern bool Execute(JSContext* cx, HandleScript script, HandleObject envChain,
                    MutableHandleValue rval);

class ExecuteState;
class InvokeState;

// RunState is passed to RunScript and RunScript then either passes it to the
// interpreter or to the JITs. RunState contains all information we need to
// construct an interpreter or JIT frame.
class MOZ_RAII RunState {
 protected:
  enum Kind { Execute, Invoke };
  Kind kind_;

  RootedScript script_;

  explicit RunState(JSContext* cx, Kind kind, JSScript* script)
      : kind_(kind), script_(cx, script) {}

 public:
  bool isExecute() const { return kind_ == Execute; }
  bool isInvoke() const { return kind_ == Invoke; }

  ExecuteState* asExecute() const {
    MOZ_ASSERT(isExecute());
    return (ExecuteState*)this;
  }
  InvokeState* asInvoke() const {
    MOZ_ASSERT(isInvoke());
    return (InvokeState*)this;
  }

  JS::HandleScript script() const { return script_; }

  InterpreterFrame* pushInterpreterFrame(JSContext* cx);
  inline void setReturnValue(const Value& v);

 private:
  RunState(const RunState& other) = delete;
  RunState(const ExecuteState& other) = delete;
  RunState(const InvokeState& other) = delete;
  void operator=(const RunState& other) = delete;
};

// Eval or global script.
class MOZ_RAII ExecuteState : public RunState {
  HandleObject envChain_;

  AbstractFramePtr evalInFrame_;
  MutableHandleValue result_;

 public:
  ExecuteState(JSContext* cx, JSScript* script, HandleObject envChain,
               AbstractFramePtr evalInFrame, MutableHandleValue result)
      : RunState(cx, Execute, script),
        envChain_(envChain),
        evalInFrame_(evalInFrame),
        result_(result) {}

  JSObject* environmentChain() const { return envChain_; }
  bool isDebuggerEval() const { return !!evalInFrame_; }

  InterpreterFrame* pushInterpreterFrame(JSContext* cx);

  void setReturnValue(const Value& v) { result_.set(v); }
};

// Data to invoke a function.
class MOZ_RAII InvokeState final : public RunState {
  const CallArgs& args_;
  MaybeConstruct construct_;

 public:
  InvokeState(JSContext* cx, const CallArgs& args, MaybeConstruct construct)
      : RunState(cx, Invoke, args.callee().as<JSFunction>().nonLazyScript()),
        args_(args),
        construct_(construct) {}

  bool constructing() const { return construct_; }
  const CallArgs& args() const { return args_; }

  InterpreterFrame* pushInterpreterFrame(JSContext* cx);

  void setReturnValue(const Value& v) { args_.rval().set(v); }
};

inline void RunState::setReturnValue(const Value& v) {
  if (isInvoke()) {
    asInvoke()->setReturnValue(v);
  } else {
    asExecute()->setReturnValue(v);
  }
}

extern bool RunScript(JSContext* cx, RunState& state);
extern bool Interpret(JSContext* cx, RunState& state);

extern JSType TypeOfObject(JSObject* obj);

extern JSType TypeOfValue(const Value& v);

// Implementation of
// https://www.ecma-international.org/ecma-262/6.0/#sec-instanceofoperator
extern bool InstanceofOperator(JSContext* cx, HandleObject obj, HandleValue v,
                               bool* bp);

// Unwind environment chain and iterator to match the scope corresponding to
// the given bytecode position.
extern void UnwindEnvironment(JSContext* cx, EnvironmentIter& ei,
                              jsbytecode* pc);

// Unwind all environments.
extern void UnwindAllEnvironmentsInFrame(JSContext* cx, EnvironmentIter& ei);

// Compute the pc needed to unwind the scope to the beginning of the block
// pointed to by the try note.
extern jsbytecode* UnwindEnvironmentToTryPc(JSScript* script,
                                            const TryNote* tn);

namespace detail {

template <class TryNoteFilter>
class MOZ_STACK_CLASS BaseTryNoteIter {
  uint32_t pcOffset_;
  TryNoteFilter isTryNoteValid_;

  const TryNote* tn_;
  const TryNote* tnEnd_;

  void settle() {
    for (; tn_ != tnEnd_; ++tn_) {
      if (!pcInRange()) {
        continue;
      }

      /*  Try notes cannot be disjoint. That is, we can't have
       *  multiple notes with disjoint pc ranges jumping to the same
       *  catch block. This interacts awkwardly with for-of loops, in
       *  which calls to IteratorClose emitted due to abnormal
       *  completion (break, throw, return) are emitted inline, at the
       *  source location of the break, throw, or return
       *  statement. For example:
       *
       *      for (x of iter) {
       *        try { return; } catch (e) { }
       *      }
       *
       *  From the try-note nesting's perspective, the IteratorClose
       *  resulting from |return| is covered by the inner try, when it
       *  should not be. If IteratorClose throws, we don't want to
       *  catch it here.
       *
       *  To make this work, we use TryNoteKind::ForOfIterClose try-notes,
       *  which cover the range of the abnormal completion. When
       *  looking up trynotes, a for-of iterclose note indicates that
       *  the enclosing for-of has just been terminated. As a result,
       *  trynotes within that for-of are no longer active. When we
       *  see a for-of-iterclose, we skip ahead in the trynotes list
       *  until we see the matching for-of.
       *
       *  Breaking out of multiple levels of for-of at once is handled
       *  using nested TryNoteKind::ForOfIterClose try-notes. Consider this
       * code:
       *
       *  try {
       *    loop: for (i of first) {
       *      <A>
       *      for (j of second) {
       *        <B>
       *        break loop; // <C1/2>
       *      }
       *    }
       *  } catch {...}
       *
       *  Here is the mapping from various PCs to try-notes that we
       *  want to return:
       *
       *        A     B     C1     C2
       *        |     |     |      |
       *        |     |     |  [---|---]     ForOfIterClose (outer)
       *        |     | [---|------|---]     ForOfIterClose (inner)
       *        |  [--X-----|------|----]    ForOf (inner)
       *    [---X-----------X------|-----]   ForOf (outer)
       *  [------------------------X------]  TryCatch
       *
       *  - At A, we find the outer for-of.
       *  - At B, we find the inner for-of.
       *  - At C1, we find one TryNoteKind::ForOfIterClose, skip past one
       *    TryNoteKind::ForOf, and find the outer for-of. (This occurs if an
       *    exception is thrown while closing the inner iterator.)
       *  - At C2, we find two TryNoteKind::ForOfIterClose, skip past two
       *    TryNoteKind::ForOf, and reach the outer try-catch. (This occurs if
       *    an exception is thrown while closing the outer iterator.)
       */
      if (tn_->kind() == TryNoteKind::ForOfIterClose) {
        uint32_t iterCloseDepth = 1;
        do {
          ++tn_;
          MOZ_ASSERT(tn_ != tnEnd_);
          if (pcInRange()) {
            if (tn_->kind() == TryNoteKind::ForOfIterClose) {
              iterCloseDepth++;
            } else if (tn_->kind() == TryNoteKind::ForOf) {
              iterCloseDepth--;
            }
          }
        } while (iterCloseDepth > 0);

        // Advance to trynote following the enclosing for-of.
        continue;
      }

      /*
       * We have a note that covers the exception pc but we must check
       * whether the interpreter has already executed the corresponding
       * handler. This is possible when the executed bytecode implements
       * break or return from inside a for-in loop.
       *
       * In this case the emitter generates additional [enditer] and [goto]
       * opcodes to close all outstanding iterators and execute the finally
       * blocks. If such an [enditer] throws an exception, its pc can still
       * be inside several nested for-in loops and try-finally statements
       * even if we have already closed the corresponding iterators and
       * invoked the finally blocks.
       *
       * To address this, we make [enditer] always decrease the stack even
       * when its implementation throws an exception. Thus already executed
       * [enditer] and [goto] opcodes will have try notes with the stack
       * depth exceeding the current one and this condition is what we use to
       * filter them out.
       */
      if (tn_ == tnEnd_ || isTryNoteValid_(tn_)) {
        return;
      }
    }
  }

 public:
  BaseTryNoteIter(JSScript* script, jsbytecode* pc,
                  TryNoteFilter isTryNoteValid)
      : pcOffset_(script->pcToOffset(pc)), isTryNoteValid_(isTryNoteValid) {
    // NOTE: The Span is a temporary so we can't use begin()/end()
    // here or the iterator will outlive the span.
    auto trynotes = script->trynotes();
    tn_ = trynotes.data();
    tnEnd_ = tn_ + trynotes.size();

    settle();
  }

  void operator++() {
    ++tn_;
    settle();
  }

  bool pcInRange() const {
    // This checks both ends of the range at once
    // because unsigned integers wrap on underflow.
    uint32_t offset = pcOffset_;
    uint32_t start = tn_->start;
    uint32_t length = tn_->length;
    return offset - start < length;
  }
  bool done() const { return tn_ == tnEnd_; }
  const TryNote* operator*() const { return tn_; }
};

}  // namespace detail

template <class TryNoteFilter>
class MOZ_STACK_CLASS TryNoteIter
    : public detail::BaseTryNoteIter<TryNoteFilter> {
  using Base = detail::BaseTryNoteIter<TryNoteFilter>;

  // Keep the script alive as long as the iterator is live.
  RootedScript script_;

 public:
  TryNoteIter(JSContext* cx, JSScript* script, jsbytecode* pc,
              TryNoteFilter isTryNoteValid)
      : Base(script, pc, isTryNoteValid), script_(cx, script) {}
};

class NoOpTryNoteFilter {
 public:
  explicit NoOpTryNoteFilter() = default;
  bool operator()(const TryNote*) { return true; }
};

// Iterator over all try notes. Code using this iterator is not allowed to
// trigger GC to make sure the script stays alive. See TryNoteIter above for the
// can-GC version.
class MOZ_STACK_CLASS TryNoteIterAllNoGC
    : public detail::BaseTryNoteIter<NoOpTryNoteFilter> {
  using Base = detail::BaseTryNoteIter<NoOpTryNoteFilter>;
  JS::AutoCheckCannotGC nogc;

 public:
  TryNoteIterAllNoGC(JSScript* script, jsbytecode* pc)
      : Base(script, pc, NoOpTryNoteFilter()) {}
};

bool HandleClosingGeneratorReturn(JSContext* cx, AbstractFramePtr frame,
                                  bool ok);

/************************************************************************/

bool ThrowOperation(JSContext* cx, HandleValue v);

bool ThrowWithStackOperation(JSContext* cx, HandleValue v, HandleValue stack);

bool GetPendingExceptionStack(JSContext* cx, MutableHandleValue vp);

bool GetProperty(JSContext* cx, HandleValue value, Handle<PropertyName*> name,
                 MutableHandleValue vp);

JSObject* LambdaBaselineFallback(JSContext* cx, HandleFunction fun,
                                 HandleObject parent, gc::AllocSite* site);
JSObject* LambdaOptimizedFallback(JSContext* cx, HandleFunction fun,
                                  HandleObject parent, gc::Heap heap);
JSObject* Lambda(JSContext* cx, HandleFunction fun, HandleObject parent,
                 gc::Heap heap = gc::Heap::Default,
                 gc::AllocSite* site = nullptr);

bool SetObjectElement(JSContext* cx, HandleObject obj, HandleValue index,
                      HandleValue value, bool strict);

bool SetObjectElementWithReceiver(JSContext* cx, HandleObject obj,
                                  HandleValue index, HandleValue value,
                                  HandleValue receiver, bool strict);

bool AddValues(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
               MutableHandleValue res);

bool SubValues(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
               MutableHandleValue res);

bool MulValues(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
               MutableHandleValue res);

bool DivValues(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
               MutableHandleValue res);

bool ModValues(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
               MutableHandleValue res);

bool PowValues(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
               MutableHandleValue res);

bool BitNot(JSContext* cx, MutableHandleValue in, MutableHandleValue res);

bool BitXor(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
            MutableHandleValue res);

bool BitOr(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
           MutableHandleValue res);

bool BitAnd(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
            MutableHandleValue res);

bool BitLsh(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
            MutableHandleValue res);

bool BitRsh(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
            MutableHandleValue res);

bool UrshValues(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
                MutableHandleValue res);

bool LessThan(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
              bool* res);

bool LessThanOrEqual(JSContext* cx, MutableHandleValue lhs,
                     MutableHandleValue rhs, bool* res);

bool GreaterThan(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
                 bool* res);

bool GreaterThanOrEqual(JSContext* cx, MutableHandleValue lhs,
                        MutableHandleValue rhs, bool* res);

template <bool strict>
bool DelPropOperation(JSContext* cx, HandleValue val,
                      Handle<PropertyName*> name, bool* res);

template <bool strict>
bool DelElemOperation(JSContext* cx, HandleValue val, HandleValue index,
                      bool* res);

JSObject* BindVarOperation(JSContext* cx, JSObject* envChain);

JSObject* ImportMetaOperation(JSContext* cx, HandleScript script);

JSObject* BuiltinObjectOperation(JSContext* cx, BuiltinObjectKind kind);

bool ThrowMsgOperation(JSContext* cx, const unsigned throwMsgKind);

bool GetAndClearException(JSContext* cx, MutableHandleValue res);

bool GetAndClearExceptionAndStack(JSContext* cx, MutableHandleValue res,
                                  MutableHandle<SavedFrame*> stack);

bool DeleteNameOperation(JSContext* cx, Handle<PropertyName*> name,
                         HandleObject envChain, MutableHandleValue res);

void ImplicitThisOperation(JSContext* cx, HandleObject env,
                           MutableHandleValue res);

bool InitPropGetterSetterOperation(JSContext* cx, jsbytecode* pc,
                                   HandleObject obj, Handle<PropertyName*> name,
                                   HandleObject val);

unsigned GetInitDataPropAttrs(JSOp op);

bool EnterWithOperation(JSContext* cx, AbstractFramePtr frame, HandleValue val,
                        Handle<WithScope*> scope);

bool InitElemGetterSetterOperation(JSContext* cx, jsbytecode* pc,
                                   HandleObject obj, HandleValue idval,
                                   HandleObject val);

bool SpreadCallOperation(JSContext* cx, HandleScript script, jsbytecode* pc,
                         HandleValue thisv, HandleValue callee, HandleValue arr,
                         HandleValue newTarget, MutableHandleValue res);

bool OptimizeSpreadCall(JSContext* cx, HandleValue arg,
                        MutableHandleValue result);

bool OptimizeGetIterator(const Value& arg, JSContext* cx);

#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
enum class SyncDisposalClosureSlots : uint8_t {
  Method,
};
bool SyncDisposalClosure(JSContext* cx, unsigned argc, JS::Value* vp);

ErrorObject* CreateSuppressedError(JSContext* cx, JS::Handle<JS::Value> error,
                                   JS::Handle<JS::Value> suppressed);

bool AddDisposableResourceToCapability(JSContext* cx, JS::Handle<JSObject*> env,
                                       JS::Handle<JS::Value> val,
                                       JS::Handle<JS::Value> method,
                                       bool needsClosure, UsingHint hint);
#endif

ArrayObject* ArrayFromArgumentsObject(JSContext* cx,
                                      Handle<ArgumentsObject*> args);

JSObject* NewObjectOperation(JSContext* cx, HandleScript script,
                             const jsbytecode* pc);

JSObject* NewPlainObjectBaselineFallback(JSContext* cx,
                                         Handle<SharedShape*> shape,
                                         gc::AllocKind allocKind,
                                         gc::AllocSite* site);

JSObject* NewPlainObjectOptimizedFallback(JSContext* cx,
                                          Handle<SharedShape*> shape,
                                          gc::AllocKind allocKind,
                                          gc::Heap initialHeap);

ArrayObject* NewArrayOperation(JSContext* cx, uint32_t length,
                               NewObjectKind newKind = GenericObject);

// Called from JIT code when inline array allocation fails.
ArrayObject* NewArrayObjectBaselineFallback(JSContext* cx, uint32_t length,
                                            gc::AllocKind allocKind,
                                            gc::AllocSite* site);
ArrayObject* NewArrayObjectOptimizedFallback(JSContext* cx, uint32_t length,
                                             gc::AllocKind allocKind,
                                             NewObjectKind newKind);

[[nodiscard]] bool GetImportOperation(JSContext* cx, HandleObject envChain,
                                      HandleScript script, jsbytecode* pc,
                                      MutableHandleValue vp);

void ReportRuntimeLexicalError(JSContext* cx, unsigned errorNumber,
                               HandleId id);

void ReportRuntimeLexicalError(JSContext* cx, unsigned errorNumber,
                               Handle<PropertyName*> name);

void ReportRuntimeLexicalError(JSContext* cx, unsigned errorNumber,
                               HandleScript script, jsbytecode* pc);

void ReportInNotObjectError(JSContext* cx, HandleValue lref, HandleValue rref);

bool ThrowCheckIsObject(JSContext* cx, CheckIsObjectKind kind);

bool ThrowUninitializedThis(JSContext* cx);

bool ThrowInitializedThis(JSContext* cx);

bool ThrowObjectCoercible(JSContext* cx, HandleValue value);

bool Debug_CheckSelfHosted(JSContext* cx, HandleValue funVal);

bool CheckClassHeritageOperation(JSContext* cx, HandleValue heritage);

PlainObject* ObjectWithProtoOperation(JSContext* cx, HandleValue proto);

JSObject* FunWithProtoOperation(JSContext* cx, HandleFunction fun,
                                HandleObject parent, HandleObject proto);

bool SetPropertySuper(JSContext* cx, HandleValue lval, HandleValue receiver,
                      Handle<PropertyName*> name, HandleValue rval,
                      bool strict);

bool SetElementSuper(JSContext* cx, HandleValue lval, HandleValue receiver,
                     HandleValue index, HandleValue rval, bool strict);

bool LoadAliasedDebugVar(JSContext* cx, JSObject* env, jsbytecode* pc,
                         MutableHandleValue result);

bool CloseIterOperation(JSContext* cx, HandleObject iter, CompletionKind kind);
} /* namespace js */

#endif /* vm_Interpreter_h */
