LCOV - code coverage report
Current view: top level - js/src/vm - ProxyObject.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 84 91 92.3 %
Date: 2017-07-14 16:53:18 Functions: 8 8 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       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 "jscompartment.h"
      10             : 
      11             : #include "proxy/DeadObjectProxy.h"
      12             : 
      13             : #include "jsobjinlines.h"
      14             : 
      15             : using namespace js;
      16             : 
      17             : static gc::AllocKind
      18       11438 : GetProxyGCObjectKind(const Class* clasp, const BaseProxyHandler* handler, const Value& priv)
      19             : {
      20       11438 :     MOZ_ASSERT(clasp->isProxy());
      21             : 
      22       11438 :     uint32_t nreserved = JSCLASS_RESERVED_SLOTS(clasp);
      23             : 
      24             :     // For now assert each Proxy Class has at least 1 reserved slot. This is
      25             :     // not a hard requirement, but helps catch Classes that need an explicit
      26             :     // JSCLASS_HAS_RESERVED_SLOTS since bug 1360523.
      27       11438 :     MOZ_ASSERT(nreserved > 0);
      28             : 
      29       11438 :     MOZ_ASSERT(js::detail::ProxyValueArray::sizeOf(nreserved) % sizeof(Value) == 0,
      30             :                "ProxyValueArray must be a multiple of Value");
      31             : 
      32       11438 :     uint32_t nslots = js::detail::ProxyValueArray::sizeOf(nreserved) / sizeof(Value);
      33       11438 :     MOZ_ASSERT(nslots <= NativeObject::MAX_FIXED_SLOTS);
      34             : 
      35       11438 :     gc::AllocKind kind = gc::GetGCObjectKind(nslots);
      36       11438 :     if (handler->finalizeInBackground(priv))
      37        8924 :         kind = GetBackgroundAllocKind(kind);
      38             : 
      39       11438 :     return kind;
      40             : }
      41             : 
      42             : /* static */ ProxyObject*
      43        9333 : ProxyObject::New(JSContext* cx, const BaseProxyHandler* handler, HandleValue priv, TaggedProto proto_,
      44             :                  const ProxyOptions& options)
      45             : {
      46       18666 :     Rooted<TaggedProto> proto(cx, proto_);
      47             : 
      48        9333 :     const Class* clasp = options.clasp();
      49             : 
      50        9333 :     MOZ_ASSERT(isValidProxyClass(clasp));
      51        9333 :     MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
      52        9333 :     MOZ_ASSERT_IF(proto.isObject(), cx->compartment() == proto.toObject()->compartment());
      53        9333 :     MOZ_ASSERT(clasp->hasFinalize());
      54             : 
      55             :     /*
      56             :      * Eagerly mark properties unknown for proxies, so we don't try to track
      57             :      * their properties and so that we don't need to walk the compartment if
      58             :      * their prototype changes later.  But don't do this for DOM proxies,
      59             :      * because we want to be able to keep track of them in typesets in useful
      60             :      * ways.
      61             :      */
      62        9333 :     if (proto.isObject() && !options.singleton() && !clasp->isDOMClass()) {
      63           0 :         RootedObject protoObj(cx, proto.toObject());
      64           0 :         if (!JSObject::setNewGroupUnknown(cx, clasp, protoObj))
      65           0 :             return nullptr;
      66             :     }
      67             : 
      68             :     // Ensure that the wrapper has the same lifetime assumptions as the
      69             :     // wrappee. Prefer to allocate in the nursery, when possible.
      70        9333 :     NewObjectKind newKind = NurseryAllocatedProxy;
      71        9333 :     if (options.singleton()) {
      72          14 :         MOZ_ASSERT(priv.isNull() || (priv.isGCThing() && priv.toGCThing()->isTenured()));
      73          14 :         newKind = SingletonObject;
      74       12765 :     } else if ((priv.isGCThing() && priv.toGCThing()->isTenured()) ||
      75        3446 :                !handler->canNurseryAllocate())
      76             :     {
      77        5918 :         newKind = TenuredObject;
      78             :     }
      79             : 
      80        9333 :     gc::AllocKind allocKind = GetProxyGCObjectKind(clasp, handler, priv);
      81             : 
      82       18666 :     AutoSetNewObjectMetadata metadata(cx);
      83             :     // Note: this will initialize the object's |data| to strange values, but we
      84             :     // will immediately overwrite those below.
      85             :     ProxyObject* proxy;
      86        9333 :     JS_TRY_VAR_OR_RETURN_NULL(cx, proxy, create(cx, clasp, proto, allocKind, newKind));
      87             : 
      88        9333 :     proxy->setInlineValueArray();
      89             : 
      90        9333 :     detail::ProxyValueArray* values = detail::GetProxyDataLayout(proxy)->values();
      91        9333 :     values->init(proxy->numReservedSlots());
      92             : 
      93        9333 :     proxy->data.handler = handler;
      94        9333 :     if (IsCrossCompartmentWrapper(proxy))
      95        9256 :         proxy->setCrossCompartmentPrivate(priv);
      96             :     else
      97          77 :         proxy->setSameCompartmentPrivate(priv);
      98             : 
      99             :     /* Don't track types of properties of non-DOM and non-singleton proxies. */
     100        9333 :     if (newKind != SingletonObject && !clasp->isDOMClass())
     101        9266 :         MarkObjectGroupUnknownProperties(cx, proxy->group());
     102             : 
     103        9333 :     return proxy;
     104             : }
     105             : 
     106             : gc::AllocKind
     107        2105 : ProxyObject::allocKindForTenure() const
     108             : {
     109        2105 :     MOZ_ASSERT(usingInlineValueArray());
     110        2105 :     Value priv = const_cast<ProxyObject*>(this)->private_();
     111        2105 :     return GetProxyGCObjectKind(getClass(), data.handler, priv);
     112             : }
     113             : 
     114             : void
     115        9266 : ProxyObject::setCrossCompartmentPrivate(const Value& priv)
     116             : {
     117        9266 :     *slotOfPrivate() = priv;
     118        9266 : }
     119             : 
     120             : void
     121         121 : ProxyObject::setSameCompartmentPrivate(const Value& priv)
     122             : {
     123         121 :     MOZ_ASSERT(IsObjectValueInCompartment(priv, compartment()));
     124         121 :     *slotOfPrivate() = priv;
     125         121 : }
     126             : 
     127             : void
     128          44 : ProxyObject::nuke()
     129             : {
     130             :     // Select a dead proxy handler based on the properties of this wrapper.
     131             :     // Do this before clearing the target.
     132          44 :     const BaseProxyHandler* handler = SelectDeadProxyHandler(this);
     133             : 
     134             :     // Clear the target reference.
     135          44 :     setSameCompartmentPrivate(NullValue());
     136             : 
     137             :     // Update the handler to make this a DeadObjectProxy.
     138          44 :     setHandler(handler);
     139             : 
     140             :     // The proxy's reserved slots are not cleared and will continue to be
     141             :     // traced. This avoids the possibility of triggering write barriers while
     142             :     // nuking proxies in dead compartments which could otherwise cause those
     143             :     // compartments to be kept alive. Note that these are slots cannot hold
     144             :     // cross compartment pointers, so this cannot cause the target compartment
     145             :     // to leak.
     146          44 : }
     147             : 
     148             : /* static */ JS::Result<ProxyObject*, JS::OOM&>
     149        9333 : ProxyObject::create(JSContext* cx, const Class* clasp, Handle<TaggedProto> proto,
     150             :                     gc::AllocKind allocKind, NewObjectKind newKind)
     151             : {
     152        9333 :     MOZ_ASSERT(clasp->isProxy());
     153             : 
     154        9333 :     JSCompartment* comp = cx->compartment();
     155       18666 :     RootedObjectGroup group(cx);
     156       18666 :     RootedShape shape(cx);
     157             : 
     158             :     // Try to look up the group and shape in the NewProxyCache.
     159        9333 :     if (!comp->newProxyCache.lookup(clasp, proto, group.address(), shape.address())) {
     160         330 :         group = ObjectGroup::defaultNewGroup(cx, clasp, proto, nullptr);
     161         330 :         if (!group)
     162           0 :             return cx->alreadyReportedOOM();
     163             : 
     164         330 :         shape = EmptyShape::getInitialShape(cx, clasp, proto, /* nfixed = */ 0);
     165         330 :         if (!shape)
     166           0 :             return cx->alreadyReportedOOM();
     167             : 
     168         330 :         comp->newProxyCache.add(group, shape);
     169             :     }
     170             : 
     171        9333 :     gc::InitialHeap heap = GetInitialHeap(newKind, clasp);
     172        9333 :     debugCheckNewObject(group, shape, allocKind, heap);
     173             : 
     174        9333 :     JSObject* obj = js::Allocate<JSObject>(cx, allocKind, /* numDynamicSlots = */ 0, heap, clasp);
     175        9333 :     if (!obj)
     176           0 :         return cx->alreadyReportedOOM();
     177             : 
     178        9333 :     ProxyObject* pobj = static_cast<ProxyObject*>(obj);
     179        9333 :     pobj->group_.init(group);
     180        9333 :     pobj->initShape(shape);
     181             : 
     182        9333 :     MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
     183        9333 :     cx->compartment()->setObjectPendingMetadata(cx, pobj);
     184             : 
     185        9333 :     js::gc::TraceCreateObject(pobj);
     186             : 
     187        9333 :     if (newKind == SingletonObject) {
     188          28 :         Rooted<ProxyObject*> pobjRoot(cx, pobj);
     189          14 :         if (!JSObject::setSingleton(cx, pobjRoot))
     190           0 :             return cx->alreadyReportedOOM();
     191          14 :         pobj = pobjRoot;
     192             :     }
     193             : 
     194        9333 :     return pobj;
     195             : }
     196             : 
     197             : JS_FRIEND_API(void)
     198         106 : js::SetValueInProxy(Value* slot, const Value& value)
     199             : {
     200             :     // Slots in proxies are not GCPtrValues, so do a cast whenever assigning
     201             :     // values to them which might trigger a barrier.
     202         106 :     *reinterpret_cast<GCPtrValue*>(slot) = value;
     203         106 : }

Generated by: LCOV version 1.13