1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef vm_FunctionFlags_h 8 #define vm_FunctionFlags_h 9 10 #include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_ASSERT_IF 11 #include "mozilla/Attributes.h" // MOZ_IMPLICIT 12 13 #include <stdint.h> // uint8_t, uint16_t 14 15 #include "jstypes.h" // JS_PUBLIC_API 16 17 class JS_PUBLIC_API JSAtom; 18 19 namespace js { 20 21 class FunctionFlags { 22 public: 23 enum FunctionKind : uint8_t { 24 NormalFunction = 0, 25 Arrow, // ES6 '(args) => body' syntax 26 Method, // ES6 MethodDefinition 27 ClassConstructor, 28 Getter, 29 Setter, 30 AsmJS, // An asm.js module or exported function 31 Wasm, // An exported WebAssembly function 32 FunctionKindLimit 33 }; 34 35 enum Flags : uint16_t { 36 // The general kind of a function. This is used to describe characteristics 37 // of functions that do not merit a dedicated flag bit below. 38 FUNCTION_KIND_SHIFT = 0, 39 FUNCTION_KIND_MASK = 0x0007, 40 41 // The AllocKind used was FunctionExtended and extra slots were allocated. 42 // These slots may be used by the engine or the embedding so care must be 43 // taken to avoid conflicts. 44 EXTENDED = 1 << 3, 45 46 // Set if function is a self-hosted builtin or intrinsic. An 'intrinsic' 47 // here means a native function used inside self-hosted code. In general, a 48 // self-hosted function should appear to script as though it were a native 49 // builtin. 50 SELF_HOSTED = 1 << 4, 51 52 // An interpreted function has or may-have bytecode and an environment. Only 53 // one of these flags may be used at a time. As a memory optimization, the 54 // SELFHOSTLAZY flag indicates there is no js::BaseScript at all and we must 55 // clone from the self-hosted realm in order to get bytecode. 56 BASESCRIPT = 1 << 5, 57 SELFHOSTLAZY = 1 << 6, 58 59 // Function may be called as a constructor. This corresponds in the spec as 60 // having a [[Construct]] internal method. 61 CONSTRUCTOR = 1 << 7, 62 63 // A 'Bound Function Exotic Object' created by Function.prototype.bind. 64 BOUND_FUN = 1 << 8, 65 66 // Function comes from a FunctionExpression, ArrowFunction, or Function() 67 // call (not a FunctionDeclaration or nonstandard function-statement). 68 LAMBDA = 1 << 9, 69 70 // The WASM function has a JIT entry which emulates the 71 // js::BaseScript::jitCodeRaw mechanism. 72 WASM_JIT_ENTRY = 1 << 10, 73 74 // Function had no explicit name, but a name was set by SetFunctionName at 75 // compile time or SetFunctionName at runtime. 76 HAS_INFERRED_NAME = 1 << 11, 77 78 // Function had no explicit name, but a name was guessed for it anyway. For 79 // a Bound function, tracks if atom_ already contains the "bound " prefix. 80 ATOM_EXTRA_FLAG = 1 << 12, 81 HAS_GUESSED_ATOM = ATOM_EXTRA_FLAG, 82 HAS_BOUND_FUNCTION_NAME_PREFIX = ATOM_EXTRA_FLAG, 83 84 // The 'length' or 'name property has been resolved. See fun_resolve. 85 RESOLVED_NAME = 1 << 13, 86 RESOLVED_LENGTH = 1 << 14, 87 88 // This function is kept only for skipping it over during delazification. 89 // 90 // This function is inside arrow function's parameter expression, and 91 // parsed twice, once before finding "=>" token, and once after finding 92 // "=>" and rewinding to the beginning of the parameters. 93 // ScriptStencil is created for both case, and the first one is kept only 94 // for delazification, to make sure delazification sees the same sequence 95 // of inner function to skip over. 96 // 97 // We call the first one "ghost". 98 // It should be kept lazy, and shouldn't be exposed to debugger. 99 GHOST_FUNCTION = 1 << 15, 100 101 // Shifted form of FunctionKinds. 102 NORMAL_KIND = NormalFunction << FUNCTION_KIND_SHIFT, 103 ASMJS_KIND = AsmJS << FUNCTION_KIND_SHIFT, 104 WASM_KIND = Wasm << FUNCTION_KIND_SHIFT, 105 ARROW_KIND = Arrow << FUNCTION_KIND_SHIFT, 106 METHOD_KIND = Method << FUNCTION_KIND_SHIFT, 107 CLASSCONSTRUCTOR_KIND = ClassConstructor << FUNCTION_KIND_SHIFT, 108 GETTER_KIND = Getter << FUNCTION_KIND_SHIFT, 109 SETTER_KIND = Setter << FUNCTION_KIND_SHIFT, 110 111 // Derived Flags combinations to use when creating functions. 112 NATIVE_FUN = NORMAL_KIND, 113 NATIVE_CTOR = CONSTRUCTOR | NORMAL_KIND, 114 ASMJS_CTOR = CONSTRUCTOR | ASMJS_KIND, 115 ASMJS_LAMBDA_CTOR = CONSTRUCTOR | LAMBDA | ASMJS_KIND, 116 WASM = WASM_KIND, 117 INTERPRETED_NORMAL = BASESCRIPT | CONSTRUCTOR | NORMAL_KIND, 118 INTERPRETED_CLASS_CTOR = BASESCRIPT | CONSTRUCTOR | CLASSCONSTRUCTOR_KIND, 119 INTERPRETED_GENERATOR_OR_ASYNC = BASESCRIPT | NORMAL_KIND, 120 INTERPRETED_LAMBDA = BASESCRIPT | LAMBDA | CONSTRUCTOR | NORMAL_KIND, 121 INTERPRETED_LAMBDA_ARROW = BASESCRIPT | LAMBDA | ARROW_KIND, 122 INTERPRETED_LAMBDA_GENERATOR_OR_ASYNC = BASESCRIPT | LAMBDA | NORMAL_KIND, 123 INTERPRETED_GETTER = BASESCRIPT | GETTER_KIND, 124 INTERPRETED_SETTER = BASESCRIPT | SETTER_KIND, 125 INTERPRETED_METHOD = BASESCRIPT | METHOD_KIND, 126 127 // Flags that XDR ignores. See also: js::BaseScript::MutableFlags. 128 MUTABLE_FLAGS = RESOLVED_NAME | RESOLVED_LENGTH, 129 130 // Flags preserved when cloning a function. 131 STABLE_ACROSS_CLONES = 132 CONSTRUCTOR | LAMBDA | SELF_HOSTED | FUNCTION_KIND_MASK | GHOST_FUNCTION 133 }; 134 135 uint16_t flags_; 136 137 public: FunctionFlags()138 FunctionFlags() : flags_() { 139 static_assert(sizeof(FunctionFlags) == sizeof(flags_), 140 "No extra members allowed is it'll grow JSFunction"); 141 static_assert(offsetof(FunctionFlags, flags_) == 0, 142 "Required for JIT flag access"); 143 } 144 FunctionFlags(uint16_t flags)145 explicit FunctionFlags(uint16_t flags) : flags_(flags) {} FunctionFlags(Flags f)146 MOZ_IMPLICIT FunctionFlags(Flags f) : flags_(f) {} 147 148 static_assert(((FunctionKindLimit - 1) << FUNCTION_KIND_SHIFT) <= 149 FUNCTION_KIND_MASK, 150 "FunctionKind doesn't fit into flags_"); 151 toRaw()152 uint16_t toRaw() const { return flags_; } 153 stableAcrossClones()154 uint16_t stableAcrossClones() const { return flags_ & STABLE_ACROSS_CLONES; } 155 156 // For flag combinations the type is int. hasFlags(uint16_t flags)157 bool hasFlags(uint16_t flags) const { return flags_ & flags; } setFlags(uint16_t flags)158 FunctionFlags& setFlags(uint16_t flags) { 159 flags_ |= flags; 160 return *this; 161 } clearFlags(uint16_t flags)162 FunctionFlags& clearFlags(uint16_t flags) { 163 flags_ &= ~flags; 164 return *this; 165 } setFlags(uint16_t flags,bool set)166 FunctionFlags& setFlags(uint16_t flags, bool set) { 167 if (set) { 168 setFlags(flags); 169 } else { 170 clearFlags(flags); 171 } 172 return *this; 173 } 174 kind()175 FunctionKind kind() const { 176 return static_cast<FunctionKind>((flags_ & FUNCTION_KIND_MASK) >> 177 FUNCTION_KIND_SHIFT); 178 } 179 180 /* A function can be classified as either native (C++) or interpreted (JS): */ isInterpreted()181 bool isInterpreted() const { 182 return hasFlags(BASESCRIPT) || hasFlags(SELFHOSTLAZY); 183 } isNativeFun()184 bool isNativeFun() const { return !isInterpreted(); } 185 isConstructor()186 bool isConstructor() const { return hasFlags(CONSTRUCTOR); } 187 isNonBuiltinConstructor()188 bool isNonBuiltinConstructor() const { 189 // Note: keep this in sync with branchIfNotFunctionIsNonBuiltinCtor in 190 // MacroAssembler.cpp. 191 return hasFlags(BASESCRIPT) && hasFlags(CONSTRUCTOR) && 192 !hasFlags(SELF_HOSTED); 193 } 194 195 /* Possible attributes of a native function: */ isAsmJSNative()196 bool isAsmJSNative() const { 197 MOZ_ASSERT_IF(kind() == AsmJS, isNativeFun()); 198 return kind() == AsmJS; 199 } isWasm()200 bool isWasm() const { 201 MOZ_ASSERT_IF(kind() == Wasm, isNativeFun()); 202 return kind() == Wasm; 203 } isWasmWithJitEntry()204 bool isWasmWithJitEntry() const { 205 MOZ_ASSERT_IF(hasFlags(WASM_JIT_ENTRY), isWasm()); 206 return hasFlags(WASM_JIT_ENTRY); 207 } isNativeWithoutJitEntry()208 bool isNativeWithoutJitEntry() const { 209 MOZ_ASSERT_IF(!hasJitEntry(), isNativeFun()); 210 return !hasJitEntry(); 211 } isBuiltinNative()212 bool isBuiltinNative() const { 213 return isNativeFun() && !isAsmJSNative() && !isWasm(); 214 } hasJitEntry()215 bool hasJitEntry() const { 216 return hasBaseScript() || hasSelfHostedLazyScript() || isWasmWithJitEntry(); 217 } 218 219 /* Possible attributes of an interpreted function: */ isBoundFunction()220 bool isBoundFunction() const { return hasFlags(BOUND_FUN); } hasInferredName()221 bool hasInferredName() const { return hasFlags(HAS_INFERRED_NAME); } hasGuessedAtom()222 bool hasGuessedAtom() const { 223 static_assert(HAS_GUESSED_ATOM == HAS_BOUND_FUNCTION_NAME_PREFIX, 224 "HAS_GUESSED_ATOM is unused for bound functions"); 225 bool hasGuessedAtom = hasFlags(HAS_GUESSED_ATOM); 226 bool boundFun = hasFlags(BOUND_FUN); 227 return hasGuessedAtom && !boundFun; 228 } hasBoundFunctionNamePrefix()229 bool hasBoundFunctionNamePrefix() const { 230 static_assert( 231 HAS_BOUND_FUNCTION_NAME_PREFIX == HAS_GUESSED_ATOM, 232 "HAS_BOUND_FUNCTION_NAME_PREFIX is only used for bound functions"); 233 MOZ_ASSERT(isBoundFunction()); 234 return hasFlags(HAS_BOUND_FUNCTION_NAME_PREFIX); 235 } isLambda()236 bool isLambda() const { return hasFlags(LAMBDA); } 237 isNamedLambda(bool hasName)238 bool isNamedLambda(bool hasName) const { 239 return hasName && isLambda() && !hasInferredName() && !hasGuessedAtom(); 240 } 241 242 // These methods determine which of the u.scripted.s union arms are active. 243 // For live JSFunctions the pointer values will always be non-null, but due 244 // to partial initialization the GC (and other features that scan the heap 245 // directly) may still return a null pointer. hasBaseScript()246 bool hasBaseScript() const { return hasFlags(BASESCRIPT); } hasSelfHostedLazyScript()247 bool hasSelfHostedLazyScript() const { return hasFlags(SELFHOSTLAZY); } 248 249 // Arrow functions store their lexical new.target in the first extended slot. isArrow()250 bool isArrow() const { return kind() == Arrow; } 251 // Every class-constructor is also a method. isMethod()252 bool isMethod() const { 253 return kind() == Method || kind() == ClassConstructor; 254 } isClassConstructor()255 bool isClassConstructor() const { return kind() == ClassConstructor; } 256 isGetter()257 bool isGetter() const { return kind() == Getter; } isSetter()258 bool isSetter() const { return kind() == Setter; } 259 allowSuperProperty()260 bool allowSuperProperty() const { 261 return isMethod() || isGetter() || isSetter(); 262 } 263 hasResolvedLength()264 bool hasResolvedLength() const { return hasFlags(RESOLVED_LENGTH); } hasResolvedName()265 bool hasResolvedName() const { return hasFlags(RESOLVED_NAME); } 266 isSelfHostedOrIntrinsic()267 bool isSelfHostedOrIntrinsic() const { return hasFlags(SELF_HOSTED); } isSelfHostedBuiltin()268 bool isSelfHostedBuiltin() const { 269 return isSelfHostedOrIntrinsic() && !isNativeFun(); 270 } isIntrinsic()271 bool isIntrinsic() const { 272 return isSelfHostedOrIntrinsic() && isNativeFun(); 273 } 274 setKind(FunctionKind kind)275 FunctionFlags& setKind(FunctionKind kind) { 276 this->flags_ &= ~FUNCTION_KIND_MASK; 277 this->flags_ |= static_cast<uint16_t>(kind) << FUNCTION_KIND_SHIFT; 278 return *this; 279 } 280 281 // Make the function constructible. setIsConstructor()282 FunctionFlags& setIsConstructor() { 283 MOZ_ASSERT(!isConstructor()); 284 MOZ_ASSERT(isSelfHostedBuiltin()); 285 return setFlags(CONSTRUCTOR); 286 } 287 setIsBoundFunction()288 FunctionFlags& setIsBoundFunction() { 289 MOZ_ASSERT(!isBoundFunction()); 290 return setFlags(BOUND_FUN); 291 } 292 setIsSelfHostedBuiltin()293 FunctionFlags& setIsSelfHostedBuiltin() { 294 MOZ_ASSERT(isInterpreted()); 295 MOZ_ASSERT(!isSelfHostedBuiltin()); 296 setFlags(SELF_HOSTED); 297 // Self-hosted functions should not be constructable. 298 return clearFlags(CONSTRUCTOR); 299 } setIsIntrinsic()300 FunctionFlags& setIsIntrinsic() { 301 MOZ_ASSERT(isNativeFun()); 302 MOZ_ASSERT(!isIntrinsic()); 303 return setFlags(SELF_HOSTED); 304 } 305 setResolvedLength()306 FunctionFlags& setResolvedLength() { return setFlags(RESOLVED_LENGTH); } setResolvedName()307 FunctionFlags& setResolvedName() { return setFlags(RESOLVED_NAME); } 308 setInferredName()309 FunctionFlags& setInferredName() { return setFlags(HAS_INFERRED_NAME); } 310 setGuessedAtom()311 FunctionFlags& setGuessedAtom() { return setFlags(HAS_GUESSED_ATOM); } 312 setPrefixedBoundFunctionName()313 FunctionFlags& setPrefixedBoundFunctionName() { 314 return setFlags(HAS_BOUND_FUNCTION_NAME_PREFIX); 315 } 316 setSelfHostedLazy()317 FunctionFlags& setSelfHostedLazy() { return setFlags(SELFHOSTLAZY); } clearSelfHostedLazy()318 FunctionFlags& clearSelfHostedLazy() { return clearFlags(SELFHOSTLAZY); } setBaseScript()319 FunctionFlags& setBaseScript() { return setFlags(BASESCRIPT); } clearBaseScript()320 FunctionFlags& clearBaseScript() { return clearFlags(BASESCRIPT); } 321 setWasmJitEntry()322 FunctionFlags& setWasmJitEntry() { return setFlags(WASM_JIT_ENTRY); } 323 isExtended()324 bool isExtended() const { return hasFlags(EXTENDED); } setIsExtended()325 FunctionFlags& setIsExtended() { return setFlags(EXTENDED); } 326 isNativeConstructor()327 bool isNativeConstructor() const { return hasFlags(NATIVE_CTOR); } 328 setIsGhost()329 FunctionFlags& setIsGhost() { return setFlags(GHOST_FUNCTION); } isGhost()330 bool isGhost() const { return hasFlags(GHOST_FUNCTION); } 331 HasJitEntryFlags(bool isConstructing)332 static uint16_t HasJitEntryFlags(bool isConstructing) { 333 uint16_t flags = BASESCRIPT | SELFHOSTLAZY; 334 if (!isConstructing) { 335 flags |= WASM_JIT_ENTRY; 336 } 337 return flags; 338 } 339 clearMutableflags(FunctionFlags flags)340 static FunctionFlags clearMutableflags(FunctionFlags flags) { 341 return FunctionFlags(flags.toRaw() & ~FunctionFlags::MUTABLE_FLAGS); 342 } 343 }; 344 345 } /* namespace js */ 346 347 #endif /* vm_FunctionFlags_h */ 348