Bug 789897 - Add scripting support + tests for isExtensible, preventExtensions traps. r=jwalden, r=jorendorff
authorEddy Bruel <ejpbruel@mozilla.com>
Fri, 22 Mar 2013 19:43:12 -0700
changeset 126572 2e564ec4c11d2cbc37a43e1247c4c72078ebcb24
parent 126571 804c5ca59d99e89c7ee1e1d223a6ceafd34e0b65
child 126573 321ce1200edea58447113365dd89f6257136a664
push id24488
push userryanvm@gmail.com
push dateFri, 29 Mar 2013 00:54:52 +0000
treeherdermozilla-central@8aeabe064932 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwalden, jorendorff
bugs789897
milestone22.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
Bug 789897 - Add scripting support + tests for isExtensible, preventExtensions traps. r=jwalden, r=jorendorff
js/src/jit-test/tests/proxy/testDirectProxyDefineProperty2.js
js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions1.js
js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions2.js
js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions3.js
js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions4.js
js/src/js.msg
js/src/jsproxy.cpp
js/src/vm/CommonPropertyNames.h
--- a/js/src/jit-test/tests/proxy/testDirectProxyDefineProperty2.js
+++ b/js/src/jit-test/tests/proxy/testDirectProxyDefineProperty2.js
@@ -19,10 +19,17 @@ var handler = {
     }
 }
 var desc = {
     value: 'bar',
     writable: true,
     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);
new file mode 100644
--- /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);
new file mode 100644
--- /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);
new file mode 100644
--- /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);
new file mode 100644
--- /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);
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -230,17 +230,17 @@ MSG_DEF(JSMSG_UNUSED176,              17
 MSG_DEF(JSMSG_UNUSED177,              177, 0, JSEXN_NONE, "")
 MSG_DEF(JSMSG_UNUSED178,              178, 0, JSEXN_NONE, "")
 MSG_DEF(JSMSG_UNUSED179,              179, 0, JSEXN_NONE, "")
 MSG_DEF(JSMSG_UNUSED180,              180, 0, JSEXN_NONE, "")
 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}")
 MSG_DEF(JSMSG_UNUSED189,              189, 0, JSEXN_NONE, "")
 MSG_DEF(JSMSG_UNUSED190,              190, 0, JSEXN_NONE, "")
 MSG_DEF(JSMSG_UNUSED191,              191, 0, JSEXN_NONE, "")
 MSG_DEF(JSMSG_UNUSED192,              192, 0, JSEXN_NONE, "")
 MSG_DEF(JSMSG_BAD_FOR_EACH_LOOP,      193, 0, JSEXN_SYNTAXERR, "invalid for each loop")
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -1039,16 +1039,17 @@ GetDirectProxyHandlerObject(JSObject *pr
 
 /* Derived class for all scripted direct proxy handlers. */
 class ScriptedDirectProxyHandler : public DirectProxyHandler {
   public:
     ScriptedDirectProxyHandler();
     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,
                                           PropertyDescriptor *desc, unsigned flags) MOZ_OVERRIDE;
     virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
                                 PropertyDescriptor *desc) MOZ_OVERRIDE;
     virtual bool getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props);
     virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) MOZ_OVERRIDE;
@@ -1607,16 +1608,58 @@ ScriptedDirectProxyHandler::ScriptedDire
         : DirectProxyHandler(&sScriptedDirectProxyHandlerFamily)
 {
 }
 
 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,
                                                   PropertyDescriptor *desc, unsigned flags)
 {
     JS_CHECK_RECURSION(cx, return false);
 
     if (!GetOwnPropertyDescriptor(cx, proxy, id, desc))
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -109,16 +109,17 @@
     macro(numeric, numeric, "numeric") \
     macro(objectNull, objectNull, "[object Null]") \
     macro(objectUndefined, objectUndefined, "[object Undefined]") \
     macro(of, of, "of") \
     macro(offset, offset, "offset") \
     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") \
     macro(sensitivity, sensitivity, "sensitivity") \
     macro(set, set, "set") \
     macro(shape, shape, "shape") \
     macro(source, source, "source") \
     macro(stack, stack, "stack") \