summaryrefslogtreecommitdiffstats
path: root/js/src/vm/PromiseLookup.h
blob: 9c4fff556441277d7d7b4d7fb4df4855a7a02912 (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
/* -*- 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_PromiseLookup_h
#define vm_PromiseLookup_h

#include "mozilla/Attributes.h"  // MOZ_NON_TEMPORARY_CLASS, MOZ_INIT_OUTSIDE_CTOR

#include <stdint.h>  // uint8_t, uint32_t

#include "js/CallArgs.h"  // JSNative

struct JS_PUBLIC_API JSContext;

class JSFunction;

namespace js {

class NativeObject;
class PromiseObject;
class Shape;

class MOZ_NON_TEMPORARY_CLASS PromiseLookup final {
  // clang-format off
    /*
     * A PromiseLookup holds the following:
     *
     *  Promise's shape (promiseConstructorShape_)
     *       To ensure that Promise has not been modified.
     *
     *  Promise.prototype's shape (promiseProtoShape_)
     *      To ensure that Promise.prototype has not been modified.
     *
     *  Promise's shape for the @@species getter. (promiseSpeciesShape_)
     *      To quickly retrieve the @@species getter for Promise.
     *
     *  Promise's slot number for resolve (promiseResolveSlot_)
     *      To quickly retrieve the Promise.resolve function.
     *
     *  Promise.prototype's slot number for constructor (promiseProtoConstructorSlot_)
     *      To quickly retrieve the Promise.prototype.constructor property.
     *
     *  Promise.prototype's slot number for then (promiseProtoThenSlot_)
     *      To quickly retrieve the Promise.prototype.then function.
     *
     * MOZ_INIT_OUTSIDE_CTOR fields below are set in |initialize()|.  The
     * constructor only initializes a |state_| field, that defines whether the
     * other fields are accessible.
     */
  // clang-format on

  // Shape of matching Promise object.
  MOZ_INIT_OUTSIDE_CTOR Shape* promiseConstructorShape_;

#ifdef DEBUG
  // Accessor Shape containing the @@species property.
  // See isPromiseStateStillSane() for why this field is debug-only.
  MOZ_INIT_OUTSIDE_CTOR Shape* promiseSpeciesShape_;
#endif

  // Shape of matching Promise.prototype object.
  MOZ_INIT_OUTSIDE_CTOR Shape* promiseProtoShape_;

  // Slots Promise.resolve, Promise.prototype.constructor, and
  // Promise.prototype.then.
  MOZ_INIT_OUTSIDE_CTOR uint32_t promiseResolveSlot_;
  MOZ_INIT_OUTSIDE_CTOR uint32_t promiseProtoConstructorSlot_;
  MOZ_INIT_OUTSIDE_CTOR uint32_t promiseProtoThenSlot_;

  enum class State : uint8_t {
    // Flags marking the lazy initialization of the above fields.
    Uninitialized,
    Initialized,

    // The disabled flag is set when we don't want to try optimizing
    // anymore because core objects were changed.
    Disabled
  };

  State state_ = State::Uninitialized;

  // Initialize the internal fields.
  //
  // The cache is successfully initialized iff
  // 1. Promise and Promise.prototype classes are initialized.
  // 2. Promise.prototype.constructor is equal to Promise.
  // 3. Promise.prototype.then is the original `then` function.
  // 4. Promise[@@species] is the original @@species getter.
  // 5. Promise.resolve is the original `resolve` function.
  void initialize(JSContext* cx);

  // Reset the cache.
  void reset();

  // Check if the global promise-related objects have not been messed with
  // in a way that would disable this cache.
  bool isPromiseStateStillSane(JSContext* cx);

  // Flags to control whether or not ensureInitialized() is allowed to
  // reinitialize the cache when the Promise state is no longer sane.
  enum class Reinitialize : bool { Allowed, Disallowed };

  // Return true if the lookup cache is properly initialized for usage.
  bool ensureInitialized(JSContext* cx, Reinitialize reinitialize);

  // Return true if the prototype of the given Promise object is
  // Promise.prototype and the object doesn't shadow properties from
  // Promise.prototype.
  bool hasDefaultProtoAndNoShadowedProperties(JSContext* cx,
                                              PromiseObject* promise);

  // Return true if the given Promise object uses the default @@species,
  // "constructor", and "then" properties.
  bool isDefaultInstance(JSContext* cx, PromiseObject* promise,
                         Reinitialize reinitialize);

  // Return the built-in Promise constructor or null if not yet initialized.
  static JSFunction* getPromiseConstructor(JSContext* cx);

  // Return the built-in Promise prototype or null if not yet initialized.
  static NativeObject* getPromisePrototype(JSContext* cx);

  // Return true if the slot contains the given native.
  static bool isDataPropertyNative(JSContext* cx, NativeObject* obj,
                                   uint32_t slot, JSNative native);

  // Return true if the accessor shape contains the given native.
  static bool isAccessorPropertyNative(JSContext* cx, Shape* shape,
                                       JSNative native);

 public:
  /** Construct a |PromiseSpeciesLookup| in the uninitialized state. */
  PromiseLookup() { reset(); }

  // Return true if the Promise constructor and Promise.prototype still use
  // the default built-in functions.
  bool isDefaultPromiseState(JSContext* cx);

  // Return true if the given Promise object uses the default @@species,
  // "constructor", and "then" properties.
  bool isDefaultInstance(JSContext* cx, PromiseObject* promise) {
    return isDefaultInstance(cx, promise, Reinitialize::Allowed);
  }

  // Return true if the given Promise object uses the default @@species,
  // "constructor", and "then" properties.
  bool isDefaultInstanceWhenPromiseStateIsSane(JSContext* cx,
                                               PromiseObject* promise) {
    return isDefaultInstance(cx, promise, Reinitialize::Disallowed);
  }

  // Purge the cache and all info associated with it.
  void purge() {
    if (state_ == State::Initialized) {
      reset();
    }
  }
};

}  // namespace js

#endif  // vm_PromiseLookup_h