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     // For a function used as an interpreted constructor, whether a 'new' type
89     // had constructor information cleared.
90     NEW_SCRIPT_CLEARED = 1 << 15,
91 
92     // Shifted form of FunctionKinds.
93     NORMAL_KIND = NormalFunction << FUNCTION_KIND_SHIFT,
94     ASMJS_KIND = AsmJS << FUNCTION_KIND_SHIFT,
95     WASM_KIND = Wasm << FUNCTION_KIND_SHIFT,
96     ARROW_KIND = Arrow << FUNCTION_KIND_SHIFT,
97     METHOD_KIND = Method << FUNCTION_KIND_SHIFT,
98     CLASSCONSTRUCTOR_KIND = ClassConstructor << FUNCTION_KIND_SHIFT,
99     GETTER_KIND = Getter << FUNCTION_KIND_SHIFT,
100     SETTER_KIND = Setter << FUNCTION_KIND_SHIFT,
101 
102     // Derived Flags combinations to use when creating functions.
103     NATIVE_FUN = NORMAL_KIND,
104     NATIVE_CTOR = CONSTRUCTOR | NORMAL_KIND,
105     ASMJS_CTOR = CONSTRUCTOR | ASMJS_KIND,
106     ASMJS_LAMBDA_CTOR = CONSTRUCTOR | LAMBDA | ASMJS_KIND,
107     WASM = WASM_KIND,
108     INTERPRETED_NORMAL = BASESCRIPT | CONSTRUCTOR | NORMAL_KIND,
109     INTERPRETED_CLASS_CTOR = BASESCRIPT | CONSTRUCTOR | CLASSCONSTRUCTOR_KIND,
110     INTERPRETED_GENERATOR_OR_ASYNC = BASESCRIPT | NORMAL_KIND,
111     INTERPRETED_LAMBDA = BASESCRIPT | LAMBDA | CONSTRUCTOR | NORMAL_KIND,
112     INTERPRETED_LAMBDA_ARROW = BASESCRIPT | LAMBDA | ARROW_KIND,
113     INTERPRETED_LAMBDA_GENERATOR_OR_ASYNC = BASESCRIPT | LAMBDA | NORMAL_KIND,
114     INTERPRETED_GETTER = BASESCRIPT | GETTER_KIND,
115     INTERPRETED_SETTER = BASESCRIPT | SETTER_KIND,
116     INTERPRETED_METHOD = BASESCRIPT | METHOD_KIND,
117 
118     // Flags that XDR ignores. See also: js::BaseScript::MutableFlags.
119     MUTABLE_FLAGS = RESOLVED_NAME | RESOLVED_LENGTH | NEW_SCRIPT_CLEARED,
120 
121     // Flags preserved when cloning a function. (Exception:
122     // js::MakeDefaultConstructor produces default constructors for ECMAScript
123     // classes by cloning self-hosted functions, and then clearing their
124     // SELF_HOSTED bit, setting their CONSTRUCTOR bit, and otherwise munging
125     // them to look like they originated with the class definition.) */
126     STABLE_ACROSS_CLONES =
127         CONSTRUCTOR | LAMBDA | SELF_HOSTED | FUNCTION_KIND_MASK
128   };
129 
130   uint16_t flags_;
131 
132  public:
FunctionFlags()133   FunctionFlags() : flags_() {
134     static_assert(sizeof(FunctionFlags) == sizeof(flags_),
135                   "No extra members allowed is it'll grow JSFunction");
136     static_assert(offsetof(FunctionFlags, flags_) == 0,
137                   "Required for JIT flag access");
138   }
139 
FunctionFlags(uint16_t flags)140   explicit FunctionFlags(uint16_t flags) : flags_(flags) {}
FunctionFlags(Flags f)141   MOZ_IMPLICIT FunctionFlags(Flags f) : flags_(f) {}
142 
143   static_assert(((FunctionKindLimit - 1) << FUNCTION_KIND_SHIFT) <=
144                     FUNCTION_KIND_MASK,
145                 "FunctionKind doesn't fit into flags_");
146 
toRaw()147   uint16_t toRaw() const { return flags_; }
148 
stableAcrossClones()149   uint16_t stableAcrossClones() const { return flags_ & STABLE_ACROSS_CLONES; }
150 
151   // For flag combinations the type is int.
hasFlags(uint16_t flags)152   bool hasFlags(uint16_t flags) const { return flags_ & flags; }
setFlags(uint16_t flags)153   void setFlags(uint16_t flags) { flags_ |= flags; }
clearFlags(uint16_t flags)154   void clearFlags(uint16_t flags) { flags_ &= ~flags; }
setFlags(uint16_t flags,bool set)155   void setFlags(uint16_t flags, bool set) {
156     if (set) {
157       setFlags(flags);
158     } else {
159       clearFlags(flags);
160     }
161   }
162 
kind()163   FunctionKind kind() const {
164     return static_cast<FunctionKind>((flags_ & FUNCTION_KIND_MASK) >>
165                                      FUNCTION_KIND_SHIFT);
166   }
167 
168   /* A function can be classified as either native (C++) or interpreted (JS): */
isInterpreted()169   bool isInterpreted() const {
170     return hasFlags(BASESCRIPT) || hasFlags(SELFHOSTLAZY);
171   }
isNative()172   bool isNative() const { return !isInterpreted(); }
173 
isConstructor()174   bool isConstructor() const { return hasFlags(CONSTRUCTOR); }
175 
176   /* Possible attributes of a native function: */
isAsmJSNative()177   bool isAsmJSNative() const {
178     MOZ_ASSERT_IF(kind() == AsmJS, isNative());
179     return kind() == AsmJS;
180   }
isWasm()181   bool isWasm() const {
182     MOZ_ASSERT_IF(kind() == Wasm, isNative());
183     return kind() == Wasm;
184   }
isWasmWithJitEntry()185   bool isWasmWithJitEntry() const {
186     MOZ_ASSERT_IF(hasFlags(WASM_JIT_ENTRY), isWasm());
187     return hasFlags(WASM_JIT_ENTRY);
188   }
isNativeWithJitEntry()189   bool isNativeWithJitEntry() const {
190     MOZ_ASSERT_IF(isWasmWithJitEntry(), isNative());
191     return isWasmWithJitEntry();
192   }
isBuiltinNative()193   bool isBuiltinNative() const {
194     return isNative() && !isAsmJSNative() && !isWasm();
195   }
196 
197   /* Possible attributes of an interpreted function: */
isBoundFunction()198   bool isBoundFunction() const { return hasFlags(BOUND_FUN); }
hasInferredName()199   bool hasInferredName() const { return hasFlags(HAS_INFERRED_NAME); }
hasGuessedAtom()200   bool hasGuessedAtom() const {
201     static_assert(HAS_GUESSED_ATOM == HAS_BOUND_FUNCTION_NAME_PREFIX,
202                   "HAS_GUESSED_ATOM is unused for bound functions");
203     bool hasGuessedAtom = hasFlags(HAS_GUESSED_ATOM);
204     bool boundFun = hasFlags(BOUND_FUN);
205     return hasGuessedAtom && !boundFun;
206   }
hasBoundFunctionNamePrefix()207   bool hasBoundFunctionNamePrefix() const {
208     static_assert(
209         HAS_BOUND_FUNCTION_NAME_PREFIX == HAS_GUESSED_ATOM,
210         "HAS_BOUND_FUNCTION_NAME_PREFIX is only used for bound functions");
211     MOZ_ASSERT(isBoundFunction());
212     return hasFlags(HAS_BOUND_FUNCTION_NAME_PREFIX);
213   }
isLambda()214   bool isLambda() const { return hasFlags(LAMBDA); }
215 
isNamedLambda(bool hasName)216   bool isNamedLambda(bool hasName) const {
217     return hasName && isLambda() && !hasInferredName() && !hasGuessedAtom();
218   }
219 
220   // These methods determine which of the u.scripted.s union arms are active.
221   // For live JSFunctions the pointer values will always be non-null, but due
222   // to partial initialization the GC (and other features that scan the heap
223   // directly) may still return a null pointer.
hasBaseScript()224   bool hasBaseScript() const { return hasFlags(BASESCRIPT); }
hasSelfHostedLazyScript()225   bool hasSelfHostedLazyScript() const { return hasFlags(SELFHOSTLAZY); }
226 
227   // Arrow functions store their lexical new.target in the first extended slot.
isArrow()228   bool isArrow() const { return kind() == Arrow; }
229   // Every class-constructor is also a method.
isMethod()230   bool isMethod() const {
231     return kind() == Method || kind() == ClassConstructor;
232   }
isClassConstructor()233   bool isClassConstructor() const { return kind() == ClassConstructor; }
234 
isGetter()235   bool isGetter() const { return kind() == Getter; }
isSetter()236   bool isSetter() const { return kind() == Setter; }
237 
allowSuperProperty()238   bool allowSuperProperty() const {
239     return isMethod() || isGetter() || isSetter();
240   }
241 
hasResolvedLength()242   bool hasResolvedLength() const { return hasFlags(RESOLVED_LENGTH); }
hasResolvedName()243   bool hasResolvedName() const { return hasFlags(RESOLVED_NAME); }
244 
isSelfHostedOrIntrinsic()245   bool isSelfHostedOrIntrinsic() const { return hasFlags(SELF_HOSTED); }
isSelfHostedBuiltin()246   bool isSelfHostedBuiltin() const {
247     return isSelfHostedOrIntrinsic() && !isNative();
248   }
isIntrinsic()249   bool isIntrinsic() const { return isSelfHostedOrIntrinsic() && isNative(); }
250 
setKind(FunctionKind kind)251   void setKind(FunctionKind kind) {
252     this->flags_ &= ~FUNCTION_KIND_MASK;
253     this->flags_ |= static_cast<uint16_t>(kind) << FUNCTION_KIND_SHIFT;
254   }
255 
256   // Make the function constructible.
setIsConstructor()257   void setIsConstructor() {
258     MOZ_ASSERT(!isConstructor());
259     MOZ_ASSERT(isSelfHostedBuiltin());
260     setFlags(CONSTRUCTOR);
261   }
262 
setIsClassConstructor()263   void setIsClassConstructor() {
264     MOZ_ASSERT(!isClassConstructor());
265     MOZ_ASSERT(isConstructor());
266 
267     setKind(ClassConstructor);
268   }
269 
setIsBoundFunction()270   void setIsBoundFunction() {
271     MOZ_ASSERT(!isBoundFunction());
272     setFlags(BOUND_FUN);
273   }
274 
setIsSelfHostedBuiltin()275   void setIsSelfHostedBuiltin() {
276     MOZ_ASSERT(isInterpreted());
277     MOZ_ASSERT(!isSelfHostedBuiltin());
278     setFlags(SELF_HOSTED);
279     // Self-hosted functions should not be constructable.
280     clearFlags(CONSTRUCTOR);
281   }
setIsIntrinsic()282   void setIsIntrinsic() {
283     MOZ_ASSERT(isNative());
284     MOZ_ASSERT(!isIntrinsic());
285     setFlags(SELF_HOSTED);
286   }
287 
setResolvedLength()288   void setResolvedLength() { setFlags(RESOLVED_LENGTH); }
setResolvedName()289   void setResolvedName() { setFlags(RESOLVED_NAME); }
290 
291   // Mark a function as having its 'new' script information cleared.
wasNewScriptCleared()292   bool wasNewScriptCleared() const { return hasFlags(NEW_SCRIPT_CLEARED); }
setNewScriptCleared()293   void setNewScriptCleared() { setFlags(NEW_SCRIPT_CLEARED); }
294 
setInferredName()295   void setInferredName() { setFlags(HAS_INFERRED_NAME); }
clearInferredName()296   void clearInferredName() { clearFlags(HAS_INFERRED_NAME); }
297 
setGuessedAtom()298   void setGuessedAtom() { setFlags(HAS_GUESSED_ATOM); }
299 
setPrefixedBoundFunctionName()300   void setPrefixedBoundFunctionName() {
301     setFlags(HAS_BOUND_FUNCTION_NAME_PREFIX);
302   }
303 
setSelfHostedLazy()304   void setSelfHostedLazy() { setFlags(SELFHOSTLAZY); }
clearSelfHostedLazy()305   void clearSelfHostedLazy() { clearFlags(SELFHOSTLAZY); }
setBaseScript()306   void setBaseScript() { setFlags(BASESCRIPT); }
clearBaseScript()307   void clearBaseScript() { clearFlags(BASESCRIPT); }
308 
setWasmJitEntry()309   void setWasmJitEntry() { setFlags(WASM_JIT_ENTRY); }
310 
isExtended()311   bool isExtended() const { return hasFlags(EXTENDED); }
setIsExtended()312   void setIsExtended() { setFlags(EXTENDED); }
313 
isNativeConstructor()314   bool isNativeConstructor() const { return hasFlags(NATIVE_CTOR); }
315 };
316 
317 } /* namespace js */
318 
319 #endif /* vm_FunctionFlags_h */
320