node_contextify: do not incept debug context
Currently a debug context is created for various calls to util. If the node debugger is being run the main context is the debug context. In this case node_contextify was freeing the debug context and causing everything to explode. This change moves around the logic and no longer frees the context. There is a concern about the dangling pointer The regression test was adapted from code submitted by @3y3 in #4815 Fixes: https://github.com/nodejs/node/issues/4440 Fixes: https://github.com/nodejs/node/issues/4815 Fixes: https://github.com/nodejs/node/issues/4597 Fixes: https://github.com/nodejs/node/issues/4952 PR-URL: https://github.com/nodejs/node/issues/4815 Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Rich Trott <rtrott@gmail.com>
This commit is contained in:
parent
878bcd43f8
commit
4897f94ec6
@ -241,37 +241,26 @@ class ContextifyContext {
|
|||||||
|
|
||||||
|
|
||||||
static void RunInDebugContext(const FunctionCallbackInfo<Value>& args) {
|
static void RunInDebugContext(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
Local<String> script_source(args[0]->ToString(args.GetIsolate()));
|
||||||
|
if (script_source.IsEmpty())
|
||||||
|
return; // Exception pending.
|
||||||
|
Local<Context> debug_context = Debug::GetDebugContext();
|
||||||
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
if (debug_context.IsEmpty()) {
|
||||||
|
// Force-load the debug context.
|
||||||
|
Debug::GetMirror(args.GetIsolate()->GetCurrentContext(), args[0]);
|
||||||
|
debug_context = Debug::GetDebugContext();
|
||||||
|
CHECK(!debug_context.IsEmpty());
|
||||||
// Ensure that the debug context has an Environment assigned in case
|
// Ensure that the debug context has an Environment assigned in case
|
||||||
// a fatal error is raised. The fatal exception handler in node.cc
|
// a fatal error is raised. The fatal exception handler in node.cc
|
||||||
// is not equipped to deal with contexts that don't have one and
|
// is not equipped to deal with contexts that don't have one and
|
||||||
// can't easily be taught that due to a deficiency in the V8 API:
|
// can't easily be taught that due to a deficiency in the V8 API:
|
||||||
// there is no way for the embedder to tell if the data index is
|
// there is no way for the embedder to tell if the data index is
|
||||||
// in use.
|
// in use.
|
||||||
struct ScopedEnvironment {
|
|
||||||
ScopedEnvironment(Local<Context> context, Environment* env)
|
|
||||||
: context_(context) {
|
|
||||||
const int index = Environment::kContextEmbedderDataIndex;
|
const int index = Environment::kContextEmbedderDataIndex;
|
||||||
context->SetAlignedPointerInEmbedderData(index, env);
|
debug_context->SetAlignedPointerInEmbedderData(index, env);
|
||||||
}
|
}
|
||||||
~ScopedEnvironment() {
|
|
||||||
const int index = Environment::kContextEmbedderDataIndex;
|
|
||||||
context_->SetAlignedPointerInEmbedderData(index, nullptr);
|
|
||||||
}
|
|
||||||
Local<Context> context_;
|
|
||||||
};
|
|
||||||
|
|
||||||
Local<String> script_source(args[0]->ToString(args.GetIsolate()));
|
|
||||||
if (script_source.IsEmpty())
|
|
||||||
return; // Exception pending.
|
|
||||||
Local<Context> debug_context = Debug::GetDebugContext();
|
|
||||||
if (debug_context.IsEmpty()) {
|
|
||||||
// Force-load the debug context.
|
|
||||||
Debug::GetMirror(args.GetIsolate()->GetCurrentContext(), args[0]);
|
|
||||||
debug_context = Debug::GetDebugContext();
|
|
||||||
CHECK(!debug_context.IsEmpty());
|
|
||||||
}
|
|
||||||
Environment* env = Environment::GetCurrent(args);
|
|
||||||
ScopedEnvironment env_scope(debug_context, env);
|
|
||||||
Context::Scope context_scope(debug_context);
|
Context::Scope context_scope(debug_context);
|
||||||
Local<Script> script = Script::Compile(script_source);
|
Local<Script> script = Script::Compile(script_source);
|
||||||
if (script.IsEmpty())
|
if (script.IsEmpty())
|
||||||
|
4
test/fixtures/debugger-util-regression-fixture.js
vendored
Normal file
4
test/fixtures/debugger-util-regression-fixture.js
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
'use strict';
|
||||||
|
const util = require('util');
|
||||||
|
const payload = util.inspect({a: 'b'});
|
||||||
|
console.log(payload);
|
69
test/parallel/test-debugger-util-regression.js
Normal file
69
test/parallel/test-debugger-util-regression.js
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
'use strict';
|
||||||
|
const path = require('path');
|
||||||
|
const spawn = require('child_process').spawn;
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
|
||||||
|
const fixture = path.join(
|
||||||
|
common.fixturesDir,
|
||||||
|
'debugger-util-regression-fixture.js'
|
||||||
|
);
|
||||||
|
|
||||||
|
const args = [
|
||||||
|
'debug',
|
||||||
|
fixture
|
||||||
|
];
|
||||||
|
|
||||||
|
const proc = spawn(process.execPath, args, { stdio: 'pipe' });
|
||||||
|
proc.stdout.setEncoding('utf8');
|
||||||
|
proc.stderr.setEncoding('utf8');
|
||||||
|
|
||||||
|
function fail() {
|
||||||
|
common.fail('the program should not hang');
|
||||||
|
}
|
||||||
|
|
||||||
|
const timer = setTimeout(fail, common.platformTimeout(4000));
|
||||||
|
|
||||||
|
let stdout = '';
|
||||||
|
let stderr = '';
|
||||||
|
|
||||||
|
let nextCount = 0;
|
||||||
|
|
||||||
|
proc.stdout.on('data', (data) => {
|
||||||
|
stdout += data;
|
||||||
|
if (stdout.includes('> 1') && nextCount < 1 ||
|
||||||
|
stdout.includes('> 2') && nextCount < 2 ||
|
||||||
|
stdout.includes('> 3') && nextCount < 3 ||
|
||||||
|
stdout.includes('> 4') && nextCount < 4) {
|
||||||
|
nextCount++;
|
||||||
|
proc.stdin.write('n\n');
|
||||||
|
}
|
||||||
|
else if (stdout.includes('{ a: \'b\' }')) {
|
||||||
|
clearTimeout(timer);
|
||||||
|
proc.stdin.write('.exit\n');
|
||||||
|
}
|
||||||
|
else if (stdout.includes('program terminated')) {
|
||||||
|
// Catch edge case present in v4.x
|
||||||
|
// process will terminate after call to util.inspect
|
||||||
|
common.fail('the program should not terminate');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
proc.stderr.on('data', (data) => stderr += data);
|
||||||
|
|
||||||
|
// FIXME
|
||||||
|
// This test has been periodically failing on certain systems due to
|
||||||
|
// uncaught errors on proc.stdin. This will stop the process from
|
||||||
|
// exploding but is still not an elegant solution. Likely a deeper bug
|
||||||
|
// causing this problem.
|
||||||
|
proc.stdin.on('error', (err) => {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on('exit', (code) => {
|
||||||
|
assert.equal(code, 0, 'the program should exit cleanly');
|
||||||
|
assert.equal(stdout.includes('{ a: \'b\' }'), true,
|
||||||
|
'the debugger should print the result of util.inspect');
|
||||||
|
assert.equal(stderr, '', 'stderr should be empty');
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user