Title: [196789] trunk/Source/_javascript_Core
Revision
196789
Author
[email protected]
Date
2016-02-18 17:07:37 -0800 (Thu, 18 Feb 2016)

Log Message

Implement Proxy.[[HasProperty]]
https://bugs.webkit.org/show_bug.cgi?id=154313

Reviewed by Filip Pizlo.

This patch is a straight forward implementation of
Proxy.[[HasProperty]] with respect to section 9.5.7
of the ECMAScript spec.
https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-hasproperty-p

* runtime/ProxyObject.cpp:
(JSC::ProxyObject::performInternalMethodGetOwnProperty):
(JSC::ProxyObject::performHasProperty):
(JSC::ProxyObject::getOwnPropertySlotCommon):
* runtime/ProxyObject.h:
* tests/es6.yaml:
* tests/stress/proxy-basic.js:
(assert):
(let.handler.has):
* tests/stress/proxy-has-property.js: Added.
(assert):
(throw.new.Error.let.handler.get has):
(throw.new.Error):
(assert.let.handler.has):
(let.handler.has):
(getOwnPropertyDescriptor):
(i.catch):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (196788 => 196789)


--- trunk/Source/_javascript_Core/ChangeLog	2016-02-19 01:03:13 UTC (rev 196788)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-02-19 01:07:37 UTC (rev 196789)
@@ -1,5 +1,35 @@
 2016-02-18  Saam Barati  <[email protected]>
 
+        Implement Proxy.[[HasProperty]]
+        https://bugs.webkit.org/show_bug.cgi?id=154313
+
+        Reviewed by Filip Pizlo.
+
+        This patch is a straight forward implementation of
+        Proxy.[[HasProperty]] with respect to section 9.5.7
+        of the ECMAScript spec.
+        https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-hasproperty-p
+
+        * runtime/ProxyObject.cpp:
+        (JSC::ProxyObject::performInternalMethodGetOwnProperty):
+        (JSC::ProxyObject::performHasProperty):
+        (JSC::ProxyObject::getOwnPropertySlotCommon):
+        * runtime/ProxyObject.h:
+        * tests/es6.yaml:
+        * tests/stress/proxy-basic.js:
+        (assert):
+        (let.handler.has):
+        * tests/stress/proxy-has-property.js: Added.
+        (assert):
+        (throw.new.Error.let.handler.get has):
+        (throw.new.Error):
+        (assert.let.handler.has):
+        (let.handler.has):
+        (getOwnPropertyDescriptor):
+        (i.catch):
+
+2016-02-18  Saam Barati  <[email protected]>
+
         Proxy's don't properly handle Symbols as PropertyKeys.
         https://bugs.webkit.org/show_bug.cgi?id=154385
 

Modified: trunk/Source/_javascript_Core/runtime/ProxyObject.cpp (196788 => 196789)


--- trunk/Source/_javascript_Core/runtime/ProxyObject.cpp	2016-02-19 01:03:13 UTC (rev 196788)
+++ trunk/Source/_javascript_Core/runtime/ProxyObject.cpp	2016-02-19 01:07:37 UTC (rev 196789)
@@ -210,18 +210,72 @@
     return true;
 }
 
+bool ProxyObject::performHasProperty(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
+{
+    VM& vm = exec->vm();
+    slot.setValue(this, None, jsUndefined()); // Nobody should rely on our value, but be safe and protect against any bad actors reading our value.
+    
+    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 hasMethod = handler->getMethod(exec, callData, callType, vm.propertyNames->has, ASCIILiteral("'has' property of a Proxy's handler should be callable."));
+    if (exec->hadException())
+        return false;
+    JSObject* target = this->target();
+    if (hasMethod.isUndefined())
+        return target->methodTable(vm)->getOwnPropertySlot(target, exec, propertyName, slot);
+
+    MarkedArgumentBuffer arguments;
+    arguments.append(target);
+    arguments.append(identifierToSafePublicJSValue(vm, Identifier::fromUid(&vm, propertyName.uid())));
+    if (exec->hadException())
+        return false;
+    JSValue trapResult = call(exec, hasMethod, callType, callData, handler, arguments);
+    if (exec->hadException())
+        return false;
+
+    bool trapResultAsBool = trapResult.toBoolean(exec);
+    if (exec->hadException())
+        return false;
+
+    if (!trapResultAsBool) {
+        PropertyDescriptor descriptor;
+        bool isPropertyDescriptorDefined = target->getOwnPropertyDescriptor(exec, propertyName, descriptor); 
+        if (exec->hadException())
+            return false;
+        if (isPropertyDescriptorDefined) {
+            if (!descriptor.configurable()) {
+                throwVMTypeError(exec, ASCIILiteral("Proxy 'has' must return 'true' for non-configurable properties."));
+                return false;
+            }
+            if (!target->isExtensible()) {
+                throwVMTypeError(exec, ASCIILiteral("Proxy 'has' must return 'true' for a non-extensible 'target' object with a configurable property."));
+                return false;
+            }
+        }
+    }
+
+    return trapResultAsBool;
+}
+
 bool ProxyObject::getOwnPropertySlotCommon(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
 {
+    slot.disableCaching();
     switch (slot.internalMethodType()) {
     case PropertySlot::InternalMethodType::Get:
         slot.setCustom(this, CustomAccessor, performProxyGet);
-        slot.disableCaching();
         return true;
     case PropertySlot::InternalMethodType::GetOwnProperty:
         return performInternalMethodGetOwnProperty(exec, propertyName, slot);
+    case PropertySlot::InternalMethodType::HasProperty:
+        return performHasProperty(exec, propertyName, slot);
     default:
-        // FIXME: Implement Proxy.[[HasProperty]].
-        // https://bugs.webkit.org/show_bug.cgi?id=154313
         return false;
     }
 

Modified: trunk/Source/_javascript_Core/runtime/ProxyObject.h (196788 => 196789)


--- trunk/Source/_javascript_Core/runtime/ProxyObject.h	2016-02-19 01:03:13 UTC (rev 196788)
+++ trunk/Source/_javascript_Core/runtime/ProxyObject.h	2016-02-19 01:07:37 UTC (rev 196789)
@@ -65,6 +65,7 @@
 
     bool getOwnPropertySlotCommon(ExecState*, PropertyName, PropertySlot&);
     bool performInternalMethodGetOwnProperty(ExecState*, PropertyName, PropertySlot&);
+    bool performHasProperty(ExecState*, PropertyName, PropertySlot&);
 
     WriteBarrier<JSObject> m_target;
     WriteBarrier<Unknown> m_handler;

Modified: trunk/Source/_javascript_Core/tests/es6.yaml (196788 => 196789)


--- trunk/Source/_javascript_Core/tests/es6.yaml	2016-02-19 01:03:13 UTC (rev 196788)
+++ trunk/Source/_javascript_Core/tests/es6.yaml	2016-02-19 01:07:37 UTC (rev 196789)
@@ -929,9 +929,9 @@
 - path: es6/Proxy_getPrototypeOf_handler.js
   cmd: runES6 :fail
 - path: es6/Proxy_has_handler.js
-  cmd: runES6 :fail
+  cmd: runES6 :normal
 - path: es6/Proxy_has_handler_instances_of_proxies.js
-  cmd: runES6 :fail
+  cmd: runES6 :normal
 - path: es6/Proxy_internal_defineProperty_calls_[[Set]].js
   cmd: runES6 :fail
 - path: es6/Proxy_internal_defineProperty_calls_SetIntegrityLevel.js
@@ -977,7 +977,7 @@
 - path: es6/Proxy_internal_get_calls_Function.prototype.bind.js
   cmd: runES6 :fail
 - path: es6/Proxy_internal_get_calls_HasBinding.js
-  cmd: runES6 :fail
+  cmd: runES6 :normal
 - path: es6/Proxy_internal_get_calls_instanceof_operator.js
   cmd: runES6 :fail
 - path: es6/Proxy_internal_get_calls_IteratorComplete_IteratorValue.js

Modified: trunk/Source/_javascript_Core/tests/stress/proxy-basic.js (196788 => 196789)


--- trunk/Source/_javascript_Core/tests/stress/proxy-basic.js	2016-02-19 01:03:13 UTC (rev 196788)
+++ trunk/Source/_javascript_Core/tests/stress/proxy-basic.js	2016-02-19 01:07:37 UTC (rev 196789)
@@ -8,11 +8,63 @@
 assert(Proxy.prototype === undefined);
 
 {
-    for (let i = 0; i < 1000; i++)
+    for (let i = 0; i < 100; i++)
         assert((new Proxy({}, {})).__proto__ === Object.prototype);
 }
 
 {
+    for (let i = 0; i < 100; i++) {
+        let threw = false;
+        try {
+            new Proxy({}, 20);
+        } catch(e) {
+            threw = true;
+            assert(e.toString() === "TypeError: A Proxy's 'handler' should be an Object");
+        }
+        assert(threw);
+    }
+}
+
+{
+    for (let i = 0; i < 100; i++) {
+        let threw = false;
+        try {
+            new Proxy({}, "");
+        } catch(e) {
+            threw = true;
+            assert(e.toString() === "TypeError: A Proxy's 'handler' should be an Object");
+        }
+        assert(threw);
+    }
+}
+
+{
+    for (let i = 0; i < 100; i++) {
+        let threw = false;
+        try {
+            new Proxy(20, {});
+        } catch(e) {
+            threw = true;
+            assert(e.toString() === "TypeError: A Proxy's 'target' should be an Object");
+        }
+        assert(threw);
+    }
+}
+
+{
+    for (let i = 0; i < 100; i++) {
+        let threw = false;
+        try {
+            new Proxy("", {});
+        } catch(e) {
+            threw = true;
+            assert(e.toString() === "TypeError: A Proxy's 'target' should be an Object");
+        }
+        assert(threw);
+    }
+}
+
+{
     // When we call Proxy it should throw
     for (let i = 0; i < 100; i++) {
         let threw = false;
@@ -282,3 +334,28 @@
         called = false;
     }
 }
+
+{
+    let prop = Symbol();
+    let theTarget = { };
+    Object.defineProperty(theTarget, prop, {
+        enumerable: true,
+        configurable: true
+    });
+    let called = false;
+    let handler = {
+        has: function(target, propName) {
+            assert(prop === propName);
+            called = true;
+            return true;
+        }
+    };
+
+    let proxy = new Proxy(theTarget, handler);
+    for (let i = 0; i < 100; i++) {
+        let result = prop in proxy;
+        assert(result);
+        assert(called);
+        called = false;
+    }
+}

Added: trunk/Source/_javascript_Core/tests/stress/proxy-has-property.js (0 => 196789)


--- trunk/Source/_javascript_Core/tests/stress/proxy-has-property.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/proxy-has-property.js	2016-02-19 01:07:37 UTC (rev 196789)
@@ -0,0 +1,369 @@
+function assert(b) {
+    if (!b)
+        throw new Error("Bad assertion.");
+}
+
+{
+    let error = null;
+    let target = {
+        x: 40
+    };
+
+    let handler = {
+        get has() {
+            error = new Error();
+            throw error;
+        }
+    };
+
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        let threw = false;
+        try {
+            "x" in proxy;
+        } catch(e) {
+            threw = true;
+            assert(e === error);
+        }
+        assert(threw);
+    }
+}
+
+{
+    let error = null;
+    let target = {
+        x: 40
+    };
+
+    let handler = {
+        has: function() {
+            error = new Error();
+            throw error;
+        }
+    };
+
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        let threw = false;
+        try {
+            "x" in proxy;
+        } catch(e) {
+            threw = true;
+            assert(e === error);
+        }
+        assert(threw);
+    }
+}
+
+{
+    let target = {
+        x: 40
+    };
+
+    let called = false;
+    let handler = {
+        has: function(theTarget, propName) {
+            called = true;
+            return propName in theTarget;
+        }
+    };
+
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        let result = "x" in proxy;
+        assert(result);
+        assert(called);
+        called = false;
+    }
+}
+
+{
+    let target = { };
+    Object.defineProperty(target, "x", { enumerable: true, configurable: true, value: 45});
+
+    let called = false;
+    let handler = {
+        has: function(theTarget, propName) {
+            called = true;
+            return propName in theTarget;
+        }
+    };
+
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        let result = "x" in proxy;
+        assert(result);
+        assert(called);
+        called = false;
+    }
+}
+
+{
+    let target = { };
+    Object.defineProperty(target, "x", { enumerable: true, configurable: true, value: 45});
+    let handler = { };
+
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        if (i % 2)
+            handler.has = null;
+        else
+            handler.has = undefined;
+        let result = "x" in proxy;
+        assert(result);
+    }
+}
+
+{
+    let target = { };
+    Object.defineProperty(target, "x", { enumerable: true, configurable: true, value: 45});
+    let handler = {
+        has: function() {
+            return false;
+        }
+    };
+
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        let result = "x" in proxy;
+        assert(!result);
+    }
+}
+
+{
+    let target = { };
+    Object.defineProperty(target, "x", { enumerable: true, configurable: false, value: 45});
+    let handler = {
+        has: function() {
+            return false;
+        }
+    };
+
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        let threw = false;
+        try {
+            "x" in proxy;
+        } catch(e) {
+            assert(e.toString() === "TypeError: Proxy 'has' must return 'true' for non-configurable properties.");
+            threw = true;
+        }
+        assert(threw);
+    }
+}
+
+{
+    let target = { };
+    Object.defineProperty(target, "x", { enumerable: true, configurable: false, value: 45});
+    let handler = {
+        has: function() {
+            return false;
+        }
+    };
+
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        let threw = false;
+        try {
+            "x" in proxy;
+        } catch(e) {
+            assert(e.toString() === "TypeError: Proxy 'has' must return 'true' for non-configurable properties.");
+            threw = true;
+        }
+        assert(threw);
+    }
+}
+
+{
+    let target = { };
+    Object.defineProperty(target, "x", { enumerable: true, configurable: false, value: 45});
+    let handler = {
+        has: function() {
+            return null;
+        }
+    };
+
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        let threw = false;
+        try {
+            "x" in proxy;
+        } catch(e) {
+            assert(e.toString() === "TypeError: Proxy 'has' must return 'true' for non-configurable properties.");
+            threw = true;
+        }
+        assert(threw);
+    }
+}
+
+{
+    let target = { };
+    Object.defineProperty(target, "x", { enumerable: true, configurable: false, value: 45});
+    let handler = {
+        has: function() {
+            return undefined;
+        }
+    };
+
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        let threw = false;
+        try {
+            "x" in proxy;
+        } catch(e) {
+            assert(e.toString() === "TypeError: Proxy 'has' must return 'true' for non-configurable properties.");
+            threw = true;
+        }
+        assert(threw);
+    }
+}
+
+{
+    let target = { };
+    Object.defineProperty(target, "x", { enumerable: true, configurable: false, value: 45});
+    let handler = {
+        has: function() {
+            return 0;
+        }
+    };
+
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        let threw = false;
+        try {
+            "x" in proxy;
+        } catch(e) {
+            assert(e.toString() === "TypeError: Proxy 'has' must return 'true' for non-configurable properties.");
+            threw = true;
+        }
+        assert(threw);
+    }
+}
+
+{
+    let target = { };
+    Object.defineProperty(target, "x", { enumerable: true, configurable: false, value: 45});
+    let called = false;
+    let handler = {
+        has: function() {
+            called = true;
+            return 1;
+        }
+    };
+
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        let result = "x" in proxy;
+        assert(result);
+        assert(called);
+        called = false;
+    }
+}
+
+{
+    let target = { };
+    Object.defineProperty(target, "x", { enumerable: true, configurable: false, value: 45});
+    let called = false;
+    let handler = {
+        has: function() {
+            called = true;
+            return "hello";
+        }
+    };
+
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        let result = "x" in proxy;
+        assert(result);
+        assert(called);
+        called = false;
+    }
+}
+
+{
+    let target = { };
+    let called = false;
+    let handler = {
+        has: function() {
+            called = true;
+            return true
+        }
+    };
+
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        let result = "x" in proxy;
+        assert(result);
+        assert(called);
+        called = false;
+    }
+}
+
+{
+    let proto = {x: 20};
+    let target = Object.create(proto);
+    assert(target.__proto__ === proto);
+    let called = false;
+    let handler = {
+        has: function(target, propName) {
+            called = true;
+            return propName in target;
+        }
+    };
+
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        let result = "x" in proxy;
+        assert(result);
+        assert(called);
+        called = false;
+    }
+}
+
+{
+    let target = {x: 20};
+    let handler = {
+        has: function(theTarget, propName) {
+            assert(theTarget === target);
+            called = true;
+            return propName in theTarget;
+        }
+    };
+    let proxy = new Proxy(target, handler);
+    let obj = Object.create(proxy);
+    assert(Reflect.getPrototypeOf(obj) === proxy);
+    let called = false;
+
+    for (let i = 0; i < 500; i++) {
+        let result = "x" in obj;
+        assert(result);
+        assert(called);
+        called = false;
+    }
+}
+
+{
+    let error = null;
+    let target = new Proxy({}, {
+        getOwnPropertyDescriptor: function() {
+            error = new Error();
+            throw error;
+        }
+    });
+    let handler = {
+        has: function(theTarget, propName) {
+            return false;
+        }
+    };
+    let proxy = new Proxy(target, handler);
+    for (let i = 0; i < 500; i++) {
+        let threw = false;
+        try {
+            "foo" in proxy;
+        } catch(e) {
+            assert(e === error);
+            threw = true;
+        }
+        assert(threw);
+    }
+}
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to