-
Notifications
You must be signed in to change notification settings - Fork 30k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proxy bug? #31989
Comments
I've found something: #26241 |
(wrong button) I recently ran into this problem too. For people actually using proxies to do useful things, this is really annoying. I ended up putting a custom inspect function on the proxy target, but that is not always possible if you don't own the proxy target. |
This is working as intended. Inspecting something should be side effect free. It was "by chance" that the specific trap was triggered with that key in Node.js 10.x. Browsers do not trigger any traps either and it caused all kinds of difficulties for people inspecting proxies. Using the proxy target is exactly how it should be done in such cases. That way it is always explicit and does not rely on a implementation detail. |
@tylerlong you can achieve what you want by writing: const util = require('util')
const a = { b: 1 }
const p = new Proxy(a, { get: (target, prop, receiver) => {
// Do things
return target[prop]
}})
p[util.inspect.custom] = () => 'Hello world!'
console.log(p) |
@tylerlong @devsnek I hope the example above helps :) if there are any other use cases that you want to look into, please let me know. |
I realize this was an intentional design choice, but it's very annoying if your proxy is being used to say, mock language features. I know that my proxies are not doing things that would be considered side effects to the consumers of them, and yet when they log them they see the wrong thing. Perhaps we can have a way to tag proxies or something? |
That reintroduces the problem that buggy proxies cause all kinds of hard to debug issues. |
To me this sounds like it would be ideal to add the custom inspect function on each proxy instance before returning it to the user. I am going to close this, since it's not clear what traps a user might want to get invoked and which ones not and there does not seem to be a way to always get this right. |
@BridgeAR proxies don't have own properties, and you don't always own the proxy target, like I said above. |
@devsnek it would be possible to use the proxy as an indirection to the actual target in such cases (by defining a "redirectTarget" with the properties that should be special handled). That would not be ideal, since all traps have to properly redirect but it should work. |
@BridgeAR that's actually not possible, proxy traps perform certain verifications on the underlying target as they're called, so you need them to line up properly. |
I wrote a library to provide a proxy. I am only in charge of the proxy. The actual object is out of my control so I cannot change it. |
The original behavior was relying on side effects of an implementation detail and it caused many issues that were partially difficult to work around. I do not think this is something we should support. A hackish way (that will mostly work for most things that can be done with an object) to implement what you want is to have a redirection as I mentioned above: const foreign = { a: true };
const redirectTarget = {
[util.inspect.custom]() {
return [1, 2, 3];
}
};
const handler = {
getPrototypeOf() { return Object.getPrototypeOf(foreign); },
setPrototypeOf(source, target) { return Object.setPrototypeOf(foreign, target); },
preventExtensions() { return Object.preventExtensions(foreign); },
getOwnPropertyDescriptor(target, prop) { return Object.getOwnPropertyDescriptor(foreign, prop); },
defineProperty(target, prop, value) { return Object.defineProperty(foreign, prop, value); },
has(target, prop) { return prop in foreign; },
get(target, prop) {
if (prop === util.inspect.custom) {
return target[prop]
}
return foreign[prop]
},
set(target, prop, value) { return foreign[prop] = value; },
deleteProperty(target, prop) { return delete foreign[prop]; },
ownKeys() { return Reflect.ownKeys(foreign); }, // This is tricky, since it's not clear what exactly should be done
// etc...
};
const proxy = new Proxy(redirectTarget, handler)
console.log(proxy) |
I don't get the point that you said it is "side effect". "proxy traps being triggered by .inspect()" so that it is side effect? proxy traps also triggered by simply getting a property, when don't you consider it a side effect? const p = new Proxy(a, { get: (target, prop, receiver) => {
// do some side effect
return target[prop]
}}) I don't think triggering an event is side effect. Side effect is in the event handling code written by end developers. And it is by design for business requirements. |
@tylerlong using I recommend to add a custom inspect function in case you want to know when that is triggered. So far it was "by chance" that accessing the custom inspect function would trigger the proxy trap and that was fixed. Maybe you could describe what you try to achieve to find out if there's an alternative way to implement the same. |
I still don't get the logic. If the logic is correct, the following should also be correct:
BUT, if the goal is to make the behavior identical to browsers', it makes sense. I don't want to start a debate on this. If browser behavior is this, then I will just accept it. |
I created a library which is "smart". The internal implementation is kind of complicated but let me simplify the use case: It provides a method called autoRun which monitor an object In order to implement it, I need to know how
Now the question is: if const f = o => {
console.log(o)
} In such case, no matter how
I cannot do this, because what I created is a library. I have no control over With the details explained, I don't believe there is a solution. |
@tylerlong how does inspection change const util = require('util')
const userObject = {
b: 1,
[util.inspect.custom]() {
this.foo = true;
this.b === 1
}
}
const proxiedObject = new Proxy(userObject, {
get(target, prop) {
console.log('GET', prop)
return target[prop]
},
set() { console.log('SET') }
})
util.inspect(proxiedObject) This would still trigger the get and set traps. There is indeed probably very little that could be done to improve the current situation. I am therefore closing this. If anyone has a solution, please open a new issue, reopen or leave a comment. |
@BridgeAR Thanks The sample code you posted makes sense. The only disadvantage is: it is changing the target object directly (adding My original plan was to add the logic to |
@tylerlong my example was meant as coming from the user as is. I wanted to indicate that if the user accesses or changes properties from inside a user defined custom inspect function, it would still be noticeable by the proxy while the object is inspected. Please be aware that the user could also use In your example above you note the following:
This still holds with the current inspection: if |
Code above will output
Hello world
in node 10.xIt will output
{ b: 1 }
in node 13.xI am not sure this is by design a breaking change or a bug.
The text was updated successfully, but these errors were encountered: