node/test/parallel/test-vm-global-property-prototype.js
Chengzhong Wu 70de19e2cc
vm: add vm proto property lookup test
Add more test coverage on vm prototype properties lookup with
`in` operator and property access.

PR-URL: https://github.com/nodejs/node/pull/54606
Refs: https://github.com/nodejs/node/issues/54436
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
2024-09-04 10:58:49 +00:00

246 lines
6.6 KiB
JavaScript

'use strict';
require('../common');
const assert = require('assert');
const vm = require('vm');
const outerProto = {
onOuterProto: 'onOuterProto',
bothProto: 'onOuterProto',
};
function onOuterProtoGetter() {
return 'onOuterProtoGetter';
}
Object.defineProperties(outerProto, {
onOuterProtoGetter: {
get: onOuterProtoGetter,
},
bothProtoGetter: {
get: onOuterProtoGetter,
},
// outer proto indexed
0: {
value: 'onOuterProtoIndexed',
writable: false,
enumerable: false,
configurable: true,
},
// both proto indexed
3: {
value: 'onOuterProtoIndexed',
writable: false,
enumerable: false,
configurable: true,
},
});
// Creating a new intermediate proto to mimic the
// window -> Window.prototype -> EventTarget.prototype chain in JSDom.
const sandboxProto = {
__proto__: outerProto,
};
const sandbox = {
__proto__: sandboxProto,
onSelf: 'onSelf',
};
function onSelfGetter() {
return 'onSelfGetter';
}
Object.defineProperties(sandbox, {
onSelfGetter: {
get: onSelfGetter,
},
1: {
value: 'onSelfIndexed',
writable: false,
enumerable: false,
configurable: true,
}
});
const ctx = vm.createContext(sandbox);
const result = vm.runInContext(`
Object.prototype.onInnerProto = 'onInnerProto';
Object.defineProperties(Object.prototype, {
onInnerProtoGetter: {
get() {
return 'onInnerProtoGetter';
},
},
2: {
value: 'onInnerProtoIndexed',
writable: false,
enumerable: false,
configurable: true,
},
});
// Override outer proto properties
Object.prototype.bothProto = 'onInnerProto';
Object.defineProperties(Object.prototype, {
bothProtoGetter: {
get() {
return 'onInnerProtoGetter';
},
},
// outer proto indexed
3: {
value: 'onInnerProtoIndexed',
writable: false,
enumerable: false,
configurable: true,
},
});
const resultHasOwn = {
onSelf: Object.hasOwn(this, 'onSelf'),
onSelfGetter: Object.hasOwn(this, 'onSelfGetter'),
onSelfIndexed: Object.hasOwn(this, 1),
onOuterProto: Object.hasOwn(this, 'onOuterProto'),
onOuterProtoGetter: Object.hasOwn(this, 'onOuterProtoGetter'),
onOuterProtoIndexed: Object.hasOwn(this, 0),
onInnerProto: Object.hasOwn(this, 'onInnerProto'),
onInnerProtoGetter: Object.hasOwn(this, 'onInnerProtoGetter'),
onInnerProtoIndexed: Object.hasOwn(this, 2),
bothProto: Object.hasOwn(this, 'bothProto'),
bothProtoGetter: Object.hasOwn(this, 'bothProtoGetter'),
bothProtoIndexed: Object.hasOwn(this, 3),
};
const getDesc = (prop) => Object.getOwnPropertyDescriptor(this, prop);
const resultDesc = {
onSelf: getDesc('onSelf'),
onSelfGetter: getDesc('onSelfGetter'),
onSelfIndexed: getDesc(1),
onOuterProto: getDesc('onOuterProto'),
onOuterProtoGetter: getDesc('onOuterProtoGetter'),
onOuterProtoIndexed: getDesc(0),
onInnerProto: getDesc('onInnerProto'),
onInnerProtoGetter: getDesc('onInnerProtoGetter'),
onInnerProtoIndexed: getDesc(2),
bothProto: getDesc('bothProto'),
bothProtoGetter: getDesc('bothProtoGetter'),
bothProtoIndexed: getDesc(3),
};
const resultIn = {
onSelf: 'onSelf' in this,
onSelfGetter: 'onSelfGetter' in this,
onSelfIndexed: 1 in this,
onOuterProto: 'onOuterProto' in this,
onOuterProtoGetter: 'onOuterProtoGetter' in this,
onOuterProtoIndexed: 0 in this,
onInnerProto: 'onInnerProto' in this,
onInnerProtoGetter: 'onInnerProtoGetter' in this,
onInnerProtoIndexed: 2 in this,
bothProto: 'bothProto' in this,
bothProtoGetter: 'bothProtoGetter' in this,
bothProtoIndexed: 3 in this,
};
const resultValue = {
onSelf: this.onSelf,
onSelfGetter: this.onSelfGetter,
onSelfIndexed: this[1],
onOuterProto: this.onOuterProto,
onOuterProtoGetter: this.onOuterProtoGetter,
onOuterProtoIndexed: this[0],
onInnerProto: this.onInnerProto,
onInnerProtoGetter: this.onInnerProtoGetter,
onInnerProtoIndexed: this[2],
bothProto: this.bothProto,
bothProtoGetter: this.bothProtoGetter,
bothProtoIndexed: this[3],
};
({
resultHasOwn,
resultDesc,
resultIn,
resultValue,
});
`, ctx);
// eslint-disable-next-line no-restricted-properties
assert.deepEqual(result, {
resultHasOwn: {
onSelf: true,
onSelfGetter: true,
onSelfIndexed: true,
// All prototype properties are not own properties.
onOuterProto: false,
onOuterProtoGetter: false,
onOuterProtoIndexed: false,
onInnerProto: false,
onInnerProtoGetter: false,
onInnerProtoIndexed: false,
bothProto: false,
bothProtoGetter: false,
bothProtoIndexed: false,
},
resultDesc: {
onSelf: { value: 'onSelf', writable: true, enumerable: true, configurable: true },
onSelfGetter: { get: onSelfGetter, set: undefined, enumerable: false, configurable: false },
onSelfIndexed: { value: 'onSelfIndexed', writable: false, enumerable: false, configurable: true },
// All prototype properties are not own properties.
onOuterProto: undefined,
onOuterProtoGetter: undefined,
onOuterProtoIndexed: undefined,
onInnerProto: undefined,
onInnerProtoGetter: undefined,
onInnerProtoIndexed: undefined,
bothProto: undefined,
bothProtoGetter: undefined,
bothProtoIndexed: undefined,
},
resultIn: {
onSelf: true,
onSelfGetter: true,
onSelfIndexed: true,
// Only properties exist on inner prototype chain will be looked up
// on `in` operator. In the VM Context, the prototype chain will be like:
// ```
// Object
// ^
// | prototype
// InnerPrototype
// ^
// | prototype
// globalThis
// ```
// Outer prototype is not in the inner global object prototype chain and it
// will not be looked up on `in` operator.
onOuterProto: false,
onOuterProtoGetter: false,
onOuterProtoIndexed: false,
onInnerProto: true,
onInnerProtoGetter: true,
onInnerProtoIndexed: true,
bothProto: true,
bothProtoGetter: true,
bothProtoIndexed: true,
},
resultValue: {
onSelf: 'onSelf',
onSelfGetter: 'onSelfGetter',
onSelfIndexed: 'onSelfIndexed',
// FIXME(legendecas): The outer prototype is not observable from the inner
// vm. Allowing property getter on the outer prototype can be confusing
// comparing to the normal JavaScript objects.
// Additionally, this may expose unexpected properties on the outer
// prototype chain, like polyfills, to the vm context.
onOuterProto: 'onOuterProto',
onOuterProtoGetter: 'onOuterProtoGetter',
onOuterProtoIndexed: 'onOuterProtoIndexed',
onInnerProto: 'onInnerProto',
onInnerProtoGetter: 'onInnerProtoGetter',
onInnerProtoIndexed: 'onInnerProtoIndexed',
bothProto: 'onOuterProto',
bothProtoGetter: 'onOuterProtoGetter',
bothProtoIndexed: 'onOuterProtoIndexed',
},
});