tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ProxyObject.cpp (6908B)


      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 #include "vm/ProxyObject.h"
      8 
      9 #include "gc/GCProbes.h"
     10 #include "gc/Marking.h"
     11 #include "gc/Zone.h"
     12 #include "proxy/DeadObjectProxy.h"
     13 #include "vm/Compartment.h"
     14 #include "vm/Realm.h"
     15 
     16 #include "gc/ObjectKind-inl.h"
     17 #include "vm/JSContext-inl.h"
     18 
     19 using namespace js;
     20 
     21 static gc::AllocKind GetProxyGCObjectKind(const JSClass* clasp,
     22                                          const BaseProxyHandler* handler,
     23                                          const Value& priv,
     24                                          bool withInlineValues) {
     25  MOZ_ASSERT(clasp->isProxyObject());
     26 
     27  uint32_t nreserved = JSCLASS_RESERVED_SLOTS(clasp);
     28 
     29  // For now assert each Proxy Class has at least 1 reserved slot. This is
     30  // not a hard requirement, but helps catch Classes that need an explicit
     31  // JSCLASS_HAS_RESERVED_SLOTS since bug 1360523.
     32  MOZ_ASSERT(nreserved > 0);
     33 
     34  uint32_t nslots = 0;
     35  if (withInlineValues) {
     36    nslots = detail::ProxyValueArray::allocCount(nreserved);
     37  }
     38 
     39  MOZ_ASSERT(nslots <= NativeObject::MAX_FIXED_SLOTS);
     40  gc::AllocKind kind = gc::GetGCObjectKind(nslots);
     41  gc::FinalizeKind finalizeKind;
     42 
     43  // Bug 1957589: Support non-finalized proxies as well.
     44  if (handler->finalizeInBackground(priv)) {
     45    finalizeKind = gc::FinalizeKind::Background;
     46  } else {
     47    finalizeKind = gc::FinalizeKind::Foreground;
     48  }
     49 
     50  return gc::GetFinalizedAllocKind(kind, finalizeKind);
     51 }
     52 
     53 void ProxyObject::init(const BaseProxyHandler* handler, HandleValue priv,
     54                       JSContext* cx) {
     55  setInlineValueArray();
     56 
     57  detail::ProxyValueArray* values = detail::GetProxyDataLayout(this)->values();
     58  values->init(numReservedSlots());
     59 
     60  data.handler = handler;
     61 
     62  if (IsCrossCompartmentWrapper(this)) {
     63    MOZ_ASSERT(cx->global() == &cx->compartment()->globalForNewCCW());
     64    setCrossCompartmentPrivate(priv);
     65  } else {
     66    setSameCompartmentPrivate(priv);
     67  }
     68 
     69  // The expando slot is nullptr until required by the installation of
     70  // a private field.
     71  setExpando(nullptr);
     72 }
     73 
     74 /* static */
     75 ProxyObject* ProxyObject::New(JSContext* cx, const BaseProxyHandler* handler,
     76                              HandleValue priv, TaggedProto proto_,
     77                              const JSClass* clasp) {
     78  Rooted<TaggedProto> proto(cx, proto_);
     79 
     80  MOZ_ASSERT(!clasp->isNativeObject());
     81  MOZ_ASSERT(clasp->isProxyObject());
     82  MOZ_ASSERT(isValidProxyClass(clasp));
     83  MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
     84  MOZ_ASSERT_IF(proto.isObject(),
     85                cx->compartment() == proto.toObject()->compartment());
     86  MOZ_ASSERT(clasp->hasFinalize());
     87 
     88 #ifdef DEBUG
     89  if (priv.isGCThing()) {
     90    JS::AssertCellIsNotGray(priv.toGCThing());
     91  }
     92 #endif
     93 
     94  gc::AllocKind allocKind = GetProxyGCObjectKind(clasp, handler, priv,
     95                                                 /* withInlineValues = */ true);
     96 
     97  Realm* realm = cx->realm();
     98 
     99  AutoSetNewObjectMetadata metadata(cx);
    100  // Try to look up the shape in the NewProxyCache.
    101  Rooted<Shape*> shape(cx);
    102  if (!realm->newProxyCache.lookup(clasp, proto, shape.address())) {
    103    shape = ProxyShape::getShape(cx, clasp, realm, proto, ObjectFlags());
    104    if (!shape) {
    105      return nullptr;
    106    }
    107 
    108    realm->newProxyCache.add(shape);
    109  }
    110 
    111  MOZ_ASSERT(shape->realm() == realm);
    112  MOZ_ASSERT(!IsAboutToBeFinalizedUnbarriered(shape.get()));
    113 
    114  // Ensure that the wrapper has the same lifetime assumptions as the
    115  // wrappee. Prefer to allocate in the nursery, when possible.
    116  gc::Heap heap;
    117  if ((priv.isGCThing() && priv.toGCThing()->isTenured()) ||
    118      !handler->canNurseryAllocate()) {
    119    heap = gc::Heap::Tenured;
    120  } else {
    121    heap = gc::Heap::Default;
    122  }
    123 
    124  debugCheckNewObject(shape, allocKind, heap);
    125 
    126  ProxyObject* proxy = cx->newCell<ProxyObject>(allocKind, heap, clasp);
    127  if (!proxy) {
    128    return nullptr;
    129  }
    130 
    131  proxy->initShape(shape);
    132 
    133  MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
    134  realm->setObjectPendingMetadata(proxy);
    135 
    136  gc::gcprobes::CreateObject(proxy);
    137 
    138  proxy->init(handler, priv, cx);
    139 
    140  return proxy;
    141 }
    142 
    143 gc::AllocKind ProxyObject::allocKindForTenure() const {
    144  Value priv = private_();
    145  return GetProxyGCObjectKind(getClass(), data.handler, priv,
    146                              usingInlineValueArray());
    147 }
    148 
    149 void ProxyObject::setCrossCompartmentPrivate(const Value& priv) {
    150  setPrivate(priv);
    151 }
    152 
    153 void ProxyObject::setSameCompartmentPrivate(const Value& priv) {
    154  MOZ_ASSERT(IsObjectValueInCompartment(priv, compartment()));
    155  setPrivate(priv);
    156 }
    157 
    158 inline void ProxyObject::setPrivate(const Value& priv) {
    159 #ifdef DEBUG
    160  JS::AssertValueIsNotGray(priv);
    161 #endif
    162  *slotOfPrivate() = priv;
    163 }
    164 
    165 void ProxyObject::setExpando(JSObject* expando) {
    166  // Ensure we're in the same compartment as the proxy object: Don't want the
    167  // expando to end up as a CCW.
    168  MOZ_ASSERT_IF(expando, expando->compartment() == compartment());
    169 
    170  // Ensure that we don't accidentally end up pointing to a
    171  // grey object, which would violate GC invariants.
    172  MOZ_ASSERT_IF(!zone()->isGCPreparing() && isMarkedBlack() && expando,
    173                !JS::GCThingIsMarkedGray(JS::GCCellPtr(expando)));
    174 
    175  *slotOfExpando() = ObjectOrNullValue(expando);
    176 }
    177 
    178 void ProxyObject::nuke() {
    179  // Notify the zone that a delegate is no longer a delegate. Be careful not to
    180  // expose this pointer, because it has already been removed from the wrapper
    181  // map yet we have assertions during tracing that will verify that it is
    182  // still present.
    183  JSObject* delegate = UncheckedUnwrapWithoutExpose(this);
    184  if (delegate != this) {
    185    delegate->zone()->beforeClearDelegate(this, delegate);
    186  }
    187 
    188  // Clear the target reference and replaced it with a value that encodes
    189  // various information about the original target.
    190  setSameCompartmentPrivate(DeadProxyTargetValue(this));
    191 
    192  // Clear out the expando
    193  setExpando(nullptr);
    194 
    195  // Update the handler to make this a DeadObjectProxy.
    196  setHandler(&DeadObjectProxy::singleton);
    197 
    198  // The proxy's reserved slots are not cleared and will continue to be
    199  // traced. This avoids the possibility of triggering write barriers while
    200  // nuking proxies in dead compartments which could otherwise cause those
    201  // compartments to be kept alive. Note that these are slots cannot hold
    202  // cross compartment pointers, so this cannot cause the target compartment
    203  // to leak.
    204 }
    205 
    206 JS_PUBLIC_API void js::detail::SetValueInProxy(Value* slot,
    207                                               const Value& value) {
    208  // Slots in proxies are not GCPtr<Value>s, so do a cast whenever assigning
    209  // values to them which might trigger a barrier.
    210  *reinterpret_cast<GCPtr<Value>*>(slot) = value;
    211 }