16:08 | <Yoonseok> | Hello, I have a question about the Proxy object. As far as I understand, the goal of the proxy object was to provide security in JavaScript program communications. |
16:08 | <Yoonseok> | But, in the current specification, a proxy does not support to handle its internal slots such as [[RegExpMatcher]], [[DateValue]], and so on. Thus, once we wrap an object by a proxy, we lose a part of its identity. And, there are many cases to confirm an identity of a given object by checking the existence of such an internal slot (one of the |
16:08 | <Yoonseok> | examples is the use of the internal function "RequireInternalSlot"). |
16:08 | <Yoonseok> | I believe that everyone who is working on the specification aware of this issue. In the case of "Array.isArray", it checks its [[ProxyTarget]] when the given object is a proxy. |
16:08 | <Yoonseok> | But, I'm just wondering why they provide only a limited feature by a proxy. Is there any other solution to restrict the capability of such objects? Or, is there any reason not to handle the internal slots by a proxy object? |
16:10 | <ljharb> | proxies alone don’t provide the security guarantees you’re referencing; you also need a membrane implementation. |
16:10 | <Yoonseok> | Ah. Of course. I'm implementing membrane. |
16:11 | <Yoonseok> | But, once you pass an object via membrane, on the other-side, you lose the identity of a special object such as Date. |
16:11 | <Yoonseok> | because the proxy does not allow to bypass the internal slots of Date object. |
16:12 | <Yoonseok> | Simple example. |
16:12 | <Yoonseok> | ```var a = /abc/``` |
16:12 | <Yoonseok> | `var b = new Proxy(a, {})` |
16:13 | <Yoonseok> | `"abc".match(a) === true` |
16:13 | <Yoonseok> | `"abc".match(b)` throws an exception. |
16:19 | <Yoonseok> | Ah. I was disconnected. Is there anyone answered my question? sorry. |
16:46 | <ljharb> | Yoonseok: by "identity" you mean the internal slots that the builtins use; that means you have to replace all those builtins too |
16:47 | <bakkot_> | Yoonseok: the design of Proxies is such that if you need to call a built-in method on the object, you have to wrap the method as well, so that e.g. `.match` does not call the original `RegExp.prototype.match`, but instead calls a wrapped version which can invoke the original one on the original target |
16:49 | <bakkot_> | it's not just internal slots which behave this way: if you do `let created = new WeakSet; let factory = () => { let r = { m(){ console.log(created.has(this)); } }; created.add(r); return r; }; let a = factory(); let b = new Proxy(a, {}); a.m(); b.m();` you get `true` for the `a.m()` and `false` for the `b.m()` |
16:50 | <bakkot_> | or, rather, I should say that internal slots - with the exception of Array, for some reason - behave the same way WeakMaps do (except for some cross-realm stuff which isn't usually relevant) |
16:50 | <bakkot_> | where access to the internal slot requires having the object itself, not just a proxy for the object |
16:52 | <bakkot_> | as to why things are the way they are, I'm not 100% sure. I think the idea was that internal slots should be like WeakMaps, and WeakMaps should work this way because they should be based on object identity, and a Proxy for an object should not have the same identity as the object because that would be confusing. |
16:53 | <bakkot_> | by the way, take a look at https://github.com/ajvincent/es-membrane if you haven't yet; it's the most complete membrane implementation I know of |
16:53 | <ljharb> | p sure internal slots predate weakmaps, but i also don't know why things are the way they are |
16:53 | <bakkot_> | and talks about some rationales |
16:54 | <bakkot_> | ljharb yes but they also predate Proxies; the decision to make Proxies not access internal slots of theirr targets was not made until WeakMaps were being introduced |
16:55 | <ljharb> | true true, you are quite correct |
16:55 | <Yoonseok> | bakkot_ Thank you for the answer. I agree that we can provide a wrapped version of 'match' when the receiver is a proxy object. It might be one of practical solution. |
16:56 | <Yoonseok> | But, in my example, the receiver is the string. So, I called "String.prototype.match". |
16:56 | <Yoonseok> | In this case, we cannot simply provide a wrapped version because the receiver is not a proxy object. |
16:57 | <bakkot_> | ah, yes. |
16:57 | <bakkot_> | (well, technically you can I think, but it relies on a feature we are discussing removing, so I don't want to encourage you to take that route) |
16:59 | <Yoonseok> | ok. thank you for the responses. I would check the membrane implementation also. |
16:59 | <ljharb> | Yoonseok: the string, or the regex, is the proxy? |
16:59 | <bakkot_> | I think it's only RegExps which have this problem (and maybe TypedArrays); it is unusual for internal slots to be accessed by code outside of the prototype |
16:59 | <bakkot_> | ljharb the regex; see the code snippet above |
16:59 | <Yoonseok> | the regex is the proxy. |
17:00 | <ljharb> | Yoonseok: then you can define Symbol.match on the proxy |
17:00 | <Yoonseok> | ah. |
17:00 | <ljharb> | and that can define whatever semantics you want |
17:01 | <bakkot_> | ljharb that was the bit I was alluding to re: features we want to remove |
17:01 | <bakkot_> | actually though I am mistaken, we don't want to remove Symbol.match |
17:01 | <ljharb> | the part we're removing is in IsRegExp; String.prototype.match wouldn't change |
17:01 | <ljharb> | the proxy definitely still wouldn't pass IsRegExp with the change we want to make |
17:02 | <bakkot_> | the proposed change is to RRegExp.prototype[@@match], I believe |
17:02 | <ljharb> | i was under the impression it was removing steps 2 and 3 in https://tc39.es/ecma262/#sec-isregexp |
17:02 | <ljharb> | hmm, can you use the subclass trick to make the proxy have a regex slot? |
17:03 | <bakkot_> | I prefer not to think about that question :P |
17:04 | <ljharb> | haha fair |
17:04 | <Yoonseok> | :) |
20:43 | <devsnek> | we should add a binary not assignment operator :P |
20:44 | <rkirsling> | oh that is kind of a weird gap |
20:45 | <bakkot_> | just gotta add ≠ first |
20:46 | <bakkot_> | ≠ is actually fewer keystrokes than != for me |
20:46 | <devsnek> | what unicode is that |
20:46 | <ljharb> | option-equals |
20:46 | <bakkot_> | (on a mac) |
20:46 | <devsnek> | oh |
20:46 | <devsnek> | neat |
20:46 | <ljharb> | alt-8800 on a windows numeric keypad |
20:46 | <devsnek> | i could probably coerce my linux into doing that |
20:47 | <bakkot_> | that... is more keystrokes |
20:47 | <ljharb> | very true. but it looks nicer |