From 8ca412b7a3d5e6e84441bc846c5f42e8dcd2f1b9 Mon Sep 17 00:00:00 2001 From: cjihrig Date: Tue, 24 Nov 2015 19:52:35 -0500 Subject: [PATCH 1/2] util: add decorateErrorStack() This commit adds the decorateErrorStack() method. This function uses the internal util's getHiddenValue() method to extract arrow messages from error objects and attach them to the error's stack trace. PR-URL: https://github.com/nodejs/node/pull/4013 Reviewed-By: Ben Noordhuis --- lib/util.js | 11 ++++++ .../test-util-decorate-error-stack.js | 35 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 test/parallel/test-util-decorate-error-stack.js diff --git a/lib/util.js b/lib/util.js index 0666300d68e809..40d6491e0f200b 100644 --- a/lib/util.js +++ b/lib/util.js @@ -899,3 +899,14 @@ exports._exceptionWithHostPort = function(err, } return ex; }; + + +exports.decorateErrorStack = function(err) { + if (!(isError(err) && err.stack)) + return; + + const arrow = internalUtil.getHiddenValue(err, 'arrowMessage'); + + if (arrow) + err.stack = arrow + err.stack; +}; diff --git a/test/parallel/test-util-decorate-error-stack.js b/test/parallel/test-util-decorate-error-stack.js new file mode 100644 index 00000000000000..b609ee3372574b --- /dev/null +++ b/test/parallel/test-util-decorate-error-stack.js @@ -0,0 +1,35 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const util = require('util'); + +assert.doesNotThrow(function() { + util.decorateErrorStack(); + util.decorateErrorStack(null); + util.decorateErrorStack(1); + util.decorateErrorStack(true); +}); + +// Verify that a stack property is not added to non-Errors +const obj = {}; +util.decorateErrorStack(obj); +assert.strictEqual(obj.stack, undefined); + +// Verify that the stack is decorated when possible +let err; + +try { + require('../fixtures/syntax/bad_syntax'); +} catch (e) { + err = e; + assert(!/var foo bar;/.test(err.stack)); + util.decorateErrorStack(err); +} + +assert(/var foo bar;/.test(err.stack)); + +// Verify that the stack is unchanged when there is no arrow message +err = new Error('foo'); +const originalStack = err.stack; +util.decorateErrorStack(err); +assert.strictEqual(originalStack, err.stack); From 459b106d6cbee05661dee7b3929610d7d06bede0 Mon Sep 17 00:00:00 2001 From: cjihrig Date: Tue, 24 Nov 2015 19:55:51 -0500 Subject: [PATCH 2/2] repl: attach location info to syntax errors Currently, when a file with a syntax error is imported in the REPL, no information is provided on the error's location. This commit adds the error's location to the stack trace. Refs: https://github.com/nodejs/node/issues/2762 Refs: https://github.com/nodejs/node/issues/3411 Refs: https://github.com/nodejs/node/issues/3784 PR-URL: https://github.com/nodejs/node/pull/4013 Reviewed-By: Ben Noordhuis --- lib/repl.js | 1 + test/parallel/test-repl-syntax-error-stack.js | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 test/parallel/test-repl-syntax-error-stack.js diff --git a/lib/repl.js b/lib/repl.js index 4485b4016b1363..4ce1a25fe1c344 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -274,6 +274,7 @@ function REPLServer(prompt, self._domain.on('error', function(e) { debug('domain error'); const top = replMap.get(self); + util.decorateErrorStack(e); top.outputStream.write((e.stack || e) + '\n'); top.lineParser.reset(); top.bufferedCommand = ''; diff --git a/test/parallel/test-repl-syntax-error-stack.js b/test/parallel/test-repl-syntax-error-stack.js new file mode 100644 index 00000000000000..573059e8d9f34f --- /dev/null +++ b/test/parallel/test-repl-syntax-error-stack.js @@ -0,0 +1,39 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); +const repl = require('repl'); +const util = require('util'); +let found = false; + +process.on('exit', () => { + assert.strictEqual(found, true); +}); + +// A stream to push an array into a REPL +function ArrayStream() { + this.run = function(data) { + data.forEach(line => { + this.emit('data', line + '\n'); + }); + }; +} +util.inherits(ArrayStream, require('stream').Stream); +ArrayStream.prototype.readable = true; +ArrayStream.prototype.writable = true; +ArrayStream.prototype.resume = function() {}; +ArrayStream.prototype.write = function(output) { + if (/var foo bar;/.test(output)) + found = true; +}; + +const putIn = new ArrayStream(); +const testMe = repl.start('', putIn); +let file = path.resolve(__dirname, '../fixtures/syntax/bad_syntax'); + +if (common.isWindows) + file = file.replace(/\\/g, '\\\\'); + +putIn.run(['.clear']); +putIn.run([`require('${file}');`]);