summaryrefslogtreecommitdiffstats
path: root/js/src/proxy/Proxy.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/proxy/Proxy.cpp')
-rw-r--r--js/src/proxy/Proxy.cpp812
1 files changed, 812 insertions, 0 deletions
diff --git a/js/src/proxy/Proxy.cpp b/js/src/proxy/Proxy.cpp
new file mode 100644
index 000000000..b43fd02d2
--- /dev/null
+++ b/js/src/proxy/Proxy.cpp
@@ -0,0 +1,812 @@
+/* -*- 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 "js/Proxy.h"
+
+#include <string.h>
+
+#include "jsapi.h"
+#include "jscntxt.h"
+#include "jsfun.h"
+#include "jsgc.h"
+#include "jswrapper.h"
+
+#include "gc/Marking.h"
+#include "proxy/DeadObjectProxy.h"
+#include "proxy/ScriptedProxyHandler.h"
+#include "vm/WrapperObject.h"
+
+#include "jsatominlines.h"
+#include "jsobjinlines.h"
+
+#include "vm/NativeObject-inl.h"
+
+using namespace js;
+using namespace js::gc;
+
+void
+js::AutoEnterPolicy::reportErrorIfExceptionIsNotPending(JSContext* cx, jsid id)
+{
+ if (JS_IsExceptionPending(cx))
+ return;
+
+ if (JSID_IS_VOID(id)) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_OBJECT_ACCESS_DENIED);
+ } else {
+ RootedValue idVal(cx, IdToValue(id));
+ JSString* str = ValueToSource(cx, idVal);
+ if (!str) {
+ return;
+ }
+ AutoStableStringChars chars(cx);
+ const char16_t* prop = nullptr;
+ if (str->ensureFlat(cx) && chars.initTwoByte(cx, str))
+ prop = chars.twoByteChars();
+
+ JS_ReportErrorNumberUC(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_ACCESS_DENIED,
+ prop);
+ }
+}
+
+#ifdef DEBUG
+void
+js::AutoEnterPolicy::recordEnter(JSContext* cx, HandleObject proxy, HandleId id, Action act)
+{
+ if (allowed()) {
+ context = cx;
+ enteredProxy.emplace(proxy);
+ enteredId.emplace(id);
+ enteredAction = act;
+ prev = cx->runtime()->enteredPolicy;
+ cx->runtime()->enteredPolicy = this;
+ }
+}
+
+void
+js::AutoEnterPolicy::recordLeave()
+{
+ if (enteredProxy) {
+ MOZ_ASSERT(context->runtime()->enteredPolicy == this);
+ context->runtime()->enteredPolicy = prev;
+ }
+}
+
+JS_FRIEND_API(void)
+js::assertEnteredPolicy(JSContext* cx, JSObject* proxy, jsid id,
+ BaseProxyHandler::Action act)
+{
+ MOZ_ASSERT(proxy->is<ProxyObject>());
+ MOZ_ASSERT(cx->runtime()->enteredPolicy);
+ MOZ_ASSERT(cx->runtime()->enteredPolicy->enteredProxy->get() == proxy);
+ MOZ_ASSERT(cx->runtime()->enteredPolicy->enteredId->get() == id);
+ MOZ_ASSERT(cx->runtime()->enteredPolicy->enteredAction & act);
+}
+#endif
+
+bool
+Proxy::getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
+ MutableHandle<PropertyDescriptor> desc)
+{
+ JS_CHECK_RECURSION(cx, return false);
+ const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
+ desc.object().set(nullptr); // default result if we refuse to perform this action
+ AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET_PROPERTY_DESCRIPTOR, true);
+ if (!policy.allowed())
+ return policy.returnValue();
+
+ // Special case. See the comment on BaseProxyHandler::mHasPrototype.
+ if (handler->hasPrototype())
+ return handler->BaseProxyHandler::getPropertyDescriptor(cx, proxy, id, desc);
+
+ return handler->getPropertyDescriptor(cx, proxy, id, desc);
+}
+
+bool
+Proxy::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
+ MutableHandle<PropertyDescriptor> desc)
+{
+ JS_CHECK_RECURSION(cx, return false);
+
+ const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
+ desc.object().set(nullptr); // default result if we refuse to perform this action
+ AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET_PROPERTY_DESCRIPTOR, true);
+ if (!policy.allowed())
+ return policy.returnValue();
+ return handler->getOwnPropertyDescriptor(cx, proxy, id, desc);
+}
+
+bool
+Proxy::defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
+ Handle<PropertyDescriptor> desc, ObjectOpResult& result)
+{
+ JS_CHECK_RECURSION(cx, return false);
+ const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
+ AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
+ if (!policy.allowed()) {
+ if (!policy.returnValue())
+ return false;
+ return result.succeed();
+ }
+ return proxy->as<ProxyObject>().handler()->defineProperty(cx, proxy, id, desc, result);
+}
+
+bool
+Proxy::ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props)
+{
+ JS_CHECK_RECURSION(cx, return false);
+ const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
+ AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::ENUMERATE, true);
+ if (!policy.allowed())
+ return policy.returnValue();
+ return proxy->as<ProxyObject>().handler()->ownPropertyKeys(cx, proxy, props);
+}
+
+bool
+Proxy::delete_(JSContext* cx, HandleObject proxy, HandleId id, ObjectOpResult& result)
+{
+ JS_CHECK_RECURSION(cx, return false);
+ const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
+ AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
+ if (!policy.allowed()) {
+ bool ok = policy.returnValue();
+ if (ok)
+ result.succeed();
+ return ok;
+ }
+ return proxy->as<ProxyObject>().handler()->delete_(cx, proxy, id, result);
+}
+
+JS_FRIEND_API(bool)
+js::AppendUnique(JSContext* cx, AutoIdVector& base, AutoIdVector& others)
+{
+ AutoIdVector uniqueOthers(cx);
+ if (!uniqueOthers.reserve(others.length()))
+ return false;
+ for (size_t i = 0; i < others.length(); ++i) {
+ bool unique = true;
+ for (size_t j = 0; j < base.length(); ++j) {
+ if (others[i].get() == base[j]) {
+ unique = false;
+ break;
+ }
+ }
+ if (unique) {
+ if (!uniqueOthers.append(others[i]))
+ return false;
+ }
+ }
+ return base.appendAll(uniqueOthers);
+}
+
+/* static */ bool
+Proxy::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject proto)
+{
+ MOZ_ASSERT(proxy->hasDynamicPrototype());
+ JS_CHECK_RECURSION(cx, return false);
+ return proxy->as<ProxyObject>().handler()->getPrototype(cx, proxy, proto);
+}
+
+/* static */ bool
+Proxy::setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto, ObjectOpResult& result)
+{
+ MOZ_ASSERT(proxy->hasDynamicPrototype());
+ JS_CHECK_RECURSION(cx, return false);
+ return proxy->as<ProxyObject>().handler()->setPrototype(cx, proxy, proto, result);
+}
+
+/* static */ bool
+Proxy::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary,
+ MutableHandleObject proto)
+{
+ JS_CHECK_RECURSION(cx, return false);
+ return proxy->as<ProxyObject>().handler()->getPrototypeIfOrdinary(cx, proxy, isOrdinary,
+ proto);
+}
+
+/* static */ bool
+Proxy::setImmutablePrototype(JSContext* cx, HandleObject proxy, bool* succeeded)
+{
+ JS_CHECK_RECURSION(cx, return false);
+ const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
+ return handler->setImmutablePrototype(cx, proxy, succeeded);
+}
+
+/* static */ bool
+Proxy::preventExtensions(JSContext* cx, HandleObject proxy, ObjectOpResult& result)
+{
+ JS_CHECK_RECURSION(cx, return false);
+ const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
+ return handler->preventExtensions(cx, proxy, result);
+}
+
+/* static */ bool
+Proxy::isExtensible(JSContext* cx, HandleObject proxy, bool* extensible)
+{
+ JS_CHECK_RECURSION(cx, return false);
+ return proxy->as<ProxyObject>().handler()->isExtensible(cx, proxy, extensible);
+}
+
+bool
+Proxy::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp)
+{
+ JS_CHECK_RECURSION(cx, return false);
+ const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
+ *bp = false; // default result if we refuse to perform this action
+ AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
+ if (!policy.allowed())
+ return policy.returnValue();
+
+ if (handler->hasPrototype()) {
+ if (!handler->hasOwn(cx, proxy, id, bp))
+ return false;
+ if (*bp)
+ return true;
+
+ RootedObject proto(cx);
+ if (!GetPrototype(cx, proxy, &proto))
+ return false;
+ if (!proto)
+ return true;
+
+ return HasProperty(cx, proto, id, bp);
+ }
+
+ return handler->has(cx, proxy, id, bp);
+}
+
+bool
+Proxy::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp)
+{
+ JS_CHECK_RECURSION(cx, return false);
+ const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
+ *bp = false; // default result if we refuse to perform this action
+ AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
+ if (!policy.allowed())
+ return policy.returnValue();
+ return handler->hasOwn(cx, proxy, id, bp);
+}
+
+static Value
+ValueToWindowProxyIfWindow(const Value& v)
+{
+ if (v.isObject())
+ return ObjectValue(*ToWindowProxyIfWindow(&v.toObject()));
+ return v;
+}
+
+bool
+Proxy::get(JSContext* cx, HandleObject proxy, HandleValue receiver_, HandleId id,
+ MutableHandleValue vp)
+{
+ JS_CHECK_RECURSION(cx, return false);
+ const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
+ vp.setUndefined(); // default result if we refuse to perform this action
+ AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
+ if (!policy.allowed())
+ return policy.returnValue();
+
+ // Use the WindowProxy as receiver if receiver_ is a Window. Proxy handlers
+ // shouldn't have to know about the Window/WindowProxy distinction.
+ RootedValue receiver(cx, ValueToWindowProxyIfWindow(receiver_));
+
+ if (handler->hasPrototype()) {
+ bool own;
+ if (!handler->hasOwn(cx, proxy, id, &own))
+ return false;
+ if (!own) {
+ RootedObject proto(cx);
+ if (!GetPrototype(cx, proxy, &proto))
+ return false;
+ if (!proto)
+ return true;
+ return GetProperty(cx, proto, receiver, id, vp);
+ }
+ }
+
+ return handler->get(cx, proxy, receiver, id, vp);
+}
+
+bool
+Proxy::set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v, HandleValue receiver_,
+ ObjectOpResult& result)
+{
+ JS_CHECK_RECURSION(cx, return false);
+ const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
+ AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
+ if (!policy.allowed()) {
+ if (!policy.returnValue())
+ return false;
+ return result.succeed();
+ }
+
+ // Use the WindowProxy as receiver if receiver_ is a Window. Proxy handlers
+ // shouldn't have to know about the Window/WindowProxy distinction.
+ RootedValue receiver(cx, ValueToWindowProxyIfWindow(receiver_));
+
+ // Special case. See the comment on BaseProxyHandler::mHasPrototype.
+ if (handler->hasPrototype())
+ return handler->BaseProxyHandler::set(cx, proxy, id, v, receiver, result);
+
+ return handler->set(cx, proxy, id, v, receiver, result);
+}
+
+bool
+Proxy::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props)
+{
+ JS_CHECK_RECURSION(cx, return false);
+ const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
+ AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::ENUMERATE, true);
+ if (!policy.allowed())
+ return policy.returnValue();
+ return handler->getOwnEnumerablePropertyKeys(cx, proxy, props);
+}
+
+bool
+Proxy::enumerate(JSContext* cx, HandleObject proxy, MutableHandleObject objp)
+{
+ JS_CHECK_RECURSION(cx, return false);
+ const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
+ objp.set(nullptr); // default result if we refuse to perform this action
+
+ if (handler->hasPrototype()) {
+ AutoIdVector props(cx);
+ if (!Proxy::getOwnEnumerablePropertyKeys(cx, proxy, props))
+ return false;
+
+ RootedObject proto(cx);
+ if (!GetPrototype(cx, proxy, &proto))
+ return false;
+ if (!proto)
+ return EnumeratedIdVectorToIterator(cx, proxy, 0, props, objp);
+ assertSameCompartment(cx, proxy, proto);
+
+ AutoIdVector protoProps(cx);
+ return GetPropertyKeys(cx, proto, 0, &protoProps) &&
+ AppendUnique(cx, props, protoProps) &&
+ EnumeratedIdVectorToIterator(cx, proxy, 0, props, objp);
+ }
+
+ AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
+ BaseProxyHandler::ENUMERATE, true);
+
+ // If the policy denies access but wants us to return true, we need
+ // to hand a valid (empty) iterator object to the caller.
+ if (!policy.allowed()) {
+ return policy.returnValue() &&
+ NewEmptyPropertyIterator(cx, 0, objp);
+ }
+ return handler->enumerate(cx, proxy, objp);
+}
+
+bool
+Proxy::call(JSContext* cx, HandleObject proxy, const CallArgs& args)
+{
+ JS_CHECK_RECURSION(cx, return false);
+ const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
+
+ // Because vp[0] is JS_CALLEE on the way in and JS_RVAL on the way out, we
+ // can only set our default value once we're sure that we're not calling the
+ // trap.
+ AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
+ BaseProxyHandler::CALL, true);
+ if (!policy.allowed()) {
+ args.rval().setUndefined();
+ return policy.returnValue();
+ }
+
+ return handler->call(cx, proxy, args);
+}
+
+bool
+Proxy::construct(JSContext* cx, HandleObject proxy, const CallArgs& args)
+{
+ JS_CHECK_RECURSION(cx, return false);
+ const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
+
+ // Because vp[0] is JS_CALLEE on the way in and JS_RVAL on the way out, we
+ // can only set our default value once we're sure that we're not calling the
+ // trap.
+ AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
+ BaseProxyHandler::CALL, true);
+ if (!policy.allowed()) {
+ args.rval().setUndefined();
+ return policy.returnValue();
+ }
+
+ return handler->construct(cx, proxy, args);
+}
+
+bool
+Proxy::nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl, const CallArgs& args)
+{
+ JS_CHECK_RECURSION(cx, return false);
+ RootedObject proxy(cx, &args.thisv().toObject());
+ // Note - we don't enter a policy here because our security architecture
+ // guards against nativeCall by overriding the trap itself in the right
+ // circumstances.
+ return proxy->as<ProxyObject>().handler()->nativeCall(cx, test, impl, args);
+}
+
+bool
+Proxy::hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp)
+{
+ JS_CHECK_RECURSION(cx, return false);
+ const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
+ *bp = false; // default result if we refuse to perform this action
+ AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::GET, true);
+ if (!policy.allowed())
+ return policy.returnValue();
+ return proxy->as<ProxyObject>().handler()->hasInstance(cx, proxy, v, bp);
+}
+
+bool
+Proxy::getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls)
+{
+ JS_CHECK_RECURSION(cx, return false);
+ return proxy->as<ProxyObject>().handler()->getBuiltinClass(cx, proxy, cls);
+}
+
+bool
+Proxy::isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer)
+{
+ JS_CHECK_RECURSION(cx, return false);
+ return proxy->as<ProxyObject>().handler()->isArray(cx, proxy, answer);
+}
+
+const char*
+Proxy::className(JSContext* cx, HandleObject proxy)
+{
+ // Check for unbounded recursion, but don't signal an error; className
+ // needs to be infallible.
+ int stackDummy;
+ if (!JS_CHECK_STACK_SIZE(GetNativeStackLimit(cx), &stackDummy))
+ return "too much recursion";
+
+ const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
+ AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
+ BaseProxyHandler::GET, /* mayThrow = */ false);
+ // Do the safe thing if the policy rejects.
+ if (!policy.allowed()) {
+ return handler->BaseProxyHandler::className(cx, proxy);
+ }
+ return handler->className(cx, proxy);
+}
+
+JSString*
+Proxy::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent)
+{
+ JS_CHECK_RECURSION(cx, return nullptr);
+ const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
+ AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
+ BaseProxyHandler::GET, /* mayThrow = */ false);
+ // Do the safe thing if the policy rejects.
+ if (!policy.allowed())
+ return handler->BaseProxyHandler::fun_toString(cx, proxy, indent);
+ return handler->fun_toString(cx, proxy, indent);
+}
+
+bool
+Proxy::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g)
+{
+ JS_CHECK_RECURSION(cx, return false);
+ return proxy->as<ProxyObject>().handler()->regexp_toShared(cx, proxy, g);
+}
+
+bool
+Proxy::boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp)
+{
+ JS_CHECK_RECURSION(cx, return false);
+ return proxy->as<ProxyObject>().handler()->boxedValue_unbox(cx, proxy, vp);
+}
+
+JSObject * const TaggedProto::LazyProto = reinterpret_cast<JSObject*>(0x1);
+
+/* static */ bool
+Proxy::watch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleObject callable)
+{
+ JS_CHECK_RECURSION(cx, return false);
+ return proxy->as<ProxyObject>().handler()->watch(cx, proxy, id, callable);
+}
+
+/* static */ bool
+Proxy::unwatch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id)
+{
+ JS_CHECK_RECURSION(cx, return false);
+ return proxy->as<ProxyObject>().handler()->unwatch(cx, proxy, id);
+}
+
+/* static */ bool
+Proxy::getElements(JSContext* cx, HandleObject proxy, uint32_t begin, uint32_t end,
+ ElementAdder* adder)
+{
+ JS_CHECK_RECURSION(cx, return false);
+ const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
+ AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::GET,
+ /* mayThrow = */ true);
+ if (!policy.allowed()) {
+ if (policy.returnValue()) {
+ MOZ_ASSERT(!cx->isExceptionPending());
+ return js::GetElementsWithAdder(cx, proxy, proxy, begin, end, adder);
+ }
+ return false;
+ }
+ return handler->getElements(cx, proxy, begin, end, adder);
+}
+
+/* static */ void
+Proxy::trace(JSTracer* trc, JSObject* proxy)
+{
+ const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
+ handler->trace(trc, proxy);
+}
+
+bool
+js::proxy_LookupProperty(JSContext* cx, HandleObject obj, HandleId id,
+ MutableHandleObject objp, MutableHandleShape propp)
+{
+ bool found;
+ if (!Proxy::has(cx, obj, id, &found))
+ return false;
+
+ if (found) {
+ MarkNonNativePropertyFound<CanGC>(propp);
+ objp.set(obj);
+ } else {
+ objp.set(nullptr);
+ propp.set(nullptr);
+ }
+ return true;
+}
+
+bool
+js::proxy_DefineProperty(JSContext* cx, HandleObject obj, HandleId id,
+ Handle<PropertyDescriptor> desc,
+ ObjectOpResult& result)
+{
+ return Proxy::defineProperty(cx, obj, id, desc, result);
+}
+
+bool
+js::proxy_HasProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* foundp)
+{
+ return Proxy::has(cx, obj, id, foundp);
+}
+
+bool
+js::proxy_GetProperty(JSContext* cx, HandleObject obj, HandleValue receiver, HandleId id,
+ MutableHandleValue vp)
+{
+ return Proxy::get(cx, obj, receiver, id, vp);
+}
+
+bool
+js::proxy_SetProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
+ HandleValue receiver, ObjectOpResult& result)
+{
+ return Proxy::set(cx, obj, id, v, receiver, result);
+}
+
+bool
+js::proxy_GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
+ MutableHandle<PropertyDescriptor> desc)
+{
+ return Proxy::getOwnPropertyDescriptor(cx, obj, id, desc);
+}
+
+bool
+js::proxy_DeleteProperty(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result)
+{
+ if (!Proxy::delete_(cx, obj, id, result))
+ return false;
+ return SuppressDeletedProperty(cx, obj, id); // XXX is this necessary?
+}
+
+void
+js::proxy_Trace(JSTracer* trc, JSObject* obj)
+{
+ MOZ_ASSERT(obj->is<ProxyObject>());
+ ProxyObject::trace(trc, obj);
+}
+
+/* static */ void
+ProxyObject::trace(JSTracer* trc, JSObject* obj)
+{
+ ProxyObject* proxy = &obj->as<ProxyObject>();
+
+ TraceEdge(trc, &proxy->shape_, "ProxyObject_shape");
+
+#ifdef DEBUG
+ if (trc->runtime()->gc.isStrictProxyCheckingEnabled() && proxy->is<WrapperObject>()) {
+ JSObject* referent = MaybeForwarded(proxy->target());
+ if (referent->compartment() != proxy->compartment()) {
+ /*
+ * Assert that this proxy is tracked in the wrapper map. We maintain
+ * the invariant that the wrapped object is the key in the wrapper map.
+ */
+ Value key = ObjectValue(*referent);
+ WrapperMap::Ptr p = proxy->compartment()->lookupWrapper(key);
+ MOZ_ASSERT(p);
+ MOZ_ASSERT(*p->value().unsafeGet() == ObjectValue(*proxy));
+ }
+ }
+#endif
+
+ // Note: If you add new slots here, make sure to change
+ // nuke() to cope.
+ TraceCrossCompartmentEdge(trc, obj, proxy->slotOfPrivate(), "private");
+ TraceEdge(trc, proxy->slotOfExtra(0), "extra0");
+
+ /*
+ * The GC can use the second reserved slot to link the cross compartment
+ * wrappers into a linked list, in which case we don't want to trace it.
+ */
+ if (!proxy->is<CrossCompartmentWrapperObject>())
+ TraceEdge(trc, proxy->slotOfExtra(1), "extra1");
+
+ Proxy::trace(trc, obj);
+}
+
+JSObject*
+js::proxy_WeakmapKeyDelegate(JSObject* obj)
+{
+ MOZ_ASSERT(obj->is<ProxyObject>());
+ return obj->as<ProxyObject>().handler()->weakmapKeyDelegate(obj);
+}
+
+void
+js::proxy_Finalize(FreeOp* fop, JSObject* obj)
+{
+ // Suppress a bogus warning about finalize().
+ JS::AutoSuppressGCAnalysis nogc;
+
+ MOZ_ASSERT(obj->is<ProxyObject>());
+ obj->as<ProxyObject>().handler()->finalize(fop, obj);
+ js_free(detail::GetProxyDataLayout(obj)->values);
+}
+
+void
+js::proxy_ObjectMoved(JSObject* obj, const JSObject* old)
+{
+ MOZ_ASSERT(obj->is<ProxyObject>());
+ obj->as<ProxyObject>().handler()->objectMoved(obj, old);
+}
+
+bool
+js::proxy_HasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp)
+{
+ return Proxy::hasInstance(cx, proxy, v, bp);
+}
+
+bool
+js::proxy_Call(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ RootedObject proxy(cx, &args.callee());
+ MOZ_ASSERT(proxy->is<ProxyObject>());
+ return Proxy::call(cx, proxy, args);
+}
+
+bool
+js::proxy_Construct(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ RootedObject proxy(cx, &args.callee());
+ MOZ_ASSERT(proxy->is<ProxyObject>());
+ return Proxy::construct(cx, proxy, args);
+}
+
+bool
+js::proxy_Watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable)
+{
+ return Proxy::watch(cx, obj, id, callable);
+}
+
+bool
+js::proxy_Unwatch(JSContext* cx, HandleObject obj, HandleId id)
+{
+ return Proxy::unwatch(cx, obj, id);
+}
+
+bool
+js::proxy_GetElements(JSContext* cx, HandleObject proxy, uint32_t begin, uint32_t end,
+ ElementAdder* adder)
+{
+ return Proxy::getElements(cx, proxy, begin, end, adder);
+}
+
+JSString*
+js::proxy_FunToString(JSContext* cx, HandleObject proxy, unsigned indent)
+{
+ return Proxy::fun_toString(cx, proxy, indent);
+}
+
+const ClassOps js::ProxyClassOps = {
+ nullptr, /* addProperty */
+ nullptr, /* delProperty */
+ nullptr, /* getProperty */
+ nullptr, /* setProperty */
+ nullptr, /* enumerate */
+ nullptr, /* resolve */
+ nullptr, /* mayResolve */
+ js::proxy_Finalize, /* finalize */
+ nullptr, /* call */
+ js::proxy_HasInstance, /* hasInstance */
+ nullptr, /* construct */
+ js::proxy_Trace, /* trace */
+};
+
+const ClassExtension js::ProxyClassExtension = PROXY_MAKE_EXT(
+ js::proxy_ObjectMoved
+);
+
+const ObjectOps js::ProxyObjectOps = {
+ js::proxy_LookupProperty,
+ js::proxy_DefineProperty,
+ js::proxy_HasProperty,
+ js::proxy_GetProperty,
+ js::proxy_SetProperty,
+ js::proxy_GetOwnPropertyDescriptor,
+ js::proxy_DeleteProperty,
+ js::proxy_Watch, js::proxy_Unwatch,
+ js::proxy_GetElements,
+ nullptr, /* enumerate */
+ js::proxy_FunToString
+};
+
+const Class js::ProxyObject::proxyClass =
+ PROXY_CLASS_DEF("Proxy", JSCLASS_HAS_CACHED_PROTO(JSProto_Proxy));
+
+const Class* const js::ProxyClassPtr = &js::ProxyObject::proxyClass;
+
+JS_FRIEND_API(JSObject*)
+js::NewProxyObject(JSContext* cx, const BaseProxyHandler* handler, HandleValue priv, JSObject* proto_,
+ const ProxyOptions& options)
+{
+ if (options.lazyProto()) {
+ MOZ_ASSERT(!proto_);
+ proto_ = TaggedProto::LazyProto;
+ }
+
+ return ProxyObject::New(cx, handler, priv, TaggedProto(proto_), options);
+}
+
+void
+ProxyObject::renew(JSContext* cx, const BaseProxyHandler* handler, const Value& priv)
+{
+ MOZ_ASSERT(!IsInsideNursery(this));
+ MOZ_ASSERT_IF(IsCrossCompartmentWrapper(this), IsDeadProxyObject(this));
+ MOZ_ASSERT(getClass() == &ProxyObject::proxyClass);
+ MOZ_ASSERT(!IsWindowProxy(this));
+ MOZ_ASSERT(hasDynamicPrototype());
+
+ setHandler(handler);
+ setCrossCompartmentPrivate(priv);
+ setExtra(0, UndefinedValue());
+ setExtra(1, UndefinedValue());
+}
+
+JS_FRIEND_API(JSObject*)
+js::InitProxyClass(JSContext* cx, HandleObject obj)
+{
+ static const JSFunctionSpec static_methods[] = {
+ JS_FN("revocable", proxy_revocable, 2, 0),
+ JS_FS_END
+ };
+
+ Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
+ RootedFunction ctor(cx);
+ ctor = global->createConstructor(cx, proxy, cx->names().Proxy, 2);
+ if (!ctor)
+ return nullptr;
+
+ if (!JS_DefineFunctions(cx, ctor, static_methods))
+ return nullptr;
+ if (!JS_DefineProperty(cx, obj, "Proxy", ctor, JSPROP_RESOLVING, JS_STUBGETTER, JS_STUBSETTER))
+ return nullptr;
+
+ global->setConstructor(JSProto_Proxy, ObjectValue(*ctor));
+ return ctor;
+}