From 0f0dc9862c3fdcc149b7ad74fda2a77e2a2799e6 Mon Sep 17 00:00:00 2001 From: Eddy Bruel Date: Fri, 22 Mar 2013 19:43:12 -0700 Subject: [PATCH] Bug 789897 - Add scripting support + tests for isExtensible, preventExtensions traps. r=jwalden, r=jorendorff --HG-- extra : rebase_source : 509886634ea50735b82c12d00e61840010359d74 --- .../proxy/testDirectProxyDefineProperty2.js | 9 +++- .../testDirectProxyPreventExtensions1.js | 4 ++ .../testDirectProxyPreventExtensions2.js | 17 ++++++++ .../testDirectProxyPreventExtensions3.js | 10 +++++ .../testDirectProxyPreventExtensions4.js | 10 +++++ js/src/js.msg | 2 +- js/src/jsproxy.cpp | 43 +++++++++++++++++++ js/src/vm/CommonPropertyNames.h | 1 + 8 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions1.js create mode 100644 js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions2.js create mode 100644 js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions3.js create mode 100644 js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions4.js diff --git a/js/src/jit-test/tests/proxy/testDirectProxyDefineProperty2.js b/js/src/jit-test/tests/proxy/testDirectProxyDefineProperty2.js index 90934d0cde0..dac98c05827 100644 --- a/js/src/jit-test/tests/proxy/testDirectProxyDefineProperty2.js +++ b/js/src/jit-test/tests/proxy/testDirectProxyDefineProperty2.js @@ -24,5 +24,12 @@ var desc = { enumerable: false, configurable: true }; -Object.defineProperty(new Proxy(target, handler), 'foo', desc); + +var p = new Proxy(target, handler); +Object.defineProperty(p, 'foo', desc); assertEq(called, true); +assertEq(Object.isExtensible(target), true); +assertEq(Object.isExtensible(p), true); +Object.preventExtensions(target); +assertEq(Object.isExtensible(target), false); +assertEq(Object.isExtensible(p), false); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions1.js b/js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions1.js new file mode 100644 index 00000000000..8839a297ee0 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions1.js @@ -0,0 +1,4 @@ +// Forward to the target if the trap is not defined +var target = {}; +Object.preventExtensions(new Proxy(target, {})); +assertEq(Object.isExtensible(target), false); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions2.js b/js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions2.js new file mode 100644 index 00000000000..122695c9379 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions2.js @@ -0,0 +1,17 @@ +/* + * Call the trap with the handler as the this value and the target as the first + * argument. + */ +var target = {}; +var called = false; +var handler = { + preventExtensions: function (target1) { + assertEq(this, handler); + assertEq(target1, target); + Object.preventExtensions(target1); + called = true; + return true; + } +}; +Object.preventExtensions(new Proxy(target, handler)); +assertEq(called, true); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions3.js b/js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions3.js new file mode 100644 index 00000000000..167e219679e --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions3.js @@ -0,0 +1,10 @@ +load(libdir + "asserts.js"); + +// Throw a TypeError if the trap reports an extensible object as non-extensible +assertThrowsInstanceOf(function () { + Object.preventExtensions(new Proxy({}, { + preventExtensions: function () { + return true; + } + })); +}, TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions4.js b/js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions4.js new file mode 100644 index 00000000000..73abf2393b1 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions4.js @@ -0,0 +1,10 @@ +load(libdir + "asserts.js"); + +// Throw a TypeError if the object refuses to be made non-extensible +assertThrowsInstanceOf(function () { + Object.preventExtensions(new Proxy({}, { + preventExtensions: function () { + return false; + } + })); +}, TypeError); diff --git a/js/src/js.msg b/js/src/js.msg index 568cdd5e7ed..13c6a06b8ff 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -235,7 +235,7 @@ MSG_DEF(JSMSG_UNUSED181, 181, 0, JSEXN_NONE, "") MSG_DEF(JSMSG_BAD_GENERATOR_SEND, 182, 1, JSEXN_TYPEERR, "attempt to send {0} to newborn generator") MSG_DEF(JSMSG_UNUSED183, 183, 0, JSEXN_NONE, "") MSG_DEF(JSMSG_UNUSED184, 184, 0, JSEXN_NONE, "") -MSG_DEF(JSMSG_UNUSED185, 185, 0, JSEXN_NONE, "") +MSG_DEF(JSMSG_CANT_REPORT_AS_NON_EXTENSIBLE, 185, 0, JSEXN_TYPEERR, "proxy can't report an extensible object as non-extensible") MSG_DEF(JSMSG_UNUSED186, 186, 0, JSEXN_NONE, "") MSG_DEF(JSMSG_UNUSED187, 187, 0, JSEXN_NONE, "") MSG_DEF(JSMSG_INCOMPATIBLE_METHOD, 188, 3, JSEXN_TYPEERR, "{0} {1} called on incompatible {2}") diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 25fea0a6c19..55973d4f0fe 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -1044,6 +1044,7 @@ class ScriptedDirectProxyHandler : public DirectProxyHandler { virtual ~ScriptedDirectProxyHandler(); /* ES5 Harmony fundamental proxy traps. */ + virtual bool preventExtensions(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE; virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, PropertyDescriptor *desc, unsigned flags) MOZ_OVERRIDE; virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, @@ -1612,6 +1613,48 @@ ScriptedDirectProxyHandler::~ScriptedDirectProxyHandler() { } +bool +ScriptedDirectProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy) +{ + // step a + RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); + + // step b + RootedObject target(cx, GetProxyTargetObject(proxy)); + + // step c + RootedValue trap(cx); + if (!JSObject::getProperty(cx, handler, handler, cx->names().preventExtensions, &trap)) + return false; + + // step d + if (trap.isUndefined()) + return DirectProxyHandler::preventExtensions(cx, proxy); + + // step e + Value argv[] = { + ObjectValue(*target) + }; + RootedValue trapResult(cx); + if (!Invoke(cx, ObjectValue(*handler), trap, 1, argv, trapResult.address())) + return false; + + // step f + bool success = ToBoolean(trapResult); + if (success) { + // step g + if (target->isExtensible()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_REPORT_AS_NON_EXTENSIBLE); + return false; + } + return true; + } + + // step h + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CHANGE_EXTENSIBILITY); + return false; +} + // FIXME: Move to Proxy::getPropertyDescriptor once ScriptedIndirectProxy is removed bool ScriptedDirectProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index f35a59a6f55..2f363a4a0d5 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -114,6 +114,7 @@ macro(parseFloat, parseFloat, "parseFloat") \ macro(parseInt, parseInt, "parseInt") \ macro(pattern, pattern, "pattern") \ + macro(preventExtensions, preventExtensions, "preventExtensions") \ macro(propertyIsEnumerable, propertyIsEnumerable, "propertyIsEnumerable") \ macro(proto, proto, "__proto__") \ macro(return, return_, "return") \