summaryrefslogtreecommitdiffstats
path: root/js/src/vm/BoundFunctionObject.h
blob: 566bdc0bed8bfab6117298338f2797f8ae34d88a (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
/* -*- 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_BoundFunctionObject_h
#define vm_BoundFunctionObject_h

#include "jstypes.h"

#include "gc/Policy.h"
#include "vm/ArrayObject.h"
#include "vm/JSAtom.h"
#include "vm/JSObject.h"

namespace js {

// Implementation of Bound Function Exotic Objects.
// ES2023 10.4.1
// https://tc39.es/ecma262/#sec-bound-function-exotic-objects
class BoundFunctionObject : public NativeObject {
 public:
  static const JSClass class_;

  // FlagsSlot uses the low bit for the is-constructor flag and the other bits
  // for the number of arguments.
  static constexpr size_t IsConstructorFlag = 0b1;
  static constexpr size_t NumBoundArgsShift = 1;

  // The maximum number of bound arguments that can be stored inline in
  // BoundArg*Slot.
  static constexpr size_t MaxInlineBoundArgs = 3;

 private:
  enum {
    // The [[BoundTargetFunction]] (a callable object).
    TargetSlot,

    // The number of arguments + the is-constructor flag, stored as Int32Value.
    FlagsSlot,

    // The [[BoundThis]] Value.
    BoundThisSlot,

    // The [[BoundArguments]]. If numBoundArgs exceeds MaxInlineBoundArgs,
    // BoundArg0Slot will contain an array object that stores the values and the
    // other two slots will be unused.
    BoundArg0Slot,
    BoundArg1Slot,
    BoundArg2Slot,

    // Initial slots for the `length` and `name` own data properties. Note that
    // these properties are configurable, so these slots can be mutated when the
    // object is exposed to JS.
    LengthSlot,
    NameSlot,

    SlotCount
  };

  // The AllocKind should match SlotCount. See assertion in functionBindImpl.
  static constexpr gc::AllocKind allocKind = gc::AllocKind::OBJECT8_BACKGROUND;

  void initFlags(size_t numBoundArgs, bool isConstructor) {
    int32_t val = (numBoundArgs << NumBoundArgsShift) | isConstructor;
    initReservedSlot(FlagsSlot, Int32Value(val));
  }

 public:
  size_t numBoundArgs() const {
    int32_t v = getReservedSlot(FlagsSlot).toInt32();
    MOZ_ASSERT(v >= 0);
    return v >> NumBoundArgsShift;
  }
  bool isConstructor() const {
    int32_t v = getReservedSlot(FlagsSlot).toInt32();
    return v & IsConstructorFlag;
  }

  Value getTargetVal() const { return getReservedSlot(TargetSlot); }
  JSObject* getTarget() const { return &getTargetVal().toObject(); }

  Value getBoundThis() const { return getReservedSlot(BoundThisSlot); }

  Value getInlineBoundArg(size_t i) const {
    MOZ_ASSERT(i < numBoundArgs());
    MOZ_ASSERT(numBoundArgs() <= MaxInlineBoundArgs);
    return getReservedSlot(BoundArg0Slot + i);
  }
  ArrayObject* getBoundArgsArray() const {
    MOZ_ASSERT(numBoundArgs() > MaxInlineBoundArgs);
    return &getReservedSlot(BoundArg0Slot).toObject().as<ArrayObject>();
  }
  Value getBoundArg(size_t i) const {
    MOZ_ASSERT(i < numBoundArgs());
    if (numBoundArgs() <= MaxInlineBoundArgs) {
      return getInlineBoundArg(i);
    }
    return getBoundArgsArray()->getDenseElement(i);
  }

  void initLength(double len) {
    MOZ_ASSERT(getReservedSlot(LengthSlot).isUndefined());
    initReservedSlot(LengthSlot, NumberValue(len));
  }
  void initName(JSAtom* name) {
    MOZ_ASSERT(getReservedSlot(NameSlot).isUndefined());
    initReservedSlot(NameSlot, StringValue(name));
  }

  // Get the `length` and `name` property values when the object has the
  // original shape. See comment for LengthSlot and NameSlot.
  Value getLengthForInitialShape() const { return getReservedSlot(LengthSlot); }
  Value getNameForInitialShape() const { return getReservedSlot(NameSlot); }

  // The [[Call]] and [[Construct]] hooks.
  static bool call(JSContext* cx, unsigned argc, Value* vp);
  static bool construct(JSContext* cx, unsigned argc, Value* vp);

  // The JSFunToStringOp implementation for Function.prototype.toString.
  static JSString* funToString(JSContext* cx, Handle<JSObject*> obj,
                               bool isToSource);

  // Implementation of Function.prototype.bind.
  static bool functionBind(JSContext* cx, unsigned argc, Value* vp);

  static SharedShape* assignInitialShape(JSContext* cx,
                                         Handle<BoundFunctionObject*> obj);

  static BoundFunctionObject* functionBindImpl(
      JSContext* cx, Handle<JSObject*> target, Value* args, uint32_t argc,
      Handle<BoundFunctionObject*> maybeBound);

  static BoundFunctionObject* createWithTemplate(
      JSContext* cx, Handle<BoundFunctionObject*> templateObj);
  static BoundFunctionObject* functionBindSpecializedBaseline(
      JSContext* cx, Handle<JSObject*> target, Value* args, uint32_t argc,
      Handle<BoundFunctionObject*> templateObj);

  static BoundFunctionObject* createTemplateObject(JSContext* cx);

  bool initTemplateSlotsForSpecializedBind(JSContext* cx, uint32_t numBoundArgs,
                                           bool targetIsConstructor,
                                           uint32_t targetLength,
                                           JSAtom* targetName);

  static constexpr size_t offsetOfTargetSlot() {
    return getFixedSlotOffset(TargetSlot);
  }
  static constexpr size_t offsetOfFlagsSlot() {
    return getFixedSlotOffset(FlagsSlot);
  }
  static constexpr size_t offsetOfBoundThisSlot() {
    return getFixedSlotOffset(BoundThisSlot);
  }
  static constexpr size_t offsetOfFirstInlineBoundArg() {
    return getFixedSlotOffset(BoundArg0Slot);
  }
  static constexpr size_t offsetOfLengthSlot() {
    return getFixedSlotOffset(LengthSlot);
  }
  static constexpr size_t offsetOfNameSlot() {
    return getFixedSlotOffset(NameSlot);
  }

  static constexpr size_t targetSlot() { return TargetSlot; }
  static constexpr size_t boundThisSlot() { return BoundThisSlot; }
  static constexpr size_t firstInlineBoundArgSlot() { return BoundArg0Slot; }
};

};  // namespace js

#endif /* vm_BoundFunctionObject_h */