You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
vm: regression for "in" operator on global between 22.4.1 and 22.5.0; breaks jsdom #54436
I've already done so. It's 30 lines with no external dependencies. The issue is specifically about class hierarchies, inherited methods, and subclassing so it's not like it can get any smaller. Maybe I could remove enumerable: true, configurable: true to save two lines if that'd help you out. Or rename the variables from descriptive names like Window and EventTarget into generic ones like Subclass and Superclass.
I've already done so. It's 30 lines with no external dependencies. The issue is specifically about class hierarchies, inherited methods, and subclassing so it's not like it can get any smaller. Maybe I could remove enumerable: true, configurable: true to save two lines if that'd help you out. Or rename the variables from descriptive names like Window and EventTarget into generic ones like Subclass and Superclass.
You are correct, I was trying to oversimplify the code, but that's not needed. Sorry for the mistake. When I get a chance I'll check this for repro-exists.
I think the best course of this issue would be setting the prototype of the inner globalThis in the vm context. The issue is caused by the class hierarchy with the globalThis:
With the fix in #53517, the inner globalThis correctly queries the own properties of the sandbox object with Object.hasOwn, however, it can not distinguish in operator with Object.hasOwn with current V8 APIs.
To fix the issue and preserving the correct Object.hasOwn behavior, we have two solutions:
Delegate prototype lookup to the sandbox object, so that in operator will lookup the property on the outer prototype instead. However, this may unconditionally leaks vm outside Object.prototype properties to the inner vm context. I believe this may already be the case for jsdom since API objects like EventTarget are inherited from the outside Object.
Manually setting the inner globalThis prototype to be the Window.prototype in userland before (1) gets implemented. This works in Node.js versions before and after v22.5.0. Moreover, in v22.5.0 and later, Object.hasOwn will return correct value.
Since Object.hasOwn behavior is not fixable in user land, I am wondering if it would make sense to you to set the inner global prototype like the following snippet:
"use strict";constvm=require("vm");classEventTarget{addEventListener(){}}constwindowConstructor=function(){};Object.setPrototypeOf(windowConstructor,EventTarget);constwindowPrototype=Object.create(EventTarget.prototype);functionWindow(){vm.createContext(this);this._globalProxy=vm.runInContext("this",this);// Set up the inner global prototypeObject.setPrototypeOf(this._globalProxy,windowPrototype);constwindow=this;Object.defineProperty(this,"window",{get(){returnwindow._globalProxy;},enumerable: true,configurable: true});}constwindow=newWindow();console.log(vm.runInContext(`"addEventListener" in window`,window));
In this way, the following snippet will output the expected results:
constjsdom=require('jsdom');constvm=require('vm');const{JSDOM}=jsdom;constdom=newJSDOM(``,{runScripts: 'outside-only',});constvmContext=dom.getInternalVMContext();// Uncomment the following line to see the difference.// vm.runInContext(`Object.setPrototypeOf(window, Window.prototype)`, vmContext); console.log(vm.runInContext(`window instanceof Object`,vmContext));// => always `false` because the window was created outside the contextconsole.log(vm.runInContext(`window instanceof EventTarget`,vmContext));console.log(vm.runInContext(`'addEventListener' in window`,vmContext));console.log(vm.runInContext(`Object.hasOwn(window, 'addEventListener')`,vmContext));console.log(vm.runInContext(`Object.getOwnPropertyDescriptor(window, 'addEventListener')`,vmContext));
I can try such things, but from past experience anything other than our current (admittedly convoluted) setup has caused lots of tests to break. I would expect that anything that causes a regression like this to be reverted...
Given how many other things this appears to fix, I'm happy to close this. In fact, I would wonder if it's possible to back-port c0962dc to v20, so that I could land jsdom/jsdom#3765 without having to wait for v22 to become the earliest LTS...
Activity
avivkeller commentedon Aug 18, 2024
Can you strip your example to just the basics? Something very small that will reproduce this?
domenic commentedon Aug 19, 2024
I've already done so. It's 30 lines with no external dependencies. The issue is specifically about class hierarchies, inherited methods, and subclassing so it's not like it can get any smaller. Maybe I could remove
enumerable: true, configurable: trueto save two lines if that'd help you out. Or rename the variables from descriptive names likeWindowandEventTargetinto generic ones likeSubclassandSuperclass.avivkeller commentedon Aug 19, 2024
You are correct, I was trying to oversimplify the code, but that's not needed. Sorry for the mistake. When I get a chance I'll check this for
repro-exists.avivkeller commentedon Aug 19, 2024
FWIW the only vm subsystem PR for that version is #53517
marco-ippolito commentedon Aug 19, 2024
Maybe c0962dc @legendecas ?
legendecas commentedon Aug 19, 2024
I can confirm the issue. Will prepare a fix soon.
Temporarily comment out subtests
legendecas commentedon Aug 19, 2024
I think the best course of this issue would be setting the prototype of the inner
globalThisin the vm context. The issue is caused by the class hierarchy with theglobalThis:If I understand correctly, the expected class hierarchy should be:
With the fix in #53517, the inner
globalThiscorrectly queries the own properties of the sandbox object withObject.hasOwn, however, it can not distinguishinoperator withObject.hasOwnwith current V8 APIs.To fix the issue and preserving the correct
Object.hasOwnbehavior, we have two solutions:prototypelookup to the sandbox object, so thatinoperator will lookup the property on the outer prototype instead. However, this may unconditionally leaks vm outsideObject.prototypeproperties to the inner vm context. I believe this may already be the case forjsdomsince API objects likeEventTargetare inherited from the outsideObject.globalThisprototype to be theWindow.prototypein userland before (1) gets implemented. This works in Node.js versions before and afterv22.5.0. Moreover, inv22.5.0and later,Object.hasOwnwill return correct value.Since
Object.hasOwnbehavior is not fixable in user land, I am wondering if it would make sense to you to set the inner global prototype like the following snippet:In this way, the following snippet will output the expected results:
The output should be in Node.js
v22.5.0:domenic commentedon Aug 20, 2024
I can try such things, but from past experience anything other than our current (admittedly convoluted) setup has caused lots of tests to break. I would expect that anything that causes a regression like this to be reverted...
domenic commentedon Aug 25, 2024
Given how many other things this appears to fix, I'm happy to close this. In fact, I would wonder if it's possible to back-port c0962dc to v20, so that I could land jsdom/jsdom#3765 without having to wait for v22 to become the earliest LTS...
legendecas commentedon Aug 27, 2024
I removed https://github.com/nodejs/node/labels/dont-land-on-v20.x on the original PR and it can be backported in the next release.
vm: add vm proto property lookup test
vm: add vm proto property lookup test