js/src/jsfun.h
author Phil Ringnalda <philringnalda@gmail.com>
Thu, 19 Nov 2015 20:01:07 -0800
changeset 273465 652bd59cdb5153cac5a7d1720ccd546862130144
parent 273429 9b4f7a838a661d526dc020cbf11695655e1525cb
child 273704 52d7c9292ecfc23a52835c49189dabd561b18675
permissions -rw-r--r--
Backed out 20 changesets (bug 1055472) for Linux64 cgc failures and an apparent crash in the cpp test test_audio on OS X 10.6 debug CLOSED TREE Backed out changeset edd1c18b5a5b (bug 1055472) Backed out changeset 41be086be0e8 (bug 1055472) Backed out changeset 6ed32cadfc31 (bug 1055472) Backed out changeset 2f0b0b246e25 (bug 1055472) Backed out changeset 33d5c8ef947c (bug 1055472) Backed out changeset 74dca890ec34 (bug 1055472) Backed out changeset b4a4144b96fa (bug 1055472) Backed out changeset 9dd0b1fff545 (bug 1055472) Backed out changeset 31c41d6a16ab (bug 1055472) Backed out changeset bf8f9604c34f (bug 1055472) Backed out changeset 69bf1faa9d85 (bug 1055472) Backed out changeset 284934443cd3 (bug 1055472) Backed out changeset 65d962413c98 (bug 1055472) Backed out changeset 94135702e1b5 (bug 1055472) Backed out changeset 1509efcfa629 (bug 1055472) Backed out changeset c7180ea9dfa4 (bug 1055472) Backed out changeset a47a3ce6f35e (bug 1055472) Backed out changeset 9b4f7a838a66 (bug 1055472) Backed out changeset e5f593ea362c (bug 1055472) Backed out changeset 8ec3005245c1 (bug 1055472)

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

/*
 * JS function definitions.
 */

#include "jsobj.h"
#include "jsscript.h"
#include "jstypes.h"

namespace js {

namespace frontend {
class FunctionBox;
}

class FunctionExtended;

typedef JSNative           Native;
} // namespace js

struct JSAtomState;

class JSFunction : public js::NativeObject
{
  public:
    static const js::Class class_;

    enum FunctionKind {
        NormalFunction = 0,
        Arrow,                      /* ES6 '(args) => body' syntax */
        Method,                     /* ES6 MethodDefinition */
        ClassConstructor,
        Getter,
        Setter,
        AsmJS,                      /* function is an asm.js module or exported function */
        FunctionKindLimit
    };

    enum Flags {
        INTERPRETED      = 0x0001,  /* function has a JSScript and environment. */
        CONSTRUCTOR      = 0x0002,  /* function that can be called as a constructor */
        EXTENDED         = 0x0004,  /* structure is FunctionExtended */
        IS_FUN_PROTO     = 0x0008,  /* function is Function.prototype for some global object */
        EXPR_BODY        = 0x0010,  /* arrow function with expression body or
                                     * expression closure: function(x) x*x */
        HAS_GUESSED_ATOM = 0x0020,  /* function had no explicit name, but a
                                       name was guessed for it anyway */
        LAMBDA           = 0x0040,  /* function comes from a FunctionExpression, ArrowFunction, or
                                       Function() call (not a FunctionDeclaration or nonstandard
                                       function-statement) */
        SELF_HOSTED      = 0x0080,  /* function is self-hosted builtin and must not be
                                       decompilable nor constructible. */
        HAS_REST         = 0x0100,  /* function has a rest (...) parameter */
        INTERPRETED_LAZY = 0x0200,  /* function is interpreted but doesn't have a script yet */
        RESOLVED_LENGTH  = 0x0400,  /* f.length has been resolved (see fun_resolve). */
        RESOLVED_NAME    = 0x0800,  /* f.name has been resolved (see fun_resolve). */
        BEING_PARSED     = 0x1000,  /* function is currently being parsed; has
                                       a funbox in place of an environment */

        FUNCTION_KIND_SHIFT = 13,
        FUNCTION_KIND_MASK  = 0x7 << FUNCTION_KIND_SHIFT,

        ASMJS_KIND = AsmJS << FUNCTION_KIND_SHIFT,
        ARROW_KIND = Arrow << FUNCTION_KIND_SHIFT,
        METHOD_KIND = Method << FUNCTION_KIND_SHIFT,
        CLASSCONSTRUCTOR_KIND = ClassConstructor << FUNCTION_KIND_SHIFT,
        GETTER_KIND = Getter << FUNCTION_KIND_SHIFT,
        SETTER_KIND = Setter << FUNCTION_KIND_SHIFT,

        /* Derived Flags values for convenience: */
        NATIVE_FUN = 0,
        NATIVE_CTOR = NATIVE_FUN | CONSTRUCTOR,
        NATIVE_CLASS_CTOR = NATIVE_FUN | CONSTRUCTOR | CLASSCONSTRUCTOR_KIND,
        ASMJS_CTOR = ASMJS_KIND | NATIVE_CTOR,
        ASMJS_LAMBDA_CTOR = ASMJS_KIND | NATIVE_CTOR | LAMBDA,
        INTERPRETED_METHOD = INTERPRETED | METHOD_KIND,
        INTERPRETED_METHOD_GENERATOR = INTERPRETED | METHOD_KIND,
        INTERPRETED_CLASS_CONSTRUCTOR = INTERPRETED | CLASSCONSTRUCTOR_KIND | CONSTRUCTOR,
        INTERPRETED_GETTER = INTERPRETED | GETTER_KIND,
        INTERPRETED_SETTER = INTERPRETED | SETTER_KIND,
        INTERPRETED_LAMBDA = INTERPRETED | LAMBDA | CONSTRUCTOR,
        INTERPRETED_LAMBDA_ARROW = INTERPRETED | LAMBDA | ARROW_KIND,
        INTERPRETED_LAMBDA_GENERATOR = INTERPRETED | LAMBDA,
        INTERPRETED_NORMAL = INTERPRETED | CONSTRUCTOR,
        INTERPRETED_GENERATOR = INTERPRETED,
        NO_XDR_FLAGS = RESOLVED_LENGTH | RESOLVED_NAME,

        STABLE_ACROSS_CLONES = IS_FUN_PROTO | CONSTRUCTOR | EXPR_BODY | HAS_GUESSED_ATOM |
                               LAMBDA | SELF_HOSTED |  HAS_REST | FUNCTION_KIND_MASK
    };

    static_assert((INTERPRETED | INTERPRETED_LAZY) == js::JS_FUNCTION_INTERPRETED_BITS,
                  "jsfriendapi.h's JSFunction::INTERPRETED-alike is wrong");
    static_assert(((FunctionKindLimit - 1) << FUNCTION_KIND_SHIFT) <= FUNCTION_KIND_MASK,
                  "FunctionKind doesn't fit into flags_");

    // Implemented in Parser.cpp. Used so the static scope chain may be walked
    // in Parser without a JSScript.
    class MOZ_STACK_CLASS AutoParseUsingFunctionBox
    {
        js::RootedFunction fun_;
        js::RootedObject oldEnv_;

      public:
        AutoParseUsingFunctionBox(js::ExclusiveContext* cx, js::frontend::FunctionBox* funbox);
        ~AutoParseUsingFunctionBox();
    };

  private:
    uint16_t        nargs_;       /* number of formal arguments
                                     (including defaults and the rest parameter unlike f.length) */
    uint16_t        flags_;       /* bitfield composed of the above Flags enum, as well as the kind */
    union U {
        class Native {
            friend class JSFunction;
            js::Native          native;       /* native method pointer or null */
            const JSJitInfo*    jitinfo;     /* Information about this function to be
                                                 used by the JIT;
                                                 use the accessor! */
        } n;
        struct Scripted {
            union {
                JSScript* script_; /* interpreted bytecode descriptor or null;
                                      use the accessor! */
                js::LazyScript* lazy_; /* lazily compiled script, or nullptr */
            } s;
            union {
                JSObject*   env_;    /* environment for new activations;
                                        use the accessor! */
                js::frontend::FunctionBox* funbox_; /* the function box when parsing */
            };
        } i;
        void*           nativeOrScript;
    } u;
    js::HeapPtrAtom  atom_;       /* name for diagnostics and decompiling */

  public:

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

        if (isNative())
            return false;

        // Note: this should be kept in sync with FunctionBox::needsCallObject().
        return nonLazyScript()->hasAnyAliasedBindings() ||
               nonLazyScript()->funHasExtensibleScope() ||
               nonLazyScript()->funNeedsDeclEnvObject() ||
               nonLazyScript()->needsHomeObject()       ||
               nonLazyScript()->isDerivedClassConstructor() ||
               isGenerator();
    }

    size_t nargs() const {
        return nargs_;
    }

    uint16_t flags() const {
        return flags_;
    }

    FunctionKind kind() const {
        return static_cast<FunctionKind>((flags_ & FUNCTION_KIND_MASK) >> FUNCTION_KIND_SHIFT);
    }

    /* A function can be classified as either native (C++) or interpreted (JS): */
    bool isInterpreted()            const { return flags() & (INTERPRETED | INTERPRETED_LAZY); }
    bool isNative()                 const { return !isInterpreted(); }

    bool isConstructor()            const { return flags() & CONSTRUCTOR; }

    /* Possible attributes of a native function: */
    bool isAsmJSNative()            const { return kind() == AsmJS; }

    /* Possible attributes of an interpreted function: */
    bool isFunctionPrototype()      const { return flags() & IS_FUN_PROTO; }
    bool isExprBody()               const { return flags() & EXPR_BODY; }
    bool hasGuessedAtom()           const { return flags() & HAS_GUESSED_ATOM; }
    bool isLambda()                 const { return flags() & LAMBDA; }
    bool isSelfHostedBuiltin()      const { return flags() & SELF_HOSTED; }
    bool hasRest()                  const { return flags() & HAS_REST; }
    bool isInterpretedLazy()        const { return flags() & INTERPRETED_LAZY; }
    bool hasScript()                const { return flags() & INTERPRETED; }
    bool isBeingParsed()            const { return flags() & BEING_PARSED; }

    // Arrow functions store their lexical |this| in the first extended slot.
    bool isArrow()                  const { return kind() == Arrow; }
    // Every class-constructor is also a method.
    bool isMethod()                 const { return kind() == Method || kind() == ClassConstructor; }
    bool isClassConstructor()       const { return kind() == ClassConstructor; }

    bool isGetter()                 const { return kind() == Getter; }
    bool isSetter()                 const { return kind() == Setter; }

    bool allowSuperProperty() const {
        return isMethod() || isGetter() || isSetter();
    }

    bool hasResolvedLength()        const { return flags() & RESOLVED_LENGTH; }
    bool hasResolvedName()          const { return flags() & RESOLVED_NAME; }

    bool hasJITCode() const {
        if (!hasScript())
            return false;

        return nonLazyScript()->hasBaselineScript() || nonLazyScript()->hasIonScript();
    }

    /* Compound attributes: */
    bool isBuiltin() const {
        return (isNative() && !isAsmJSNative()) || isSelfHostedBuiltin();
    }

    bool isNamedLambda() const {
        return isLambda() && displayAtom() && !hasGuessedAtom();
    }

    bool isBuiltinFunctionConstructor();

    /* Returns the strictness of this function, which must be interpreted. */
    bool strict() const {
        MOZ_ASSERT(isInterpreted());
        return isInterpretedLazy() ? lazyScript()->strict() : nonLazyScript()->strict();
    }

    void setFlags(uint16_t flags) {
        this->flags_ = flags;
    }
    void setKind(FunctionKind kind) {
        this->flags_ &= ~FUNCTION_KIND_MASK;
        this->flags_ |= static_cast<uint16_t>(kind) << FUNCTION_KIND_SHIFT;
    }

    // Make the function constructible.
    void setIsConstructor() {
        MOZ_ASSERT(!isConstructor());
        MOZ_ASSERT(isSelfHostedBuiltin());
        flags_ |= CONSTRUCTOR;
    }

    // Can be called multiple times by the parser.
    void setArgCount(uint16_t nargs) {
        this->nargs_ = nargs;
    }

    // Can be called multiple times by the parser.
    void setHasRest() {
        flags_ |= HAS_REST;
    }

    void setIsSelfHostedBuiltin() {
        MOZ_ASSERT(!isSelfHostedBuiltin());
        flags_ |= SELF_HOSTED;
        // Self-hosted functions should not be constructable.
        flags_ &= ~CONSTRUCTOR;
    }

    void setIsFunctionPrototype() {
        MOZ_ASSERT(!isFunctionPrototype());
        flags_ |= IS_FUN_PROTO;
    }

    // Can be called multiple times by the parser.
    void setIsExprBody() {
        flags_ |= EXPR_BODY;
    }

    void setArrow() {
        setKind(Arrow);
    }

    void setResolvedLength() {
        flags_ |= RESOLVED_LENGTH;
    }

    void setResolvedName() {
        flags_ |= RESOLVED_NAME;
    }

    JSAtom* atom() const { return hasGuessedAtom() ? nullptr : atom_.get(); }

    js::PropertyName* name() const {
        return hasGuessedAtom() || !atom_ ? nullptr : atom_->asPropertyName();
    }

    void initAtom(JSAtom* atom) { atom_.init(atom); }

    JSAtom* displayAtom() const {
        return atom_;
    }

    void setGuessedAtom(JSAtom* atom) {
        MOZ_ASSERT(!atom_);
        MOZ_ASSERT(atom);
        MOZ_ASSERT(!hasGuessedAtom());
        atom_ = atom;
        flags_ |= HAS_GUESSED_ATOM;
    }

    /* 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() && !isBeingParsed());
        return u.i.env_;
    }

    void setEnvironment(JSObject* obj) {
        MOZ_ASSERT(isInterpreted() && !isBeingParsed());
        *reinterpret_cast<js::HeapPtrObject*>(&u.i.env_) = obj;
    }

    void initEnvironment(JSObject* obj) {
        MOZ_ASSERT(isInterpreted() && !isBeingParsed());
        reinterpret_cast<js::HeapPtrObject*>(&u.i.env_)->init(obj);
    }

    void unsetEnvironment() {
        setEnvironment(nullptr);
    }

  private:
    void setFunctionBox(js::frontend::FunctionBox* funbox) {
        MOZ_ASSERT(isInterpreted());
        MOZ_ASSERT_IF(!isBeingParsed(), !environment());
        flags_ |= BEING_PARSED;
        u.i.funbox_ = funbox;
    }

    void unsetFunctionBox() {
        MOZ_ASSERT(isBeingParsed());
        flags_ &= ~BEING_PARSED;
        u.i.funbox_ = nullptr;
    }

  public:
    static inline size_t offsetOfNargs() { return offsetof(JSFunction, nargs_); }
    static inline size_t offsetOfFlags() { return offsetof(JSFunction, flags_); }
    static inline size_t offsetOfEnvironment() { return offsetof(JSFunction, u.i.env_); }
    static inline size_t offsetOfAtom() { return offsetof(JSFunction, atom_); }

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

    // Function Scripts
    //
    // Interpreted functions may either have an explicit JSScript (hasScript())
    // or be lazy with sufficient information to construct the JSScript if
    // necessary (isInterpretedLazy()).
    //
    // A lazy function will have a LazyScript if the function came from parsed
    // source, or nullptr if the function is a clone of a self hosted function.
    //
    // 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 inlined functions which may have a LazyScript but whose JSScript
    //   is known to exist, existingScriptForInlinedFunction() will get the
    //   script and delazify the function if necessary.
    //
    // - For functions known to have a JSScript, nonLazyScript() will get it.

    JSScript* getOrCreateScript(JSContext* cx) {
        MOZ_ASSERT(isInterpreted());
        MOZ_ASSERT(cx);
        if (isInterpretedLazy()) {
            JS::RootedFunction self(cx, this);
            if (!createScriptForLazilyInterpretedFunction(cx, self))
                return nullptr;
            return self->nonLazyScript();
        }
        return nonLazyScript();
    }

    JSScript* existingScriptForInlinedFunction() {
        MOZ_ASSERT(isInterpreted());
        if (isInterpretedLazy()) {
            // Get the script from the canonical function. Ion used the
            // canonical function to inline the script and because it has
            // Baseline code it has not been relazified. Note that we can't
            // use lazyScript->script_ here as it may be null in some cases,
            // see bug 976536.
            js::LazyScript* lazy = lazyScript();
            JSFunction* fun = lazy->functionNonDelazifying();
            MOZ_ASSERT(fun);
            JSScript* script = fun->nonLazyScript();

            if (shadowZone()->needsIncrementalBarrier())
                js::LazyScript::writeBarrierPre(lazy);

            flags_ &= ~INTERPRETED_LAZY;
            flags_ |= INTERPRETED;
            initScript(script);
        }
        return nonLazyScript();
    }

    // The state of a JSFunction whose script errored out during bytecode
    // compilation. Such JSFunctions are only reachable via GC iteration and
    // not from script.
    bool hasUncompiledScript() const {
        MOZ_ASSERT(hasScript());
        return !u.i.s.script_;
    }

    JSScript* nonLazyScript() const {
        MOZ_ASSERT(!hasUncompiledScript());
        return u.i.s.script_;
    }

    bool getLength(JSContext* cx, uint16_t* length) {
        JS::RootedFunction self(cx, this);
        if (self->isInterpretedLazy() && !self->getOrCreateScript(cx))
            return false;

        *length = self->hasScript() ? self->nonLazyScript()->funLength()
                                    : (self->nargs() - self->hasRest());
        return true;
    }

    js::LazyScript* lazyScript() const {
        MOZ_ASSERT(isInterpretedLazy() && u.i.s.lazy_);
        return u.i.s.lazy_;
    }

    js::LazyScript* lazyScriptOrNull() const {
        MOZ_ASSERT(isInterpretedLazy());
        return u.i.s.lazy_;
    }

    js::frontend::FunctionBox* functionBox() const {
        MOZ_ASSERT(isBeingParsed());
        return u.i.funbox_;
    }

    js::GeneratorKind generatorKind() const {
        if (!isInterpreted())
            return js::NotGenerator;
        if (hasScript())
            return nonLazyScript()->generatorKind();
        if (js::LazyScript* lazy = lazyScriptOrNull())
            return lazy->generatorKind();
        MOZ_ASSERT(isSelfHostedBuiltin());
        return js::NotGenerator;
    }

    bool isGenerator() const { return generatorKind() != js::NotGenerator; }

    bool isLegacyGenerator() const { return generatorKind() == js::LegacyGenerator; }

    bool isStarGenerator() const { return generatorKind() == js::StarGenerator; }

    void setScript(JSScript* script_) {
        mutableScript() = script_;
    }

    void initScript(JSScript* script_) {
        mutableScript().init(script_);
    }

    void setUnlazifiedScript(JSScript* script) {
        // Note: createScriptForLazilyInterpretedFunction triggers a barrier on
        // lazy script before it is overwritten here.
        MOZ_ASSERT(isInterpretedLazy());
        if (lazyScriptOrNull() && !lazyScript()->maybeScript())
            lazyScript()->initScript(script);
        flags_ &= ~INTERPRETED_LAZY;
        flags_ |= INTERPRETED;
        initScript(script);
    }

    void initLazyScript(js::LazyScript* lazy) {
        MOZ_ASSERT(isInterpreted());
        flags_ &= ~INTERPRETED;
        flags_ |= INTERPRETED_LAZY;
        u.i.s.lazy_ = lazy;
    }

    JSNative native() const {
        MOZ_ASSERT(isNative());
        return u.n.native;
    }

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

    void initNative(js::Native native, const JSJitInfo* jitinfo) {
        MOZ_ASSERT(native);
        u.n.native = native;
        u.n.jitinfo = jitinfo;
    }

    const JSJitInfo* jitInfo() const {
        MOZ_ASSERT(isNative());
        return u.n.jitinfo;
    }

    void setJitInfo(const JSJitInfo* data) {
        MOZ_ASSERT(isNative());
        u.n.jitinfo = data;
    }

    bool isDerivedClassConstructor() {
        bool derived;
        if (isInterpretedLazy())
            derived = lazyScript()->isDerivedClassConstructor();
        else
            derived = nonLazyScript()->isDerivedClassConstructor();
        MOZ_ASSERT_IF(derived, isClassConstructor());
        return derived;
    }

    static unsigned offsetOfNativeOrScript() {
        static_assert(offsetof(U, n.native) == offsetof(U, i.s.script_),
                      "native and script pointers must be in the same spot "
                      "for offsetOfNativeOrScript() have any sense");
        static_assert(offsetof(U, n.native) == offsetof(U, nativeOrScript),
                      "U::nativeOrScript must be at the same offset as "
                      "native");

        return offsetof(JSFunction, u.nativeOrScript);
    }

    inline void trace(JSTracer* trc);

    /* Bound function accessors. */

    inline bool initBoundFunction(JSContext* cx, js::HandleObject target, js::HandleValue thisArg,
                                  const js::Value* args, unsigned argslen);

    JSObject* getBoundFunctionTarget() const;
    const js::Value& getBoundFunctionThis() const;
    const js::Value& getBoundFunctionArgument(unsigned which) const;
    size_t getBoundFunctionArgumentCount() const;

  private:
    js::HeapPtrScript& mutableScript() {
        MOZ_ASSERT(hasScript());
        return *(js::HeapPtrScript*)&u.i.s.script_;
    }

    inline js::FunctionExtended* toExtended();
    inline const js::FunctionExtended* toExtended() const;

  public:
    inline bool isExtended() const {
        bool extended = !!(flags() & EXTENDED);
        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 initializeExtended();
    inline void initExtendedSlot(size_t which, const js::Value& val);
    inline void setExtendedSlot(size_t which, const js::Value& val);
    inline const js::Value& getExtendedSlot(size_t which) const;

    /* Constructs a new type for the function if necessary. */
    static bool setTypeForScriptedFunction(js::ExclusiveContext* cx, js::HandleFunction fun,
                                           bool singleton = false);

    /* 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;
    }
};

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

extern JSString*
fun_toStringHelper(JSContext* cx, js::HandleObject obj, unsigned indent);

namespace js {

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

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

// Allocate a new function backed by a JSNative.
extern JSFunction*
NewNativeFunction(ExclusiveContext* cx, JSNative native, unsigned nargs, HandleAtom atom,
                  gc::AllocKind allocKind = gc::AllocKind::FUNCTION,
                  NewObjectKind newKind = GenericObject);

// Allocate a new constructor backed by a JSNative.
extern JSFunction*
NewNativeConstructor(ExclusiveContext* cx, JSNative native, unsigned nargs, HandleAtom atom,
                     gc::AllocKind allocKind = gc::AllocKind::FUNCTION,
                     NewObjectKind newKind = GenericObject,
                     JSFunction::Flags flags = JSFunction::NATIVE_CTOR);

// Allocate a new scripted function.  If enclosingDynamicScope is null, the
// global will be used.  In all cases the parent of the resulting object will be
// the global.
extern JSFunction*
NewScriptedFunction(ExclusiveContext* cx, unsigned nargs, JSFunction::Flags flags,
                    HandleAtom atom, gc::AllocKind allocKind = gc::AllocKind::FUNCTION,
                    NewObjectKind newKind = GenericObject,
                    HandleObject enclosingDynamicScope = nullptr);

// If proto is nullptr, Function.prototype is used instead.  If
// enclosingDynamicScope is null, the function will have a null environment()
// (yes, null, not the global).  In all cases, the global will be used as the
// parent.
extern JSFunction*
NewFunctionWithProto(ExclusiveContext* cx, JSNative native, unsigned nargs,
                     JSFunction::Flags flags, HandleObject enclosingDynamicScope, HandleAtom atom,
                     HandleObject proto, gc::AllocKind allocKind = gc::AllocKind::FUNCTION,
                     NewObjectKind newKind = GenericObject);

extern JSAtom*
IdToFunctionName(JSContext* cx, HandleId id);

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

bool
FunctionHasResolveHook(const JSAtomState& atomState, jsid id);

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

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

/*
 * 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:
    static const unsigned NUM_EXTENDED_SLOTS = 2;

    /* Arrow functions store their lexical |this| in the first extended slot. */
    static const unsigned ARROW_THIS_SLOT = 0;
    static const unsigned ARROW_NEWTARGET_SLOT = 1;

    static const unsigned METHOD_HOMEOBJECT_SLOT = 0;

    static inline size_t offsetOfExtendedSlot(unsigned which) {
        MOZ_ASSERT(which < NUM_EXTENDED_SLOTS);
        return offsetof(FunctionExtended, extendedSlots) + which * sizeof(HeapValue);
    }
    static inline size_t offsetOfArrowThisSlot() {
        return offsetOfExtendedSlot(ARROW_THIS_SLOT);
    }
    static inline size_t offsetOfArrowNewTargetSlot() {
        return offsetOfExtendedSlot(ARROW_NEWTARGET_SLOT);
    }

  private:
    friend class JSFunction;

    /* Reserved slots available for storage by particular native functions. */
    HeapValue extendedSlots[NUM_EXTENDED_SLOTS];
};

extern bool
CanReuseScriptForClone(JSCompartment* compartment, HandleFunction fun, HandleObject newParent);

extern JSFunction*
CloneFunctionReuseScript(JSContext* cx, HandleFunction fun, HandleObject parent,
                         gc::AllocKind kind = gc::AllocKind::FUNCTION,
                         NewObjectKind newKindArg = GenericObject,
                         HandleObject proto = nullptr);

// Functions whose scripts are cloned are always given singleton types.
extern JSFunction*
CloneFunctionAndScript(JSContext* cx, HandleFunction fun, HandleObject parent,
                       HandleObject newStaticScope,
                       gc::AllocKind kind = gc::AllocKind::FUNCTION,
                       HandleObject proto = nullptr);

extern bool
FindBody(JSContext* cx, HandleFunction fun, HandleLinearString src, size_t* bodyStart,
         size_t* bodyEnd);

} // namespace js

inline js::FunctionExtended*
JSFunction::toExtended()
{
    MOZ_ASSERT(isExtended());
    return static_cast<js::FunctionExtended*>(this);
}

inline const js::FunctionExtended*
JSFunction::toExtended() const
{
    MOZ_ASSERT(isExtended());
    return static_cast<const js::FunctionExtended*>(this);
}

inline void
JSFunction::initializeExtended()
{
    MOZ_ASSERT(isExtended());

    MOZ_ASSERT(mozilla::ArrayLength(toExtended()->extendedSlots) == 2);
    toExtended()->extendedSlots[0].init(js::UndefinedValue());
    toExtended()->extendedSlots[1].init(js::UndefinedValue());
}

inline void
JSFunction::initExtendedSlot(size_t which, const js::Value& val)
{
    MOZ_ASSERT(which < mozilla::ArrayLength(toExtended()->extendedSlots));
    toExtended()->extendedSlots[which].init(val);
}

inline void
JSFunction::setExtendedSlot(size_t which, const js::Value& val)
{
    MOZ_ASSERT(which < mozilla::ArrayLength(toExtended()->extendedSlots));
    toExtended()->extendedSlots[which] = val;
}

inline const js::Value&
JSFunction::getExtendedSlot(size_t which) const
{
    MOZ_ASSERT(which < mozilla::ArrayLength(toExtended()->extendedSlots));
    return toExtended()->extendedSlots[which];
}

namespace js {

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

template<XDRMode mode>
bool
XDRInterpretedFunction(XDRState<mode>* xdr, HandleObject enclosingScope,
                       HandleScript enclosingScript, MutableHandleFunction objp);

/*
 * 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, CallReceiver call, const Class* clasp);

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

bool
CallOrConstructBoundFunction(JSContext*, unsigned, js::Value*);

extern const JSFunctionSpec function_methods[];

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(Value calleev);

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

#endif /* jsfun_h */