author | Jason Orendorff <jorendorff@mozilla.com> |
Fri, 07 Aug 2015 10:23:05 -0500 | |
changeset 257764 | dc21224de25bd42bb8ad4728c71988dfdb26ea26 |
parent 257763 | b268c7a67b6256c66efa40fdd7b3a39480183738 |
child 257765 | 8f410f4e8f5cc1960eb0812ff7c469dc96a08f9c |
push id | 29226 |
push user | ryanvm@gmail.com |
push date | Fri, 14 Aug 2015 13:01:14 +0000 |
treeherder | mozilla-central@1b2402247429 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | efaust |
bugs | 1105045 |
milestone | 43.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -999,17 +999,18 @@ GetObjectClassName(JSContext* cx, Handle * * As a result, we have calls to these three "substitute-this-object-for-that- * object" functions sprinkled at apparently arbitrary (but actually *very* * carefully and nervously selected) places throughout the engine and indeed * the universe. */ /* - * If obj a WindowProxy, return its current inner Window. Otherwise return obj. + * If obj is a WindowProxy, return its current inner Window. Otherwise return + * obj. This function can't fail and never returns nullptr. * * GetInnerObject is called when we need a scope chain; you never want a * WindowProxy on a scope chain. * * It's also called in a few places where an object comes in from script, and * the user probably intends to operate on the Window, not the * WindowProxy. Object.prototype.watch and various Debugger features do * this. (Users can't simply pass the Window, because the Window isn't exposed @@ -1022,16 +1023,17 @@ GetInnerObject(JSObject* obj) JS::AutoSuppressGCAnalysis nogc; return op(obj); } return obj; } /* * If obj is a Window object, return the WindowProxy. Otherwise return obj. + * This function can't fail; it never sets an exception or returns nullptr. * * This must be called before passing an object to script, if the object might * be a Window. (But usually those cases involve scope objects, and for those, * it is better to call GetThisObject instead.) */ inline JSObject* GetOuterObject(JSContext* cx, HandleObject obj) {
--- a/js/src/proxy/Proxy.cpp +++ b/js/src/proxy/Proxy.cpp @@ -255,27 +255,41 @@ Proxy::hasOwn(JSContext* cx, HandleObjec 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 +OuterizeValue(JSContext* cx, HandleValue v) +{ + if (v.isObject()) { + RootedObject obj(cx, &v.toObject()); + return ObjectValue(*GetOuterObject(cx, obj)); + } + return v; +} + bool -Proxy::get(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id, +Proxy::get(JSContext* cx, HandleObject proxy, HandleObject 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(); + // Outerize the receiver. Proxy handlers shouldn't have to know about + // the Window/WindowProxy distinction. + RootedObject receiver(cx, GetOuterObject(cx, 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; @@ -303,28 +317,32 @@ Proxy::callProp(JSContext* cx, HandleObj return false; } #endif return true; } bool -Proxy::set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v, HandleValue receiver, +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(); } + // Outerize the receiver. Proxy handlers shouldn't have to know about + // the Window/WindowProxy distinction. + RootedValue receiver(cx, OuterizeValue(cx, 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
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_6/Proxy/global-receiver.js @@ -0,0 +1,29 @@ +// The global object can be the receiver passed to the get and set traps of a Proxy. + +var global = this; +var proto = Object.getPrototypeOf(global); +var gets = 0, sets = 0; +Object.setPrototypeOf(global, new Proxy(proto, { + has(t, id) { + return id === "bareword" || Reflect.has(t, id); + }, + get(t, id, r) { + gets++; + assertEq(r, global); + return Reflect.get(t, id, r); + }, + set(t, id, v, r) { + sets++; + assertEq(r, global); + return Reflect.set(t, id, v, r); + } +})); + +assertEq(bareword, undefined); +assertEq(gets, 1); + +bareword = 12; +assertEq(sets, 1); +assertEq(global.bareword, 12); + +reportCompare(0, 0);