diff options
Diffstat (limited to 'js/src/vm/FunctionFlags.h')
-rw-r--r-- | js/src/vm/FunctionFlags.h | 445 |
1 files changed, 445 insertions, 0 deletions
diff --git a/js/src/vm/FunctionFlags.h b/js/src/vm/FunctionFlags.h new file mode 100644 index 0000000000..d927056230 --- /dev/null +++ b/js/src/vm/FunctionFlags.h @@ -0,0 +1,445 @@ +/* -*- 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_FunctionFlags_h +#define vm_FunctionFlags_h + +#include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_ASSERT_IF +#include "mozilla/Attributes.h" // MOZ_IMPLICIT + +#include <stdint.h> // uint8_t, uint16_t + +#include "jstypes.h" // JS_PUBLIC_API + +class JS_PUBLIC_API JSAtom; + +namespace js { + +class FunctionFlags { + public: + // Syntactic characteristics of a function. + enum FunctionKind : uint8_t { + // Regular function that doesn't match any of the other kinds. + // + // This kind is used by the following scipted functions: + // * FunctionDeclaration + // * FunctionExpression + // * Function created from Function() call or its variants + // + // Also all native functions excluding AsmJS and Wasm use this kind. + NormalFunction = 0, + + // ES6 '(args) => body' syntax. + // This kind is used only by scripted function. + Arrow, + + // ES6 MethodDefinition syntax. + // This kind is used only by scripted function. + Method, + + // Class constructor syntax, or default constructor. + // This kind is used only by scripted function and default constructor. + // + // WARNING: This is independent from Flags::CONSTRUCTOR. + ClassConstructor, + + // Getter and setter syntax in objects or classes, or + // native getter and setter created from JSPropertySpec. + // This kind is used both by scripted functions and native functions. + Getter, + Setter, + + // An asm.js module or exported function. + // + // This kind is used only by scripted function, and used only when the + // asm.js module is created. + // + // "use asm" directive itself doesn't necessarily imply this kind. + // e.g. arrow function with "use asm" becomes Arrow kind, + // + // See EstablishPreconditions in js/src/wasm/AsmJS.cpp + AsmJS, + + // An exported WebAssembly function. + Wasm, + + FunctionKindLimit + }; + + enum Flags : uint16_t { + // FunctionKind enum value. + FUNCTION_KIND_SHIFT = 0, + FUNCTION_KIND_MASK = 0x0007, + + // The AllocKind used was FunctionExtended and extra slots were allocated. + // These slots may be used by the engine or the embedding so care must be + // taken to avoid conflicts. + // + // This flag is used both by scripted functions and native functions. + EXTENDED = 1 << 3, + + // Set if function is a self-hosted builtin or intrinsic. An 'intrinsic' + // here means a native function used inside self-hosted code. In general, a + // self-hosted function should appear to script as though it were a native + // builtin. + SELF_HOSTED = 1 << 4, + + // An interpreted function has or may-have bytecode and an environment. Only + // one of these flags may be used at a time. As a memory optimization, the + // SELFHOSTLAZY flag indicates there is no js::BaseScript at all and we must + // clone from the self-hosted realm in order to get bytecode. + BASESCRIPT = 1 << 5, + SELFHOSTLAZY = 1 << 6, + + // Function may be called as a constructor. This corresponds in the spec as + // having a [[Construct]] internal method. + // + // e.g. FunctionDeclaration has this flag, but GeneratorDeclaration doesn't + // have this flag. + // + // This flag is used both by scripted functions and native functions. + // + // WARNING: This is independent from FunctionKind::ClassConstructor. + CONSTRUCTOR = 1 << 7, + + // Function is either getter or setter, with "get " or "set " prefix, + // but JSFunction::AtomSlot contains unprefixed name, and the function name + // is lazily constructed on the first access. + LAZY_ACCESSOR_NAME = 1 << 8, + + // Function comes from a FunctionExpression, ArrowFunction, or Function() + // call (not a FunctionDeclaration). + // + // This flag is used only by scripted functions and AsmJS. + LAMBDA = 1 << 9, + + // The WASM function has a JIT entry which emulates the + // js::BaseScript::jitCodeRaw mechanism. + WASM_JIT_ENTRY = 1 << 10, + + // Function had no explicit name, but a name was set by SetFunctionName at + // compile time or SetFunctionName at runtime. + // + // This flag can be used both by scripted functions and native functions. + HAS_INFERRED_NAME = 1 << 11, + + // Function had no explicit name, but a name was guessed for it anyway. + // + // This flag is used only by scripted function. + HAS_GUESSED_ATOM = 1 << 12, + + // The 'length' or 'name property has been resolved. See fun_resolve. + // + // These flags are used both by scripted functions and native functions. + RESOLVED_NAME = 1 << 13, + RESOLVED_LENGTH = 1 << 14, + + // This function is kept only for skipping it over during delazification. + // + // This function is inside arrow function's parameter expression, and + // parsed twice, once before finding "=>" token, and once after finding + // "=>" and rewinding to the beginning of the parameters. + // ScriptStencil is created for both case, and the first one is kept only + // for delazification, to make sure delazification sees the same sequence + // of inner function to skip over. + // + // We call the first one "ghost". + // It should be kept lazy, and shouldn't be exposed to debugger. + // + // This flag is used only by scripted functions. + GHOST_FUNCTION = 1 << 15, + + // Shifted form of FunctionKinds. + NORMAL_KIND = NormalFunction << FUNCTION_KIND_SHIFT, + ASMJS_KIND = AsmJS << FUNCTION_KIND_SHIFT, + WASM_KIND = Wasm << 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 combinations to use when creating functions. + NATIVE_FUN = NORMAL_KIND, + NATIVE_CTOR = CONSTRUCTOR | NORMAL_KIND, + NATIVE_GETTER_WITH_LAZY_NAME = LAZY_ACCESSOR_NAME | GETTER_KIND, + NATIVE_SETTER_WITH_LAZY_NAME = LAZY_ACCESSOR_NAME | SETTER_KIND, + ASMJS_CTOR = CONSTRUCTOR | ASMJS_KIND, + ASMJS_LAMBDA_CTOR = CONSTRUCTOR | LAMBDA | ASMJS_KIND, + WASM = WASM_KIND, + INTERPRETED_NORMAL = BASESCRIPT | CONSTRUCTOR | NORMAL_KIND, + INTERPRETED_CLASS_CTOR = BASESCRIPT | CONSTRUCTOR | CLASSCONSTRUCTOR_KIND, + INTERPRETED_GENERATOR_OR_ASYNC = BASESCRIPT | NORMAL_KIND, + INTERPRETED_LAMBDA = BASESCRIPT | LAMBDA | CONSTRUCTOR | NORMAL_KIND, + INTERPRETED_LAMBDA_ARROW = BASESCRIPT | LAMBDA | ARROW_KIND, + INTERPRETED_LAMBDA_GENERATOR_OR_ASYNC = BASESCRIPT | LAMBDA | NORMAL_KIND, + INTERPRETED_GETTER = BASESCRIPT | GETTER_KIND, + INTERPRETED_SETTER = BASESCRIPT | SETTER_KIND, + INTERPRETED_METHOD = BASESCRIPT | METHOD_KIND, + + // Flags that XDR ignores. See also: js::BaseScript::MutableFlags. + MUTABLE_FLAGS = RESOLVED_NAME | RESOLVED_LENGTH, + + // Flags preserved when cloning a function. + STABLE_ACROSS_CLONES = + CONSTRUCTOR | LAMBDA | SELF_HOSTED | FUNCTION_KIND_MASK | GHOST_FUNCTION + }; + + uint16_t flags_; + + public: + FunctionFlags() : flags_() { + static_assert(sizeof(FunctionFlags) == sizeof(flags_), + "No extra members allowed is it'll grow JSFunction"); + static_assert(offsetof(FunctionFlags, flags_) == 0, + "Required for JIT flag access"); + } + + explicit FunctionFlags(uint16_t flags) : flags_(flags) {} + MOZ_IMPLICIT FunctionFlags(Flags f) : flags_(f) {} + + static_assert(((FunctionKindLimit - 1) << FUNCTION_KIND_SHIFT) <= + FUNCTION_KIND_MASK, + "FunctionKind doesn't fit into flags_"); + + uint16_t toRaw() const { return flags_; } + + uint16_t stableAcrossClones() const { return flags_ & STABLE_ACROSS_CLONES; } + + // For flag combinations the type is int. + bool hasFlags(uint16_t flags) const { return flags_ & flags; } + FunctionFlags& setFlags(uint16_t flags) { + flags_ |= flags; + return *this; + } + FunctionFlags& clearFlags(uint16_t flags) { + flags_ &= ~flags; + return *this; + } + FunctionFlags& setFlags(uint16_t flags, bool set) { + if (set) { + setFlags(flags); + } else { + clearFlags(flags); + } + return *this; + } + + FunctionKind kind() const { + return static_cast<FunctionKind>((flags_ & FUNCTION_KIND_MASK) >> + FUNCTION_KIND_SHIFT); + } + +#ifdef DEBUG + void assertFunctionKindIntegrity() { + switch (kind()) { + case FunctionKind::NormalFunction: + MOZ_ASSERT(!hasFlags(LAZY_ACCESSOR_NAME)); + MOZ_ASSERT(!hasFlags(WASM_JIT_ENTRY)); + break; + + case FunctionKind::Arrow: + MOZ_ASSERT(hasFlags(BASESCRIPT) || hasFlags(SELFHOSTLAZY)); + MOZ_ASSERT(!hasFlags(CONSTRUCTOR)); + MOZ_ASSERT(!hasFlags(LAZY_ACCESSOR_NAME)); + MOZ_ASSERT(hasFlags(LAMBDA)); + MOZ_ASSERT(!hasFlags(WASM_JIT_ENTRY)); + break; + case FunctionKind::Method: + MOZ_ASSERT(hasFlags(BASESCRIPT) || hasFlags(SELFHOSTLAZY)); + MOZ_ASSERT(!hasFlags(CONSTRUCTOR)); + MOZ_ASSERT(!hasFlags(LAZY_ACCESSOR_NAME)); + MOZ_ASSERT(!hasFlags(LAMBDA)); + MOZ_ASSERT(!hasFlags(WASM_JIT_ENTRY)); + break; + case FunctionKind::ClassConstructor: + MOZ_ASSERT(hasFlags(BASESCRIPT) || hasFlags(SELFHOSTLAZY)); + MOZ_ASSERT(hasFlags(CONSTRUCTOR)); + MOZ_ASSERT(!hasFlags(LAZY_ACCESSOR_NAME)); + MOZ_ASSERT(!hasFlags(LAMBDA)); + MOZ_ASSERT(!hasFlags(WASM_JIT_ENTRY)); + break; + case FunctionKind::Getter: + MOZ_ASSERT(!hasFlags(CONSTRUCTOR)); + MOZ_ASSERT(!hasFlags(LAMBDA)); + MOZ_ASSERT(!hasFlags(WASM_JIT_ENTRY)); + break; + case FunctionKind::Setter: + MOZ_ASSERT(!hasFlags(CONSTRUCTOR)); + MOZ_ASSERT(!hasFlags(LAMBDA)); + MOZ_ASSERT(!hasFlags(WASM_JIT_ENTRY)); + break; + + case FunctionKind::AsmJS: + MOZ_ASSERT(!hasFlags(BASESCRIPT)); + MOZ_ASSERT(!hasFlags(SELFHOSTLAZY)); + MOZ_ASSERT(!hasFlags(LAZY_ACCESSOR_NAME)); + MOZ_ASSERT(!hasFlags(WASM_JIT_ENTRY)); + break; + case FunctionKind::Wasm: + MOZ_ASSERT(!hasFlags(BASESCRIPT)); + MOZ_ASSERT(!hasFlags(SELFHOSTLAZY)); + MOZ_ASSERT(!hasFlags(CONSTRUCTOR)); + MOZ_ASSERT(!hasFlags(LAZY_ACCESSOR_NAME)); + MOZ_ASSERT(!hasFlags(LAMBDA)); + break; + default: + break; + } + } +#endif + + /* A function can be classified as either native (C++) or interpreted (JS): */ + bool isInterpreted() const { + return hasFlags(BASESCRIPT) || hasFlags(SELFHOSTLAZY); + } + bool isNativeFun() const { return !isInterpreted(); } + + bool isConstructor() const { return hasFlags(CONSTRUCTOR); } + + bool isNonBuiltinConstructor() const { + // Note: keep this in sync with branchIfNotFunctionIsNonBuiltinCtor in + // MacroAssembler.cpp. + return hasFlags(BASESCRIPT) && hasFlags(CONSTRUCTOR) && + !hasFlags(SELF_HOSTED); + } + + /* Possible attributes of a native function: */ + bool isAsmJSNative() const { + MOZ_ASSERT_IF(kind() == AsmJS, isNativeFun()); + return kind() == AsmJS; + } + bool isWasm() const { + MOZ_ASSERT_IF(kind() == Wasm, isNativeFun()); + return kind() == Wasm; + } + bool isWasmWithJitEntry() const { + MOZ_ASSERT_IF(hasFlags(WASM_JIT_ENTRY), isWasm()); + return hasFlags(WASM_JIT_ENTRY); + } + bool isNativeWithoutJitEntry() const { + MOZ_ASSERT_IF(!hasJitEntry(), isNativeFun()); + return !hasJitEntry(); + } + bool isBuiltinNative() const { + return isNativeFun() && !isAsmJSNative() && !isWasm(); + } + bool hasJitEntry() const { + return hasBaseScript() || hasSelfHostedLazyScript() || isWasmWithJitEntry(); + } + + /* Possible attributes of an interpreted function: */ + bool hasInferredName() const { return hasFlags(HAS_INFERRED_NAME); } + bool hasGuessedAtom() const { return hasFlags(HAS_GUESSED_ATOM); } + bool isLambda() const { return hasFlags(LAMBDA); } + + bool isNamedLambda(bool hasName) const { + return hasName && isLambda() && !hasInferredName() && !hasGuessedAtom(); + } + + // 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 hasBaseScript() const { return hasFlags(BASESCRIPT); } + bool hasSelfHostedLazyScript() const { return hasFlags(SELFHOSTLAZY); } + + // 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 isAccessorWithLazyName() const { return hasFlags(LAZY_ACCESSOR_NAME); } + + bool allowSuperProperty() const { + return isMethod() || isGetter() || isSetter(); + } + + bool hasResolvedLength() const { return hasFlags(RESOLVED_LENGTH); } + bool hasResolvedName() const { return hasFlags(RESOLVED_NAME); } + + bool isSelfHostedOrIntrinsic() const { return hasFlags(SELF_HOSTED); } + bool isSelfHostedBuiltin() const { + return isSelfHostedOrIntrinsic() && !isNativeFun(); + } + bool isIntrinsic() const { + return isSelfHostedOrIntrinsic() && isNativeFun(); + } + + FunctionFlags& setKind(FunctionKind kind) { + this->flags_ &= ~FUNCTION_KIND_MASK; + this->flags_ |= static_cast<uint16_t>(kind) << FUNCTION_KIND_SHIFT; + return *this; + } + + // Make the function constructible. + FunctionFlags& setIsConstructor() { + MOZ_ASSERT(!isConstructor()); + MOZ_ASSERT(isSelfHostedBuiltin()); + return setFlags(CONSTRUCTOR); + } + + FunctionFlags& setIsSelfHostedBuiltin() { + MOZ_ASSERT(isInterpreted()); + MOZ_ASSERT(!isSelfHostedBuiltin()); + setFlags(SELF_HOSTED); + // Self-hosted functions should not be constructable. + return clearFlags(CONSTRUCTOR); + } + FunctionFlags& setIsIntrinsic() { + MOZ_ASSERT(isNativeFun()); + MOZ_ASSERT(!isIntrinsic()); + return setFlags(SELF_HOSTED); + } + + FunctionFlags& setResolvedLength() { return setFlags(RESOLVED_LENGTH); } + FunctionFlags& setResolvedName() { return setFlags(RESOLVED_NAME); } + + FunctionFlags& setInferredName() { return setFlags(HAS_INFERRED_NAME); } + + FunctionFlags& setGuessedAtom() { return setFlags(HAS_GUESSED_ATOM); } + + FunctionFlags& setSelfHostedLazy() { return setFlags(SELFHOSTLAZY); } + FunctionFlags& clearSelfHostedLazy() { return clearFlags(SELFHOSTLAZY); } + FunctionFlags& setBaseScript() { return setFlags(BASESCRIPT); } + FunctionFlags& clearBaseScript() { return clearFlags(BASESCRIPT); } + + FunctionFlags& clearLazyAccessorName() { + return clearFlags(LAZY_ACCESSOR_NAME); + } + + FunctionFlags& setWasmJitEntry() { return setFlags(WASM_JIT_ENTRY); } + + bool isExtended() const { return hasFlags(EXTENDED); } + FunctionFlags& setIsExtended() { return setFlags(EXTENDED); } + + bool isNativeConstructor() const { return hasFlags(NATIVE_CTOR); } + + FunctionFlags& setIsGhost() { return setFlags(GHOST_FUNCTION); } + bool isGhost() const { return hasFlags(GHOST_FUNCTION); } + + static uint16_t HasJitEntryFlags(bool isConstructing) { + uint16_t flags = BASESCRIPT | SELFHOSTLAZY; + if (!isConstructing) { + flags |= WASM_JIT_ENTRY; + } + return flags; + } + + static FunctionFlags clearMutableflags(FunctionFlags flags) { + return FunctionFlags(flags.toRaw() & ~FunctionFlags::MUTABLE_FLAGS); + } +}; + +} /* namespace js */ + +#endif /* vm_FunctionFlags_h */ |