Skip to content

Commit

Permalink
vm: properly handle defining symbol props
Browse files Browse the repository at this point in the history
This PR is a follow-up of #46615.

When bumping V8 for node 18, various bugs appeared around vm.
Some have been fixed along the flow, but there is at least one
remaining and described at:
jestjs/jest#13338.

The current fix does nothing on node 20: the new bump of v8 done for
node 20 seems to fix it. But it would fix the problem in both node 18
and node 19. I confirmed it would fix node 19 by cherry-picking the
commit on v19.x-staging and launching `make -j4 jstest` on it.

PR-URL: #47572
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
dubzzz authored and targos committed May 30, 2023
1 parent 8b89f13 commit ed45142
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 26 deletions.
1 change: 1 addition & 0 deletions src/node_contextify.cc
Expand Up @@ -524,6 +524,7 @@ void ContextifyContext::PropertySetterCallback(
!is_function)
return;

if (!is_declared && property->IsSymbol()) return;
if (ctx->sandbox()->Set(context, property, value).IsNothing()) return;

Local<Value> desc;
Expand Down
105 changes: 105 additions & 0 deletions test/parallel/test-vm-global-get-own.js
@@ -0,0 +1,105 @@
'use strict';
require('../common');
const assert = require('assert');
const vm = require('vm');

// These assertions check that we can set new keys to the global context,
// get them back and also list them via getOwnProperty* or in.
//
// Related to:
// - https://github.com/nodejs/node/issues/45983

const global = vm.runInContext('this', vm.createContext());

function runAssertions(data, property, viaDefine, value1, value2, value3) {
// Define the property for the first time
setPropertyAndAssert(data, property, viaDefine, value1);
// Update the property
setPropertyAndAssert(data, property, viaDefine, value2);
// Delete the property
deletePropertyAndAssert(data, property);
// Re-define the property
setPropertyAndAssert(data, property, viaDefine, value3);
// Delete the property again
deletePropertyAndAssert(data, property);
}

const fun1 = () => 1;
const fun2 = () => 2;
const fun3 = () => 3;

function runAssertionsOnSandbox(builder) {
const sandboxContext = vm.createContext({ runAssertions, fun1, fun2, fun3 });
vm.runInContext(builder('this'), sandboxContext);
vm.runInContext(builder('{}'), sandboxContext);
}

// Assertions on: define property
runAssertions(global, 'toto', true, 1, 2, 3);
runAssertions(global, Symbol.for('toto'), true, 1, 2, 3);
runAssertions(global, 'tutu', true, fun1, fun2, fun3);
runAssertions(global, Symbol.for('tutu'), true, fun1, fun2, fun3);
runAssertions(global, 'tyty', true, fun1, 2, 3);
runAssertions(global, Symbol.for('tyty'), true, fun1, 2, 3);

// Assertions on: direct assignment
runAssertions(global, 'titi', false, 1, 2, 3);
runAssertions(global, Symbol.for('titi'), false, 1, 2, 3);
runAssertions(global, 'tata', false, fun1, fun2, fun3);
runAssertions(global, Symbol.for('tata'), false, fun1, fun2, fun3);
runAssertions(global, 'tztz', false, fun1, 2, 3);
runAssertions(global, Symbol.for('tztz'), false, fun1, 2, 3);

// Assertions on: define property from sandbox
runAssertionsOnSandbox(
(variable) => `
runAssertions(${variable}, 'toto', true, 1, 2, 3);
runAssertions(${variable}, Symbol.for('toto'), true, 1, 2, 3);
runAssertions(${variable}, 'tutu', true, fun1, fun2, fun3);
runAssertions(${variable}, Symbol.for('tutu'), true, fun1, fun2, fun3);
runAssertions(${variable}, 'tyty', true, fun1, 2, 3);
runAssertions(${variable}, Symbol.for('tyty'), true, fun1, 2, 3);`
);

// Assertions on: direct assignment from sandbox
runAssertionsOnSandbox(
(variable) => `
runAssertions(${variable}, 'titi', false, 1, 2, 3);
runAssertions(${variable}, Symbol.for('titi'), false, 1, 2, 3);
runAssertions(${variable}, 'tata', false, fun1, fun2, fun3);
runAssertions(${variable}, Symbol.for('tata'), false, fun1, fun2, fun3);
runAssertions(${variable}, 'tztz', false, fun1, 2, 3);
runAssertions(${variable}, Symbol.for('tztz'), false, fun1, 2, 3);`
);

// Helpers

// Set the property on data and assert it worked
function setPropertyAndAssert(data, property, viaDefine, value) {
if (viaDefine) {
Object.defineProperty(data, property, {
enumerable: true,
writable: true,
value: value,
configurable: true,
});
} else {
data[property] = value;
}
assert.strictEqual(data[property], value);
assert.ok(property in data);
if (typeof property === 'string') {
assert.ok(Object.getOwnPropertyNames(data).includes(property));
} else {
assert.ok(Object.getOwnPropertySymbols(data).includes(property));
}
}

// Delete the property from data and assert it worked
function deletePropertyAndAssert(data, property) {
delete data[property];
assert.strictEqual(data[property], undefined);
assert.ok(!(property in data));
assert.ok(!Object.getOwnPropertyNames(data).includes(property));
assert.ok(!Object.getOwnPropertySymbols(data).includes(property));
}
26 changes: 0 additions & 26 deletions test/parallel/test-vm-global-symbol.js

This file was deleted.

0 comments on commit ed45142

Please sign in to comment.