/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * 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/. */
// Used by private fields to manipulate the ProxyExpando: // All the following methods are called iff the handler for the proxy // returns true for useProxyExpandoObjectForPrivateFields. staticbool ProxySetOnExpando(JSContext* cx, HandleObject proxy, HandleId id,
HandleValue v, HandleValue receiver,
ObjectOpResult& result) {
MOZ_ASSERT(id.isPrivateName());
// For BaseProxyHandler, private names are stored in the expando object.
RootedObject expando(cx, proxy->as<ProxyObject>().expando().toObjectOrNull());
// SetPrivateElementOperation checks for hasOwn first, which ensures the // expando exsists. // // If we don't have an expando, then we're probably misusing debugger apis and // should just throw. if (!expando) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_SET_MISSING_PRIVATE); returnfalse;
}
Rooted<mozilla::Maybe<PropertyDescriptor>> ownDesc(cx); if (!GetOwnPropertyDescriptor(cx, expando, id, &ownDesc)) { returnfalse;
} if (ownDesc.isNothing()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_SET_MISSING_PRIVATE); returnfalse;
}
staticbool ProxyGetOnExpando(JSContext* cx, HandleObject proxy,
HandleValue receiver, HandleId id,
MutableHandleValue vp) { // For BaseProxyHandler, private names are stored in the expando object.
RootedObject expando(cx, proxy->as<ProxyObject>().expando().toObjectOrNull());
// We must have the expando, or GetPrivateElemOperation didn't call // hasPrivate first. if (!expando) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_GET_MISSING_PRIVATE); returnfalse;
}
// Because we controlled the creation of the expando, we know it's not a // proxy, and so can safely call internal methods on it without worrying about // exposing information about private names.
Rooted<mozilla::Maybe<PropertyDescriptor>> desc(cx); if (!GetOwnPropertyDescriptor(cx, expando, id, &desc)) { returnfalse;
} // We must have the object, same reasoning as the expando. if (desc.isNothing()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_SET_MISSING_PRIVATE); returnfalse;
}
// If the private name has a getter, delegate to that. if (desc->hasGetter()) {
RootedValue getter(cx, JS::ObjectValue(*desc->getter())); return js::CallGetter(cx, receiver, getter, vp);
}
// For BaseProxyHandler, private names are stored in the expando object.
RootedObject expando(cx, proxy->as<ProxyObject>().expando().toObjectOrNull());
if (!expando) {
expando = NewPlainObjectWithProto(cx, nullptr); if (!expando) { returnfalse;
}
// We shouldn't be definining a private field if we are supposed to throw; // this ought to have been caught by CheckPrivateField.
MOZ_ASSERT_IF(id.isPrivateName(), !handler->throwOnPrivateField());
AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true); if (!policy.allowed()) { if (!policy.returnValue()) { returnfalse;
} return result.succeed();
}
// Private field accesses have different semantics depending on the kind // of proxy involved, and so take a different path compared to regular // [[Get]] operations. For example, scripted handlers don't fire traps // when accessing private fields (because of the WeakMap semantics) if (id.isPrivateName() && handler->useProxyExpandoObjectForPrivateFields()) { return ProxyDefineOnExpando(cx, proxy, id, desc, result);
}
bool Proxy::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) {
AutoCheckRecursionLimit recursion(cx); if (!recursion.check(cx)) { returnfalse;
} const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
*bp = false; // default result if we refuse to perform this action
// If the handler is supposed to throw, we'll never have a private field so // simply return, as we shouldn't throw an invalid security error when // checking for the presence of a private field (WeakMap model). if (id.isPrivateName() && handler->throwOnPrivateField()) { returntrue;
}
// Private field accesses have different semantics depending on the kind // of proxy involved, and so take a different path compared to regular // [[Get]] operations. For example, scripted handlers don't fire traps // when accessing private fields (because of the WeakMap semantics) if (id.isPrivateName() && handler->useProxyExpandoObjectForPrivateFields()) { return ProxyHasOnExpando(cx, proxy, id, bp);
}
// Shouldn't have gotten here, as this should have been caught earlier.
MOZ_ASSERT_IF(id.isPrivateName(), !handler->throwOnPrivateField());
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();
}
// Private field accesses have different semantics depending on the kind // of proxy involved, and so take a different path compared to regular // [[Get]] operations. For example, scripted handlers don't fire traps // when accessing private fields (because of the WeakMap semantics) if (id.isPrivateName() && handler->useProxyExpandoObjectForPrivateFields()) { return ProxyGetOnExpando(cx, proxy, receiver, id, vp);
}
if (handler->hasPrototype()) { bool own; if (!handler->hasOwn(cx, proxy, id, &own)) { returnfalse;
} if (!own) {
RootedObject proto(cx); if (!GetPrototype(cx, proxy, &proto)) { returnfalse;
} if (!proto) { returntrue;
} return GetProperty(cx, proto, receiver, id, vp);
}
}
bool Proxy::get(JSContext* cx, HandleObject proxy, HandleValue receiver_,
HandleId id, MutableHandleValue vp) { // 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_, proxy)); return getInternal(cx, proxy, receiver, id, vp);
}
// Should have been handled already.
MOZ_ASSERT_IF(id.isPrivateName(), !handler->throwOnPrivateField());
AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true); if (!policy.allowed()) { if (!policy.returnValue()) { returnfalse;
} return result.succeed();
}
// Private field accesses have different semantics depending on the kind // of proxy involved, and so take a different path compared to regular // [[Set]] operations. // // This doesn't interact with hasPrototype, as PrivateFields are always // own propertiers, and so we never deal with prototype traversals. if (id.isPrivateName() && handler->useProxyExpandoObjectForPrivateFields()) { return ProxySetOnExpando(cx, proxy, id, v, receiver, result);
}
// Special case. See the comment on BaseProxyHandler::mHasPrototype. if (handler->hasPrototype()) { return handler->BaseProxyHandler::set(cx, proxy, id, v, receiver, result);
}
bool Proxy::set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v,
HandleValue receiver_, ObjectOpResult& result) { // 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_, proxy)); return setInternal(cx, proxy, id, v, receiver, result);
}
// If the policy denies access but wants us to return true, we need // to return an empty |props| list. if (!policy.allowed()) {
MOZ_ASSERT(props.empty()); return policy.returnValue();
}
// 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, JS::VoidHandlePropertyKey,
BaseProxyHandler::CALL, true); if (!policy.allowed()) {
args.rval().setUndefined(); return policy.returnValue();
}
// 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, JS::VoidHandlePropertyKey,
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) {
AutoCheckRecursionLimit recursion(cx); if (!recursion.check(cx)) { returnfalse;
}
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);
}
constchar* Proxy::className(JSContext* cx, HandleObject proxy) { // Check for unbounded recursion, but don't signal an error; className // needs to be infallible.
AutoCheckRecursionLimit recursion(cx); if (!recursion.checkDontReport(cx)) { return"too much recursion";
}
const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
AutoEnterPolicy policy(cx, handler, proxy, JS::VoidHandlePropertyKey,
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, bool isToSource) {
AutoCheckRecursionLimit recursion(cx); if (!recursion.check(cx)) { return nullptr;
} const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
AutoEnterPolicy policy(cx, handler, proxy, JS::VoidHandlePropertyKey,
BaseProxyHandler::GET, /* mayThrow = */ false); // Do the safe thing if the policy rejects. if (!policy.allowed()) { return handler->BaseProxyHandler::fun_toString(cx, proxy, isToSource);
} return handler->fun_toString(cx, proxy, isToSource);
}
#ifdef DEBUG staticinlinevoid CheckProxyIsInCCWMap(ProxyObject* proxy) { if (proxy->zone()->isGCCompacting()) { // Skip this check during compacting GC since objects' object groups may be // forwarded. It's not impossible to make this work, but requires adding a // parallel lookupWrapper() path for this one case. return;
}
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.
ObjectWrapperMap::Ptr p = proxy->compartment()->lookupWrapper(referent);
MOZ_ASSERT(p);
MOZ_ASSERT(*p->value().unsafeGet() == proxy);
}
} #endif
// Note: If you add new slots here, make sure to change // nuke() to cope.
traceEdgeToTarget(trc, proxy);
size_t nreserved = proxy->numReservedSlots(); for (size_t i = 0; i < nreserved; i++) { /* * 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>() &&
i == CrossCompartmentWrapperObject::GrayLinkReservedSlot) { continue;
}
TraceEdge(trc, proxy->reservedSlotPtr(i), "proxy_reserved");
}
Proxy::trace(trc, obj);
}
staticvoid proxy_Finalize(JS::GCContext* gcx, JSObject* obj) { // Suppress a bogus warning about finalize().
JS::AutoSuppressGCAnalysis nogc;
// This can be called from the compartment wrap hooks while in a realm with a // gray global. Trigger the read barrier on the global to ensure this is // unmarked.
cx->realm()->maybeGlobal();
if (proto_ != TaggedProto::LazyProto) {
cx->check(proto_); // |priv| might be cross-compartment.
}
if (options.lazyProto()) {
MOZ_ASSERT(!proto_);
proto_ = TaggedProto::LazyProto;
}
setHandler(handler);
setCrossCompartmentPrivate(priv); for (size_t i = 0; i < numReservedSlots(); i++) {
setReservedSlot(i, UndefinedValue());
}
}
// This implementation of HostEnsureCanAddPrivateElement is designed to work in // collaboration with Gecko to support the HTML implementation, which applies // only to Proxy type objects, and as a result we can simply provide proxy // handlers to correctly match the required semantics. bool DefaultHostEnsureCanAddPrivateElementCallback(JSContext* cx,
HandleValue val) { if (!val.isObject()) { returntrue;
}
Rooted<JSObject*> valObj(cx, &val.toObject()); if (!IsProxy(valObj)) { returntrue;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.