ProxyObject.h (5764B)
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_ProxyObject_h 8 #define vm_ProxyObject_h 9 10 #include "js/Proxy.h" 11 #include "js/shadow/Object.h" // JS::shadow::Object 12 #include "vm/JSObject.h" 13 14 namespace js { 15 16 /** 17 * This is the base class for the various kinds of proxy objects. It's never 18 * instantiated. 19 * 20 * Proxy objects use their shape primarily to record flags. Property 21 * information, &c. is all dynamically computed. 22 * 23 * There is no class_ member to force specialization of JSObject::is<T>(). 24 * The implementation in JSObject is incorrect for proxies since it doesn't 25 * take account of the handler type. 26 */ 27 class ProxyObject : public JSObject { 28 // GetProxyDataLayout computes the address of this field. 29 detail::ProxyDataLayout data; 30 31 void static_asserts() { 32 static_assert(sizeof(ProxyObject) == sizeof(JSObject_Slots0), 33 "proxy object size must match GC thing size"); 34 static_assert(offsetof(ProxyObject, data) == detail::ProxyDataOffset, 35 "proxy object layout must match shadow interface"); 36 static_assert(offsetof(ProxyObject, data.reservedSlots) == 37 offsetof(JS::shadow::Object, slots), 38 "Proxy reservedSlots must overlay native object slots field"); 39 } 40 41 public: 42 static ProxyObject* New(JSContext* cx, const BaseProxyHandler* handler, 43 HandleValue priv, TaggedProto proto_, 44 const JSClass* clasp); 45 46 void init(const BaseProxyHandler* handler, HandleValue priv, JSContext* cx); 47 48 // Proxies usually store their ProxyValueArray inline in the object. 49 // There's one unfortunate exception: when a proxy is swapped with another 50 // object, and the sizes don't match, we malloc the ProxyValueArray. 51 void* inlineDataStart() const { 52 return (void*)(uintptr_t(this) + sizeof(ProxyObject)); 53 } 54 bool usingInlineValueArray() const { 55 return data.values() == inlineDataStart(); 56 } 57 void setInlineValueArray() { 58 data.reservedSlots = 59 &reinterpret_cast<detail::ProxyValueArray*>(inlineDataStart()) 60 ->reservedSlots; 61 } 62 63 // For use from JSObject::swap. 64 [[nodiscard]] bool prepareForSwap(JSContext* cx, 65 MutableHandleValueVector valuesOut); 66 [[nodiscard]] bool fixupAfterSwap(JSContext* cx, HandleValueVector values); 67 68 const Value& private_() const { return GetProxyPrivate(this); } 69 const Value& expando() const { return GetProxyExpando(this); } 70 71 void setExpando(JSObject* expando); 72 73 void setCrossCompartmentPrivate(const Value& priv); 74 void setSameCompartmentPrivate(const Value& priv); 75 76 JSObject* target() const { return private_().toObjectOrNull(); } 77 78 const BaseProxyHandler* handler() const { return GetProxyHandler(this); } 79 80 void setHandler(const BaseProxyHandler* handler) { 81 SetProxyHandler(this, handler); 82 } 83 84 static size_t offsetOfReservedSlots() { 85 return offsetof(ProxyObject, data.reservedSlots); 86 } 87 static size_t offsetOfHandler() { 88 return offsetof(ProxyObject, data.handler); 89 } 90 91 size_t numReservedSlots() const { return JSCLASS_RESERVED_SLOTS(getClass()); } 92 const Value& reservedSlot(size_t n) const { 93 return GetProxyReservedSlot(this, n); 94 } 95 96 void setReservedSlot(size_t n, const Value& extra) { 97 SetProxyReservedSlot(this, n, extra); 98 } 99 100 gc::AllocKind allocKindForTenure() const; 101 102 private: 103 GCPtr<Value>* reservedSlotPtr(size_t n) { 104 return reinterpret_cast<GCPtr<Value>*>( 105 &detail::GetProxyDataLayout(this)->reservedSlots->slots[n]); 106 } 107 108 GCPtr<Value>* slotOfPrivate() { 109 return reinterpret_cast<GCPtr<Value>*>( 110 &detail::GetProxyDataLayout(this)->values()->privateSlot); 111 } 112 113 GCPtr<Value>* slotOfExpando() { 114 return reinterpret_cast<GCPtr<Value>*>( 115 &detail::GetProxyDataLayout(this)->values()->expandoSlot); 116 } 117 118 void setPrivate(const Value& priv); 119 120 static bool isValidProxyClass(const JSClass* clasp) { 121 // Since we can take classes from the outside, make sure that they 122 // are "sane". They have to quack enough like proxies for us to belive 123 // they should be treated as such. 124 125 // Proxy classes are not allowed to have call or construct hooks directly. 126 // Their callability is instead decided by handler()->isCallable(). 127 return clasp->isProxyObject() && clasp->isTrace(ProxyObject::trace) && 128 !clasp->getCall() && !clasp->getConstruct(); 129 } 130 131 public: 132 static unsigned grayLinkReservedSlot(JSObject* obj); 133 134 void renew(const BaseProxyHandler* handler, const Value& priv); 135 136 static void trace(JSTracer* trc, JSObject* obj); 137 138 static void traceEdgeToTarget(JSTracer* trc, ProxyObject* obj); 139 140 void nurseryProxyTenured(ProxyObject* old); 141 142 void nuke(); 143 }; 144 145 bool IsDerivedProxyObject(const JSObject* obj, 146 const js::BaseProxyHandler* handler); 147 148 } // namespace js 149 150 template <> 151 inline bool JSObject::is<js::ProxyObject>() const { 152 // Note: this method is implemented in terms of the IsProxy() friend API 153 // functions to ensure the implementations are tied together. 154 // Note 2: this specialization isn't used for subclasses of ProxyObject 155 // which must supply their own implementation. 156 return js::IsProxy(this); 157 } 158 159 inline bool js::IsDerivedProxyObject(const JSObject* obj, 160 const js::BaseProxyHandler* handler) { 161 return obj->is<js::ProxyObject>() && 162 obj->as<js::ProxyObject>().handler() == handler; 163 } 164 165 #endif /* vm_ProxyObject_h */