diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /js/src/proxy/Proxy.cpp | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | uxp-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz |
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/src/proxy/Proxy.cpp')
-rw-r--r-- | js/src/proxy/Proxy.cpp | 812 |
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 0000000000..b43fd02d21 --- /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; +} |