<div dir="ltr"><div>I have an idea I’d like to propose with regards to Proxy objects. I would like to make a change to spec section 6.1.7.3 (as seen in the latest draft). The change is as follows: </div><div><br></div><div>The Internal Methods of Objects of an ECMAScript engine must conform to the list of invariants specified below. Ordinary ECMAScript Objects as well as all standard exotic objects in this specification maintain these invariants. ECMAScript Proxy objects maintain these invariants by means of runtime checks on the result of traps invoked on the [[ProxyHandler]] object using either the corresponding traps on the [[ProxyHandler]] should the needed trap be defined, or the default internal method otherwise.</div><div><br></div><div>Put simply, the change that I’m requesting would have the runtime checks verify the selfconsistency of the results returned by the [[ProxyHandler]] object instead of testing the [[ProxyHandler]] results against the target. The rationale behind this change is to allow Proxy objects the freedom to behave in a manner that is inconsistent with the behavior that would have been should the target have been accessed directly, while still requiring that the consistency of the behavior of the essential internal methods be upheld. In this way, a Proxy object would be able to project new properties for a proxied target even if the target object is not extensible, or even completely hide non-configurable properties. The requirement to do so would be implementation of the appropriate [[ProxyHandler]] methods so as to satisfy all of the corresponding invariants for all implemented handlers. The ECMAScript engine would then see results consistent with expectations, despite the fact that the results are inconsistent with the actual nature of the proxied target object.</div><div><br></div><div>An example might be the case of a library to mock objects.</div><div><br></div><div><font face="monospace, monospace">/* SourceFile.js */</font></div><div><font face="monospace, monospace">var obj = { bar: "The real bar. Accept no imitations!", fubar: "Always has been." };</font></div><div><font face="monospace, monospace">obj.seal();</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">export default obj;</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">/* TestFile.js */</font></div><div><font face="monospace, monospace">import testObj from "SourceFile";</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">var mock = new Proxy(testObj, {</font></div><div><font face="monospace, monospace">  has: function(target, key) {</font></div><div><font face="monospace, monospace">    var retval = false;</font></div><div><font face="monospace, monospace">    if (key == "foo") { //add the foo property</font></div><div><font face="monospace, monospace">      retval = true;</font></div><div><font face="monospace, monospace">    }</font></div><div><font face="monospace, monospace">    else if (key != "bar") { //hide the bar property</font></div><div><font face="monospace, monospace">      retval = (key in target);</font></div><div><font face="monospace, monospace">    }</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">    return retval;</font></div><div><font face="monospace, monospace">  },</font></div><div><font face="monospace, monospace">  ownKeys: function(target) {</font></div><div><font face="monospace, monospace">    var retval = Reflect.ownKeys(target);</font></div><div><font face="monospace, monospace">    var barIndex = retval.indexOf(bar);</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">    if (barIndex != -1)</font></div><div><font face="monospace, monospace">      retval.splice(barIndex, 1);</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">    retval.push("foo");</font></div><div><font face="monospace, monospace">    return retval;</font></div><div><font face="monospace, monospace">  },</font></div><div><font face="monospace, monospace">  defineProperty: function(target, key, descriptor) {</font></div><div><font face="monospace, monospace">    var retval = true;</font></div><div><font face="monospace, monospace">    if ((key == "foo") || (key == "bar")) {</font></div><div><font face="monospace, monospace">      retval = false;</font></div><div><font face="monospace, monospace">    }</font></div><div><font face="monospace, monospace">    else {</font></div><div><font face="monospace, monospace">      Reflect.defineProperty(target, key, descriptor);</font></div><div><font face="monospace, monospace">    }</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">    return retval;</font></div><div><font face="monospace, monospace">  },</font></div><div><font face="monospace, monospace">  get: function(target, key) {</font></div><div><font face="monospace, monospace">    var retval = undefined;</font></div><div><font face="monospace, monospace">    </font></div><div><font face="monospace, monospace">    if (key == "foo") {</font></div><div><font face="monospace, monospace">      retval = "You got the fake property!"</font></div><div><font face="monospace, monospace">    }</font></div><div><font face="monospace, monospace">    else if (key != "bar") {</font></div><div><font face="monospace, monospace">      retval = Reflect.deleteProperty(target, key);</font></div><div><font face="monospace, monospace">    }</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">    return retval;</font></div><div><font face="monospace, monospace">  },</font></div><div><font face="monospace, monospace">  set: function(target, key, value) {</font></div><div><font face="monospace, monospace">    var retval = false;</font></div><div><font face="monospace, monospace">    if ((key != "foo") && (key != "bar"))</font></div><div><font face="monospace, monospace">      retval = Reflect.set(target, key);</font></div><div><font face="monospace, monospace">    }</font></div><div><font face="monospace, monospace">    return retval;</font></div><div><font face="monospace, monospace">  },</font></div><div><font face="monospace, monospace">  deleteProperty: function(target, key) {</font></div><div><font face="monospace, monospace">    var retval = false;</font></div><div><font face="monospace, monospace">    if ((key != "foo") && (key != "bar"))</font></div><div><font face="monospace, monospace">      retval = Reflect.deleteProperty(target, key);</font></div><div><font face="monospace, monospace">    }</font></div><div><font face="monospace, monospace">    return retval;</font></div><div><font face="monospace, monospace">  },</font></div><div><font face="monospace, monospace">  getOwnPropertyDescriptor: function(target, key) {</font></div><div><font face="monospace, monospace">    var retval;</font></div><div><font face="monospace, monospace">    if (key == "foo") {</font></div><div><font face="monospace, monospace">      retval = {</font></div><div><font face="monospace, monospace">        enumerable: true, </font></div><div><font face="monospace, monospace">        writable: false, </font></div><div><font face="monospace, monospace">        configurable: false, </font></div><div><font face="monospace, monospace">        value: "You got the fake property!"</font></div><div><font face="monospace, monospace">      };</font></div><div><font face="monospace, monospace">    }</font></div><div><font face="monospace, monospace">    else if (key != "bar") {</font></div><div><font face="monospace, monospace">      retval = Reflect.getOwnPropertyDescriptor(target, key);</font></div><div><font face="monospace, monospace">    }</font></div><div><font face="monospace, monospace">    return retval;</font></div><div><font face="monospace, monospace">  }</font></div><div><font face="monospace, monospace">});</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">console.log(mock.fubar); // "Always has been"</font></div><div><font face="monospace, monospace">console.log(mock.foo);   // #1</font></div><div><font face="monospace, monospace">console.log(mock.bar);   // #2</font></div><div><br></div><div><br></div><div>Currently, if the above code were run, an error would be thrown at comment #1. Even if that line was commented out, an error would be thrown at comment #2. With my proposed change, the code would run successfully. Comment #1 would be "You got the fake property!" and comment #2 would be undefined. As a matter of completeness, if the handler only contained the "get" and "set" methods, this code would throw an error just as it currently would. The reason that it works with all of the handlers this that these handlers ensure that a consistent description of the is being presented to the interpreter since the interpreter would rely on the methods of the proxy object's handler to supply the data needed to validate the response of the original call.</div></div>