diff --git a/.eslintrc b/.eslintrc index 8d9988acef7adc..e4dd84b8893da0 100644 --- a/.eslintrc +++ b/.eslintrc @@ -33,6 +33,16 @@ rules: no-unreachable: 2 ## require valid typeof compared string like typeof foo === 'strnig' valid-typeof: 2 + ## disallow controls characters in regular expressions + no-control-regex: 2 + ## disallow extra boolean casts + no-extra-boolean-cast : 2 + ## validate regular expressions + no-invalid-regexp: 2 + ## forbid weird whitespace characters + no-irregular-whitespace: 2 + ## avoid unexpected multiline expressions + no-unexpected-multiline: 2 # Best Practices # list: https://github.com/eslint/eslint/tree/master/docs/rules#best-practices diff --git a/README.md b/README.md index c534c64fbe29a7..09280af783c584 100644 --- a/README.md +++ b/README.md @@ -440,12 +440,12 @@ Releases of Node.js and io.js will be signed with one of the following GPG keys: * **Chris Dickinson** <christopher.s.dickinson@gmail.com> `9554F04D7259F04124DE6B476D5A82AC7E37093B` * **Colin Ihrig** <cjihrig@gmail.com> `94AE36675C464D64BAFA68DD7434390BDBE9B9C5` -* **Sam Roberts** <octetcloud@keybase.io> `0034A06D9D9B0064CE8ADF6BF1747F4AD2306D93` -* **Jeremiah Senkpiel** <fishrock@keybase.io> `FD3A5288F042B6850C66B31F09FE44734EB7990E` +* **Evan Lucas** <evanlucas@me.com> `B9AE9905FFD7803F25714661B63B535A4C206CA9` * **James M Snell** <jasnell@keybase.io> `71DCFD284A79C3B38668286BC97EC7A07EDE3FC1` -* **Rod Vagg** <rod@vagg.org> `DD8F2338BAE7501E3DD5AC78C273792F7D83545D` +* **Jeremiah Senkpiel** <fishrock@keybase.io> `FD3A5288F042B6850C66B31F09FE44734EB7990E` * **Myles Borins** <myles.borins@gmail.com> `C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8` -* **Evan Lucas** <evanlucas@me.com> `B9AE9905FFD7803F25714661B63B535A4C206CA9` +* **Rod Vagg** <rod@vagg.org> `DD8F2338BAE7501E3DD5AC78C273792F7D83545D` +* **Sam Roberts** <octetcloud@keybase.io> `0034A06D9D9B0064CE8ADF6BF1747F4AD2306D93` The full set of trusted release keys can be imported by running: @@ -466,6 +466,6 @@ details on what to do with these keys to verify that a downloaded file is offici Previous releases of Node.js have been signed with one of the following GPG keys: -* Julien Gilli <jgilli@fastmail.fm> `114F43EE0176B71C7BC219DD50A3051F888C628D` -* Timothy J Fontaine <tjfontaine@gmail.com> `7937DFD2AB06298B2293C3187D33FF9D0246406D` -* Isaac Z. Schlueter <i@izs.me> `93C7E9E91B49E432C2F75674B0A78B0A6C481CF6` +* **Isaac Z. Schlueter** <i@izs.me> `93C7E9E91B49E432C2F75674B0A78B0A6C481CF6` +* **Julien Gilli** <jgilli@fastmail.fm> `114F43EE0176B71C7BC219DD50A3051F888C628D` +* **Timothy J Fontaine** <tjfontaine@gmail.com> `7937DFD2AB06298B2293C3187D33FF9D0246406D` diff --git a/doc/api/addons.markdown b/doc/api/addons.markdown index 986c151097ee78..93ece69d738275 100644 --- a/doc/api/addons.markdown +++ b/doc/api/addons.markdown @@ -364,7 +364,7 @@ adding the function as a property of `exports`. To test it, run the following JavaScript: -```cpp +```js // test.js const addon = require('./build/Release/addon'); diff --git a/doc/api/buffer.markdown b/doc/api/buffer.markdown index d64825f6dc0acb..8be347a74c725d 100644 --- a/doc/api/buffer.markdown +++ b/doc/api/buffer.markdown @@ -1008,7 +1008,7 @@ When undefined, `offset` defaults to `0`, and `encoding` defaults to `'utf8'`. The `length` parameter is the number of bytes to write, when undefined `length` defaults to `buffer.length - offset`. If the Buffer did not contain enough space to fit the entire string, only a partial amount of the string will be -written however, the will not write only partially encoded characters. The +written however, it will not write only partially encoded characters. The return value indicates the number of octets written. ```js diff --git a/doc/api/crypto.markdown b/doc/api/crypto.markdown index 49e86023a36c1a..eac697d1845aa2 100644 --- a/doc/api/crypto.markdown +++ b/doc/api/crypto.markdown @@ -235,7 +235,7 @@ decipher.end(); Example: Using `Decipher` and piped streams: -``` +```js const crypto = require('crypto'); const fs = require('fs'); const decipher = crypto.createDecipher('aes192', 'a password'); diff --git a/doc/api/http.markdown b/doc/api/http.markdown index 9af951332180e3..fc55777acf6ada 100644 --- a/doc/api/http.markdown +++ b/doc/api/http.markdown @@ -939,6 +939,8 @@ The 3-digit HTTP response status code. E.G. `404`. **Only valid for response obtained from [`http.ClientRequest`][].** +The HTTP response status message (reason phrase). E.G. `OK` or `Internal Server Error`. + ### message.socket The [`net.Socket`][] object associated with the connection. @@ -946,8 +948,6 @@ The [`net.Socket`][] object associated with the connection. With HTTPS support, use [`request.socket.getPeerCertificate()`][] to obtain the client's authentication details. -The HTTP response status message (reason phrase). E.G. `OK` or `Internal Server Error`. - ### message.trailers The request/response trailers object. Only populated at the `'end'` event. diff --git a/doc/api/https.markdown b/doc/api/https.markdown index c39a6c48561f5d..39401de8c5135c 100644 --- a/doc/api/https.markdown +++ b/doc/api/https.markdown @@ -185,6 +185,7 @@ The following options from [`tls.connect()`][] can also be specified. However, a - `secureProtocol`: The SSL method to use, e.g. `SSLv3_method` to force SSL version 3. The possible values depend on your installation of OpenSSL and are defined in the constant [`SSL_METHODS`][]. +- `servername`: Servername for SNI (Server Name Indication) TLS extension. In order to specify these options, use a custom [`Agent`][]. diff --git a/doc/api/net.markdown b/doc/api/net.markdown index c6cf897d53d4d8..6178623254954b 100644 --- a/doc/api/net.markdown +++ b/doc/api/net.markdown @@ -51,7 +51,8 @@ var server = net.createServer((socket) => { }); // grab a random port. -server.listen(() => { +server.listen((err) => { + if (err) throw err; address = server.address(); console.log('opened server on %j', address); }); @@ -528,7 +529,8 @@ Here is an example of a client of the previously described echo server: ```js const net = require('net'); -const client = net.connect({port: 8124}, () => { //'connect' listener +const client = net.connect({port: 8124}, () => { + // 'connect' listener console.log('connected to server!'); client.write('world!\r\n'); }); @@ -581,8 +583,8 @@ Here is an example of a client of the previously described echo server: ```js const net = require('net'); -const client = net.connect({port: 8124}, - () => { //'connect' listener +const client = net.connect({port: 8124}, () => { + //'connect' listener console.log('connected to server!'); client.write('world!\r\n'); }); @@ -649,7 +651,8 @@ on port 8124: ```js const net = require('net'); -const server = net.createServer((c) => { //'connection' listener +const server = net.createServer((c) => { + // 'connection' listener console.log('client connected'); c.on('end', () => { console.log('client disconnected'); @@ -657,7 +660,9 @@ const server = net.createServer((c) => { //'connection' listener c.write('hello\r\n'); c.pipe(c); }); -server.listen(8124, () => { //'listening' listener +server.listen(8124, (err) => { + // 'listening' listener + if (err) throw err; console.log('server bound'); }); ``` @@ -672,7 +677,10 @@ To listen on the socket `/tmp/echo.sock` the third line from the last would just be changed to ```js -server.listen('/tmp/echo.sock', () => { /* 'listening' listener*/ }) +server.listen('/tmp/echo.sock', (err) => { + // 'listening' listener + if (err) throw err; +}); ``` Use `nc` to connect to a UNIX domain socket server: diff --git a/doc/api/process.markdown b/doc/api/process.markdown index 981125eecf12a6..ee6a81b8ecaea7 100644 --- a/doc/api/process.markdown +++ b/doc/api/process.markdown @@ -693,7 +693,7 @@ function MyThing(options) { process.nextTick(() => { this.startDoingStuff(); - }.bind(this)); + }); } var thing = new MyThing(); diff --git a/doc/api/util.markdown b/doc/api/util.markdown index 6c7a9272f68196..7839418e39d60a 100644 --- a/doc/api/util.markdown +++ b/doc/api/util.markdown @@ -544,10 +544,21 @@ Deprecated predecessor of `console.log`. ## util.puts([...]) - Stability: 0 - Deprecated: Use console.log() instead. + Stability: 0 - Deprecated: Use `console.log()` instead. Deprecated predecessor of `console.log`. + +## util._extend(obj) + + Stability: 0 - Deprecated: Use Object.assign() instead. + +`_extend` was never intended to be used outside of internal NodeJS modules. The +community found and used it. + +It is deprecated and should not be used in new code. JavaScript comes with very +similar built in functionality through `Object.assign`. + [`Array.isArray`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray [constructor]: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/constructor [Customizing `util.inspect` colors]: #util_customizing_util_inspect_colors diff --git a/doc/api/vm.markdown b/doc/api/vm.markdown index a1f412e6baf4dc..1b494a468c947d 100644 --- a/doc/api/vm.markdown +++ b/doc/api/vm.markdown @@ -32,12 +32,18 @@ The options when creating a script are: 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. - Applies only to syntax errors compiling the code; errors while running the - code are controlled by the options to the script's methods. +- `displayErrors`: if `true`, on error, attach the line of code that caused + the error to the stack trace. Applies only to syntax errors compiling the + code; errors while running the code are controlled by the options to the + script's methods. - `timeout`: a number of milliseconds to execute `code` before terminating execution. If execution is terminated, an [`Error`][] will be thrown. +- `cachedData`: an optional `Buffer` with V8's code cache data for the supplied + source. When supplied `cachedDataRejected` value will be set to either + `true` or `false` depending on acceptance of the data by V8. +- `produceCachedData`: if `true` and no `cachedData` is present - a `Buffer` + with V8's code cache data will be produced and stored in `cachedData` property + of the returned `vm.Script` instance. ### script.runInContext(contextifiedSandbox[, options]) @@ -144,10 +150,10 @@ The options for running a script are: 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. - Applies only to runtime errors executing the code; it is impossible to create - a `Script` instance with syntax errors, as the constructor will throw. +- `displayErrors`: if `true`, on error, attach the line of code that caused + the error to the stack trace. Applies only to runtime errors executing the + code; it is impossible to create a `Script` instance with syntax errors, as + the constructor will throw. - `timeout`: a number of milliseconds to execute the script before terminating execution. If execution is terminated, an [`Error`][] will be thrown. @@ -284,10 +290,10 @@ e.g. `(0,eval)('code')`. However, it also has the following additional options: 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 - thrown by executing the compiled code. Defaults to `true`. +- `displayErrors`: if `true`, on error, attach the line of code that caused + the error to the stack trace. Will capture both syntax errors from compiling + `code` and runtime errors thrown by executing the compiled code. Defaults to + `true`. - `timeout`: a number of milliseconds to execute `code` before terminating execution. If execution is terminated, an [`Error`][] will be thrown. diff --git a/lib/_stream_readable.js b/lib/_stream_readable.js index ffdb81a16ee626..c64333c58a1f16 100644 --- a/lib/_stream_readable.js +++ b/lib/_stream_readable.js @@ -130,10 +130,10 @@ function readableAddChunk(stream, state, chunk, encoding, addToFront) { onEofChunk(stream, state); } else if (state.objectMode || chunk && chunk.length > 0) { if (state.ended && !addToFront) { - var e = new Error('stream.push() after EOF'); + const e = new Error('stream.push() after EOF'); stream.emit('error', e); } else if (state.endEmitted && addToFront) { - var e = new Error('stream.unshift() after end event'); + const e = new Error('stream.unshift() after end event'); stream.emit('error', e); } else { if (state.decoder && !addToFront && !encoding) @@ -640,13 +640,13 @@ Readable.prototype.unpipe = function(dest) { state.pipesCount = 0; state.flowing = false; - for (var i = 0; i < len; i++) + for (let i = 0; i < len; i++) dests[i].emit('unpipe', this); return this; } // try to find the right one. - var i = state.pipes.indexOf(dest); + const i = state.pipes.indexOf(dest); if (i === -1) return this; @@ -847,7 +847,7 @@ function fromList(n, state) { if (n < list[0].length) { // just take a part of the first list item. // slice is the same for buffers and strings. - var buf = list[0]; + const buf = list[0]; ret = buf.slice(0, n); list[0] = buf.slice(n); } else if (n === list[0].length) { @@ -863,7 +863,7 @@ function fromList(n, state) { var c = 0; for (var i = 0, l = list.length; i < l && c < n; i++) { - var buf = list[0]; + const buf = list[0]; var cpy = Math.min(n - c, buf.length); if (stringMode) diff --git a/lib/_tls_common.js b/lib/_tls_common.js index 94564ce59c0330..382ba5e8f479ef 100644 --- a/lib/_tls_common.js +++ b/lib/_tls_common.js @@ -48,7 +48,7 @@ exports.createSecureContext = function createSecureContext(options, context) { // cert's issuer in C++ code. if (options.ca) { if (Array.isArray(options.ca)) { - for (var i = 0, len = options.ca.length; i < len; i++) { + for (let i = 0, len = options.ca.length; i < len; i++) { c.context.addCACert(options.ca[i]); } } else { @@ -60,7 +60,7 @@ exports.createSecureContext = function createSecureContext(options, context) { if (options.cert) { if (Array.isArray(options.cert)) { - for (var i = 0; i < options.cert.length; i++) + for (let i = 0; i < options.cert.length; i++) c.context.setCert(options.cert[i]); } else { c.context.setCert(options.cert); @@ -73,7 +73,7 @@ exports.createSecureContext = function createSecureContext(options, context) { // which leads to the crash later on. if (options.key) { if (Array.isArray(options.key)) { - for (var i = 0; i < options.key.length; i++) { + for (let i = 0; i < options.key.length; i++) { var key = options.key[i]; if (key.passphrase) @@ -108,7 +108,7 @@ exports.createSecureContext = function createSecureContext(options, context) { if (options.crl) { if (Array.isArray(options.crl)) { - for (var i = 0, len = options.crl.length; i < len; i++) { + for (let i = 0, len = options.crl.length; i < len; i++) { c.context.addCRL(options.crl[i]); } } else { diff --git a/lib/dns.js b/lib/dns.js index be735f2cfeac24..6c9795384fbf51 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -189,6 +189,9 @@ exports.lookupService = function(host, port, callback) { if (cares.isIP(host) === 0) throw new TypeError('"host" argument needs to be a valid IP address'); + if (typeof port !== 'number') + throw new TypeError(`"port" argument must be a number, got "${port}"`); + callback = makeAsync(callback); var req = new GetNameInfoReqWrap(); diff --git a/lib/fs.js b/lib/fs.js index af8001bb181698..8fd0d35dc47489 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -1300,18 +1300,17 @@ fs.appendFileSync = function(path, data, options) { function FSWatcher() { EventEmitter.call(this); - var self = this; this._handle = new FSEvent(); this._handle.owner = this; - this._handle.onchange = function(status, event, filename) { + this._handle.onchange = (status, event, filename) => { if (status < 0) { - self._handle.close(); + this._handle.close(); const error = errnoException(status, `watch ${filename}`); error.filename = filename; - self.emit('error', error); + this.emit('error', error); } else { - self.emit('change', event, filename); + this.emit('change', event, filename); } }; } @@ -1367,24 +1366,23 @@ fs.watch = function(filename) { function StatWatcher() { EventEmitter.call(this); - var self = this; this._handle = new binding.StatWatcher(); // uv_fs_poll is a little more powerful than ev_stat but we curb it for // the sake of backwards compatibility var oldStatus = -1; - this._handle.onchange = function(current, previous, newStatus) { + this._handle.onchange = (current, previous, newStatus) => { if (oldStatus === -1 && newStatus === -1 && current.nlink === previous.nlink) return; oldStatus = newStatus; - self.emit('change', current, previous); + this.emit('change', current, previous); }; - this._handle.onstop = function() { - self.emit('stop'); + this._handle.onstop = () => { + this.emit('stop'); }; } util.inherits(StatWatcher, EventEmitter); @@ -1766,20 +1764,20 @@ function ReadStream(path, options) { fs.FileReadStream = fs.ReadStream; // support the legacy name ReadStream.prototype.open = function() { - var self = this; - fs.open(this.path, this.flags, this.mode, function(er, fd) { + + fs.open(this.path, this.flags, this.mode, (er, fd) => { if (er) { - if (self.autoClose) { - self.destroy(); + if (this.autoClose) { + this.destroy(); } - self.emit('error', er); + this.emit('error', er); return; } - self.fd = fd; - self.emit('open', fd); + this.fd = fd; + this.emit('open', fd); // start the flow of data. - self.read(); + this.read(); }); }; @@ -1814,7 +1812,6 @@ ReadStream.prototype._read = function(n) { return this.push(null); // the actual read. - var self = this; fs.read(this.fd, pool, pool.used, toRead, this.pos, onread); // move the pool positions, and internal position for reading. @@ -1822,6 +1819,7 @@ ReadStream.prototype._read = function(n) { this.pos += toRead; pool.used += toRead; + var self = this; function onread(er, bytesRead) { if (er) { if (self.autoClose) { @@ -1848,7 +1846,17 @@ ReadStream.prototype.destroy = function() { ReadStream.prototype.close = function(cb) { - var self = this; + + const close = (fd) => { + fs.close(fd || this.fd, (er) => { + if (er) + this.emit('error', er); + else + this.emit('close'); + }); + this.fd = null; + }; + if (cb) this.once('close', cb); if (this.closed || typeof this.fd !== 'number') { @@ -1860,16 +1868,7 @@ ReadStream.prototype.close = function(cb) { } this.closed = true; close(); - - function close(fd) { - fs.close(fd || self.fd, function(er) { - if (er) - self.emit('error', er); - else - self.emit('close'); - }); - self.fd = null; - } + }; @@ -1957,15 +1956,14 @@ WriteStream.prototype._write = function(data, encoding, cb) { this._write(data, encoding, cb); }); - var self = this; - fs.write(this.fd, data, 0, data.length, this.pos, function(er, bytes) { + fs.write(this.fd, data, 0, data.length, this.pos, (er, bytes) => { if (er) { - if (self.autoClose) { - self.destroy(); + if (this.autoClose) { + this.destroy(); } return cb(er); } - self.bytesWritten += bytes; + this.bytesWritten += bytes; cb(); }); @@ -1992,7 +1990,6 @@ WriteStream.prototype._writev = function(data, cb) { this._writev(data, cb); }); - const self = this; const len = data.length; const chunks = new Array(len); var size = 0; @@ -2004,12 +2001,12 @@ WriteStream.prototype._writev = function(data, cb) { size += chunk.length; } - writev(this.fd, chunks, this.pos, function(er, bytes) { + writev(this.fd, chunks, this.pos, (er, bytes) => { if (er) { - self.destroy(); + this.destroy(); return cb(er); } - self.bytesWritten += bytes; + this.bytesWritten += bytes; cb(); }); diff --git a/lib/http.js b/lib/http.js index 64788dd5c07f9f..203883676ce4ae 100644 --- a/lib/http.js +++ b/lib/http.js @@ -59,10 +59,10 @@ function Client(port, host) { } util.inherits(Client, EventEmitter); Client.prototype.request = function(method, path, headers) { - var self = this; + var options = {}; - options.host = self.host; - options.port = self.port; + options.host = this.host; + options.port = this.port; if (method[0] === '/') { headers = path; path = method; @@ -71,22 +71,22 @@ Client.prototype.request = function(method, path, headers) { options.method = method; options.path = path; options.headers = headers; - options.agent = self.agent; + options.agent = this.agent; var c = new ClientRequest(options); - c.on('error', function(e) { - self.emit('error', e); + c.on('error', (e) => { + this.emit('error', e); }); // The old Client interface emitted 'end' on socket end. // This doesn't map to how we want things to operate in the future // but it will get removed when we remove this legacy interface. - c.on('socket', function(s) { - s.on('end', function() { - if (self._decoder) { - var ret = self._decoder.end(); + c.on('socket', (s) => { + s.on('end', () => { + if (this._decoder) { + var ret = this._decoder.end(); if (ret) - self.emit('data', ret); + this.emit('data', ret); } - self.emit('end'); + this.emit('end'); }); }); return c; diff --git a/lib/https.js b/lib/https.js index 7008a79131c663..9f32b2f6e96582 100644 --- a/lib/https.js +++ b/lib/https.js @@ -77,12 +77,11 @@ function createConnection(port, host, options) { } } - const self = this; - const socket = tls.connect(options, function() { + const socket = tls.connect(options, () => { if (!options._agentKey) return; - self._cacheSession(options._agentKey, socket.getSession()); + this._cacheSession(options._agentKey, socket.getSession()); }); return socket; } diff --git a/lib/module.js b/lib/module.js index a068bffa7d9056..eabc27a0f8715e 100644 --- a/lib/module.js +++ b/lib/module.js @@ -23,16 +23,6 @@ function hasOwnProperty(obj, prop) { } -function tryWrapper(wrapper, opts) { - try { - return runInThisContext(wrapper, opts); - } catch (e) { - internalUtil.decorateErrorStack(e); - throw e; - } -} - - function stat(filename) { filename = path._makeLong(filename); const cache = stat.cache; @@ -394,8 +384,12 @@ Module.prototype._compile = function(content, filename) { // create wrapper function var wrapper = Module.wrap(content); - var compiledWrapper = tryWrapper(wrapper, - { filename: filename, lineOffset: 0 }); + var compiledWrapper = runInThisContext(wrapper, { + filename: filename, + lineOffset: 0, + displayErrors: true + }); + if (global.v8debug) { if (!resolvedArgv) { // we enter the repl if we're not given a filename argument. diff --git a/src/env.h b/src/env.h index bb0868e1d83140..254f0c8fa123a6 100644 --- a/src/env.h +++ b/src/env.h @@ -61,6 +61,8 @@ namespace node { V(buffer_string, "buffer") \ V(bytes_string, "bytes") \ V(bytes_parsed_string, "bytesParsed") \ + V(cached_data_string, "cachedData") \ + V(cached_data_rejected_string, "cachedDataRejected") \ V(callback_string, "callback") \ V(change_string, "change") \ V(oncertcb_string, "oncertcb") \ @@ -175,6 +177,7 @@ namespace node { V(preference_string, "preference") \ V(priority_string, "priority") \ V(processed_string, "processed") \ + V(produce_cached_data_string, "produceCachedData") \ V(prototype_string, "prototype") \ V(raw_string, "raw") \ V(rdev_string, "rdev") \ diff --git a/src/node.js b/src/node.js index 60bffc454065c7..1f29386a87714f 100644 --- a/src/node.js +++ b/src/node.js @@ -605,7 +605,7 @@ 'global.require = require;\n' + 'return require("vm").runInThisContext(' + JSON.stringify(body) + ', { filename: ' + - JSON.stringify(name) + ' });\n'; + JSON.stringify(name) + ', displayErrors: true });\n'; // Defer evaluation for a tick. This is a workaround for deferred // events not firing when evaluating scripts from the command line, // see https://github.com/nodejs/node/issues/1600. @@ -988,7 +988,8 @@ var fn = runInThisContext(source, { filename: this.filename, - lineOffset: 0 + lineOffset: 0, + displayErrors: true }); fn(this.exports, NativeModule.require, this, this.filename); diff --git a/src/node_contextify.cc b/src/node_contextify.cc index 6675e2d8a08f70..7ca622de43425b 100644 --- a/src/node_contextify.cc +++ b/src/node_contextify.cc @@ -13,6 +13,7 @@ namespace node { using v8::AccessType; using v8::Array; +using v8::ArrayBuffer; using v8::Boolean; using v8::Context; using v8::Debug; @@ -40,6 +41,7 @@ using v8::ScriptCompiler; using v8::ScriptOrigin; using v8::String; using v8::TryCatch; +using v8::Uint8Array; using v8::UnboundScript; using v8::V8; using v8::Value; @@ -507,24 +509,57 @@ class ContextifyScript : public BaseObject { Local lineOffset = GetLineOffsetArg(args, 1); Local columnOffset = GetColumnOffsetArg(args, 1); bool display_errors = GetDisplayErrorsArg(args, 1); + MaybeLocal cached_data_buf = GetCachedData(env, args, 1); + bool produce_cached_data = GetProduceCachedData(env, args, 1); if (try_catch.HasCaught()) { try_catch.ReThrow(); return; } + ScriptCompiler::CachedData* cached_data = nullptr; + if (!cached_data_buf.IsEmpty()) { + ArrayBuffer::Contents contents = + cached_data_buf.ToLocalChecked()->Buffer()->GetContents(); + cached_data = new ScriptCompiler::CachedData( + static_cast(contents.Data()), contents.ByteLength()); + } + ScriptOrigin origin(filename, lineOffset, columnOffset); - ScriptCompiler::Source source(code, origin); - Local v8_script = - ScriptCompiler::CompileUnbound(env->isolate(), &source); + ScriptCompiler::Source source(code, origin, cached_data); + ScriptCompiler::CompileOptions compile_options = + ScriptCompiler::kNoCompileOptions; + + if (source.GetCachedData() != nullptr) + compile_options = ScriptCompiler::kConsumeCodeCache; + else if (produce_cached_data) + compile_options = ScriptCompiler::kProduceCodeCache; + + Local v8_script = ScriptCompiler::CompileUnbound( + env->isolate(), + &source, + compile_options); if (v8_script.IsEmpty()) { if (display_errors) { - AppendExceptionLine(env, try_catch.Exception(), try_catch.Message()); + DecorateErrorStack(env, try_catch); } try_catch.ReThrow(); return; } contextify_script->script_.Reset(env->isolate(), v8_script); + + if (compile_options == ScriptCompiler::kConsumeCodeCache) { + args.This()->Set( + env->cached_data_rejected_string(), + Boolean::New(env->isolate(), source.GetCachedData()->rejected)); + } else if (compile_options == ScriptCompiler::kProduceCodeCache) { + const ScriptCompiler::CachedData* cached_data = source.GetCachedData(); + MaybeLocal buf = Buffer::Copy( + env, + reinterpret_cast(cached_data->data), + cached_data->length); + args.This()->Set(env->cached_data_string(), buf.ToLocalChecked()); + } } @@ -605,6 +640,30 @@ class ContextifyScript : public BaseObject { } } + static void DecorateErrorStack(Environment* env, const TryCatch& try_catch) { + Local exception = try_catch.Exception(); + + if (!exception->IsObject()) + return; + + Local err_obj = exception.As(); + + if (IsExceptionDecorated(env, err_obj)) + return; + + AppendExceptionLine(env, exception, try_catch.Message()); + Local stack = err_obj->Get(env->stack_string()); + Local arrow = err_obj->GetHiddenValue(env->arrow_message_string()); + + if (!(stack->IsString() && arrow->IsString())) + return; + + Local decorated_stack = String::Concat(arrow.As(), + stack.As()); + err_obj->Set(env->stack_string(), decorated_stack); + err_obj->SetHiddenValue(env->decorated_string(), True(env->isolate())); + } + static int64_t GetTimeoutArg(const FunctionCallbackInfo& args, const int i) { if (args[i]->IsUndefined() || args[i]->IsString()) { @@ -677,6 +736,43 @@ class ContextifyScript : public BaseObject { } + static MaybeLocal GetCachedData( + Environment* env, + const FunctionCallbackInfo& args, + const int i) { + if (!args[i]->IsObject()) { + return MaybeLocal(); + } + Local value = args[i].As()->Get(env->cached_data_string()); + if (value->IsUndefined()) { + return MaybeLocal(); + } + + if (!value->IsUint8Array()) { + Environment::ThrowTypeError( + args.GetIsolate(), + "options.cachedData must be a Buffer instance"); + return MaybeLocal(); + } + + return value.As(); + } + + + static bool GetProduceCachedData( + Environment* env, + const FunctionCallbackInfo& args, + const int i) { + if (!args[i]->IsObject()) { + return false; + } + Local value = + args[i].As()->Get(env->produce_cached_data_string()); + + return value->IsTrue(); + } + + static Local GetLineOffsetArg( const FunctionCallbackInfo& args, const int i) { @@ -744,7 +840,7 @@ class ContextifyScript : public BaseObject { if (result.IsEmpty()) { // Error occurred during execution of the script. if (display_errors) { - AppendExceptionLine(env, try_catch.Exception(), try_catch.Message()); + DecorateErrorStack(env, try_catch); } try_catch.ReThrow(); return false; diff --git a/src/node_internals.h b/src/node_internals.h index c720f70eda0d62..ea83d4d9fc1238 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -125,6 +125,8 @@ inline static int snprintf(char *buffer, size_t n, const char *format, ...) { # define NO_RETURN #endif +bool IsExceptionDecorated(Environment* env, v8::Local er); + void AppendExceptionLine(Environment* env, v8::Local er, v8::Local message); diff --git a/test/message/vm_display_syntax_error.js b/test/message/vm_display_syntax_error.js index 23525c14d822fb..62bbd432a2f98a 100644 --- a/test/message/vm_display_syntax_error.js +++ b/test/message/vm_display_syntax_error.js @@ -4,6 +4,12 @@ var vm = require('vm'); console.error('beginning'); +try { + vm.runInThisContext('var 4;', { filename: 'foo.vm', displayErrors: true }); +} catch (err) { + console.error(err.stack); +} + vm.runInThisContext('var 5;', { filename: 'test.vm' }); console.error('end'); diff --git a/test/message/vm_display_syntax_error.out b/test/message/vm_display_syntax_error.out index 086944870263f6..14df3a8284a64f 100644 --- a/test/message/vm_display_syntax_error.out +++ b/test/message/vm_display_syntax_error.out @@ -1,5 +1,18 @@ beginning +foo.vm:1 +var 4; + ^ +SyntaxError: Unexpected number + at Object.exports.runInThisContext (vm.js:*) + at Object. (*test*message*vm_display_syntax_error.js:*) + at Module._compile (module.js:*) + at Object.Module._extensions..js (module.js:*) + at Module.load (module.js:*) + at Function.Module._load (module.js:*) + at Function.Module.runMain (module.js:*) + at startup (node.js:*) + at node.js:* test.vm:1 var 5; ^ diff --git a/test/parallel/parallel.status b/test/parallel/parallel.status index 0fe1bf9027f580..3d1bfa20b3c69f 100644 --- a/test/parallel/parallel.status +++ b/test/parallel/parallel.status @@ -9,8 +9,10 @@ prefix parallel [$system==win32] test-debug-no-context : PASS,FLAKY test-tls-ticket-cluster : PASS,FLAKY +test-tick-processor : PASS,FLAKY [$system==linux] +test-tick-processor : PASS,FLAKY [$system==macos] diff --git a/test/parallel/test-assert.js b/test/parallel/test-assert.js index 818f4574daba04..f644470ebca01e 100644 --- a/test/parallel/test-assert.js +++ b/test/parallel/test-assert.js @@ -73,9 +73,11 @@ assert.throws(makeBlock(a.deepEqual, /a/i, /a/)); assert.throws(makeBlock(a.deepEqual, /a/m, /a/)); assert.throws(makeBlock(a.deepEqual, /a/igm, /a/im)); -var re1 = /a/; -re1.lastIndex = 3; -assert.throws(makeBlock(a.deepEqual, re1, /a/)); +{ + const re1 = /a/; + re1.lastIndex = 3; + assert.throws(makeBlock(a.deepEqual, re1, /a/)); +} // 7.4 @@ -172,10 +174,11 @@ assert.throws(makeBlock(a.deepStrictEqual, /a/i, /a/)); assert.throws(makeBlock(a.deepStrictEqual, /a/m, /a/)); assert.throws(makeBlock(a.deepStrictEqual, /a/igm, /a/im)); -var re1 = /a/; -re1.lastIndex = 3; -assert.throws(makeBlock(a.deepStrictEqual, re1, /a/)); - +{ + const re1 = /a/; + re1.lastIndex = 3; + assert.throws(makeBlock(a.deepStrictEqual, re1, /a/)); +} // 7.4 - strict assert.throws(makeBlock(a.deepStrictEqual, 4, '4'), diff --git a/test/parallel/test-dns.js b/test/parallel/test-dns.js index 3362e8c534eaab..84b74e022ab813 100644 --- a/test/parallel/test-dns.js +++ b/test/parallel/test-dns.js @@ -145,3 +145,19 @@ assert.doesNotThrow(function() { hints: dns.ADDRCONFIG | dns.V4MAPPED }, noop); }); + +assert.throws(function() { + dns.lookupService('0.0.0.0'); +}, /Invalid arguments/); + +assert.throws(function() { + dns.lookupService('fasdfdsaf', 0, noop); +}, /"host" argument needs to be a valid IP address/); + +assert.throws(function() { + dns.lookupService('0.0.0.0', '0', noop); +}, /"port" argument must be a number, got "0"/); + +assert.doesNotThrow(function() { + dns.lookupService('0.0.0.0', 0, noop); +}); diff --git a/test/parallel/test-fs-link.js b/test/parallel/test-fs-link.js index acbedc1452231d..292d48fb53e5c6 100644 --- a/test/parallel/test-fs-link.js +++ b/test/parallel/test-fs-link.js @@ -7,14 +7,14 @@ const fs = require('fs'); common.refreshTmpDir(); // test creating and reading hard link -const srcPath = path.join(common.fixturesDir, 'cycles', 'root.js'); +const srcPath = path.join(common.tmpDir, 'hardlink-target.txt'); const dstPath = path.join(common.tmpDir, 'link1.js'); +fs.writeFileSync(srcPath, 'hello world'); const callback = function(err) { if (err) throw err; - const srcContent = fs.readFileSync(srcPath, 'utf8'); const dstContent = fs.readFileSync(dstPath, 'utf8'); - assert.strictEqual(srcContent, dstContent); + assert.strictEqual('hello world', dstContent); }; fs.link(srcPath, dstPath, common.mustCall(callback)); diff --git a/test/parallel/test-net-settimeout.js b/test/parallel/test-net-settimeout.js index a1172b92e1dead..76ed7b020fb89e 100644 --- a/test/parallel/test-net-settimeout.js +++ b/test/parallel/test-net-settimeout.js @@ -2,36 +2,28 @@ // This example sets a timeout then immediately attempts to disable the timeout // https://github.com/joyent/node/pull/2245 -var common = require('../common'); -var net = require('net'); -var assert = require('assert'); +const common = require('../common'); +const net = require('net'); +const assert = require('assert'); -var T = 100; +const T = 100; -var server = net.createServer(function(c) { +const server = net.createServer(function(c) { c.write('hello'); }); server.listen(common.PORT); -var killers = [0]; +const socket = net.createConnection(common.PORT, 'localhost'); -var left = killers.length; -killers.forEach(function(killer) { - var socket = net.createConnection(common.PORT, 'localhost'); +const s = socket.setTimeout(T, function() { + common.fail('Socket timeout event is not expected to fire'); +}); +assert.ok(s instanceof net.Socket); - var s = socket.setTimeout(T, function() { - socket.destroy(); - if (--left === 0) server.close(); - assert.ok(killer !== 0); - clearTimeout(timeout); - }); - assert.ok(s instanceof net.Socket); +socket.setTimeout(0); - socket.setTimeout(killer); +setTimeout(function() { + socket.destroy(); + server.close(); +}, T * 2); - var timeout = setTimeout(function() { - socket.destroy(); - if (--left === 0) server.close(); - assert.ok(killer === 0); - }, T * 2); -}); diff --git a/test/parallel/test-promises-unhandled-rejections.js b/test/parallel/test-promises-unhandled-rejections.js index 8345096445098c..5da2bff00acaa2 100644 --- a/test/parallel/test-promises-unhandled-rejections.js +++ b/test/parallel/test-promises-unhandled-rejections.js @@ -385,7 +385,7 @@ asyncTest('Catching the Promise.all() of a collection that includes a' + 'rejected promise prevents unhandledRejection', function(done) { var e = new Error(); onUnhandledFail(done); - Promise.all([Promise.reject(e)]).then(common.fail, function() {}); + Promise.all([Promise.reject(e)]).then(common.fail, function() {}); }); asyncTest( @@ -401,7 +401,7 @@ asyncTest( }); p = Promise.all([p]); process.nextTick(function() { - p.then(common.fail, function() {}); + p.then(common.fail, function() {}); }); } ); @@ -455,7 +455,7 @@ asyncTest('Waiting for some combination of process.nextTick + promise' + Promise.resolve().then(function() { process.nextTick(function() { Promise.resolve().then(function() { - a.catch(function() {}); + a.catch(function() {}); }); }); }); @@ -474,7 +474,7 @@ asyncTest('Waiting for some combination of process.nextTick + promise' + Promise.resolve().then(function() { process.nextTick(function() { Promise.resolve().then(function() { - a.catch(function() {}); + a.catch(function() {}); }); }); }); @@ -494,7 +494,7 @@ asyncTest('Waiting for some combination of process.nextTick + promise ' + Promise.resolve().then(function() { process.nextTick(function() { Promise.resolve().then(function() { - a.catch(function() {}); + a.catch(function() {}); }); }); }); @@ -514,7 +514,7 @@ asyncTest('Waiting for some combination of promise microtasks + ' + process.nextTick(function() { Promise.resolve().then(function() { process.nextTick(function() { - a.catch(function() {}); + a.catch(function() {}); }); }); }); @@ -535,7 +535,7 @@ asyncTest( process.nextTick(function() { Promise.resolve().then(function() { process.nextTick(function() { - a.catch(function() {}); + a.catch(function() {}); }); }); }); @@ -556,7 +556,7 @@ asyncTest('Waiting for some combination of promise microtasks +' + process.nextTick(function() { Promise.resolve().then(function() { process.nextTick(function() { - a.catch(function() {}); + a.catch(function() {}); }); }); }); diff --git a/test/parallel/test-vm-cached-data.js b/test/parallel/test-vm-cached-data.js new file mode 100644 index 00000000000000..47ea592b531538 --- /dev/null +++ b/test/parallel/test-vm-cached-data.js @@ -0,0 +1,37 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const vm = require('vm'); +const Buffer = require('buffer').Buffer; + +const originalSource = '(function bcd() { return \'original\'; })'; + +// It should produce code cache +const original = new vm.Script(originalSource, { + produceCachedData: true +}); +assert(original.cachedData instanceof Buffer); + +assert.equal(original.runInThisContext()(), 'original'); + +// It should consume code cache +const success = new vm.Script(originalSource, { + cachedData: original.cachedData +}); +assert(!success.cachedDataRejected); + +assert.equal(success.runInThisContext()(), 'original'); + +// It should reject invalid code cache +const reject = new vm.Script('(function abc() { return \'invalid\'; })', { + cachedData: original.cachedData +}); +assert(reject.cachedDataRejected); +assert.equal(reject.runInThisContext()(), 'invalid'); + +// It should throw on non-Buffer cachedData +assert.throws(() => { + new vm.Script('function abc() {}', { + cachedData: 'ohai' + }); +}); diff --git a/tools/release.sh b/tools/release.sh index d54252b8100cff..3f6754275a9636 100755 --- a/tools/release.sh +++ b/tools/release.sh @@ -20,7 +20,7 @@ signcmd=dist-sign echo "# Selecting GPG key ..." -gpgkey=$(gpg --list-secret-keys | grep '^sec' | awk -F'( +|/)' '{print $3}') +gpgkey=$(gpg --list-secret-keys | awk -F'( +|/)' '/^(sec|ssb)/{print $3}') keycount=$(echo $gpgkey | wc -w) if [ $keycount -eq 0 ]; then