diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /js/src/vm/JSFunction.h | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/vm/JSFunction.h')
-rw-r--r-- | js/src/vm/JSFunction.h | 928 |
1 files changed, 928 insertions, 0 deletions
diff --git a/js/src/vm/JSFunction.h b/js/src/vm/JSFunction.h new file mode 100644 index 0000000000..fe50a68ab9 --- /dev/null +++ b/js/src/vm/JSFunction.h @@ -0,0 +1,928 @@ +/* -*- 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 <iterator> + +#include "jstypes.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/JSObject.h" +#include "vm/JSScript.h" + +class JSJitInfo; + +namespace js { + +class FunctionExtended; +struct SelfHostedLazyScript; + +using Native = JSNative; + +static constexpr uint32_t BoundFunctionEnvTargetSlot = 2; +static constexpr uint32_t BoundFunctionEnvThisSlot = 3; +static constexpr uint32_t BoundFunctionEnvArgsSlot = 4; + +static const char FunctionConstructorMedialSigils[] = ") {\n"; +static const char FunctionConstructorFinalBrace[] = "\n}"; + +} // namespace js + +class JSFunction : public js::NativeObject { + public: + static const JSClass class_; + + private: + /* + * number of formal arguments + * (including defaults and the rest parameter unlike f.length) + */ + uint16_t nargs_; + + /* + * Bitfield composed of the above Flags enum, as well as the kind. + * + * If any of these flags needs to be accessed in off-thread JIT + * compilation, copy it to js::jit::WrappedFunction. + */ + using FunctionFlags = js::FunctionFlags; + FunctionFlags flags_; + + union U { + class { + friend class JSFunction; + js::Native func_; /* native method pointer or null */ + // Warning: this |extra| union MUST NOT store a value that could be a + // valid BaseScript* pointer! JIT guards depend on this. + union { + // Information about this function to be used by the JIT, only + // used if isBuiltinNative(); use the accessor! + const JSJitInfo* jitInfo_; + // For wasm/asm.js without a jit entry. Always has the low bit set to + // ensure it's never identical to a BaseScript* pointer. See warning + // above. + uintptr_t taggedWasmFuncIndex_; + // for wasm that has been given a jit entry + void** wasmJitEntry_; + } extra; + } native; + struct { + JSObject* env_; /* environment for new activations */ + union { + js::BaseScript* script_; + js::SelfHostedLazyScript* selfHostedLazy_; + } s; + } scripted; + } u; + + // The `atom_` field can have different meanings depending on the function + // type and flags. It is used for diagnostics, decompiling, and + // + // 1. If the function is not a bound function: + // 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. + // + // 2. If the function is a bound function: + // a. To store the initial value of the "name" property. + // b. If HAS_BOUND_FUNCTION_NAME_PREFIX is not set, `atom_` doesn't + // contain the "bound " prefix which is prepended to the "name" + // property of bound functions per ECMAScript. + // c. Bound functions can never have an inferred or guessed name. + // d. `atom_` is never null for bound functions. + // + // 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().) + js::GCPtrAtom atom_; + + public: + static inline JS::Result<JSFunction*, JS::OOM> create( + JSContext* cx, js::gc::AllocKind kind, js::gc::InitialHeap heap, + js::HandleShape shape, js::HandleObjectGroup group); + + /* 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(); + } + + static constexpr size_t NArgsBits = sizeof(nargs_) * CHAR_BIT; + size_t nargs() const { return nargs_; } + + FunctionFlags flags() { return flags_; } + + 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 isNative() const { return flags_.isNative(); } + + 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 isBoundFunction() const { return flags_.isBoundFunction(); } + bool hasInferredName() const { return flags_.hasInferredName(); } + bool hasGuessedAtom() const { return flags_.hasGuessedAtom(); } + bool hasBoundFunctionNamePrefix() const { + return flags_.hasBoundFunctionNamePrefix(); + } + + bool isLambda() const { return flags_.isLambda(); } + + // These methods determine which of the u.scripted.s union arms are active. + // 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(); + } + + // 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 isBoundFunction() || isDerivedClassConstructor(); + } + + /* Returns the strictness of this function, which must be interpreted. */ + bool strict() const { return baseScript()->strict(); } + + void setFlags(uint16_t flags) { flags_ = FunctionFlags(flags); } + void setFlags(FunctionFlags flags) { flags_ = flags; } + + // Make the function constructible. + void setIsConstructor() { flags_.setIsConstructor(); } + void setIsClassConstructor() { flags_.setIsClassConstructor(); } + + // Can be called multiple times by the parser. + void setArgCount(uint16_t nargs) { this->nargs_ = nargs; } + + void setIsBoundFunction() { flags_.setIsBoundFunction(); } + void setIsSelfHostedBuiltin() { flags_.setIsSelfHostedBuiltin(); } + void setIsIntrinsic() { flags_.setIsIntrinsic(); } + + void setResolvedLength() { flags_.setResolvedLength(); } + void setResolvedName() { flags_.setResolvedName(); } + + static bool getUnresolvedLength(JSContext* cx, js::HandleFunction fun, + js::MutableHandleValue v); + + JSAtom* infallibleGetUnresolvedName(JSContext* cx); + + static bool getUnresolvedName(JSContext* cx, js::HandleFunction fun, + js::MutableHandleValue v); + + static JSLinearString* getBoundFunctionName(JSContext* cx, + js::HandleFunction fun); + + JSAtom* explicitName() const { + return (hasInferredName() || hasGuessedAtom()) ? nullptr : atom_.get(); + } + + JSAtom* explicitOrInferredName() const { + return hasGuessedAtom() ? nullptr : atom_.get(); + } + + void initAtom(JSAtom* atom) { + MOZ_ASSERT_IF(atom, js::AtomIsMarked(zone(), atom)); + atom_.init(atom); + } + + void setAtom(JSAtom* atom) { + MOZ_ASSERT_IF(atom, js::AtomIsMarked(zone(), atom)); + atom_ = atom; + } + + JSAtom* displayAtom() const { return atom_; } + + void setInferredName(JSAtom* atom) { + MOZ_ASSERT(!atom_); + MOZ_ASSERT(atom); + MOZ_ASSERT(!hasGuessedAtom()); + setAtom(atom); + flags_.setInferredName(); + } + JSAtom* inferredName() const { + MOZ_ASSERT(hasInferredName()); + MOZ_ASSERT(atom_); + return atom_; + } + + void setGuessedAtom(JSAtom* atom) { + MOZ_ASSERT(!atom_); + MOZ_ASSERT(atom); + MOZ_ASSERT(!hasInferredName()); + MOZ_ASSERT(!hasGuessedAtom()); + MOZ_ASSERT(!isBoundFunction()); + setAtom(atom); + flags_.setGuessedAtom(); + } + + void setPrefixedBoundFunctionName(JSAtom* atom) { + MOZ_ASSERT(!hasBoundFunctionNamePrefix()); + MOZ_ASSERT(atom); + flags_.setPrefixedBoundFunctionName(); + setAtom(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.scripted.env_; + } + + void initEnvironment(JSObject* obj) { + MOZ_ASSERT(isInterpreted()); + reinterpret_cast<js::GCPtrObject*>(&u.scripted.env_)->init(obj); + } + + public: + static constexpr size_t offsetOfNargs() { + return offsetof(JSFunction, nargs_); + } + static constexpr size_t offsetOfFlags() { + return offsetof(JSFunction, flags_); + } + static size_t offsetOfEnvironment() { + return offsetof(JSFunction, u.scripted.env_); + } + static size_t offsetOfAtom() { return offsetof(JSFunction, atom_); } + + 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()); + JS::Rooted<js::BaseScript*> script(cx, fun->baseScript()); + + if (!script->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; + } + + // 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() && !u.scripted.s.script_; } + + JSScript* nonLazyScript() const { + MOZ_ASSERT(hasBytecode()); + MOZ_ASSERT(u.scripted.s.script_); + return static_cast<JSScript*>(u.scripted.s.script_); + } + + js::SelfHostedLazyScript* selfHostedLazyScript() const { + MOZ_ASSERT(hasSelfHostedLazyScript()); + MOZ_ASSERT(u.scripted.s.selfHostedLazy_); + return u.scripted.s.selfHostedLazy_; + } + + // Access fields defined on both lazy and non-lazy scripts. + js::BaseScript* baseScript() const { + MOZ_ASSERT(hasBaseScript()); + MOZ_ASSERT(u.scripted.s.script_); + return u.scripted.s.script_; + } + + static 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()); + u.scripted.s.script_ = script; + } + + void initSelfHostedLazyScript(js::SelfHostedLazyScript* lazy) { + MOZ_ASSERT(isSelfHostedBuiltin()); + MOZ_ASSERT(isInterpreted()); + flags_.clearBaseScript(); + flags_.setSelfHostedLazy(); + u.scripted.s.selfHostedLazy_ = lazy; + MOZ_ASSERT(hasSelfHostedLazyScript()); + } + + void clearSelfHostedLazyScript() { + // Note: The selfHostedLazy_ field is not a GC-thing pointer so we don't + // need to trigger barriers. + flags_.clearSelfHostedLazy(); + flags_.setBaseScript(); + u.scripted.s.script_ = nullptr; + MOZ_ASSERT(isIncomplete()); + } + + JSNative native() const { + MOZ_ASSERT(isNative()); + return u.native.func_; + } + JSNative nativeUnchecked() const { + // Called by Ion off-main thread. + return u.native.func_; + } + + JSNative maybeNative() const { return isInterpreted() ? nullptr : native(); } + + void initNative(js::Native native, const JSJitInfo* jitInfo) { + MOZ_ASSERT(isNative()); + MOZ_ASSERT_IF(jitInfo, isBuiltinNative()); + MOZ_ASSERT(native); + u.native.func_ = native; + u.native.extra.jitInfo_ = jitInfo; + } + bool hasJitInfo() const { + return isBuiltinNative() && u.native.extra.jitInfo_; + } + const JSJitInfo* jitInfo() const { + MOZ_ASSERT(hasJitInfo()); + return u.native.extra.jitInfo_; + } + const JSJitInfo* jitInfoUnchecked() const { + // Called by Ion off-main thread. + return u.native.extra.jitInfo_; + } + void setJitInfo(const JSJitInfo* data) { + MOZ_ASSERT(isBuiltinNative()); + u.native.extra.jitInfo_ = 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(!u.native.extra.taggedWasmFuncIndex_); + // See wasmFuncIndex_ comment for why we set the low bit. + u.native.extra.taggedWasmFuncIndex_ = (uintptr_t(funcIndex) << 1) | 1; + } + uint32_t wasmFuncIndex() const { + MOZ_ASSERT(isWasm() || isAsmJSNative()); + MOZ_ASSERT(!isWasmWithJitEntry()); + MOZ_ASSERT(u.native.extra.taggedWasmFuncIndex_ & 1); + return u.native.extra.taggedWasmFuncIndex_ >> 1; + } + void setWasmJitEntry(void** entry) { + MOZ_ASSERT(*entry); + MOZ_ASSERT(isWasm()); + MOZ_ASSERT(!isWasmWithJitEntry()); + flags_.setWasmJitEntry(); + u.native.extra.wasmJitEntry_ = entry; + MOZ_ASSERT(isWasmWithJitEntry()); + } + void** wasmJitEntry() const { + MOZ_ASSERT(isWasmWithJitEntry()); + MOZ_ASSERT(u.native.extra.wasmJitEntry_); + return u.native.extra.wasmJitEntry_; + } + + bool isDerivedClassConstructor() const; + bool isFieldInitializer() const; + + static unsigned offsetOfNative() { + return offsetof(JSFunction, u.native.func_); + } + static unsigned offsetOfScript() { + static_assert(offsetof(U, scripted.s.script_) == + offsetof(U, native.extra.wasmJitEntry_), + "scripted.s.script_ must be at the same offset as " + "native.extra.wasmJitEntry_"); + return offsetof(JSFunction, u.scripted.s.script_); + } + static unsigned offsetOfNativeOrEnv() { + static_assert( + offsetof(U, native.func_) == offsetof(U, scripted.env_), + "U.native.func_ must be at the same offset as U.scripted.env_"); + return offsetOfNative(); + } + static unsigned offsetOfBaseScript() { + return offsetof(JSFunction, u.scripted.s.script_); + } + + static unsigned offsetOfJitInfo() { + return offsetof(JSFunction, u.native.extra.jitInfo_); + } + + inline void trace(JSTracer* trc); + + /* Bound function accessors. */ + + JSObject* getBoundFunctionTarget() const; + const js::Value& getBoundFunctionThis() const; + const js::Value& getBoundFunctionArgument(unsigned which) const; + size_t getBoundFunctionArgumentCount() const; + + /* + * Used to mark bound functions as such and make them constructible if the + * target is. Also assigns the prototype and sets the name and correct length. + */ + static bool finishBoundFunctionInit(JSContext* cx, js::HandleFunction bound, + js::HandleObject targetObj, + int32_t argCount); + + private: + inline js::FunctionExtended* toExtended(); + inline const js::FunctionExtended* toExtended() const; + + 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 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; + + /* + * Same as `toExtended` and `getExtendedSlot`, but `this` is guaranteed to be + * an extended function. + * + * This function is supposed to be used off-thread, especially the JIT + * compilation thread, that cannot access JSFunction.flags_, because of + * a race condition. + * + * See Also: WrappedFunction.isExtended_ + */ + inline js::FunctionExtended* toExtendedOffMainThread(); + inline const js::FunctionExtended* toExtendedOffMainThread() const; + inline const js::Value& getExtendedSlotOffMainThread(size_t which) 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; + } +}; + +static_assert(sizeof(JSFunction) == sizeof(JS::shadow::Function), + "shadow interface must match actual interface"); + +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, HandleAtom 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, HandleAtom 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, HandleAtom 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); +} + +// Allocate a new scripted function. If enclosingEnv is null, the +// global lexical environment will be used. In all cases the terminating +// environment of the resulting object will be the global. +extern JSFunction* NewScriptedFunction( + JSContext* cx, unsigned nargs, FunctionFlags flags, HandleAtom atom, + HandleObject proto = nullptr, + gc::AllocKind allocKind = gc::AllocKind::FUNCTION, + NewObjectKind newKind = GenericObject, HandleObject enclosingEnv = nullptr); + +// 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: + 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; + + // Stores the length for bound functions, so the .length property doesn't need + // to be resolved eagerly. + static const unsigned BOUND_FUNCTION_LENGTH_SLOT = 1; + + // 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 wasm::TlsData pointer of their + // instance. + static const unsigned WASM_TLSDATA_SLOT = 1; + + // asm.js module functions store their WasmModuleObject in the first slot. + static const unsigned ASMJS_MODULE_SLOT = 0; + + // Async module callback handlers store their ModuleObject in the first slot. + static const unsigned 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); + } + static inline size_t offsetOfMethodHomeObjectSlot() { + return offsetOfExtendedSlot(METHOD_HOMEOBJECT_SLOT); + } + static inline size_t offsetOfBoundFunctionLengthSlot() { + return offsetOfExtendedSlot(BOUND_FUNCTION_LENGTH_SLOT); + } + + private: + friend class JSFunction; + + /* Reserved slots available for storage by particular native functions. */ + GCPtrValue extendedSlots[NUM_EXTENDED_SLOTS]; +}; + +extern bool CanReuseScriptForClone(JS::Realm* realm, HandleFunction fun, + HandleObject newEnclosingEnv); + +extern JSFunction* CloneFunctionReuseScript(JSContext* cx, HandleFunction fun, + HandleObject enclosingEnv, + gc::AllocKind kind, + HandleObject proto); + +extern JSFunction* CloneFunctionAndScript( + JSContext* cx, HandleFunction fun, HandleObject enclosingEnv, + HandleScope newScope, Handle<ScriptSourceObject*> sourceObject, + gc::AllocKind kind, HandleObject proto = nullptr); + +extern JSFunction* CloneAsmJSModuleFunction(JSContext* cx, HandleFunction fun); + +extern JSFunction* CloneSelfHostingIntrinsic(JSContext* cx, HandleFunction fun); + +} // 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 js::FunctionExtended* JSFunction::toExtendedOffMainThread() { + return static_cast<js::FunctionExtended*>(this); +} + +inline const js::FunctionExtended* JSFunction::toExtendedOffMainThread() const { + return static_cast<const js::FunctionExtended*>(this); +} + +inline void JSFunction::initializeExtended() { + MOZ_ASSERT(isExtended()); + + MOZ_ASSERT(std::size(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 < std::size(toExtended()->extendedSlots)); + MOZ_ASSERT(js::IsObjectValueInCompartment(val, compartment())); + toExtended()->extendedSlots[which].init(val); +} + +inline void JSFunction::setExtendedSlot(size_t which, const js::Value& val) { + MOZ_ASSERT(which < std::size(toExtended()->extendedSlots)); + MOZ_ASSERT(js::IsObjectValueInCompartment(val, compartment())); + toExtended()->extendedSlots[which] = val; +} + +inline const js::Value& JSFunction::getExtendedSlot(size_t which) const { + MOZ_ASSERT(which < std::size(toExtended()->extendedSlots)); + return toExtended()->extendedSlots[which]; +} + +inline const js::Value& JSFunction::getExtendedSlotOffMainThread( + size_t which) const { + MOZ_ASSERT(which < std::size(toExtendedOffMainThread()->extendedSlots)); + return toExtendedOffMainThread()->extendedSlots[which]; +} + +namespace js { + +JSString* FunctionToString(JSContext* cx, HandleFunction fun, bool isToSource); + +template <XDRMode mode> +XDRResult XDRInterpretedFunction(XDRState<mode>* xdr, + HandleScope enclosingScope, + HandleScriptSourceObject sourceObject, + 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 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 */ |