summaryrefslogtreecommitdiffstats
path: root/js/src/vm/FunctionFlags.h
blob: 7522fdb1ebd94780d7d5aabab97efd66eaa80de4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
/* -*- 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:
  enum FunctionKind : uint8_t {
    NormalFunction = 0,
    Arrow,   // ES6 '(args) => body' syntax
    Method,  // ES6 MethodDefinition
    ClassConstructor,
    Getter,
    Setter,
    AsmJS,  // An asm.js module or exported function
    Wasm,   // An exported WebAssembly function
    FunctionKindLimit
  };

  enum Flags : uint16_t {
    // The general kind of a function. This is used to describe characteristics
    // of functions that do not merit a dedicated flag bit below.
    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.
    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.
    CONSTRUCTOR = 1 << 7,

    // A 'Bound Function Exotic Object' created by Function.prototype.bind.
    BOUND_FUN = 1 << 8,

    // Function comes from a FunctionExpression, ArrowFunction, or Function()
    // call (not a FunctionDeclaration or nonstandard function-statement).
    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.
    HAS_INFERRED_NAME = 1 << 11,

    // Function had no explicit name, but a name was guessed for it anyway. For
    // a Bound function, tracks if atom_ already contains the "bound " prefix.
    ATOM_EXTRA_FLAG = 1 << 12,
    HAS_GUESSED_ATOM = ATOM_EXTRA_FLAG,
    HAS_BOUND_FUNCTION_NAME_PREFIX = ATOM_EXTRA_FLAG,

    // The 'length' or 'name property has been resolved. See fun_resolve.
    RESOLVED_NAME = 1 << 13,
    RESOLVED_LENGTH = 1 << 14,

    // (1 << 15 is unused)

    // 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,
    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. (Exception:
    // js::MakeDefaultConstructor produces default constructors for ECMAScript
    // classes by cloning self-hosted functions, and then clearing their
    // SELF_HOSTED bit, setting their CONSTRUCTOR bit, and otherwise munging
    // them to look like they originated with the class definition.) */
    STABLE_ACROSS_CLONES =
        CONSTRUCTOR | LAMBDA | SELF_HOSTED | FUNCTION_KIND_MASK
  };

  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; }
  void setFlags(uint16_t flags) { flags_ |= flags; }
  void clearFlags(uint16_t flags) { flags_ &= ~flags; }
  void setFlags(uint16_t flags, bool set) {
    if (set) {
      setFlags(flags);
    } else {
      clearFlags(flags);
    }
  }

  FunctionKind kind() const {
    return static_cast<FunctionKind>((flags_ & FUNCTION_KIND_MASK) >>
                                     FUNCTION_KIND_SHIFT);
  }

  /* A function can be classified as either native (C++) or interpreted (JS): */
  bool isInterpreted() const {
    return hasFlags(BASESCRIPT) || hasFlags(SELFHOSTLAZY);
  }
  bool isNative() 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, isNative());
    return kind() == AsmJS;
  }
  bool isWasm() const {
    MOZ_ASSERT_IF(kind() == Wasm, isNative());
    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(), isNative());
    return !hasJitEntry();
  }
  bool isBuiltinNative() const {
    return isNative() && !isAsmJSNative() && !isWasm();
  }
  bool hasJitEntry() const {
    return hasBaseScript() || hasSelfHostedLazyScript() || isWasmWithJitEntry();
  }

  /* Possible attributes of an interpreted function: */
  bool isBoundFunction() const { return hasFlags(BOUND_FUN); }
  bool hasInferredName() const { return hasFlags(HAS_INFERRED_NAME); }
  bool hasGuessedAtom() const {
    static_assert(HAS_GUESSED_ATOM == HAS_BOUND_FUNCTION_NAME_PREFIX,
                  "HAS_GUESSED_ATOM is unused for bound functions");
    bool hasGuessedAtom = hasFlags(HAS_GUESSED_ATOM);
    bool boundFun = hasFlags(BOUND_FUN);
    return hasGuessedAtom && !boundFun;
  }
  bool hasBoundFunctionNamePrefix() const {
    static_assert(
        HAS_BOUND_FUNCTION_NAME_PREFIX == HAS_GUESSED_ATOM,
        "HAS_BOUND_FUNCTION_NAME_PREFIX is only used for bound functions");
    MOZ_ASSERT(isBoundFunction());
    return hasFlags(HAS_BOUND_FUNCTION_NAME_PREFIX);
  }
  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 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() && !isNative();
  }
  bool isIntrinsic() const { return isSelfHostedOrIntrinsic() && isNative(); }

  void setKind(FunctionKind kind) {
    this->flags_ &= ~FUNCTION_KIND_MASK;
    this->flags_ |= static_cast<uint16_t>(kind) << FUNCTION_KIND_SHIFT;
  }

  // Make the function constructible.
  void setIsConstructor() {
    MOZ_ASSERT(!isConstructor());
    MOZ_ASSERT(isSelfHostedBuiltin());
    setFlags(CONSTRUCTOR);
  }

  void setIsClassConstructor() {
    MOZ_ASSERT(!isClassConstructor());
    MOZ_ASSERT(isConstructor());

    setKind(ClassConstructor);
  }

  void setIsBoundFunction() {
    MOZ_ASSERT(!isBoundFunction());
    setFlags(BOUND_FUN);
  }

  void setIsSelfHostedBuiltin() {
    MOZ_ASSERT(isInterpreted());
    MOZ_ASSERT(!isSelfHostedBuiltin());
    setFlags(SELF_HOSTED);
    // Self-hosted functions should not be constructable.
    clearFlags(CONSTRUCTOR);
  }
  void setIsIntrinsic() {
    MOZ_ASSERT(isNative());
    MOZ_ASSERT(!isIntrinsic());
    setFlags(SELF_HOSTED);
  }

  void setResolvedLength() { setFlags(RESOLVED_LENGTH); }
  void setResolvedName() { setFlags(RESOLVED_NAME); }

  void setInferredName() { setFlags(HAS_INFERRED_NAME); }

  void setGuessedAtom() { setFlags(HAS_GUESSED_ATOM); }

  void setPrefixedBoundFunctionName() {
    setFlags(HAS_BOUND_FUNCTION_NAME_PREFIX);
  }

  void setSelfHostedLazy() { setFlags(SELFHOSTLAZY); }
  void clearSelfHostedLazy() { clearFlags(SELFHOSTLAZY); }
  void setBaseScript() { setFlags(BASESCRIPT); }
  void clearBaseScript() { clearFlags(BASESCRIPT); }

  void setWasmJitEntry() { setFlags(WASM_JIT_ENTRY); }

  bool isExtended() const { return hasFlags(EXTENDED); }
  void setIsExtended() { setFlags(EXTENDED); }

  bool isNativeConstructor() const { return hasFlags(NATIVE_CTOR); }

  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 */