/* -*- 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_JSFunction_h
#define vm_JSFunction_h

/*
 * JS function definitions.
 */

#include <string_view>

#include "jstypes.h"

#include "gc/Policy.h"
#include "js/shadow/Function.h"        // JS::shadow::Function
#include "vm/FunctionFlags.h"          // FunctionFlags
#include "vm/FunctionPrefixKind.h"     // FunctionPrefixKind
#include "vm/GeneratorAndAsyncKind.h"  // GeneratorKind, FunctionAsyncKind
#include "vm/JSAtom.h"
#include "vm/JSObject.h"
#include "vm/JSScript.h"

class JSJitInfo;

namespace js {

class FunctionExtended;
struct SelfHostedLazyScript;

using Native = JSNative;

static constexpr std::string_view FunctionConstructorMedialSigils = ") {\n";
static constexpr std::string_view FunctionConstructorFinalBrace = "\n}";

// JSFunctions can have one of two classes:
extern const JSClass FunctionClass;
extern const JSClass ExtendedFunctionClass;

namespace wasm {

class Instance;

}  // namespace wasm
}  // namespace js

class JSFunction : public js::NativeObject {
 public:
  static_assert(sizeof(js::FunctionFlags) == sizeof(uint16_t));
  static constexpr size_t ArgCountShift = 16;
  static constexpr size_t FlagsMask = js::BitMask(ArgCountShift);
  static constexpr size_t ArgCountMask = js::BitMask(16) << ArgCountShift;

  enum {
    /*
     * Bitfield composed of FunctionFlags and argument count, stored as a
     * PrivateUint32Value.
     *
     * If any of these flags needs to be accessed in off-thread JIT compilation,
     * copy it to js::jit::WrappedFunction.
     */
    FlagsAndArgCountSlot,

    /*
     * For native functions, the native method pointer stored as a private
     * value, or undefined.
     *
     * For interpreted functions, the environment object for new activations or
     * null.
     */
    NativeFuncOrInterpretedEnvSlot,

    /*
     * For native functions this is one of:
     *
     *  - JSJitInfo* to be used by the JIT, only used if isBuiltinNative() for
     *    builtin natives
     *
     *  - wasm function index for wasm/asm.js without a jit entry. Always has
     *    the low bit set to ensure it's never identical to a BaseScript*
     *    pointer
     *
     *  - a wasm JIT entry
     *
     * The JIT depends on none of the above being a valid BaseScript pointer.
     *
     * For interpreted functions this is either a BaseScript or the
     * SelfHostedLazyScript pointer.
     *
     * These are all stored as private values, because the JIT assumes that it
     * can access the SelfHostedLazyScript and BaseScript pointer in the same
     * way.
     */
    NativeJitInfoOrInterpretedScriptSlot,

    // The `atom_` field can have different meanings depending on the function
    // type and flags. It is used for diagnostics, decompiling, and
    //
    //   a. If HAS_GUESSED_ATOM is not set, to store the initial value of the
    //      "name" property of functions. But also see RESOLVED_NAME.
    //   b. If HAS_GUESSED_ATOM is set, `atom_` is only used for diagnostics,
    //      but must not be used for the "name" property.
    //   c. If HAS_INFERRED_NAME is set, the function wasn't given an explicit
    //      name in the source text, e.g. `function fn(){}`, but instead it
    //      was inferred based on how the function was defined in the source
    //      text. The exact name inference rules are defined in the ECMAScript
    //      specification.
    //      Name inference can happen at compile-time, for example in
    //      `var fn = function(){}`, or it can happen at runtime, for example
    //      in `var o = {[Symbol.iterator]: function(){}}`. When it happens at
    //      compile-time, the HAS_INFERRED_NAME is set directly in the
    //      bytecode emitter, when it happens at runtime, the flag is set when
    //      evaluating the JSOp::SetFunName bytecode.
    //   d. HAS_GUESSED_ATOM and HAS_INFERRED_NAME cannot both be set.
    //   e. `atom_` can be null if neither an explicit, nor inferred, nor a
    //      guessed name was set.
    //
    // Self-hosted functions have two names. For example, Array.prototype.sort
    // has the standard name "sort", but the implementation in Array.js is named
    // "ArraySort".
    //
    // -   In the self-hosting realm, these functions have `_atom` set to the
    //     implementation name.
    //
    // -   When we clone these functions into normal realms, we set `_atom` to
    //     the standard name. (The self-hosted name is also stored on the clone,
    //     in another slot; see GetClonedSelfHostedFunctionName().)
    AtomSlot,

    SlotCount
  };

 private:
  using FunctionFlags = js::FunctionFlags;

 public:
  static inline JSFunction* create(JSContext* cx, js::gc::AllocKind kind,
                                   js::gc::Heap heap,
                                   js::Handle<js::SharedShape*> shape);

  /* Call objects must be created for each invocation of this function. */
  bool needsCallObject() const;

  bool needsExtraBodyVarEnvironment() const;
  bool needsNamedLambdaEnvironment() const;

  bool needsFunctionEnvironmentObjects() const {
    bool res = nonLazyScript()->needsFunctionEnvironmentObjects();
    MOZ_ASSERT(res == (needsCallObject() || needsNamedLambdaEnvironment()));
    return res;
  }

  bool needsSomeEnvironmentObject() const {
    return needsFunctionEnvironmentObjects() || needsExtraBodyVarEnvironment();
  }

  uint32_t flagsAndArgCountRaw() const {
    return getFixedSlot(FlagsAndArgCountSlot).toPrivateUint32();
  }

  void initFlagsAndArgCount() {
    initFixedSlot(FlagsAndArgCountSlot, JS::PrivateUint32Value(0));
  }

  size_t nargs() const { return flagsAndArgCountRaw() >> ArgCountShift; }

  FunctionFlags flags() const {
    return FunctionFlags(uint16_t(flagsAndArgCountRaw() & FlagsMask));
  }

  FunctionFlags::FunctionKind kind() const { return flags().kind(); }

  /* A function can be classified as either native (C++) or interpreted (JS): */
  bool isInterpreted() const { return flags().isInterpreted(); }
  bool isNativeFun() const { return flags().isNativeFun(); }

  bool isConstructor() const { return flags().isConstructor(); }

  bool isNonBuiltinConstructor() const {
    return flags().isNonBuiltinConstructor();
  }

  /* Possible attributes of a native function: */
  bool isAsmJSNative() const { return flags().isAsmJSNative(); }

  bool isWasm() const { return flags().isWasm(); }
  bool isWasmWithJitEntry() const { return flags().isWasmWithJitEntry(); }
  bool isNativeWithoutJitEntry() const {
    return flags().isNativeWithoutJitEntry();
  }
  bool isBuiltinNative() const { return flags().isBuiltinNative(); }

  bool hasJitEntry() const { return flags().hasJitEntry(); }

  /* Possible attributes of an interpreted function: */
  bool hasInferredName() const { return flags().hasInferredName(); }
  bool hasGuessedAtom() const { return flags().hasGuessedAtom(); }

  bool isLambda() const { return flags().isLambda(); }

  // These methods determine which kind of script we hold.
  //
  // For live JSFunctions the pointer values will always be non-null, but due to
  // partial initialization the GC (and other features that scan the heap
  // directly) may still return a null pointer.
  bool hasSelfHostedLazyScript() const {
    return flags().hasSelfHostedLazyScript();
  }
  bool hasBaseScript() const { return flags().hasBaseScript(); }

  bool hasBytecode() const {
    MOZ_ASSERT(!isIncomplete());
    return hasBaseScript() && baseScript()->hasBytecode();
  }

  bool isGhost() const { return flags().isGhost(); }

  // Arrow functions store their lexical new.target in the first extended slot.
  bool isArrow() const { return flags().isArrow(); }
  // Every class-constructor is also a method.
  bool isMethod() const { return flags().isMethod(); }
  bool isClassConstructor() const { return flags().isClassConstructor(); }

  bool isGetter() const { return flags().isGetter(); }
  bool isSetter() const { return flags().isSetter(); }

  bool allowSuperProperty() const { return flags().allowSuperProperty(); }

  bool hasResolvedLength() const { return flags().hasResolvedLength(); }
  bool hasResolvedName() const { return flags().hasResolvedName(); }

  bool isSelfHostedOrIntrinsic() const {
    return flags().isSelfHostedOrIntrinsic();
  }
  bool isSelfHostedBuiltin() const { return flags().isSelfHostedBuiltin(); }

  bool isIntrinsic() const { return flags().isIntrinsic(); }

  bool hasJitScript() const {
    if (!hasBaseScript()) {
      return false;
    }

    return baseScript()->hasJitScript();
  }

  /* Compound attributes: */
  bool isBuiltin() const { return isBuiltinNative() || isSelfHostedBuiltin(); }

  bool isNamedLambda() const {
    return flags().isNamedLambda(displayAtom() != nullptr);
  }

  bool hasLexicalThis() const { return isArrow(); }

  bool isBuiltinFunctionConstructor();
  bool needsPrototypeProperty();

  // Returns true if this function must have a non-configurable .prototype data
  // property. This is used to ensure looking up .prototype elsewhere will have
  // no side-effects.
  bool hasNonConfigurablePrototypeDataProperty();

  // Returns true if |new Fun()| should not allocate a new object caller-side
  // but pass the uninitialized-lexical MagicValue and rely on the callee to
  // construct its own |this| object.
  bool constructorNeedsUninitializedThis() const {
    MOZ_ASSERT(isConstructor());
    MOZ_ASSERT(isInterpreted());
    return isDerivedClassConstructor();
  }

  /* Returns the strictness of this function, which must be interpreted. */
  bool strict() const { return baseScript()->strict(); }

  void setFlags(FunctionFlags flags) { setFlags(flags.toRaw()); }
  void setFlags(uint16_t flags) {
    uint32_t flagsAndArgCount = flagsAndArgCountRaw();
    flagsAndArgCount &= ~FlagsMask;
    flagsAndArgCount |= flags;
    js::HeapSlot& slot = getFixedSlotRef(FlagsAndArgCountSlot);
    slot.unbarrieredSet(JS::PrivateUint32Value(flagsAndArgCount));
  }

  // Make the function constructible.
  void setIsConstructor() { setFlags(flags().setIsConstructor()); }

  // Can be called multiple times by the parser.
  void setArgCount(uint16_t nargs) {
    uint32_t flagsAndArgCount = flagsAndArgCountRaw();
    flagsAndArgCount &= ~ArgCountMask;
    flagsAndArgCount |= nargs << ArgCountShift;
    js::HeapSlot& slot = getFixedSlotRef(FlagsAndArgCountSlot);
    slot.unbarrieredSet(JS::PrivateUint32Value(flagsAndArgCount));
  }

  void setIsSelfHostedBuiltin() { setFlags(flags().setIsSelfHostedBuiltin()); }
  void setIsIntrinsic() { setFlags(flags().setIsIntrinsic()); }

  void setResolvedLength() { setFlags(flags().setResolvedLength()); }
  void setResolvedName() { setFlags(flags().setResolvedName()); }

  static inline bool getUnresolvedLength(JSContext* cx, js::HandleFunction fun,
                                         uint16_t* length);

  inline JSAtom* infallibleGetUnresolvedName(JSContext* cx);

  JSAtom* explicitName() const {
    return (hasInferredName() || hasGuessedAtom()) ? nullptr : rawAtom();
  }

  JSAtom* explicitOrInferredName() const {
    return hasGuessedAtom() ? nullptr : rawAtom();
  }

  void initAtom(JSAtom* atom) {
    MOZ_ASSERT_IF(atom, js::AtomIsMarked(zone(), atom));
    MOZ_ASSERT(getFixedSlot(AtomSlot).isUndefined());
    if (atom) {
      initFixedSlot(AtomSlot, JS::StringValue(atom));
    }
  }

  void setAtom(JSAtom* atom) {
    MOZ_ASSERT_IF(atom, js::AtomIsMarked(zone(), atom));
    setFixedSlot(AtomSlot, atom ? JS::StringValue(atom) : JS::UndefinedValue());
  }

  JSAtom* displayAtom() const { return rawAtom(); }

  JSAtom* rawAtom() const {
    JS::Value value = getFixedSlot(AtomSlot);
    return value.isUndefined() ? nullptr : &value.toString()->asAtom();
  }

  void setInferredName(JSAtom* atom) {
    MOZ_ASSERT(!rawAtom());
    MOZ_ASSERT(atom);
    MOZ_ASSERT(!hasGuessedAtom());
    setAtom(atom);
    setFlags(flags().setInferredName());
  }
  JSAtom* inferredName() const {
    MOZ_ASSERT(hasInferredName());
    MOZ_ASSERT(rawAtom());
    return rawAtom();
  }

  void setGuessedAtom(JSAtom* atom) {
    MOZ_ASSERT(!rawAtom());
    MOZ_ASSERT(atom);
    MOZ_ASSERT(!hasInferredName());
    MOZ_ASSERT(!hasGuessedAtom());
    setAtom(atom);
    setFlags(flags().setGuessedAtom());
  }

  /* uint16_t representation bounds number of call object dynamic slots. */
  enum { MAX_ARGS_AND_VARS = 2 * ((1U << 16) - 1) };

  /*
   * For an interpreted function, accessors for the initial scope object of
   * activations (stack frames) of the function.
   */
  JSObject* environment() const {
    MOZ_ASSERT(isInterpreted());
    return getFixedSlot(NativeFuncOrInterpretedEnvSlot).toObjectOrNull();
  }

  void initEnvironment(JSObject* obj) {
    MOZ_ASSERT(isInterpreted());
    initFixedSlot(NativeFuncOrInterpretedEnvSlot, JS::ObjectOrNullValue(obj));
  }

 public:
  static constexpr size_t offsetOfFlagsAndArgCount() {
    return getFixedSlotOffset(FlagsAndArgCountSlot);
  }
  static size_t offsetOfEnvironment() { return offsetOfNativeOrEnv(); }
  static size_t offsetOfAtom() { return getFixedSlotOffset(AtomSlot); }

  static bool delazifyLazilyInterpretedFunction(JSContext* cx,
                                                js::HandleFunction fun);
  static bool delazifySelfHostedLazyFunction(JSContext* cx,
                                             js::HandleFunction fun);
  void maybeRelazify(JSRuntime* rt);

  // Function Scripts
  //
  // Interpreted functions have either a BaseScript or a SelfHostedLazyScript. A
  // BaseScript may either be lazy or non-lazy (hasBytecode()). Methods may
  // return a JSScript* if underlying BaseScript is known to have bytecode.
  //
  // There are several methods to get the script of an interpreted function:
  //
  // - For all interpreted functions, getOrCreateScript() will get the
  //   JSScript, delazifying the function if necessary. This is the safest to
  //   use, but has extra checks, requires a cx and may trigger a GC.
  //
  // - For functions known to have a JSScript, nonLazyScript() will get it.

  static JSScript* getOrCreateScript(JSContext* cx, js::HandleFunction fun) {
    MOZ_ASSERT(fun->isInterpreted());
    MOZ_ASSERT(cx);

    if (fun->hasSelfHostedLazyScript()) {
      if (!delazifySelfHostedLazyFunction(cx, fun)) {
        return nullptr;
      }
      return fun->nonLazyScript();
    }

    MOZ_ASSERT(fun->hasBaseScript());

    if (!fun->baseScript()->hasBytecode()) {
      if (!delazifyLazilyInterpretedFunction(cx, fun)) {
        return nullptr;
      }
    }
    return fun->nonLazyScript();
  }

  // If this is a scripted function, returns its canonical function (the
  // original function allocated by the frontend). Note that lazy self-hosted
  // builtins don't have a lazy script so in that case we also return nullptr.
  JSFunction* maybeCanonicalFunction() const {
    if (hasBaseScript()) {
      return baseScript()->function();
    }
    return nullptr;
  }

 private:
  void* nativeJitInfoOrInterpretedScript() const {
    return getFixedSlot(NativeJitInfoOrInterpretedScriptSlot).toPrivate();
  }
  void setNativeJitInfoOrInterpretedScript(void* ptr) {
    // This always stores a PrivateValue and so doesn't require a barrier.
    js::HeapSlot& slot = getFixedSlotRef(NativeJitInfoOrInterpretedScriptSlot);
    slot.unbarrieredSet(JS::PrivateValue(ptr));
  }

 public:
  // The default state of a JSFunction that is not ready for execution. If
  // observed outside initialization, this is the result of failure during
  // bytecode compilation.
  //
  // A BaseScript is fully initialized before u.script.s.script_ is initialized
  // with a reference to it.
  bool isIncomplete() const {
    return isInterpreted() && !nativeJitInfoOrInterpretedScript();
  }

  JSScript* nonLazyScript() const {
    MOZ_ASSERT(hasBytecode());
    return static_cast<JSScript*>(baseScript());
  }

  js::SelfHostedLazyScript* selfHostedLazyScript() const {
    MOZ_ASSERT(hasSelfHostedLazyScript());
    return static_cast<js::SelfHostedLazyScript*>(
        nativeJitInfoOrInterpretedScript());
  }

  // Access fields defined on both lazy and non-lazy scripts.
  js::BaseScript* baseScript() const {
    MOZ_ASSERT(hasBaseScript());
    return static_cast<JSScript*>(nativeJitInfoOrInterpretedScript());
  }

  static inline bool getLength(JSContext* cx, js::HandleFunction fun,
                               uint16_t* length);

  js::Scope* enclosingScope() const { return baseScript()->enclosingScope(); }

  void setEnclosingLazyScript(js::BaseScript* enclosingScript) {
    baseScript()->setEnclosingScript(enclosingScript);
  }

  js::GeneratorKind generatorKind() const {
    if (hasBaseScript()) {
      return baseScript()->generatorKind();
    }
    if (hasSelfHostedLazyScript()) {
      return clonedSelfHostedGeneratorKind();
    }
    return js::GeneratorKind::NotGenerator;
  }

  js::GeneratorKind clonedSelfHostedGeneratorKind() const;

  bool isGenerator() const {
    return generatorKind() == js::GeneratorKind::Generator;
  }

  js::FunctionAsyncKind asyncKind() const {
    if (hasBaseScript()) {
      return baseScript()->asyncKind();
    }
    return js::FunctionAsyncKind::SyncFunction;
  }

  bool isAsync() const {
    return asyncKind() == js::FunctionAsyncKind::AsyncFunction;
  }

  bool isGeneratorOrAsync() const { return isGenerator() || isAsync(); }

  void initScript(js::BaseScript* script) {
    MOZ_ASSERT_IF(script, realm() == script->realm());
    MOZ_ASSERT(isInterpreted());
    MOZ_ASSERT_IF(hasBaseScript(),
                  !baseScript());  // No write barrier required.
    setNativeJitInfoOrInterpretedScript(script);
  }

  void initSelfHostedLazyScript(js::SelfHostedLazyScript* lazy) {
    MOZ_ASSERT(isSelfHostedBuiltin());
    MOZ_ASSERT(isInterpreted());
    if (hasBaseScript()) {
      js::gc::PreWriteBarrier(baseScript());
    }
    FunctionFlags f = flags();
    f.clearBaseScript();
    f.setSelfHostedLazy();
    setFlags(f);
    setNativeJitInfoOrInterpretedScript(lazy);
    MOZ_ASSERT(hasSelfHostedLazyScript());
  }

  void clearSelfHostedLazyScript() {
    MOZ_ASSERT(isSelfHostedBuiltin());
    MOZ_ASSERT(isInterpreted());
    MOZ_ASSERT(!hasBaseScript());  // No write barrier required.
    FunctionFlags f = flags();
    f.clearSelfHostedLazy();
    f.setBaseScript();
    setFlags(f);
    setNativeJitInfoOrInterpretedScript(nullptr);
    MOZ_ASSERT(isIncomplete());
  }

  JSNative native() const {
    MOZ_ASSERT(isNativeFun());
    return nativeUnchecked();
  }
  JSNative nativeUnchecked() const {
    // Can be called by Ion off-main thread.
    JS::Value value = getFixedSlot(NativeFuncOrInterpretedEnvSlot);
    return reinterpret_cast<JSNative>(value.toPrivate());
  }

  JSNative maybeNative() const { return isInterpreted() ? nullptr : native(); }

  void initNative(js::Native native, const JSJitInfo* jitInfo) {
    MOZ_ASSERT(isNativeFun());
    MOZ_ASSERT_IF(jitInfo, isBuiltinNative());
    MOZ_ASSERT(native);
    initFixedSlot(NativeFuncOrInterpretedEnvSlot,
                  JS::PrivateValue(reinterpret_cast<void*>(native)));
    setNativeJitInfoOrInterpretedScript(const_cast<JSJitInfo*>(jitInfo));
  }
  bool hasJitInfo() const { return isBuiltinNative() && jitInfoUnchecked(); }
  const JSJitInfo* jitInfo() const {
    MOZ_ASSERT(hasJitInfo());
    return jitInfoUnchecked();
  }
  const JSJitInfo* jitInfoUnchecked() const {
    // Can be called by Ion off-main thread.
    return static_cast<const JSJitInfo*>(nativeJitInfoOrInterpretedScript());
  }
  void setJitInfo(const JSJitInfo* data) {
    MOZ_ASSERT(isBuiltinNative());
    MOZ_ASSERT(data);
    setNativeJitInfoOrInterpretedScript(const_cast<JSJitInfo*>(data));
  }

  // wasm functions are always natives and either:
  //  - store a function-index in u.n.extra and can only be called through the
  //    fun->native() entry point from C++.
  //  - store a jit-entry code pointer in u.n.extra and can be called by jit
  //    code directly. C++ callers can still use the fun->native() entry point
  //    (computing the function index from the jit-entry point).
  void setWasmFuncIndex(uint32_t funcIndex) {
    MOZ_ASSERT(isWasm() || isAsmJSNative());
    MOZ_ASSERT(!isWasmWithJitEntry());
    MOZ_ASSERT(!nativeJitInfoOrInterpretedScript());
    // See wasmFuncIndex_ comment for why we set the low bit.
    uintptr_t tagged = (uintptr_t(funcIndex) << 1) | 1;
    setNativeJitInfoOrInterpretedScript(reinterpret_cast<void*>(tagged));
  }
  uint32_t wasmFuncIndex() const {
    MOZ_ASSERT(isWasm() || isAsmJSNative());
    MOZ_ASSERT(!isWasmWithJitEntry());
    uintptr_t tagged = uintptr_t(nativeJitInfoOrInterpretedScript());
    MOZ_ASSERT(tagged & 1);
    return tagged >> 1;
  }
  void setWasmJitEntry(void** entry) {
    MOZ_ASSERT(*entry);
    MOZ_ASSERT(isWasm());
    MOZ_ASSERT(!isWasmWithJitEntry());
    setFlags(flags().setWasmJitEntry());
    setNativeJitInfoOrInterpretedScript(entry);
    MOZ_ASSERT(isWasmWithJitEntry());
  }
  void** wasmJitEntry() const {
    MOZ_ASSERT(isWasmWithJitEntry());
    return static_cast<void**>(nativeJitInfoOrInterpretedScript());
  }
  inline js::wasm::Instance& wasmInstance() const;

  bool isDerivedClassConstructor() const;
  bool isSyntheticFunction() const;

  static unsigned offsetOfNativeOrEnv() {
    return getFixedSlotOffset(NativeFuncOrInterpretedEnvSlot);
  }
  static unsigned offsetOfJitInfoOrScript() {
    return getFixedSlotOffset(NativeJitInfoOrInterpretedScriptSlot);
  }

  inline void trace(JSTracer* trc);

 public:
  inline bool isExtended() const {
    bool extended = flags().isExtended();
    MOZ_ASSERT_IF(isTenured(),
                  extended == (asTenured().getAllocKind() ==
                               js::gc::AllocKind::FUNCTION_EXTENDED));
    return extended;
  }

  /*
   * Accessors for data stored in extended functions. Use setExtendedSlot if the
   * function has already been initialized. Otherwise use initExtendedSlot.
   */
  inline void initExtendedSlot(uint32_t slot, const js::Value& val);
  inline void setExtendedSlot(uint32_t slot, const js::Value& val);
  inline const js::Value& getExtendedSlot(uint32_t slot) const;

  /* GC support. */
  js::gc::AllocKind getAllocKind() const {
    static_assert(
        js::gc::AllocKind::FUNCTION != js::gc::AllocKind::FUNCTION_EXTENDED,
        "extended/non-extended AllocKinds have to be different "
        "for getAllocKind() to have a reason to exist");

    js::gc::AllocKind kind = js::gc::AllocKind::FUNCTION;
    if (isExtended()) {
      kind = js::gc::AllocKind::FUNCTION_EXTENDED;
    }
    MOZ_ASSERT_IF(isTenured(), kind == asTenured().getAllocKind());
    return kind;
  }

  // If we're constructing with this function, choose an appropriate
  // allocKind.
  static bool getAllocKindForThis(JSContext* cx, js::HandleFunction func,
                                  js::gc::AllocKind& allocKind);
};

static_assert(sizeof(JSFunction) == sizeof(JS::shadow::Function),
              "shadow interface must match actual interface");

static_assert(unsigned(JSFunction::FlagsAndArgCountSlot) ==
              unsigned(JS::shadow::Function::FlagsAndArgCountSlot));
static_assert(unsigned(JSFunction::NativeFuncOrInterpretedEnvSlot) ==
              unsigned(JS::shadow::Function::NativeFuncOrInterpretedEnvSlot));
static_assert(
    unsigned(JSFunction::NativeJitInfoOrInterpretedScriptSlot) ==
    unsigned(JS::shadow::Function::NativeJitInfoOrInterpretedScriptSlot));
static_assert(unsigned(JSFunction::AtomSlot) ==
              unsigned(JS::shadow::Function::AtomSlot));

extern JSString* fun_toStringHelper(JSContext* cx, js::HandleObject obj,
                                    bool isToSource);

namespace js {

extern bool Function(JSContext* cx, unsigned argc, Value* vp);

extern bool Generator(JSContext* cx, unsigned argc, Value* vp);

extern bool AsyncFunctionConstructor(JSContext* cx, unsigned argc, Value* vp);

extern bool AsyncGeneratorConstructor(JSContext* cx, unsigned argc, Value* vp);

// If enclosingEnv is null, the function will have a null environment()
// (yes, null, not the global lexical environment).  In all cases, the global
// will be used as the terminating environment.

extern JSFunction* NewFunctionWithProto(
    JSContext* cx, JSNative native, unsigned nargs, FunctionFlags flags,
    HandleObject enclosingEnv, Handle<JSAtom*> atom, HandleObject proto,
    gc::AllocKind allocKind = gc::AllocKind::FUNCTION,
    NewObjectKind newKind = GenericObject);

// Allocate a new function backed by a JSNative.  Note that by default this
// creates a tenured object.
inline JSFunction* NewNativeFunction(
    JSContext* cx, JSNative native, unsigned nargs, Handle<JSAtom*> atom,
    gc::AllocKind allocKind = gc::AllocKind::FUNCTION,
    NewObjectKind newKind = TenuredObject,
    FunctionFlags flags = FunctionFlags::NATIVE_FUN) {
  MOZ_ASSERT(native);
  return NewFunctionWithProto(cx, native, nargs, flags, nullptr, atom, nullptr,
                              allocKind, newKind);
}

// Allocate a new constructor backed by a JSNative.  Note that by default this
// creates a tenured object.
inline JSFunction* NewNativeConstructor(
    JSContext* cx, JSNative native, unsigned nargs, Handle<JSAtom*> atom,
    gc::AllocKind allocKind = gc::AllocKind::FUNCTION,
    NewObjectKind newKind = TenuredObject,
    FunctionFlags flags = FunctionFlags::NATIVE_CTOR) {
  MOZ_ASSERT(native);
  MOZ_ASSERT(flags.isNativeConstructor());
  return NewFunctionWithProto(cx, native, nargs, flags, nullptr, atom, nullptr,
                              allocKind, newKind);
}

// Determine which [[Prototype]] to use when creating a new function using the
// requested generator and async kind.
//
// This sets `proto` to `nullptr` for non-generator, synchronous functions to
// mean "the builtin %FunctionPrototype% in the current realm", the common case.
//
// We could set it to `cx->global()->getOrCreateFunctionPrototype()`, but
// nullptr gets a fast path in e.g. js::NewObjectWithClassProtoCommon.
extern bool GetFunctionPrototype(JSContext* cx, js::GeneratorKind generatorKind,
                                 js::FunctionAsyncKind asyncKind,
                                 js::MutableHandleObject proto);

extern JSAtom* IdToFunctionName(
    JSContext* cx, HandleId id,
    FunctionPrefixKind prefixKind = FunctionPrefixKind::None);

extern bool SetFunctionName(JSContext* cx, HandleFunction fun, HandleValue name,
                            FunctionPrefixKind prefixKind);

extern JSFunction* DefineFunction(
    JSContext* cx, HandleObject obj, HandleId id, JSNative native,
    unsigned nargs, unsigned flags,
    gc::AllocKind allocKind = gc::AllocKind::FUNCTION);

extern bool fun_toString(JSContext* cx, unsigned argc, Value* vp);

extern void ThrowTypeErrorBehavior(JSContext* cx);

/*
 * Function extended with reserved slots for use by various kinds of functions.
 * Most functions do not have these extensions, but enough do that efficient
 * storage is required (no malloc'ed reserved slots).
 */
class FunctionExtended : public JSFunction {
 public:
  enum {
    FirstExtendedSlot = JSFunction::SlotCount,
    SecondExtendedSlot,

    SlotCount
  };

  static const uint32_t NUM_EXTENDED_SLOTS = 2;

  static const uint32_t METHOD_HOMEOBJECT_SLOT = 0;

  // wasm/asm.js exported functions store a code pointer to their direct entry
  // point (see CodeRange::funcUncheckedCallEntry()) to support the call_ref
  // instruction.
  static const uint32_t WASM_FUNC_UNCHECKED_ENTRY_SLOT = 0;

  // wasm/asm.js exported functions store the wasm::Instance pointer of their
  // instance.
  static const uint32_t WASM_INSTANCE_SLOT = 1;

  // asm.js module functions store their WasmModuleObject in the first slot.
  static const uint32_t ASMJS_MODULE_SLOT = 0;

  // Async module callback handlers store their ModuleObject in the first slot.
  static const uint32_t MODULE_SLOT = 0;

  static inline size_t offsetOfExtendedSlot(uint32_t which) {
    MOZ_ASSERT(which < NUM_EXTENDED_SLOTS);
    return getFixedSlotOffset(FirstExtendedSlot + which);
  }
  static inline size_t offsetOfMethodHomeObjectSlot() {
    return offsetOfExtendedSlot(METHOD_HOMEOBJECT_SLOT);
  }

 private:
  friend class JSFunction;
};

extern JSFunction* CloneFunctionReuseScript(JSContext* cx, HandleFunction fun,
                                            HandleObject enclosingEnv,
                                            HandleObject proto);

extern JSFunction* CloneAsmJSModuleFunction(JSContext* cx, HandleFunction fun);

}  // namespace js

template <>
inline bool JSObject::is<JSFunction>() const {
  return getClass()->isJSFunction();
}

inline void JSFunction::initExtendedSlot(uint32_t which, const js::Value& val) {
  MOZ_ASSERT(isExtended());
  MOZ_ASSERT(which < js::FunctionExtended::NUM_EXTENDED_SLOTS);
  MOZ_ASSERT(js::IsObjectValueInCompartment(val, compartment()));
  initFixedSlot(js::FunctionExtended::FirstExtendedSlot + which, val);
}

inline void JSFunction::setExtendedSlot(uint32_t which, const js::Value& val) {
  MOZ_ASSERT(isExtended());
  MOZ_ASSERT(which < js::FunctionExtended::NUM_EXTENDED_SLOTS);
  MOZ_ASSERT(js::IsObjectValueInCompartment(val, compartment()));
  setFixedSlot(js::FunctionExtended::FirstExtendedSlot + which, val);
}

inline const js::Value& JSFunction::getExtendedSlot(uint32_t which) const {
  MOZ_ASSERT(isExtended());
  MOZ_ASSERT(which < js::FunctionExtended::NUM_EXTENDED_SLOTS);
  return getFixedSlot(js::FunctionExtended::FirstExtendedSlot + which);
}

inline js::wasm::Instance& JSFunction::wasmInstance() const {
  MOZ_ASSERT(isWasm() || isAsmJSNative());
  MOZ_ASSERT(
      !getExtendedSlot(js::FunctionExtended::WASM_INSTANCE_SLOT).isUndefined());
  return *static_cast<js::wasm::Instance*>(
      getExtendedSlot(js::FunctionExtended::WASM_INSTANCE_SLOT).toPrivate());
}

namespace js {

JSString* FunctionToString(JSContext* cx, HandleFunction fun, bool isToSource);

/*
 * Report an error that call.thisv is not compatible with the specified class,
 * assuming that the method (clasp->name).prototype.<name of callee function>
 * is what was called.
 */
extern void ReportIncompatibleMethod(JSContext* cx, const CallArgs& args,
                                     const JSClass* clasp);

/*
 * Report an error that call.thisv is not an acceptable this for the callee
 * function.
 */
extern void ReportIncompatible(JSContext* cx, const CallArgs& args);

extern bool fun_apply(JSContext* cx, unsigned argc, Value* vp);

extern bool fun_call(JSContext* cx, unsigned argc, Value* vp);

} /* namespace js */

#ifdef DEBUG
namespace JS {
namespace detail {

JS_PUBLIC_API void CheckIsValidConstructible(const Value& calleev);

}  // namespace detail
}  // namespace JS
#endif

#endif /* vm_JSFunction_h */
