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 void setFlags(uint16_t flags) { flags_ |= flags; } clearFlags(uint16_t flags)159 void clearFlags(uint16_t flags) { flags_ &= ~flags; } setFlags(uint16_t flags,bool set)160 void setFlags(uint16_t flags, bool set) { 161 if (set) { 162 setFlags(flags); 163 } else { 164 clearFlags(flags); 165 } 166 } 167 kind()168 FunctionKind kind() const { 169 return static_cast<FunctionKind>((flags_ & FUNCTION_KIND_MASK) >> 170 FUNCTION_KIND_SHIFT); 171 } 172 173 /* A function can be classified as either native (C++) or interpreted (JS): */ isInterpreted()174 bool isInterpreted() const { 175 return hasFlags(BASESCRIPT) || hasFlags(SELFHOSTLAZY); 176 } isNativeFun()177 bool isNativeFun() const { return !isInterpreted(); } 178 isConstructor()179 bool isConstructor() const { return hasFlags(CONSTRUCTOR); } 180 isNonBuiltinConstructor()181 bool isNonBuiltinConstructor() const { 182 // Note: keep this in sync with branchIfNotFunctionIsNonBuiltinCtor in 183 // MacroAssembler.cpp. 184 return hasFlags(BASESCRIPT) && hasFlags(CONSTRUCTOR) && 185 !hasFlags(SELF_HOSTED); 186 } 187 188 /* Possible attributes of a native function: */ isAsmJSNative()189 bool isAsmJSNative() const { 190 MOZ_ASSERT_IF(kind() == AsmJS, isNativeFun()); 191 return kind() == AsmJS; 192 } isWasm()193 bool isWasm() const { 194 MOZ_ASSERT_IF(kind() == Wasm, isNativeFun()); 195 return kind() == Wasm; 196 } isWasmWithJitEntry()197 bool isWasmWithJitEntry() const { 198 MOZ_ASSERT_IF(hasFlags(WASM_JIT_ENTRY), isWasm()); 199 return hasFlags(WASM_JIT_ENTRY); 200 } isNativeWithoutJitEntry()201 bool isNativeWithoutJitEntry() const { 202 MOZ_ASSERT_IF(!hasJitEntry(), isNativeFun()); 203 return !hasJitEntry(); 204 } isBuiltinNative()205 bool isBuiltinNative() const { 206 return isNativeFun() && !isAsmJSNative() && !isWasm(); 207 } hasJitEntry()208 bool hasJitEntry() const { 209 return hasBaseScript() || hasSelfHostedLazyScript() || isWasmWithJitEntry(); 210 } 211 212 /* Possible attributes of an interpreted function: */ isBoundFunction()213 bool isBoundFunction() const { return hasFlags(BOUND_FUN); } hasInferredName()214 bool hasInferredName() const { return hasFlags(HAS_INFERRED_NAME); } hasGuessedAtom()215 bool hasGuessedAtom() const { 216 static_assert(HAS_GUESSED_ATOM == HAS_BOUND_FUNCTION_NAME_PREFIX, 217 "HAS_GUESSED_ATOM is unused for bound functions"); 218 bool hasGuessedAtom = hasFlags(HAS_GUESSED_ATOM); 219 bool boundFun = hasFlags(BOUND_FUN); 220 return hasGuessedAtom && !boundFun; 221 } hasBoundFunctionNamePrefix()222 bool hasBoundFunctionNamePrefix() const { 223 static_assert( 224 HAS_BOUND_FUNCTION_NAME_PREFIX == HAS_GUESSED_ATOM, 225 "HAS_BOUND_FUNCTION_NAME_PREFIX is only used for bound functions"); 226 MOZ_ASSERT(isBoundFunction()); 227 return hasFlags(HAS_BOUND_FUNCTION_NAME_PREFIX); 228 } isLambda()229 bool isLambda() const { return hasFlags(LAMBDA); } 230 isNamedLambda(bool hasName)231 bool isNamedLambda(bool hasName) const { 232 return hasName && isLambda() && !hasInferredName() && !hasGuessedAtom(); 233 } 234 235 // These methods determine which of the u.scripted.s union arms are active. 236 // For live JSFunctions the pointer values will always be non-null, but due 237 // to partial initialization the GC (and other features that scan the heap 238 // directly) may still return a null pointer. hasBaseScript()239 bool hasBaseScript() const { return hasFlags(BASESCRIPT); } hasSelfHostedLazyScript()240 bool hasSelfHostedLazyScript() const { return hasFlags(SELFHOSTLAZY); } 241 242 // Arrow functions store their lexical new.target in the first extended slot. isArrow()243 bool isArrow() const { return kind() == Arrow; } 244 // Every class-constructor is also a method. isMethod()245 bool isMethod() const { 246 return kind() == Method || kind() == ClassConstructor; 247 } isClassConstructor()248 bool isClassConstructor() const { return kind() == ClassConstructor; } 249 isGetter()250 bool isGetter() const { return kind() == Getter; } isSetter()251 bool isSetter() const { return kind() == Setter; } 252 allowSuperProperty()253 bool allowSuperProperty() const { 254 return isMethod() || isGetter() || isSetter(); 255 } 256 hasResolvedLength()257 bool hasResolvedLength() const { return hasFlags(RESOLVED_LENGTH); } hasResolvedName()258 bool hasResolvedName() const { return hasFlags(RESOLVED_NAME); } 259 isSelfHostedOrIntrinsic()260 bool isSelfHostedOrIntrinsic() const { return hasFlags(SELF_HOSTED); } isSelfHostedBuiltin()261 bool isSelfHostedBuiltin() const { 262 return isSelfHostedOrIntrinsic() && !isNativeFun(); 263 } isIntrinsic()264 bool isIntrinsic() const { 265 return isSelfHostedOrIntrinsic() && isNativeFun(); 266 } 267 setKind(FunctionKind kind)268 void setKind(FunctionKind kind) { 269 this->flags_ &= ~FUNCTION_KIND_MASK; 270 this->flags_ |= static_cast<uint16_t>(kind) << FUNCTION_KIND_SHIFT; 271 } 272 273 // Make the function constructible. setIsConstructor()274 void setIsConstructor() { 275 MOZ_ASSERT(!isConstructor()); 276 MOZ_ASSERT(isSelfHostedBuiltin()); 277 setFlags(CONSTRUCTOR); 278 } 279 setIsBoundFunction()280 void setIsBoundFunction() { 281 MOZ_ASSERT(!isBoundFunction()); 282 setFlags(BOUND_FUN); 283 } 284 setIsSelfHostedBuiltin()285 void setIsSelfHostedBuiltin() { 286 MOZ_ASSERT(isInterpreted()); 287 MOZ_ASSERT(!isSelfHostedBuiltin()); 288 setFlags(SELF_HOSTED); 289 // Self-hosted functions should not be constructable. 290 clearFlags(CONSTRUCTOR); 291 } setIsIntrinsic()292 void setIsIntrinsic() { 293 MOZ_ASSERT(isNativeFun()); 294 MOZ_ASSERT(!isIntrinsic()); 295 setFlags(SELF_HOSTED); 296 } 297 setResolvedLength()298 void setResolvedLength() { setFlags(RESOLVED_LENGTH); } setResolvedName()299 void setResolvedName() { setFlags(RESOLVED_NAME); } 300 setInferredName()301 void setInferredName() { setFlags(HAS_INFERRED_NAME); } 302 setGuessedAtom()303 void setGuessedAtom() { setFlags(HAS_GUESSED_ATOM); } 304 setPrefixedBoundFunctionName()305 void setPrefixedBoundFunctionName() { 306 setFlags(HAS_BOUND_FUNCTION_NAME_PREFIX); 307 } 308 setSelfHostedLazy()309 void setSelfHostedLazy() { setFlags(SELFHOSTLAZY); } clearSelfHostedLazy()310 void clearSelfHostedLazy() { clearFlags(SELFHOSTLAZY); } setBaseScript()311 void setBaseScript() { setFlags(BASESCRIPT); } clearBaseScript()312 void clearBaseScript() { clearFlags(BASESCRIPT); } 313 setWasmJitEntry()314 void setWasmJitEntry() { setFlags(WASM_JIT_ENTRY); } 315 isExtended()316 bool isExtended() const { return hasFlags(EXTENDED); } setIsExtended()317 void setIsExtended() { setFlags(EXTENDED); } 318 isNativeConstructor()319 bool isNativeConstructor() const { return hasFlags(NATIVE_CTOR); } 320 setIsGhost()321 void setIsGhost() { setFlags(GHOST_FUNCTION); } isGhost()322 bool isGhost() const { return hasFlags(GHOST_FUNCTION); } 323 HasJitEntryFlags(bool isConstructing)324 static uint16_t HasJitEntryFlags(bool isConstructing) { 325 uint16_t flags = BASESCRIPT | SELFHOSTLAZY; 326 if (!isConstructing) { 327 flags |= WASM_JIT_ENTRY; 328 } 329 return flags; 330 } 331 clearMutableflags(FunctionFlags flags)332 static FunctionFlags clearMutableflags(FunctionFlags flags) { 333 return FunctionFlags(flags.toRaw() & ~FunctionFlags::MUTABLE_FLAGS); 334 } 335 }; 336 337 } /* namespace js */ 338 339 #endif /* vm_FunctionFlags_h */ 340