summaryrefslogtreecommitdiffstats
path: root/js/src/proxy/Wrapper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/proxy/Wrapper.cpp')
-rw-r--r--js/src/proxy/Wrapper.cpp420
1 files changed, 420 insertions, 0 deletions
diff --git a/js/src/proxy/Wrapper.cpp b/js/src/proxy/Wrapper.cpp
new file mode 100644
index 000000000..43d559ff3
--- /dev/null
+++ b/js/src/proxy/Wrapper.cpp
@@ -0,0 +1,420 @@
+/* -*- 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 "jscntxt.h"
+#include "jscompartment.h"
+#include "jsexn.h"
+#include "jswrapper.h"
+
+#include "js/Proxy.h"
+#include "vm/ErrorObject.h"
+#include "vm/ProxyObject.h"
+#include "vm/WrapperObject.h"
+
+#include "jsobjinlines.h"
+
+#include "vm/NativeObject-inl.h"
+
+using namespace js;
+
+bool
+Wrapper::finalizeInBackground(const Value& priv) const
+{
+ if (!priv.isObject())
+ return true;
+
+ /*
+ * Make the 'background-finalized-ness' of the wrapper the same as the
+ * wrapped object, to allow transplanting between them.
+ *
+ * If the wrapped object is in the nursery then we know it doesn't have a
+ * finalizer, and so background finalization is ok.
+ */
+ if (IsInsideNursery(&priv.toObject()))
+ return true;
+ return IsBackgroundFinalized(priv.toObject().asTenured().getAllocKind());
+}
+
+bool
+Wrapper::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
+ MutableHandle<PropertyDescriptor> desc) const
+{
+ assertEnteredPolicy(cx, proxy, id, GET | SET | GET_PROPERTY_DESCRIPTOR);
+ RootedObject target(cx, proxy->as<ProxyObject>().target());
+ return GetOwnPropertyDescriptor(cx, target, id, desc);
+}
+
+bool
+Wrapper::defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
+ Handle<PropertyDescriptor> desc, ObjectOpResult& result) const
+{
+ assertEnteredPolicy(cx, proxy, id, SET);
+ RootedObject target(cx, proxy->as<ProxyObject>().target());
+ return DefineProperty(cx, target, id, desc, result);
+}
+
+bool
+Wrapper::ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props) const
+{
+ assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
+ RootedObject target(cx, proxy->as<ProxyObject>().target());
+ return GetPropertyKeys(cx, target, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &props);
+}
+
+bool
+Wrapper::delete_(JSContext* cx, HandleObject proxy, HandleId id, ObjectOpResult& result) const
+{
+ assertEnteredPolicy(cx, proxy, id, SET);
+ RootedObject target(cx, proxy->as<ProxyObject>().target());
+ return DeleteProperty(cx, target, id, result);
+}
+
+bool
+Wrapper::enumerate(JSContext* cx, HandleObject proxy, MutableHandleObject objp) const
+{
+ assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
+ MOZ_ASSERT(!hasPrototype()); // Should never be called if there's a prototype.
+ RootedObject target(cx, proxy->as<ProxyObject>().target());
+ return GetIterator(cx, target, 0, objp);
+}
+
+bool
+Wrapper::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject protop) const
+{
+ RootedObject target(cx, proxy->as<ProxyObject>().target());
+ return GetPrototype(cx, target, protop);
+}
+
+bool
+Wrapper::setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto,
+ ObjectOpResult& result) const
+{
+ RootedObject target(cx, proxy->as<ProxyObject>().target());
+ return SetPrototype(cx, target, proto, result);
+}
+
+bool
+Wrapper::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy,
+ bool* isOrdinary, MutableHandleObject protop) const
+{
+ RootedObject target(cx, proxy->as<ProxyObject>().target());
+ return GetPrototypeIfOrdinary(cx, target, isOrdinary, protop);
+}
+
+bool
+Wrapper::setImmutablePrototype(JSContext* cx, HandleObject proxy, bool* succeeded) const
+{
+ RootedObject target(cx, proxy->as<ProxyObject>().target());
+ return SetImmutablePrototype(cx, target, succeeded);
+}
+
+bool
+Wrapper::preventExtensions(JSContext* cx, HandleObject proxy, ObjectOpResult& result) const
+{
+ RootedObject target(cx, proxy->as<ProxyObject>().target());
+ return PreventExtensions(cx, target, result);
+}
+
+bool
+Wrapper::isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const
+{
+ RootedObject target(cx, proxy->as<ProxyObject>().target());
+ return IsExtensible(cx, target, extensible);
+}
+
+bool
+Wrapper::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const
+{
+ assertEnteredPolicy(cx, proxy, id, GET);
+ MOZ_ASSERT(!hasPrototype()); // Should never be called if there's a prototype.
+ RootedObject target(cx, proxy->as<ProxyObject>().target());
+ return HasProperty(cx, target, id, bp);
+}
+
+bool
+Wrapper::get(JSContext* cx, HandleObject proxy, HandleValue receiver, HandleId id,
+ MutableHandleValue vp) const
+{
+ assertEnteredPolicy(cx, proxy, id, GET);
+ RootedObject target(cx, proxy->as<ProxyObject>().target());
+ return GetProperty(cx, target, receiver, id, vp);
+}
+
+bool
+Wrapper::set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v, HandleValue receiver,
+ ObjectOpResult& result) const
+{
+ assertEnteredPolicy(cx, proxy, id, SET);
+ RootedObject target(cx, proxy->as<ProxyObject>().target());
+ return SetProperty(cx, target, id, v, receiver, result);
+}
+
+bool
+Wrapper::call(JSContext* cx, HandleObject proxy, const CallArgs& args) const
+{
+ assertEnteredPolicy(cx, proxy, JSID_VOID, CALL);
+ RootedValue target(cx, proxy->as<ProxyObject>().private_());
+
+ InvokeArgs iargs(cx);
+ if (!FillArgumentsFromArraylike(cx, iargs, args))
+ return false;
+
+ return js::Call(cx, target, args.thisv(), iargs, args.rval());
+}
+
+bool
+Wrapper::construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const
+{
+ assertEnteredPolicy(cx, proxy, JSID_VOID, CALL);
+
+ RootedValue target(cx, proxy->as<ProxyObject>().private_());
+ if (!IsConstructor(target)) {
+ ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, target, nullptr);
+ return false;
+ }
+
+ ConstructArgs cargs(cx);
+ if (!FillArgumentsFromArraylike(cx, cargs, args))
+ return false;
+
+ RootedObject obj(cx);
+ if (!Construct(cx, target, cargs, args.newTarget(), &obj))
+ return false;
+
+ args.rval().setObject(*obj);
+ return true;
+}
+
+bool
+Wrapper::getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
+ MutableHandle<PropertyDescriptor> desc) const
+{
+ assertEnteredPolicy(cx, proxy, id, GET | SET | GET_PROPERTY_DESCRIPTOR);
+ MOZ_ASSERT(!hasPrototype()); // Should never be called if there's a prototype.
+ RootedObject target(cx, proxy->as<ProxyObject>().target());
+ return GetPropertyDescriptor(cx, target, id, desc);
+}
+
+bool
+Wrapper::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const
+{
+ assertEnteredPolicy(cx, proxy, id, GET);
+ RootedObject target(cx, proxy->as<ProxyObject>().target());
+ return HasOwnProperty(cx, target, id, bp);
+}
+
+bool
+Wrapper::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy,
+ AutoIdVector& props) const
+{
+ assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
+ RootedObject target(cx, proxy->as<ProxyObject>().target());
+ return GetPropertyKeys(cx, target, JSITER_OWNONLY, &props);
+}
+
+bool
+Wrapper::nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
+ const CallArgs& args) const
+{
+ args.setThis(ObjectValue(*args.thisv().toObject().as<ProxyObject>().target()));
+ if (!test(args.thisv())) {
+ ReportIncompatible(cx, args);
+ return false;
+ }
+
+ return CallNativeImpl(cx, impl, args);
+}
+
+bool
+Wrapper::hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v,
+ bool* bp) const
+{
+ assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
+ RootedObject target(cx, proxy->as<ProxyObject>().target());
+ return HasInstance(cx, target, v, bp);
+}
+
+bool
+Wrapper::getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls) const
+{
+ RootedObject target(cx, proxy->as<ProxyObject>().target());
+ return GetBuiltinClass(cx, target, cls);
+}
+
+bool
+Wrapper::isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const
+{
+ RootedObject target(cx, proxy->as<ProxyObject>().target());
+ return IsArray(cx, target, answer);
+}
+
+const char*
+Wrapper::className(JSContext* cx, HandleObject proxy) const
+{
+ assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
+ RootedObject target(cx, proxy->as<ProxyObject>().target());
+ return GetObjectClassName(cx, target);
+}
+
+JSString*
+Wrapper::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const
+{
+ assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
+ RootedObject target(cx, proxy->as<ProxyObject>().target());
+ return fun_toStringHelper(cx, target, indent);
+}
+
+bool
+Wrapper::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const
+{
+ RootedObject target(cx, proxy->as<ProxyObject>().target());
+ return RegExpToShared(cx, target, g);
+}
+
+bool
+Wrapper::boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const
+{
+ RootedObject target(cx, proxy->as<ProxyObject>().target());
+ return Unbox(cx, target, vp);
+}
+
+bool
+Wrapper::isCallable(JSObject* obj) const
+{
+ JSObject * target = obj->as<ProxyObject>().target();
+ return target->isCallable();
+}
+
+bool
+Wrapper::isConstructor(JSObject* obj) const
+{
+ // For now, all wrappers are constructable if they are callable. We will want to eventually
+ // decouple this behavior, but none of the Wrapper infrastructure is currently prepared for
+ // that.
+ return isCallable(obj);
+}
+
+JSObject*
+Wrapper::weakmapKeyDelegate(JSObject* proxy) const
+{
+ return UncheckedUnwrap(proxy);
+}
+
+JSObject*
+Wrapper::New(JSContext* cx, JSObject* obj, const Wrapper* handler,
+ const WrapperOptions& options)
+{
+ RootedValue priv(cx, ObjectValue(*obj));
+ return NewProxyObject(cx, handler, priv, options.proto(), options);
+}
+
+JSObject*
+Wrapper::Renew(JSContext* cx, JSObject* existing, JSObject* obj, const Wrapper* handler)
+{
+ existing->as<ProxyObject>().renew(cx, handler, ObjectValue(*obj));
+ return existing;
+}
+
+const Wrapper*
+Wrapper::wrapperHandler(JSObject* wrapper)
+{
+ MOZ_ASSERT(wrapper->is<WrapperObject>());
+ return static_cast<const Wrapper*>(wrapper->as<ProxyObject>().handler());
+}
+
+JSObject*
+Wrapper::wrappedObject(JSObject* wrapper)
+{
+ MOZ_ASSERT(wrapper->is<WrapperObject>());
+ JSObject* target = wrapper->as<ProxyObject>().target();
+ if (target)
+ JS::ExposeObjectToActiveJS(target);
+ return target;
+}
+
+JS_FRIEND_API(JSObject*)
+js::UncheckedUnwrap(JSObject* wrapped, bool stopAtWindowProxy, unsigned* flagsp)
+{
+ unsigned flags = 0;
+ while (true) {
+ if (!wrapped->is<WrapperObject>() ||
+ MOZ_UNLIKELY(stopAtWindowProxy && IsWindowProxy(wrapped)))
+ {
+ break;
+ }
+ flags |= Wrapper::wrapperHandler(wrapped)->flags();
+ wrapped = wrapped->as<ProxyObject>().private_().toObjectOrNull();
+
+ // This can be called from Wrapper::weakmapKeyDelegate() on a wrapper
+ // whose referent has been moved while it is still unmarked.
+ if (wrapped)
+ wrapped = MaybeForwarded(wrapped);
+ }
+ if (flagsp)
+ *flagsp = flags;
+ return wrapped;
+}
+
+JS_FRIEND_API(JSObject*)
+js::CheckedUnwrap(JSObject* obj, bool stopAtWindowProxy)
+{
+ while (true) {
+ JSObject* wrapper = obj;
+ obj = UnwrapOneChecked(obj, stopAtWindowProxy);
+ if (!obj || obj == wrapper)
+ return obj;
+ }
+}
+
+JS_FRIEND_API(JSObject*)
+js::UnwrapOneChecked(JSObject* obj, bool stopAtWindowProxy)
+{
+ if (!obj->is<WrapperObject>() ||
+ MOZ_UNLIKELY(IsWindowProxy(obj) && stopAtWindowProxy))
+ {
+ return obj;
+ }
+
+ const Wrapper* handler = Wrapper::wrapperHandler(obj);
+ return handler->hasSecurityPolicy() ? nullptr : Wrapper::wrappedObject(obj);
+}
+
+const char Wrapper::family = 0;
+const Wrapper Wrapper::singleton((unsigned)0);
+const Wrapper Wrapper::singletonWithPrototype((unsigned)0, true);
+JSObject* Wrapper::defaultProto = TaggedProto::LazyProto;
+
+/* Compartments. */
+
+JSObject*
+js::TransparentObjectWrapper(JSContext* cx, HandleObject existing, HandleObject obj)
+{
+ // Allow wrapping outer window proxies.
+ MOZ_ASSERT(!obj->is<WrapperObject>() || IsWindowProxy(obj));
+ return Wrapper::New(cx, obj, &CrossCompartmentWrapper::singleton);
+}
+
+ErrorCopier::~ErrorCopier()
+{
+ JSContext* cx = ac->context()->asJSContext();
+
+ // The provenance of Debugger.DebuggeeWouldRun is the topmost locking
+ // debugger compartment; it should not be copied around.
+ if (ac->origin() != cx->compartment() &&
+ cx->isExceptionPending() &&
+ !cx->isThrowingDebuggeeWouldRun())
+ {
+ RootedValue exc(cx);
+ if (cx->getPendingException(&exc) && exc.isObject() && exc.toObject().is<ErrorObject>()) {
+ cx->clearPendingException();
+ ac.reset();
+ Rooted<ErrorObject*> errObj(cx, &exc.toObject().as<ErrorObject>());
+ JSObject* copyobj = CopyErrorObject(cx, errObj);
+ if (copyobj)
+ cx->setPendingException(ObjectValue(*copyobj));
+ }
+ }
+}