BoundFunctionObject.h (6108B)
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_BoundFunctionObject_h 8 #define vm_BoundFunctionObject_h 9 10 #include "jstypes.h" 11 12 #include "gc/Policy.h" 13 #include "vm/ArrayObject.h" 14 #include "vm/JSObject.h" 15 16 namespace js { 17 18 // Implementation of Bound Function Exotic Objects. 19 // ES2023 10.4.1 20 // https://tc39.es/ecma262/#sec-bound-function-exotic-objects 21 class BoundFunctionObject : public NativeObject { 22 public: 23 static const JSClass class_; 24 25 // FlagsSlot uses the low bit for the is-constructor flag and the other bits 26 // for the number of arguments. 27 static constexpr size_t IsConstructorFlag = 0b1; 28 static constexpr size_t NumBoundArgsShift = 1; 29 30 // The maximum number of bound arguments that can be stored inline in 31 // BoundArg*Slot. 32 static constexpr size_t MaxInlineBoundArgs = 3; 33 34 private: 35 enum { 36 // The [[BoundTargetFunction]] (a callable object). 37 TargetSlot, 38 39 // The number of arguments + the is-constructor flag, stored as Int32Value. 40 FlagsSlot, 41 42 // The [[BoundThis]] Value. 43 BoundThisSlot, 44 45 // The [[BoundArguments]]. If numBoundArgs exceeds MaxInlineBoundArgs, 46 // BoundArg0Slot will contain an array object that stores the values and the 47 // other two slots will be unused. 48 BoundArg0Slot, 49 BoundArg1Slot, 50 BoundArg2Slot, 51 52 // Initial slots for the `length` and `name` own data properties. Note that 53 // these properties are configurable, so these slots can be mutated when the 54 // object is exposed to JS. 55 LengthSlot, 56 NameSlot, 57 58 SlotCount 59 }; 60 61 // The AllocKind should match SlotCount. See assertion in functionBindImpl. 62 static constexpr gc::AllocKind allocKind = gc::AllocKind::OBJECT8; 63 64 void initFlags(size_t numBoundArgs, bool isConstructor) { 65 int32_t val = (numBoundArgs << NumBoundArgsShift) | isConstructor; 66 initReservedSlot(FlagsSlot, Int32Value(val)); 67 } 68 69 public: 70 size_t numBoundArgs() const { 71 int32_t v = getReservedSlot(FlagsSlot).toInt32(); 72 MOZ_ASSERT(v >= 0); 73 return v >> NumBoundArgsShift; 74 } 75 bool isConstructor() const { 76 int32_t v = getReservedSlot(FlagsSlot).toInt32(); 77 return v & IsConstructorFlag; 78 } 79 80 Value getTargetVal() const { return getReservedSlot(TargetSlot); } 81 JSObject* getTarget() const { return &getTargetVal().toObject(); } 82 83 Value getBoundThis() const { return getReservedSlot(BoundThisSlot); } 84 85 Value getInlineBoundArg(size_t i) const { 86 MOZ_ASSERT(i < numBoundArgs()); 87 MOZ_ASSERT(numBoundArgs() <= MaxInlineBoundArgs); 88 return getReservedSlot(BoundArg0Slot + i); 89 } 90 ArrayObject* getBoundArgsArray() const { 91 MOZ_ASSERT(numBoundArgs() > MaxInlineBoundArgs); 92 return &getReservedSlot(BoundArg0Slot).toObject().as<ArrayObject>(); 93 } 94 Value getBoundArg(size_t i) const { 95 MOZ_ASSERT(i < numBoundArgs()); 96 if (numBoundArgs() <= MaxInlineBoundArgs) { 97 return getInlineBoundArg(i); 98 } 99 return getBoundArgsArray()->getDenseElement(i); 100 } 101 102 void initLength(double len) { 103 MOZ_ASSERT(getReservedSlot(LengthSlot).isUndefined()); 104 initReservedSlot(LengthSlot, NumberValue(len)); 105 } 106 void initName(JSAtom* name) { 107 MOZ_ASSERT(getReservedSlot(NameSlot).isUndefined()); 108 initReservedSlot(NameSlot, StringValue(name)); 109 } 110 111 // Get the `length` and `name` property values when the object has the 112 // original shape. See comment for LengthSlot and NameSlot. 113 Value getLengthForInitialShape() const { return getReservedSlot(LengthSlot); } 114 Value getNameForInitialShape() const { return getReservedSlot(NameSlot); } 115 116 // The [[Call]] and [[Construct]] hooks. 117 static bool call(JSContext* cx, unsigned argc, Value* vp); 118 static bool construct(JSContext* cx, unsigned argc, Value* vp); 119 120 // The JSFunToStringOp implementation for Function.prototype.toString. 121 static JSString* funToString(JSContext* cx, Handle<JSObject*> obj, 122 bool isToSource); 123 124 // Implementation of Function.prototype.bind. 125 static bool functionBind(JSContext* cx, unsigned argc, Value* vp); 126 127 static SharedShape* assignInitialShape(JSContext* cx, 128 Handle<BoundFunctionObject*> obj); 129 130 static BoundFunctionObject* functionBindImpl( 131 JSContext* cx, Handle<JSObject*> target, Value* args, uint32_t argc, 132 Handle<BoundFunctionObject*> maybeBound); 133 134 static BoundFunctionObject* createWithTemplate( 135 JSContext* cx, Handle<BoundFunctionObject*> templateObj); 136 static BoundFunctionObject* functionBindSpecializedBaseline( 137 JSContext* cx, Handle<JSObject*> target, Value* args, uint32_t argc, 138 Handle<BoundFunctionObject*> templateObj); 139 140 static BoundFunctionObject* createTemplateObject(JSContext* cx); 141 142 bool initTemplateSlotsForSpecializedBind(JSContext* cx, uint32_t numBoundArgs, 143 bool targetIsConstructor, 144 uint32_t targetLength, 145 JSAtom* targetName); 146 147 static constexpr size_t offsetOfTargetSlot() { 148 return getFixedSlotOffset(TargetSlot); 149 } 150 static constexpr size_t offsetOfFlagsSlot() { 151 return getFixedSlotOffset(FlagsSlot); 152 } 153 static constexpr size_t offsetOfBoundThisSlot() { 154 return getFixedSlotOffset(BoundThisSlot); 155 } 156 static constexpr size_t offsetOfFirstInlineBoundArg() { 157 return getFixedSlotOffset(BoundArg0Slot); 158 } 159 static constexpr size_t offsetOfLengthSlot() { 160 return getFixedSlotOffset(LengthSlot); 161 } 162 static constexpr size_t offsetOfNameSlot() { 163 return getFixedSlotOffset(NameSlot); 164 } 165 166 static constexpr size_t targetSlot() { return TargetSlot; } 167 static constexpr size_t boundThisSlot() { return BoundThisSlot; } 168 static constexpr size_t firstInlineBoundArgSlot() { return BoundArg0Slot; } 169 }; 170 171 }; // namespace js 172 173 #endif /* vm_BoundFunctionObject_h */