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