diff --git a/doc/api/vm.markdown b/doc/api/vm.markdown index b1453249ec0..b903828b8e7 100644 --- a/doc/api/vm.markdown +++ b/doc/api/vm.markdown @@ -40,6 +40,10 @@ e.g. `(0,eval)('code')`. However, it also has the following additional options: - `filename`: allows you to control the filename that shows up in any stack traces produced. +- `lineOffset`: allows you to add an offset to the line number that is + displayed in stack traces +- `columnOffset`: allows you to add an offset to the column number that is + displayed in stack traces - `displayErrors`: whether or not to print any errors to stderr, with the line of code that caused them highlighted, before throwing an exception. Will capture both syntax errors from compiling `code` and runtime errors diff --git a/lib/module.js b/lib/module.js index 0f1f38c7bb0..aafbd72ae4b 100644 --- a/lib/module.js +++ b/lib/module.js @@ -441,8 +441,10 @@ Module.prototype._compile = function(content, filename) { // create wrapper function var wrapper = Module.wrap(content); + var wrapperOffset = Module.wrapper[0].length; - var compiledWrapper = runInThisContext(wrapper, { filename: filename }); + var compiledWrapper = runInThisContext(wrapper, + { filename: filename, columnOffset: -wrapperOffset }); if (global.v8debug) { if (!resolvedArgv) { // we enter the repl if we're not given a filename argument. diff --git a/src/node_contextify.cc b/src/node_contextify.cc index 2e8fd2cade6..b7e863fc6b0 100644 --- a/src/node_contextify.cc +++ b/src/node_contextify.cc @@ -485,13 +485,15 @@ class ContextifyScript : public BaseObject { TryCatch try_catch; Local code = args[0]->ToString(); Local filename = GetFilenameArg(args, 1); + Local lineOffset = GetLineOffsetArg(args, 1); + Local columnOffset = GetColumnOffsetArg(args, 1); bool display_errors = GetDisplayErrorsArg(args, 1); if (try_catch.HasCaught()) { try_catch.ReThrow(); return; } - ScriptOrigin origin(filename); + ScriptOrigin origin(filename, lineOffset, columnOffset); ScriptCompiler::Source source(code, origin); Local v8_script = ScriptCompiler::CompileUnbound(env->isolate(), &source); @@ -658,6 +660,51 @@ class ContextifyScript : public BaseObject { } + static Local GetLineOffsetArg( + const FunctionCallbackInfo& args, + const int i) { + Local defaultLineOffset = Integer::New(args.GetIsolate(), 0); + + if (args[i]->IsUndefined()) { + return defaultLineOffset; + } + if (args[i]->IsInt32()) { + return args[i].As(); + } + if (!args[i]->IsObject()) { + return defaultLineOffset; + } + + Local key = FIXED_ONE_BYTE_STRING(args.GetIsolate(), "lineOffset"); + Local value = args[i].As()->Get(key); + + return value->IsUndefined() ? defaultLineOffset : value->ToInteger(); + } + + + static Local GetColumnOffsetArg( + const FunctionCallbackInfo& args, + const int i) { + Local defaultColumnOffset = Integer::New(args.GetIsolate(), 0); + + if (args[i]->IsUndefined()) { + return defaultColumnOffset; + } + if (args[i]->IsInt32()) { + return args[i].As(); + } + if (!args[i]->IsObject()) { + return defaultColumnOffset; + } + + Local key = FIXED_ONE_BYTE_STRING(args.GetIsolate(), + "columnOffset"); + Local value = args[i].As()->Get(key); + + return value->IsUndefined() ? defaultColumnOffset : value->ToInteger(); + } + + static bool EvalMachine(Environment* env, const int64_t timeout, const bool display_errors, diff --git a/test/fixtures/module-err.js b/test/fixtures/module-err.js new file mode 100644 index 00000000000..b4785957bc9 --- /dev/null +++ b/test/fixtures/module-err.js @@ -0,0 +1 @@ +s diff --git a/test/simple/test-module-loading.js b/test/simple/test-module-loading.js index 8687c3b9864..3b72a5415b6 100644 --- a/test/simple/test-module-loading.js +++ b/test/simple/test-module-loading.js @@ -306,3 +306,18 @@ process.on('exit', function() { // #1440 Loading files with a byte order marker. assert.equal(42, require('../fixtures/utf8-bom.js')); assert.equal(42, require('../fixtures/utf8-bom.json')); + +// #9445 Error on first line of a module have the correct column number. +var gh9445Exception; +try { + var err_js = require('../fixtures/module-err.js'); +} +catch (e) { + gh9445Exception = e; + assert.ok(/module-err.js:1:1/.test(e.stack), + 'expected appearance of proper offset in Error stack'); +} +assert.ok(gh9445Exception, + 'expected exception from runInContext offset test'); + + diff --git a/test/simple/test-vm-context.js b/test/simple/test-vm-context.js index c843addc695..6e612e0e022 100644 --- a/test/simple/test-vm-context.js +++ b/test/simple/test-vm-context.js @@ -61,6 +61,22 @@ catch (e) { assert.ok(gh1140Exception, 'expected exception from runInContext signature test'); +// Issue GH-9445: +console.error('test runInContext offset'); +var gh9445Exception; +try { + vm.runInContext('throw new Error()', context, { filename: 'expected-filename.js', + lineOffset: 32, + columnOffset: 123}); +} +catch (e) { + gh9445Exception = e; + assert.ok(/expected-filename.js:33:130/.test(e.stack), + 'expected appearance of proper offset in Error stack'); +} +assert.ok(gh9445Exception, + 'expected exception from runInContext offset test'); + // GH-558, non-context argument segfaults / raises assertion [undefined, null, 0, 0.0, '', {}, []].forEach(function(e) { assert.throws(function() { script.runInContext(e); }, TypeError);