DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Implementation

Mercurial (b6d82b1a6b02)

VCS Links

Line Code
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
/* -*- 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_ProxyObject_h
#define vm_ProxyObject_h

#include "js/Proxy.h"
#include "vm/JSObject.h"

namespace js {

/**
 * This is the base class for the various kinds of proxy objects.  It's never
 * instantiated.
 *
 * Proxy objects use their shape primarily to record flags. Property
 * information, &c. is all dynamically computed.
 *
 * There is no class_ member to force specialization of JSObject::is<T>().
 * The implementation in JSObject is incorrect for proxies since it doesn't
 * take account of the handler type.
 */
class ProxyObject : public JSObject {
  // GetProxyDataLayout computes the address of this field.
  detail::ProxyDataLayout data;

  void static_asserts() {
    static_assert(sizeof(ProxyObject) == sizeof(JSObject_Slots0),
                  "proxy object size must match GC thing size");
    static_assert(offsetof(ProxyObject, data) == detail::ProxyDataOffset,
                  "proxy object layout must match shadow interface");
    static_assert(offsetof(ProxyObject, data.reservedSlots) ==
                      offsetof(shadow::Object, slots),
                  "Proxy reservedSlots must overlay native object slots field");
  }

  static JS::Result<ProxyObject*, JS::OOM&> create(JSContext* cx,
                                                   const JSClass* clasp,
                                                   Handle<TaggedProto> proto,
                                                   js::gc::AllocKind allocKind,
                                                   js::NewObjectKind newKind);

 public:
  static ProxyObject* New(JSContext* cx, const BaseProxyHandler* handler,
                          HandleValue priv, TaggedProto proto_,
                          const ProxyOptions& options);

  // Proxies usually store their ProxyValueArray inline in the object.
  // There's one unfortunate exception: when a proxy is swapped with another
  // object, and the sizes don't match, we malloc the ProxyValueArray.
  void* inlineDataStart() const {
    return (void*)(uintptr_t(this) + sizeof(ProxyObject));
  }
  bool usingInlineValueArray() const {
    return data.values() == inlineDataStart();
  }
  void setInlineValueArray() {
    data.reservedSlots =
        &reinterpret_cast<detail::ProxyValueArray*>(inlineDataStart())
             ->reservedSlots;
  }
  MOZ_MUST_USE bool initExternalValueArrayAfterSwap(JSContext* cx,
                                                    HandleValueVector values);

  const Value& private_() const { return GetProxyPrivate(this); }

  void setCrossCompartmentPrivate(const Value& priv);
  void setSameCompartmentPrivate(const Value& priv);

  JSObject* target() const { return private_().toObjectOrNull(); }

  const BaseProxyHandler* handler() const { return GetProxyHandler(this); }

  void setHandler(const BaseProxyHandler* handler) {
    SetProxyHandler(this, handler);
  }

  static size_t offsetOfReservedSlots() {
    return offsetof(ProxyObject, data.reservedSlots);
  }
  static size_t offsetOfHandler() {
    return offsetof(ProxyObject, data.handler);
  }

  size_t numReservedSlots() const { return JSCLASS_RESERVED_SLOTS(getClass()); }
  const Value& reservedSlot(size_t n) const {
    return GetProxyReservedSlot(this, n);
  }

  void setReservedSlot(size_t n, const Value& extra) {
    SetProxyReservedSlot(this, n, extra);
  }

  gc::AllocKind allocKindForTenure() const;

 private:
  GCPtrValue* reservedSlotPtr(size_t n) {
    return reinterpret_cast<GCPtrValue*>(
        &detail::GetProxyDataLayout(this)->reservedSlots->slots[n]);
  }

  GCPtrValue* slotOfPrivate() {
    return reinterpret_cast<GCPtrValue*>(
        &detail::GetProxyDataLayout(this)->values()->privateSlot);
  }

  void setPrivate(const Value& priv);

  static bool isValidProxyClass(const JSClass* clasp) {
    // Since we can take classes from the outside, make sure that they
    // are "sane". They have to quack enough like proxies for us to belive
    // they should be treated as such.

    // Proxy classes are not allowed to have call or construct hooks directly.
    // Their callability is instead decided by handler()->isCallable().
    return clasp->isProxy() && clasp->isTrace(ProxyObject::trace) &&
           !clasp->getCall() && !clasp->getConstruct();
  }

 public:
  static unsigned grayLinkReservedSlot(JSObject* obj);

  void renew(const BaseProxyHandler* handler, const Value& priv);

  static void trace(JSTracer* trc, JSObject* obj);

  static void traceEdgeToTarget(JSTracer* trc, ProxyObject* obj);

  void nuke();
};

inline bool IsProxyClass(const JSClass* clasp) { return clasp->isProxy(); }

bool IsDerivedProxyObject(const JSObject* obj,
                          const js::BaseProxyHandler* handler);

}  // namespace js

template <>
inline bool JSObject::is<js::ProxyObject>() const {
  // Note: this method is implemented in terms of the IsProxy() friend API
  // functions to ensure the implementations are tied together.
  // Note 2: this specialization isn't used for subclasses of ProxyObject
  // which must supply their own implementation.
  return js::IsProxy(this);
}

inline bool js::IsDerivedProxyObject(const JSObject* obj,
                                     const js::BaseProxyHandler* handler) {
  return obj->is<js::ProxyObject>() &&
         obj->as<js::ProxyObject>().handler() == handler;
}

#endif /* vm_ProxyObject_h */