summaryrefslogtreecommitdiffstats
path: root/js/src/vm/ProxyObject.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/vm/ProxyObject.cpp')
-rw-r--r--js/src/vm/ProxyObject.cpp150
1 files changed, 150 insertions, 0 deletions
diff --git a/js/src/vm/ProxyObject.cpp b/js/src/vm/ProxyObject.cpp
new file mode 100644
index 000000000..49ed5a624
--- /dev/null
+++ b/js/src/vm/ProxyObject.cpp
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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/. */
+
+#include "vm/ProxyObject.h"
+
+#include "jscompartment.h"
+
+#include "proxy/DeadObjectProxy.h"
+#include "proxy/ScriptedProxyHandler.h"
+
+#include "jsobjinlines.h"
+
+using namespace js;
+
+/* static */ ProxyObject*
+ProxyObject::New(JSContext* cx, const BaseProxyHandler* handler, HandleValue priv, TaggedProto proto_,
+ const ProxyOptions& options)
+{
+ Rooted<TaggedProto> proto(cx, proto_);
+
+ const Class* clasp = options.clasp();
+
+ MOZ_ASSERT(isValidProxyClass(clasp));
+ MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
+ MOZ_ASSERT_IF(proto.isObject(), cx->compartment() == proto.toObject()->compartment());
+ MOZ_ASSERT(clasp->hasFinalize());
+
+ /*
+ * Eagerly mark properties unknown for proxies, so we don't try to track
+ * their properties and so that we don't need to walk the compartment if
+ * their prototype changes later. But don't do this for DOM proxies,
+ * because we want to be able to keep track of them in typesets in useful
+ * ways.
+ */
+ if (proto.isObject() && !options.singleton() && !clasp->isDOMClass()) {
+ RootedObject protoObj(cx, proto.toObject());
+ if (!JSObject::setNewGroupUnknown(cx, clasp, protoObj))
+ return nullptr;
+ }
+
+ // Ensure that the wrapper has the same lifetime assumptions as the
+ // wrappee. Prefer to allocate in the nursery, when possible.
+ NewObjectKind newKind = NurseryAllocatedProxy;
+ if (options.singleton()) {
+ MOZ_ASSERT(priv.isGCThing() && priv.toGCThing()->isTenured());
+ newKind = SingletonObject;
+ } else if ((priv.isGCThing() && priv.toGCThing()->isTenured()) ||
+ !handler->canNurseryAllocate() ||
+ !handler->finalizeInBackground(priv))
+ {
+ newKind = TenuredObject;
+ }
+
+ gc::AllocKind allocKind = gc::GetGCObjectKind(clasp);
+ if (handler->finalizeInBackground(priv))
+ allocKind = GetBackgroundAllocKind(allocKind);
+
+ AutoSetNewObjectMetadata metadata(cx);
+ // Note: this will initialize the object's |data| to strange values, but we
+ // will immediately overwrite those below.
+ RootedObject obj(cx, NewObjectWithGivenTaggedProto(cx, clasp, proto, allocKind,
+ newKind));
+ if (!obj)
+ return nullptr;
+
+ Rooted<ProxyObject*> proxy(cx, &obj->as<ProxyObject>());
+ new (proxy->data.values) detail::ProxyValueArray;
+ proxy->data.handler = handler;
+ proxy->setCrossCompartmentPrivate(priv);
+
+ /* Don't track types of properties of non-DOM and non-singleton proxies. */
+ if (newKind != SingletonObject && !clasp->isDOMClass())
+ MarkObjectGroupUnknownProperties(cx, proxy->group());
+
+ return proxy;
+}
+
+gc::AllocKind
+ProxyObject::allocKindForTenure() const
+{
+ gc::AllocKind allocKind = gc::GetGCObjectKind(group()->clasp());
+ if (data.handler->finalizeInBackground(const_cast<ProxyObject*>(this)->private_()))
+ allocKind = GetBackgroundAllocKind(allocKind);
+ return allocKind;
+}
+
+/* static */ size_t
+ProxyObject::objectMovedDuringMinorGC(TenuringTracer* trc, JSObject* dst, JSObject* src)
+{
+ ProxyObject& psrc = src->as<ProxyObject>();
+ ProxyObject& pdst = dst->as<ProxyObject>();
+
+ // We're about to sweep the nursery heap, so migrate the inline
+ // ProxyValueArray to the malloc heap if they were nursery allocated.
+ if (trc->runtime()->gc.nursery.isInside(psrc.data.values))
+ pdst.data.values = js_new<detail::ProxyValueArray>(*psrc.data.values);
+ else
+ trc->runtime()->gc.nursery.removeMallocedBuffer(psrc.data.values);
+ return sizeof(detail::ProxyValueArray);
+}
+
+void
+ProxyObject::setCrossCompartmentPrivate(const Value& priv)
+{
+ *slotOfPrivate() = priv;
+}
+
+void
+ProxyObject::setSameCompartmentPrivate(const Value& priv)
+{
+ MOZ_ASSERT(IsObjectValueInCompartment(priv, compartment()));
+ *slotOfPrivate() = priv;
+}
+
+void
+ProxyObject::nuke()
+{
+ // When nuking scripted proxies, isCallable and isConstructor values for
+ // the proxy needs to be preserved. Do this before clearing the target.
+ uint32_t callable = handler()->isCallable(this)
+ ? ScriptedProxyHandler::IS_CALLABLE : 0;
+ uint32_t constructor = handler()->isConstructor(this)
+ ? ScriptedProxyHandler::IS_CONSTRUCTOR : 0;
+ setExtra(ScriptedProxyHandler::IS_CALLCONSTRUCT_EXTRA,
+ PrivateUint32Value(callable | constructor));
+
+ // Clear the target reference.
+ setSameCompartmentPrivate(NullValue());
+
+ // Update the handler to make this a DeadObjectProxy.
+ setHandler(&DeadObjectProxy::singleton);
+
+ // The proxy's extra slots are not cleared and will continue to be
+ // traced. This avoids the possibility of triggering write barriers while
+ // nuking proxies in dead compartments which could otherwise cause those
+ // compartments to be kept alive. Note that these are slots cannot hold
+ // cross compartment pointers, so this cannot cause the target compartment
+ // to leak.
+}
+
+JS_FRIEND_API(void)
+js::SetValueInProxy(Value* slot, const Value& value)
+{
+ // Slots in proxies are not GCPtrValues, so do a cast whenever assigning
+ // values to them which might trigger a barrier.
+ *reinterpret_cast<GCPtrValue*>(slot) = value;
+}