[ES6] Implement Proxy.[[IsExtensible]]
authorsbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 1 Mar 2016 23:51:53 +0000 (23:51 +0000)
committersbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 1 Mar 2016 23:51:53 +0000 (23:51 +0000)
https://bugs.webkit.org/show_bug.cgi?id=154872

Reviewed by Oliver Hunt.

This patch is a direct implementation of Proxy.[[IsExtensible]] with respect to section 9.5.3
of the ECMAScript 6 spec.
https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-isextensible

* runtime/ProxyObject.cpp:
(JSC::ProxyObject::preventExtensions):
(JSC::ProxyObject::performIsExtensible):
(JSC::ProxyObject::isExtensible):
(JSC::ProxyObject::visitChildren):
* runtime/ProxyObject.h:
* tests/es6.yaml:
* tests/stress/proxy-is-extensible.js: Added.
(assert):
(throw.new.Error.let.handler.get isExtensible):
(throw.new.Error):
(assert.let.handler.isExtensible):
(assert.):
(let.handler.isExtensible):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@197420 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/runtime/ProxyObject.cpp
Source/JavaScriptCore/runtime/ProxyObject.h
Source/JavaScriptCore/tests/es6.yaml
Source/JavaScriptCore/tests/stress/proxy-is-extensible.js [new file with mode: 0644]

index 4f04fed..5972956 100644 (file)
@@ -1,5 +1,31 @@
 2016-03-01  Saam barati  <sbarati@apple.com>
 
+        [ES6] Implement Proxy.[[IsExtensible]]
+        https://bugs.webkit.org/show_bug.cgi?id=154872
+
+        Reviewed by Oliver Hunt.
+
+        This patch is a direct implementation of Proxy.[[IsExtensible]] with respect to section 9.5.3
+        of the ECMAScript 6 spec.
+        https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-isextensible
+
+        * runtime/ProxyObject.cpp:
+        (JSC::ProxyObject::preventExtensions):
+        (JSC::ProxyObject::performIsExtensible):
+        (JSC::ProxyObject::isExtensible):
+        (JSC::ProxyObject::visitChildren):
+        * runtime/ProxyObject.h:
+        * tests/es6.yaml:
+        * tests/stress/proxy-is-extensible.js: Added.
+        (assert):
+        (throw.new.Error.let.handler.get isExtensible):
+        (throw.new.Error):
+        (assert.let.handler.isExtensible):
+        (assert.):
+        (let.handler.isExtensible):
+
+2016-03-01  Saam barati  <sbarati@apple.com>
+
         [ES6] Implement Proxy.[[PreventExtensions]]
         https://bugs.webkit.org/show_bug.cgi?id=154873
 
index 805f879..3cfa47c 100644 (file)
@@ -645,6 +645,60 @@ bool ProxyObject::preventExtensions(JSObject* object, ExecState* exec)
     return jsCast<ProxyObject*>(object)->performPreventExtensions(exec);
 }
 
+bool ProxyObject::performIsExtensible(ExecState* exec)
+{
+    VM& vm = exec->vm();
+
+    JSValue handlerValue = this->handler();
+    if (handlerValue.isNull()) {
+        throwVMTypeError(exec, ASCIILiteral("Proxy 'handler' is null. It should be an Object."));
+        return false;
+    }
+
+    JSObject* handler = jsCast<JSObject*>(handlerValue);
+    CallData callData;
+    CallType callType;
+    JSValue isExtensibleMethod = handler->getMethod(exec, callData, callType, makeIdentifier(vm, "isExtensible"), ASCIILiteral("'isExtensible' property of a Proxy's handler should be callable."));
+    if (exec->hadException())
+        return false;
+
+    JSObject* target = this->target();
+    if (isExtensibleMethod.isUndefined())
+        return target->isExtensibleInline(exec);
+
+    MarkedArgumentBuffer arguments;
+    arguments.append(target);
+    JSValue trapResult = call(exec, isExtensibleMethod, callType, callData, handler, arguments);
+    if (exec->hadException())
+        return false;
+
+    bool trapResultAsBool = trapResult.toBoolean(exec);
+    if (exec->hadException())
+        return false;
+
+    bool isTargetExtensible = target->isExtensibleInline(exec);
+    if (exec->hadException())
+        return false;
+
+    if (trapResultAsBool != isTargetExtensible) {
+        if (isTargetExtensible) {
+            ASSERT(!trapResultAsBool);
+            throwVMTypeError(exec, ASCIILiteral("Proxy object's 'isExtensible' trap returned false when the target is extensible. It should have returned true."));
+        } else {
+            ASSERT(!isTargetExtensible);
+            ASSERT(trapResultAsBool);
+            throwVMTypeError(exec, ASCIILiteral("Proxy object's 'isExtensible' trap returned true when the target is non-extensible. It should have returned false."));
+        }
+    }
+    
+    return trapResultAsBool;
+}
+
+bool ProxyObject::isExtensible(JSObject* object, ExecState* exec)
+{
+    return jsCast<ProxyObject*>(object)->performIsExtensible(exec);
+}
+
 void ProxyObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
 {
     ProxyObject* thisObject = jsCast<ProxyObject*>(cell);
index 28dedaf..0036170 100644 (file)
@@ -70,6 +70,7 @@ private:
     static bool deleteProperty(JSCell*, ExecState*, PropertyName);
     static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName);
     static bool preventExtensions(JSObject*, ExecState*);
+    static bool isExtensible(JSObject*, ExecState*);
     static void visitChildren(JSCell*, SlotVisitor&);
 
     bool getOwnPropertySlotCommon(ExecState*, PropertyName, PropertySlot&);
@@ -80,6 +81,7 @@ private:
     template <typename PerformDefaultPutFunction>
     void performPut(ExecState*, JSValue putValue, JSValue thisValue, PropertyName, PerformDefaultPutFunction);
     bool performPreventExtensions(ExecState*);
+    bool performIsExtensible(ExecState*);
 
     WriteBarrier<JSObject> m_target;
     WriteBarrier<Unknown> m_handler;
index 61b6fbe..d490061 100644 (file)
 - path: es6/Proxy_internal_set_calls_Object.assign.js
   cmd: runES6 :normal
 - path: es6/Proxy_isExtensible_handler.js
-  cmd: runES6 :fail
+  cmd: runES6 :normal
 - path: es6/Proxy_JSON.stringify_support.js
   cmd: runES6 :fail
 - path: es6/Proxy_ownKeys_handler.js
diff --git a/Source/JavaScriptCore/tests/stress/proxy-is-extensible.js b/Source/JavaScriptCore/tests/stress/proxy-is-extensible.js
new file mode 100644 (file)
index 0000000..045debd
--- /dev/null
@@ -0,0 +1,345 @@
+function assert(b) {
+    if (!b)
+        throw new Error("Bad assertion");
+}
+
+{
+    let target = {};
+    let error = null;
+    let handler = {
+        get isExtensible() {
+            error = new Error;
+            throw error;
+        }
+    };
+    
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        let threw = false;
+        try {
+            Reflect.isExtensible(proxy);
+        } catch(e) {
+            assert(e === error);
+            threw = true;
+        }
+        assert(threw);
+    }
+}
+
+{
+    let target = {};
+    let error = null;
+    let handler = {
+        isExtensible: function() {
+            error = new Error;
+            throw error;
+        }
+    };
+    
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        let threw = false;
+        try {
+            Reflect.isExtensible(proxy);
+        } catch(e) {
+            assert(e === error);
+            threw = true;
+        }
+        assert(threw);
+    }
+}
+
+{
+    let error = null;
+    let target = new Proxy({}, {
+        isExtensible: function() {
+            error = new Error;
+            throw error;
+        }
+    });
+    let handler = {
+        isExtensible: function(theTarget) {
+            return Reflect.isExtensible(theTarget);
+        }
+    };
+    
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        let threw = false;
+        try {
+            Reflect.isExtensible(proxy);
+        } catch(e) {
+            assert(e === error);
+            threw = true;
+        }
+        assert(threw);
+    }
+}
+
+{
+    let target = {};
+    let handler = {
+        isExtensible: function(theTarget) {
+            return false;
+        }
+    };
+    
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        let threw = false;
+        try {
+            Reflect.isExtensible(proxy);
+        } catch(e) {
+            assert(e.toString() === "TypeError: Proxy object's 'isExtensible' trap returned false when the target is extensible. It should have returned true.");
+            threw = true;
+        }
+        assert(threw);
+    }
+}
+
+{
+    let target = {};
+    Reflect.preventExtensions(target);
+    let handler = {
+        isExtensible: function(theTarget) {
+            return true;
+        }
+    };
+    
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        let threw = false;
+        try {
+            Reflect.isExtensible(proxy);
+        } catch(e) {
+            assert(e.toString() === "TypeError: Proxy object's 'isExtensible' trap returned true when the target is non-extensible. It should have returned false.");
+            threw = true;
+        }
+        assert(threw);
+    }
+}
+
+{
+    let target = {};
+    Object.freeze(target);
+    let handler = {
+        isExtensible: function(theTarget) {
+            return true;
+        }
+    };
+    
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        let threw = false;
+        try {
+            Reflect.isExtensible(proxy);
+        } catch(e) {
+            assert(e.toString() === "TypeError: Proxy object's 'isExtensible' trap returned true when the target is non-extensible. It should have returned false.");
+            threw = true;
+        }
+        assert(threw);
+    }
+}
+
+{
+    let target = {};
+    Object.seal(target);
+    let handler = {
+        isExtensible: function(theTarget) {
+            return true;
+        }
+    };
+    
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        let threw = false;
+        try {
+            Reflect.isExtensible(proxy);
+        } catch(e) {
+            assert(e.toString() === "TypeError: Proxy object's 'isExtensible' trap returned true when the target is non-extensible. It should have returned false.");
+            threw = true;
+        }
+        assert(threw);
+    }
+}
+
+{
+    let target = {};
+    Object.preventExtensions(target);
+    let handler = {
+        isExtensible: function(theTarget) {
+            return true;
+        }
+    };
+    
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        let threw = false;
+        try {
+            Reflect.isExtensible(proxy);
+        } catch(e) {
+            assert(e.toString() === "TypeError: Proxy object's 'isExtensible' trap returned true when the target is non-extensible. It should have returned false.");
+            threw = true;
+        }
+        assert(threw);
+    }
+}
+
+{
+    let target = {};
+    let called = false;
+    let handler = {
+        isExtensible: function(theTarget) {
+            called = true;
+            return true;
+        }
+    };
+    
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        let threw = false;
+        let result = Reflect.isExtensible(proxy);
+        assert(result);
+        assert(called);
+        called = false;
+    }
+}
+
+{
+    let target = {};
+    let called = false;
+    Reflect.preventExtensions(target);
+    let handler = {
+        isExtensible: function(theTarget) {
+            assert(theTarget === target);
+            called = true;
+            return Reflect.isExtensible(theTarget);
+        }
+    };
+    
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        let threw = false;
+        let result = Object.isExtensible(proxy);
+        assert(!result);
+        assert(called);
+        called = false;
+        
+        result = Object.isFrozen(proxy);
+        assert(result);
+        assert(called);
+        called = false;
+
+        result = Object.isSealed(proxy);
+        assert(result);
+        assert(called);
+        called = false;
+    }
+}
+
+{
+    let target = {};
+    let called = false;
+    Object.freeze(target);
+    let handler = {
+        isExtensible: function(theTarget) {
+            assert(theTarget === target);
+            called = true;
+            return Reflect.isExtensible(theTarget);
+        }
+    };
+    
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        let threw = false;
+        let result = Object.isExtensible(proxy);
+        assert(!result);
+        assert(called);
+        called = false;
+        
+        result = Object.isFrozen(proxy);
+        assert(result);
+        assert(called);
+        called = false;
+
+        result = Object.isSealed(proxy);
+        assert(result);
+        assert(called);
+        called = false;
+    }
+}
+
+{
+    let target = {};
+    let called = false;
+    Object.seal(target);
+    let handler = {
+        isExtensible: function(theTarget) {
+            assert(theTarget === target);
+            called = true;
+            return Reflect.isExtensible(theTarget);
+        }
+    };
+    
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        let threw = false;
+        let result = Object.isExtensible(proxy);
+        assert(!result);
+        assert(called);
+        called = false;
+        
+        result = Object.isFrozen(proxy);
+        assert(result);
+        assert(called);
+        called = false;
+
+        result = Object.isSealed(proxy);
+        assert(result);
+        assert(called);
+        called = false;
+    }
+}
+
+// FIXME: https://bugs.webkit.org/show_bug.cgi?id=154650
+// needs to land for this test to pass because it depends on Proxy.[[OwnPropertyKeys]].
+/*
+{
+    let target = {};
+    Object.defineProperty(target, "x", {
+        writable: true,
+        configurable: true,
+        value: 45,
+        enumerable: true
+    });
+    let called = false;
+    Reflect.preventExtensions(target);
+    let handler = {
+        isExtensible: function(theTarget) {
+            assert(theTarget === target);
+            called = true;
+            return Reflect.isExtensible(theTarget);
+        }
+    };
+    
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        let threw = false;
+        let result = Object.isExtensible(proxy);
+        assert(!result);
+        assert(called);
+        called = false;
+        
+        result = Object.isFrozen(proxy);
+        assert(!result);
+        assert(called);
+        called = false;
+
+        result = Object.isSealed(proxy);
+        assert(!result);
+        assert(called);
+        called = false;
+    }
+}
+*/
+