Skip to content
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

src: use new V8 API in vm module #16293

Closed
wants to merge 1 commit into from
Closed

Conversation

fhinkel
Copy link
Member

@fhinkel fhinkel commented Oct 18, 2017

This PR removes the CopyProperties() hack in the vm module, i.e., Contextify.
Instead, it uses different V8 API methods, that allow interception of
DefineProperty() and do not flatten accessor descriptors to
property descriptors.

Move many known issues to test cases. Factor out the last test in
test-vm-context.js for
#10223
into its own file, test-vm-strict-assign.js.

Part of this CL is taken from a stalled PR by
https://github.com/AnnaMag
#13265

Refs: #6283
Refs: #15114
Refs: #13265

Fixes: #2734
Fixes: #10223
Fixes: #11803
Fixes: #11902

This PR requires a backport of
37a3a15 otherwise some tests will fail. Cherry pick PR.

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines
Affected core subsystem(s)

src

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines
Affected core subsystem(s)

src

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. vm Issues and PRs related to the vm subsystem. labels Oct 18, 2017
Copy link
Member

@jasnell jasnell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

largely rubber stamp lgtm

ctx->global_proxy()->GetRealNamedPropertyAttributes(context,
property);
}
Local<String> key = property->ToString(isolate);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use the overload that takes a Local<Context>?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Local<Value> descriptor_intercepted = has.IsNothing() ?
ctx->global_proxy()->GetOwnPropertyDescriptor(context, key)
.ToLocalChecked() :
sandbox->GetOwnPropertyDescriptor(context, key).ToLocalChecked();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd write this as an if statement. It's rather hard to read.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. Done.

// If the property had already been defined on the sandbox or global object,
// we intercept, query and return it.
// Else, there is no need to intercept with first time definitions.
if (!descriptor_intercepted->IsUndefined()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose the conditional isn't needed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? If we intercept, it signals that the property was found. I think that's not correct if the property is not present.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Undefined is the default return value of API callbacks. I.e., the return value is going to be the same, with or without the IsUndefined() check.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReturnValue on the CallbackArgs is set to undefined, but the call to the interceptor returns true or false depending on whether the return value was set. This then changes the control flow, see objects.cc.

But taking a closer look, I think the logic can be substantially simplified: take the value from the sandbox if present, otherwise proceed regularly. What do you think?

} else {
Local<Value> value =
desc.has_value() ? desc.value() :
v8::Undefined(isolate).As<Value>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fits on one line (well, two, if you count the LHS.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}
}

printf("foo\n");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stray debug code, I presume?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops

@@ -65,6 +66,12 @@ using v8::WeakCallbackInfo;

namespace {

v8::Local<v8::Name> UIntToName(v8::Isolate* isolate, uint32_t index) {
const char* x = std::to_string(index).c_str();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this function gets called a lot (which it looks like it is), it's arguably better to use snprintf() instead to avoid the extra allocation, or even to do manual stringification. C++17 adds std::to_chars() but we can't use that yet, unfortunately.

It might even be worthwhile to implement this as Uint32::New(...)->ToString(...) because that would reuse V8's number-to-string cache.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, if there's any larger array, this is called for every index. Using Uint32::New()->ToString().

bool is_function = value->IsFunction();

if (!is_declared && args.ShouldThrowOnError() && is_contextual_store &&
!is_function)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You fixed the style for this further up but not here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}
}

printf("foo\n");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug code again.

There's a lot of duplication. Couldn't you implement them by calling UIntToName(isolate, index) and passing the result to GlobalPropertyDefinerCallback(), etc.?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's all duplication. Let me think about how to get rid of the code duplication while maintaining readability.

assert.strictEqual(ctx.x, undefined); // Not copied out by cloneProperty().

// This is fixed now!
// assert.strictEqual(ctx.x, undefined); // Not copied out by cloneProperty().
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you drop the comment?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can drop the copyright boilerplate, we don't use it in new files.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@fhinkel fhinkel added this to the 9.0.0 milestone Oct 18, 2017
@fhinkel
Copy link
Member Author

fhinkel commented Oct 19, 2017

@cpojer, since this is fixing bugs/altering behavior, do you want to check if that causes problems for Jest?

@TimothyGu TimothyGu self-assigned this Oct 19, 2017
@TimothyGu
Copy link
Member

I'll run this through the jsdom test suite as well.

@fhinkel
Copy link
Member Author

fhinkel commented Oct 19, 2017

Nits addressed, refactoring the code duplication part. Will squash afterwards.

@fhinkel fhinkel force-pushed the Oct/defineProp branch 2 times, most recently from 367473e to fecefd0 Compare October 19, 2017 08:19
Copy link
Member

@bnoordhuis bnoordhuis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment about the default return value but otherwise LGTM. Nice work, Franziska and @AnnaMag.

// Still initializing
if (ctx->context_.IsEmpty())
return;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some extraneous blank lines here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -7,7 +7,7 @@ const vm = require('vm');

const ctx = vm.createContext();
vm.runInContext('Object.defineProperty(this, "x", { value: 42 })', ctx);
assert.strictEqual(ctx.x, undefined); // Not copied out by cloneProperty().

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you remove the blank line here?

@cpojer
Copy link

cpojer commented Oct 19, 2017

@fhinkel mind running this against Jest's version on master?

@fhinkel
Copy link
Member Author

fhinkel commented Oct 19, 2017

@cpojer Jest's tests helped me find a fatal error on my side 😄
I get the same test failures with Node master with and without my patch on Jest master. I guess that means you're not relying on any of our broken behavior that this patch fixes.

jest/integration_tests/coverage-threshold/__tests__/a-banana.js
  1:1  error  Delete `⏎······`  prettier/prettier

@cpojer
Copy link

cpojer commented Oct 19, 2017

Can you just try yarn jest on the repo, that'll run the JS tests? It seems like there is some weird eslint issue on your state of the Jest repo (master is green, so not sure what's up).

@fhinkel
Copy link
Member Author

fhinkel commented Oct 19, 2017

@cpojer I'm not getting very far with Node master, opend an issue here jestjs/jest#4731.

@SimenB
Copy link
Member

SimenB commented Oct 19, 2017

As mentioned in the Jest issue, using node 9 RC fails worker-farm's tests as well. Jest uses worker-farm to parallelise test runs.

https://github.com/rvagg/node-worker-farm

$ npm version
{ 'worker-farm': '1.5.0',
  npm: '5.3.0',
  ares: '1.13.0',
  cldr: '31.0.1',
  http_parser: '2.7.0',
  icu: '59.1',
  modules: '58',
  nghttp2: '1.25.0',
  node: '9.0.0-rc.0',
  openssl: '1.0.2l',
  tz: '2017b',
  unicode: '9.0',
  uv: '1.15.0',
  v8: '6.1.534.42',
  zlib: '1.2.11' }
> worker-farm@1.5.0 test /Users/simbekkh/repos/node-worker-farm
> node ./tests/

TAP version 13
# simple, exports=function test
ok 1 pid makes sense
ok 2 pid makes sense
ok 3 rnd result makes sense
ok 4 workerFarm ended
# simple, exports.fn test
ok 5 pid makes sense
ok 6 pid makes sense
ok 7 rnd result makes sense
ok 8 workerFarm ended
# single worker
ok 9 only a single process (by pid)
ok 10 workerFarm ended
# two workers
ok 11 only two child processes (by pid)
ok 12 workerFarm ended
# many workers
ok 13 pids are all the same (by pid)
ok 14 workerFarm ended
# auto start workers
ok 15 child has been up before the request
ok 16 child has been up before the request
ok 17 child has been up before the request
ok 18 workerFarm ended
# single call per worker
ok 19 one process for each call (by pid)
ok 20 workerFarm ended
# two calls per worker
ok 21 one process for each call (by pid)
ok 22 workerFarm ended
# many concurrent calls
ok 23 processed tasks concurrently (173ms)
ok 24 workerFarm ended
# single concurrent call
ok 25 processed tasks sequentially (304ms)
ok 26 workerFarm ended
# multiple concurrent calls
ok 27 processed tasks concurrently (277ms)
ok 28 workerFarm ended
# durability
events.js:195
      throw er; // Unhandled 'error' event
      ^

Error [ERR_IPC_CHANNEL_CLOSED]: Channel closed
    at ChildProcess.target.send (internal/child_process.js:606:16)
    at Object.send (/Users/simbekkh/repos/node-worker-farm/lib/fork.js:24:17)
    at Farm.stopChild (/Users/simbekkh/repos/node-worker-farm/lib/farm.js:131:11)
    at Farm.<anonymous> (/Users/simbekkh/repos/node-worker-farm/lib/farm.js:96:10)
    at ontimeout (timers.js:478:11)
    at tryOnTimeout (timers.js:302:5)
    at Timer.listOnTimeout (timers.js:262:5)
npm ERR! Test failed.  See above for more details.

@SimenB
Copy link
Member

SimenB commented Oct 19, 2017

git bisect blames f2b01cb

#4670

Bisect script if anyone wants to double-check

#!/bin/sh

./configure

make -j4 || exit 125 # an exit code of 125 asks "git bisect" to "skip" the current commit

# run the application and check that it produces good output
./node ../node-worker-farm/tests 2>&1 | grep 'ERR_IPC_CHANNEL_CLOSED'

if [ $? -eq 0 ]; then
   exit 1
fi

exit 0

I can confirm that reverting f2b01cb makes both worker-farm's and jest's test suites pass

@fhinkel
Copy link
Member Author

fhinkel commented Oct 19, 2017

@SimenB Do you mind putting your code snippets in details, as they are not related to this issue? Thanks!

@fhinkel
Copy link
Member Author

fhinkel commented Oct 23, 2017

Remove the CopyProperties() hack in the vm module, i.e., Contextify.
Use different V8 API methods, that allow interception of
DefineProperty() and do not flatten accessor descriptors to
property descriptors.

Move many known issues to test cases. Factor out the last test in
test-vm-context.js for
nodejs#10223
into its own file, test-vm-strict-assign.js.

Part of this CL is taken from a stalled PR by
https://github.com/AnnaMag
nodejs#13265

This PR requires a backport of
https://chromium.googlesource.com/v8/v8/+/37a3a15c3e52e2146e45f41c427f24414e4d7f6f

Refs: nodejs#6283
Refs: nodejs#15114
Refs: nodejs#13265

Fixes: nodejs#2734
Fixes: nodejs#10223
Fixes: nodejs#11803
Fixes: nodejs#11902
@fhinkel
Copy link
Member Author

fhinkel commented Oct 23, 2017

@fhinkel
Copy link
Member Author

fhinkel commented Oct 23, 2017

Landed in f1d6b04

@fhinkel fhinkel closed this Oct 23, 2017
fhinkel added a commit to fhinkel/node that referenced this pull request Oct 23, 2017
Remove the CopyProperties() hack in the vm module, i.e., Contextify.
Use different V8 API methods, that allow interception of
DefineProperty() and do not flatten accessor descriptors to
property descriptors.

Move many known issues to test cases. Factor out the last test in
test-vm-context.js for
nodejs#10223
into its own file, test-vm-strict-assign.js.

Part of this CL is taken from a stalled PR by
https://github.com/AnnaMag
nodejs#13265

This PR requires a backport of
https://chromium.googlesource.com/v8/v8/+/37a3a15c3e52e2146e45f41c427f24414e4d7f6f

PR-URL: nodejs#16293
Fixes: nodejs#2734
Fixes: nodejs#10223
Fixes: nodejs#11803
Fixes: nodejs#11902
Ref: nodejs#6283
Ref: nodejs#15114
Ref: nodejs#13265
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Tobias Nießen <tniessen@tnie.de>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Timothy Gu <timothygu99@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
@targos
Copy link
Member

targos commented Oct 23, 2017

Nice. Thank you and @AnnaMag for doing this!

targos added a commit to targos/node that referenced this pull request Oct 23, 2017
fhinkel added a commit to fhinkel/node that referenced this pull request Oct 25, 2017
The known issue is fixed with
nodejs#16293.

The text needs to call `Object.hasOwnProperty(this)`
instead of `this.hasOwnProperty()`, otherwise `this` is
from the wrong context is used.

Add a second test case taken verbatim from issue
nodejs#5350

Fixes: nodejs#5350
Refs: nodejs#16293
fhinkel added a commit to fhinkel/node that referenced this pull request Oct 25, 2017
The known issue is fixed with
nodejs#16293.

The text needs to call `Object.hasOwnProperty(this)`
instead of `this.hasOwnProperty()`, otherwise `this` is
from the wrong context is used.

Add a second test case taken verbatim from issue
nodejs#5350

PR-URL: nodejs#16411
Fixes: nodejs#5350
Ref: nodejs#16293
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
targos added a commit that referenced this pull request Oct 26, 2017
PR-URL: #16409
Refs: #16293
Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
addaleax pushed a commit to ayojs/ayo that referenced this pull request Oct 26, 2017
Remove the CopyProperties() hack in the vm module, i.e., Contextify.
Use different V8 API methods, that allow interception of
DefineProperty() and do not flatten accessor descriptors to
property descriptors.

Move many known issues to test cases. Factor out the last test in
test-vm-context.js for
nodejs/node#10223
into its own file, test-vm-strict-assign.js.

Part of this CL is taken from a stalled PR by
https://github.com/AnnaMag
nodejs/node#13265

This PR requires a backport of
https://chromium.googlesource.com/v8/v8/+/37a3a15c3e52e2146e45f41c427f24414e4d7f6f

PR-URL: nodejs/node#16293
Fixes: nodejs/node#2734
Fixes: nodejs/node#10223
Fixes: nodejs/node#11803
Fixes: nodejs/node#11902
Ref: nodejs/node#6283
Ref: nodejs/node#15114
Ref: nodejs/node#13265
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Tobias Nießen <tniessen@tnie.de>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Timothy Gu <timothygu99@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
addaleax pushed a commit to ayojs/ayo that referenced this pull request Oct 26, 2017
The known issue is fixed with
nodejs/node#16293.

The text needs to call `Object.hasOwnProperty(this)`
instead of `this.hasOwnProperty()`, otherwise `this` is
from the wrong context is used.

Add a second test case taken verbatim from issue
nodejs/node#5350

PR-URL: nodejs/node#16411
Fixes: nodejs/node#5350
Ref: nodejs/node#16293
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
addaleax pushed a commit to ayojs/ayo that referenced this pull request Oct 26, 2017
PR-URL: nodejs/node#16409
Refs: nodejs/node#16293
Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
gibfahn pushed a commit that referenced this pull request Oct 30, 2017
The known issue is fixed with
#16293.

The text needs to call `Object.hasOwnProperty(this)`
instead of `this.hasOwnProperty()`, otherwise `this` is
from the wrong context is used.

Add a second test case taken verbatim from issue
#5350

PR-URL: #16411
Fixes: #5350
Ref: #16293
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
addaleax pushed a commit to ayojs/ayo that referenced this pull request Dec 7, 2017
Remove the CopyProperties() hack in the vm module, i.e., Contextify.
Use different V8 API methods, that allow interception of
DefineProperty() and do not flatten accessor descriptors to
property descriptors.

Move many known issues to test cases. Factor out the last test in
test-vm-context.js for
nodejs/node#10223
into its own file, test-vm-strict-assign.js.

Part of this CL is taken from a stalled PR by
https://github.com/AnnaMag
nodejs/node#13265

This PR requires a backport of
https://chromium.googlesource.com/v8/v8/+/37a3a15c3e52e2146e45f41c427f24414e4d7f6f

PR-URL: nodejs/node#16293
Fixes: nodejs/node#2734
Fixes: nodejs/node#10223
Fixes: nodejs/node#11803
Fixes: nodejs/node#11902
Ref: nodejs/node#6283
Ref: nodejs/node#15114
Ref: nodejs/node#13265
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Tobias Nießen <tniessen@tnie.de>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Timothy Gu <timothygu99@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
addaleax pushed a commit to ayojs/ayo that referenced this pull request Dec 7, 2017
The known issue is fixed with
nodejs/node#16293.

The text needs to call `Object.hasOwnProperty(this)`
instead of `this.hasOwnProperty()`, otherwise `this` is
from the wrong context is used.

Add a second test case taken verbatim from issue
nodejs/node#5350

PR-URL: nodejs/node#16411
Fixes: nodejs/node#5350
Ref: nodejs/node#16293
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
addaleax pushed a commit to ayojs/ayo that referenced this pull request Dec 7, 2017
PR-URL: nodejs/node#16409
Refs: nodejs/node#16293
Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ Issues and PRs that require attention from people who are familiar with C++. vm Issues and PRs related to the vm subsystem.
Projects
None yet
10 participants