diff options
Diffstat (limited to 'js/src/jsfun.h')
-rw-r--r-- | js/src/jsfun.h | 863 |
1 files changed, 863 insertions, 0 deletions
diff --git a/js/src/jsfun.h b/js/src/jsfun.h new file mode 100644 index 000000000..ef4a603d0 --- /dev/null +++ b/js/src/jsfun.h @@ -0,0 +1,863 @@ +/* -*- 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 { + +class FunctionExtended; + +typedef JSNative Native; +} // namespace js + +struct JSAtomState; + +static const uint32_t JSSLOT_BOUND_FUNCTION_TARGET = 2; +static const uint32_t JSSLOT_BOUND_FUNCTION_THIS = 3; +static const uint32_t JSSLOT_BOUND_FUNCTION_ARGS = 4; + +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 */ + BOUND_FUN = 0x0008, /* function was created with Function.prototype.bind. */ + 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). */ + + 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 = 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_"); + + 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; + JSObject* env_; /* environment for new activations */ + } i; + void* nativeOrScript; + } u; + js::GCPtrAtom atom_; /* name for diagnostics and decompiling */ + + public: + /* Call objects must be created for each invocation of this function. */ + bool needsCallObject() const { + MOZ_ASSERT(!isInterpretedLazy()); + + if (isNative()) + return false; + + // Note: this should be kept in sync with + // FunctionBox::needsCallObjectRegardlessOfBindings(). + MOZ_ASSERT_IF(nonLazyScript()->funHasExtensibleScope() || + nonLazyScript()->needsHomeObject() || + nonLazyScript()->isDerivedClassConstructor() || + isGenerator(), + nonLazyScript()->bodyScope()->hasEnvironment()); + + return nonLazyScript()->bodyScope()->hasEnvironment(); + } + + bool needsExtraBodyVarEnvironment() const; + bool needsNamedLambdaEnvironment() const; + + bool needsFunctionEnvironmentObjects() const { + return needsCallObject() || needsNamedLambdaEnvironment(); + } + + bool needsSomeEnvironmentObject() const { + return needsFunctionEnvironmentObjects() || needsExtraBodyVarEnvironment(); + } + + 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 isExprBody() const { return flags() & EXPR_BODY; } + bool hasGuessedAtom() const { return flags() & HAS_GUESSED_ATOM; } + bool isLambda() const { return flags() & LAMBDA; } + bool isBoundFunction() const { return flags() & BOUND_FUN; } + bool hasRest() const { return flags() & HAS_REST; } + bool isInterpretedLazy() const { return flags() & INTERPRETED_LAZY; } + bool hasScript() const { return flags() & INTERPRETED; } + + bool infallibleIsDefaultClassConstructor(JSContext* cx) const; + + // Arrow functions store their lexical new.target 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 isSelfHostedOrIntrinsic() const { return flags() & SELF_HOSTED; } + bool isSelfHostedBuiltin() const { return isSelfHostedOrIntrinsic() && !isNative(); } + bool isIntrinsic() const { return isSelfHostedOrIntrinsic() && isNative(); } + + 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 hasLexicalThis() const { + return isArrow() || nonLazyScript()->isGeneratorExp(); + } + + 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; + } + + void setIsClassConstructor() { + MOZ_ASSERT(!isClassConstructor()); + MOZ_ASSERT(isConstructor()); + + setKind(ClassConstructor); + } + + // 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 setIsBoundFunction() { + MOZ_ASSERT(!isBoundFunction()); + flags_ |= BOUND_FUN; + } + + void setIsSelfHostedBuiltin() { + MOZ_ASSERT(isInterpreted()); + MOZ_ASSERT(!isSelfHostedBuiltin()); + flags_ |= SELF_HOSTED; + // Self-hosted functions should not be constructable. + flags_ &= ~CONSTRUCTOR; + } + void setIsIntrinsic() { + MOZ_ASSERT(isNative()); + MOZ_ASSERT(!isIntrinsic()); + flags_ |= SELF_HOSTED; + } + + // 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; + } + + void setAsyncKind(js::FunctionAsyncKind asyncKind) { + if (isInterpretedLazy()) + lazyScript()->setAsyncKind(asyncKind); + else + nonLazyScript()->setAsyncKind(asyncKind); + } + + bool getUnresolvedLength(JSContext* cx, js::MutableHandleValue v); + + JSAtom* getUnresolvedName(JSContext* cx); + + JSAtom* name() const { return hasGuessedAtom() ? nullptr : atom_.get(); } + + // Because display names (see Debugger.Object.displayName) are already stored + // on functions and will always contain a valid es6 function name, as described + // in "ECMA-262 (2016-02-27) 9.2.11 SetFunctionName," we have opted to save + // memory by parsing the existing display name when a function's name property + // is accessed. + JSAtom* functionName(JSContext* cx) const; + + void initAtom(JSAtom* atom) { atom_.init(atom); } + + void setAtom(JSAtom* atom) { atom_ = 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; + } + void clearGuessedAtom() { + MOZ_ASSERT(hasGuessedAtom()); + MOZ_ASSERT(atom_); + atom_ = nullptr; + 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()); + return u.i.env_; + } + + void setEnvironment(JSObject* obj) { + MOZ_ASSERT(isInterpreted()); + *reinterpret_cast<js::GCPtrObject*>(&u.i.env_) = obj; + } + + void initEnvironment(JSObject* obj) { + MOZ_ASSERT(isInterpreted()); + reinterpret_cast<js::GCPtrObject*>(&u.i.env_)->init(obj); + } + + void unsetEnvironment() { + setEnvironment(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, existingScript() will get the script and delazify + // the function if necessary. If the function should not be delazified, + // use existingScriptNonDelazifying(). + // + // - 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* existingScriptNonDelazifying() const { + 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); + return fun->nonLazyScript(); + } + return nonLazyScript(); + } + + JSScript* existingScript() { + MOZ_ASSERT(isInterpreted()); + if (isInterpretedLazy()) { + if (shadowZone()->needsIncrementalBarrier()) + js::LazyScript::writeBarrierPre(lazyScript()); + JSScript* script = existingScriptNonDelazifying(); + 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::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::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; } + + js::FunctionAsyncKind asyncKind() const { + return isInterpretedLazy() ? lazyScript()->asyncKind() : nonLazyScript()->asyncKind(); + } + + bool isAsync() const { + if (isInterpretedLazy()) + return lazyScript()->asyncKind() == js::AsyncFunction; + if (hasScript()) + return nonLazyScript()->asyncKind() == js::AsyncFunction; + return false; + } + + void setScript(JSScript* script_) { + mutableScript() = script_; + } + + void initScript(JSScript* script_) { + mutableScript().init(script_); + } + + void setUnlazifiedScript(JSScript* script) { + MOZ_ASSERT(isInterpretedLazy()); + if (lazyScriptOrNull()) { + // Trigger a pre barrier on the lazy script being overwritten. + js::LazyScript::writeBarrierPre(lazyScriptOrNull()); + if (!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(); + + 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. */ + + JSObject* getBoundFunctionTarget() const; + const js::Value& getBoundFunctionThis() const; + const js::Value& getBoundFunctionArgument(JSContext* cx, unsigned which) const; + size_t getBoundFunctionArgumentCount() const; + + private: + js::GCPtrScript& mutableScript() { + MOZ_ASSERT(hasScript()); + return *(js::GCPtrScript*)&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); + +extern bool +AsyncFunctionConstructor(JSContext* cx, unsigned argc, Value* vp); + +// Allocate a new function backed by a JSNative. Note that by default this +// creates a singleton object. +extern JSFunction* +NewNativeFunction(ExclusiveContext* cx, JSNative native, unsigned nargs, HandleAtom atom, + gc::AllocKind allocKind = gc::AllocKind::FUNCTION, + NewObjectKind newKind = SingletonObject); + +// Allocate a new constructor backed by a JSNative. Note that by default this +// creates a singleton object. +extern JSFunction* +NewNativeConstructor(ExclusiveContext* cx, JSNative native, unsigned nargs, HandleAtom atom, + gc::AllocKind allocKind = gc::AllocKind::FUNCTION, + NewObjectKind newKind = SingletonObject, + JSFunction::Flags flags = JSFunction::NATIVE_CTOR); + +// Allocate a new scripted function. If enclosingEnv 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, HandleObject proto = nullptr, + gc::AllocKind allocKind = gc::AllocKind::FUNCTION, + NewObjectKind newKind = GenericObject, + HandleObject enclosingEnv = nullptr); + +// By default, if proto is nullptr, Function.prototype is used instead.i +// If protoHandling is NewFunctionExactProto, and proto is nullptr, the created +// function will use nullptr as its [[Prototype]] instead. If +// enclosingEnv 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. + +enum NewFunctionProtoHandling { + NewFunctionClassProto, + NewFunctionGivenProto +}; +extern JSFunction* +NewFunctionWithProto(ExclusiveContext* cx, JSNative native, unsigned nargs, + JSFunction::Flags flags, HandleObject enclosingEnv, HandleAtom atom, + HandleObject proto, gc::AllocKind allocKind = gc::AllocKind::FUNCTION, + NewObjectKind newKind = GenericObject, + NewFunctionProtoHandling protoHandling = NewFunctionClassProto); + +extern JSAtom* +IdToFunctionName(JSContext* cx, HandleId id, const char* prefix = nullptr); + +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); + +struct WellKnownSymbols; + +extern bool +FunctionHasDefaultHasInstance(JSFunction* fun, const WellKnownSymbols& symbols); + +extern bool +fun_symbolHasInstance(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 new.target in the first extended slot. */ + static const unsigned ARROW_NEWTARGET_SLOT = 0; + + static const unsigned METHOD_HOMEOBJECT_SLOT = 0; + + /* + * Exported asm.js/wasm functions store their WasmInstanceObject in the + * first slot. + */ + static const unsigned WASM_INSTANCE_SLOT = 0; + + /* + * wasm/asm.js exported functions store the function index of the exported + * function in the original module. + */ + static const unsigned WASM_FUNC_INDEX_SLOT = 1; + + /* + * asm.js module functions store their WasmModuleObject in the first slot. + */ + static const unsigned ASMJS_MODULE_SLOT = 0; + + + static inline size_t offsetOfExtendedSlot(unsigned which) { + MOZ_ASSERT(which < NUM_EXTENDED_SLOTS); + return offsetof(FunctionExtended, extendedSlots) + which * sizeof(GCPtrValue); + } + static inline size_t offsetOfArrowNewTargetSlot() { + return offsetOfExtendedSlot(ARROW_NEWTARGET_SLOT); + } + + private: + friend class JSFunction; + + /* Reserved slots available for storage by particular native functions. */ + GCPtrValue 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, + HandleScope newScope, + 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)); + MOZ_ASSERT_IF(js::IsMarkedBlack(this) && val.isMarkable(), + !JS::GCThingIsMarkedGray(JS::GCCellPtr(val))); + 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, HandleScope 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, const CallArgs& args, const Class* 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 const JSFunctionSpec function_methods[]; +extern const JSFunctionSpec function_selfhosted_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(const Value& calleev); + +} // namespace detail +} // namespace JS +#endif + +#endif /* jsfun_h */ |