From e12a9c567cc0c615ef9ab9f59f58701bab0e0258 Mon Sep 17 00:00:00 2001 From: Myles Borins Date: Wed, 16 Aug 2017 14:38:47 -0700 Subject: [PATCH 001/231] build: re-enable snapshots in v8.x When 5d5e31c was backported to v8.x as e2b306c831 it was missing the configure changes from 8dce05fa7. This patch actually re-enables snapshots! Fixes: https://github.com/nodejs/node/issues/14870 PR-URL: https://github.com/nodejs/node/pull/14875 Reviewed-By: Anna Henningsen Reviewed-By: Michael Dawson Reviewed-By: James M Snell Reviewed-By: Gibson Fahnestock --- configure | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/configure b/configure index 0b9c3a16ed09ff..44ec5d2c472ed6 100755 --- a/configure +++ b/configure @@ -437,12 +437,12 @@ parser.add_option('--without-perfctr', # Dummy option for backwards compatibility parser.add_option('--with-snapshot', action='store_true', - dest='with_snapshot', + dest='unused_with_snapshot', help=optparse.SUPPRESS_HELP) parser.add_option('--without-snapshot', action='store_true', - dest='unused_without_snapshot', + dest='without_snapshot', help=optparse.SUPPRESS_HELP) parser.add_option('--without-ssl', @@ -827,7 +827,7 @@ def configure_node(o): cross_compiling = (options.cross_compiling if options.cross_compiling is not None else target_arch != host_arch) - want_snapshots = 1 if options.with_snapshot else 0 + want_snapshots = not options.without_snapshot o['variables']['want_separate_host_toolset'] = int( cross_compiling and want_snapshots) o['variables']['want_separate_host_toolset_mkpeephole'] = int( @@ -984,7 +984,7 @@ def configure_v8(o): o['variables']['v8_optimized_debug'] = 0 # Compile with -O0 in debug builds. o['variables']['v8_random_seed'] = 0 # Use a random seed for hash tables. o['variables']['v8_promise_internal_field_count'] = 1 # Add internal field to promises for async hooks. - o['variables']['v8_use_snapshot'] = b(options.with_snapshot) + o['variables']['v8_use_snapshot'] = 'false' if options.without_snapshot else 'true' o['variables']['v8_trace_maps'] = 1 if options.trace_maps else 0 o['variables']['node_use_v8_platform'] = b(not options.without_v8_platform) o['variables']['node_use_bundled_v8'] = b(not options.without_bundled_v8) From 8fb089517620f5940752ca34e7bf0482a42ea2e3 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 24 Jul 2017 12:08:28 -0700 Subject: [PATCH 002/231] test: improve multiple vm tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/14458 Reviewed-By: Anna Henningsen Reviewed-By: Rich Trott Reviewed-By: Michaël Zasso Reviewed-By: Colin Ihrig Reviewed-By: Tobias Nießen Reviewed-By: Franziska Hinkelmann --- test/parallel/test-vm-context-async-script.js | 8 ++++---- test/parallel/test-vm-context.js | 18 +++++++++--------- .../test-vm-create-and-run-in-context.js | 12 ++++++------ test/parallel/test-vm-function-declaration.js | 2 -- .../parallel/test-vm-new-script-new-context.js | 8 ++++---- .../test-vm-new-script-this-context.js | 10 +++++----- test/parallel/test-vm-run-in-new-context.js | 14 +++++++------- test/parallel/test-vm-syntax-error-message.js | 14 +++++--------- test/parallel/test-vm-syntax-error-stderr.js | 12 ++++-------- 9 files changed, 44 insertions(+), 54 deletions(-) diff --git a/test/parallel/test-vm-context-async-script.js b/test/parallel/test-vm-context-async-script.js index 87b6f501111a7c..879315e37bb3e0 100644 --- a/test/parallel/test-vm-context-async-script.js +++ b/test/parallel/test-vm-context-async-script.js @@ -20,16 +20,16 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; -require('../common'); +const common = require('../common'); const assert = require('assert'); const vm = require('vm'); -const sandbox = { setTimeout: setTimeout }; +const sandbox = { setTimeout }; const ctx = vm.createContext(sandbox); vm.runInContext('setTimeout(function() { x = 3; }, 0);', ctx); -setTimeout(function() { +setTimeout(common.mustCall(() => { assert.strictEqual(sandbox.x, 3); assert.strictEqual(ctx.x, 3); -}, 1); +}), 1); diff --git a/test/parallel/test-vm-context.js b/test/parallel/test-vm-context.js index 1cf2d5f90aaf54..7e5404796e7533 100644 --- a/test/parallel/test-vm-context.js +++ b/test/parallel/test-vm-context.js @@ -27,29 +27,29 @@ const vm = require('vm'); const Script = vm.Script; let script = new Script('"passed";'); -console.error('run in a new empty context'); +// Run in a new empty context let context = vm.createContext(); let result = script.runInContext(context); assert.strictEqual('passed', result); -console.error('create a new pre-populated context'); -context = vm.createContext({'foo': 'bar', 'thing': 'lala'}); +// Create a new pre-populated context +context = vm.createContext({ 'foo': 'bar', 'thing': 'lala' }); assert.strictEqual('bar', context.foo); assert.strictEqual('lala', context.thing); -console.error('test updating context'); +// Test updating context script = new Script('foo = 3;'); result = script.runInContext(context); assert.strictEqual(3, context.foo); assert.strictEqual('lala', context.thing); // Issue GH-227: -assert.throws(function() { +assert.throws(() => { vm.runInNewContext('', null, 'some.js'); }, /^TypeError: sandbox must be an object$/); // Issue GH-1140: -console.error('test runInContext signature'); +// Test runInContext signature let gh1140Exception; try { vm.runInContext('throw new Error()', context, 'expected-filename.js'); @@ -77,7 +77,7 @@ const contextifiedSandboxErrorMsg = }); // Issue GH-693: -console.error('test RegExp as argument to assert.throws'); +// Test RegExp as argument to assert.throws script = vm.createScript('const assert = require(\'assert\'); assert.throws(' + 'function() { throw "hello world"; }, /hello/);', 'some.js'); @@ -92,13 +92,13 @@ assert.strictEqual(script.runInContext(ctx), false); // Error on the first line of a module should // have the correct line and column number -assert.throws(function() { +assert.throws(() => { vm.runInContext('throw new Error()', context, { filename: 'expected-filename.js', lineOffset: 32, columnOffset: 123 }); -}, function(err) { +}, (err) => { return /expected-filename\.js:33:130/.test(err.stack); }, 'Expected appearance of proper offset in Error stack'); diff --git a/test/parallel/test-vm-create-and-run-in-context.js b/test/parallel/test-vm-create-and-run-in-context.js index 96d7631f24886f..09f643aa90df46 100644 --- a/test/parallel/test-vm-create-and-run-in-context.js +++ b/test/parallel/test-vm-create-and-run-in-context.js @@ -26,24 +26,24 @@ const assert = require('assert'); const vm = require('vm'); -console.error('run in a new empty context'); +// Run in a new empty context let context = vm.createContext(); let result = vm.runInContext('"passed";', context); assert.strictEqual('passed', result); -console.error('create a new pre-populated context'); -context = vm.createContext({'foo': 'bar', 'thing': 'lala'}); +// Create a new pre-populated context +context = vm.createContext({ 'foo': 'bar', 'thing': 'lala' }); assert.strictEqual('bar', context.foo); assert.strictEqual('lala', context.thing); -console.error('test updating context'); +// Test updating context result = vm.runInContext('var foo = 3;', context); assert.strictEqual(3, context.foo); assert.strictEqual('lala', context.thing); // https://github.com/nodejs/node/issues/5768 -console.error('run in contextified sandbox without referencing the context'); -const sandbox = {x: 1}; +// Run in contextified sandbox without referencing the context +const sandbox = { x: 1 }; vm.createContext(sandbox); global.gc(); vm.runInContext('x = 2', sandbox); diff --git a/test/parallel/test-vm-function-declaration.js b/test/parallel/test-vm-function-declaration.js index d42a3a3f48058a..875ecef22a7851 100644 --- a/test/parallel/test-vm-function-declaration.js +++ b/test/parallel/test-vm-function-declaration.js @@ -42,5 +42,3 @@ assert.strictEqual(res.name, 'b', 'res should be named b'); assert.strictEqual(typeof o.a, 'function', 'a should be function'); assert.strictEqual(typeof o.b, 'function', 'b should be function'); assert.strictEqual(res, o.b, 'result should be global b function'); - -console.log('ok'); diff --git a/test/parallel/test-vm-new-script-new-context.js b/test/parallel/test-vm-new-script-new-context.js index 045c37ce335199..7015499879959f 100644 --- a/test/parallel/test-vm-new-script-new-context.js +++ b/test/parallel/test-vm-new-script-new-context.js @@ -36,14 +36,14 @@ const Script = require('vm').Script; { const script = new Script('throw new Error(\'test\');'); - assert.throws(function() { + assert.throws(() => { script.runInNewContext(); }, /^Error: test$/); } { const script = new Script('foo.bar = 5;'); - assert.throws(function() { + assert.throws(() => { script.runInNewContext(); }, /^ReferenceError: foo is not defined$/); } @@ -94,14 +94,14 @@ const Script = require('vm').Script; script.runInNewContext({ f: f }); assert.strictEqual(f.a, 2); - assert.throws(function() { + assert.throws(() => { script.runInNewContext(); }, /^ReferenceError: f is not defined$/); } { const script = new Script(''); - assert.throws(function() { + assert.throws(() => { script.runInNewContext.call('\'hello\';'); }, /^TypeError: this\.runInContext is not a function$/); } diff --git a/test/parallel/test-vm-new-script-this-context.js b/test/parallel/test-vm-new-script-this-context.js index dfa9b1130b1f55..6da47e67249c2b 100644 --- a/test/parallel/test-vm-new-script-this-context.js +++ b/test/parallel/test-vm-new-script-this-context.js @@ -26,14 +26,14 @@ const Script = require('vm').Script; common.globalCheck = false; -console.error('run a string'); +// Run a string let script = new Script('\'passed\';'); const result = script.runInThisContext(script); assert.strictEqual('passed', result); -console.error('thrown error'); +// Thrown error script = new Script('throw new Error(\'test\');'); -assert.throws(function() { +assert.throws(() => { script.runInThisContext(script); }, /^Error: test$/); @@ -43,7 +43,7 @@ script.runInThisContext(script); assert.strictEqual(2, global.hello); -console.error('pass values'); +// Pass values global.code = 'foo = 1;' + 'bar = 2;' + 'if (typeof baz !== "undefined") throw new Error("test fail");'; @@ -55,7 +55,7 @@ assert.strictEqual(0, global.obj.foo); assert.strictEqual(2, global.bar); assert.strictEqual(1, global.foo); -console.error('call a function'); +// Call a function global.f = function() { global.foo = 100; }; script = new Script('f()'); script.runInThisContext(script); diff --git a/test/parallel/test-vm-run-in-new-context.js b/test/parallel/test-vm-run-in-new-context.js index 0da73ea6a90726..6157b2445dfccc 100644 --- a/test/parallel/test-vm-run-in-new-context.js +++ b/test/parallel/test-vm-run-in-new-context.js @@ -31,12 +31,12 @@ assert.strictEqual(typeof global.gc, 'function', common.globalCheck = false; -console.error('run a string'); +// Run a string const result = vm.runInNewContext('\'passed\';'); assert.strictEqual('passed', result); -console.error('thrown error'); -assert.throws(function() { +// Thrown error +assert.throws(() => { vm.runInNewContext('throw new Error(\'test\');'); }, /^Error: test$/); @@ -45,7 +45,7 @@ vm.runInNewContext('hello = 2'); assert.strictEqual(5, global.hello); -console.error('pass values in and out'); +// Pass values in and out global.code = 'foo = 1;' + 'bar = 2;' + 'if (baz !== 3) throw new Error(\'test fail\');'; @@ -58,17 +58,17 @@ assert.strictEqual(1, global.obj.foo); assert.strictEqual(2, global.obj.bar); assert.strictEqual(2, global.foo); -console.error('call a function by reference'); +// Call a function by reference function changeFoo() { global.foo = 100; } vm.runInNewContext('f()', { f: changeFoo }); assert.strictEqual(global.foo, 100); -console.error('modify an object by reference'); +// Modify an object by reference const f = { a: 1 }; vm.runInNewContext('f.a = 2', { f: f }); assert.strictEqual(f.a, 2); -console.error('use function in context without referencing context'); +// Use function in context without referencing context const fn = vm.runInNewContext('(function() { obj.p = {}; })', { obj: {} }); global.gc(); fn(); diff --git a/test/parallel/test-vm-syntax-error-message.js b/test/parallel/test-vm-syntax-error-message.js index 89589c5cc9d881..5a16239f5653e9 100644 --- a/test/parallel/test-vm-syntax-error-message.js +++ b/test/parallel/test-vm-syntax-error-message.js @@ -1,5 +1,5 @@ 'use strict'; -require('../common'); +const common = require('../common'); const assert = require('assert'); const child_process = require('child_process'); @@ -11,16 +11,12 @@ const p = child_process.spawn(process.execPath, [ 'catch (e) { console.log(e.message); }' ]); -p.stderr.on('data', function(data) { - assert.fail(`Unexpected stderr data: ${data}`); -}); +p.stderr.on('data', common.mustNotCall()); let output = ''; -p.stdout.on('data', function(data) { - output += data; -}); +p.stdout.on('data', (data) => output += data); -process.on('exit', function() { +p.stdout.on('end', common.mustCall(() => { assert.strictEqual(output.replace(/[\r\n]+/g, ''), 'boo'); -}); +})); diff --git a/test/parallel/test-vm-syntax-error-stderr.js b/test/parallel/test-vm-syntax-error-stderr.js index 805bea0f874065..e40edc907c6639 100644 --- a/test/parallel/test-vm-syntax-error-stderr.js +++ b/test/parallel/test-vm-syntax-error-stderr.js @@ -12,18 +12,14 @@ const p = child_process.spawn(process.execPath, [ wrong_script ]); -p.stdout.on('data', function(data) { - assert.fail(`Unexpected stdout data: ${data}`); -}); +p.stdout.on('data', common.mustNotCall()); let output = ''; -p.stderr.on('data', function(data) { - output += data; -}); +p.stderr.on('data', (data) => output += data); -process.on('exit', function() { +p.stderr.on('end', common.mustCall(() => { assert(/BEGIN CERT/.test(output)); assert(/^\s+\^/m.test(output)); assert(/Invalid left-hand side expression in prefix operation/.test(output)); -}); +})); From c76ec7130e14471daad265c200b64b7767698392 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 24 Jul 2017 11:21:48 -0700 Subject: [PATCH 003/231] test: improve multiple zlib tests PR-URL: https://github.com/nodejs/node/pull/14455 Reviewed-By: Anna Henningsen --- ...st-zlib-from-gzip-with-trailing-garbage.js | 8 +- test/parallel/test-zlib-from-gzip.js | 4 +- test/parallel/test-zlib-from-string.js | 22 +- test/parallel/test-zlib-invalid-input.js | 48 ++-- test/parallel/test-zlib-random-byte-pipes.js | 192 +++++++--------- test/parallel/test-zlib-sync-no-event.js | 10 +- test/parallel/test-zlib-write-after-flush.js | 25 +- test/parallel/test-zlib.js | 217 ++++++++---------- 8 files changed, 231 insertions(+), 295 deletions(-) diff --git a/test/parallel/test-zlib-from-gzip-with-trailing-garbage.js b/test/parallel/test-zlib-from-gzip-with-trailing-garbage.js index 0cc4953d41c514..e5588e75f2f183 100644 --- a/test/parallel/test-zlib-from-gzip-with-trailing-garbage.js +++ b/test/parallel/test-zlib-from-gzip-with-trailing-garbage.js @@ -38,9 +38,11 @@ assert.throws( ); zlib.gunzip(data, common.mustCall((err, result) => { - assert(err instanceof Error); - assert.strictEqual(err.code, 'Z_DATA_ERROR'); - assert.strictEqual(err.message, 'unknown compression method'); + common.expectsError({ + code: 'Z_DATA_ERROR', + type: Error, + message: 'unknown compression method' + })(err); assert.strictEqual(result, undefined); })); diff --git a/test/parallel/test-zlib-from-gzip.js b/test/parallel/test-zlib-from-gzip.js index a69df87a97f90b..98a581f01e4c98 100644 --- a/test/parallel/test-zlib-from-gzip.js +++ b/test/parallel/test-zlib-from-gzip.js @@ -42,10 +42,10 @@ const inp = fs.createReadStream(fixture); const out = fs.createWriteStream(outputFile); inp.pipe(gunzip).pipe(out); -out.on('close', function() { +out.on('close', common.mustCall(() => { const actual = fs.readFileSync(outputFile); assert.strictEqual(actual.length, expect.length, 'length should match'); for (let i = 0, l = actual.length; i < l; i++) { assert.strictEqual(actual[i], expect[i], `byte[${i}]`); } -}); +})); diff --git a/test/parallel/test-zlib-from-string.js b/test/parallel/test-zlib-from-string.js index a17f2bb728bcba..f393a3d67974c1 100644 --- a/test/parallel/test-zlib-from-string.js +++ b/test/parallel/test-zlib-from-string.js @@ -22,7 +22,7 @@ 'use strict'; // test compressing and uncompressing a string with zlib -require('../common'); +const common = require('../common'); const assert = require('assert'); const zlib = require('zlib'); @@ -54,32 +54,32 @@ const expectedBase64Gzip = 'H4sIAAAAAAAAA11RS05DMQy8yhzg6d2BPSAkJPZu4laWkjiN4' + 'mHo33kJO8xfkckmLjE5XMKBQ4gxIsfvCZ44doUThF2mcZq8q2' + 'sHnHNzRtagj5AQAA'; -zlib.deflate(inputString, function(err, buffer) { +zlib.deflate(inputString, common.mustCall((err, buffer) => { assert.strictEqual(buffer.toString('base64'), expectedBase64Deflate, 'deflate encoded string should match'); -}); +})); -zlib.gzip(inputString, function(err, buffer) { +zlib.gzip(inputString, common.mustCall((err, buffer) => { // Can't actually guarantee that we'll get exactly the same // deflated bytes when we compress a string, since the header // depends on stuff other than the input string itself. // However, decrypting it should definitely yield the same // result that we're expecting, and this should match what we get // from inflating the known valid deflate data. - zlib.gunzip(buffer, function(err, gunzipped) { + zlib.gunzip(buffer, common.mustCall((err, gunzipped) => { assert.strictEqual(gunzipped.toString(), inputString, 'Should get original string after gzip/gunzip'); - }); -}); + })); +})); let buffer = Buffer.from(expectedBase64Deflate, 'base64'); -zlib.unzip(buffer, function(err, buffer) { +zlib.unzip(buffer, common.mustCall((err, buffer) => { assert.strictEqual(buffer.toString(), inputString, 'decoded inflated string should match'); -}); +})); buffer = Buffer.from(expectedBase64Gzip, 'base64'); -zlib.unzip(buffer, function(err, buffer) { +zlib.unzip(buffer, common.mustCall((err, buffer) => { assert.strictEqual(buffer.toString(), inputString, 'decoded gunzipped string should match'); -}); +})); diff --git a/test/parallel/test-zlib-invalid-input.js b/test/parallel/test-zlib-invalid-input.js index f6d3a85b957662..758e489ca078a9 100644 --- a/test/parallel/test-zlib-invalid-input.js +++ b/test/parallel/test-zlib-invalid-input.js @@ -22,14 +22,26 @@ 'use strict'; // test uncompressing invalid input -require('../common'); +const common = require('../common'); const assert = require('assert'); const zlib = require('zlib'); -const nonStringInputs = [1, true, {a: 1}, ['a']]; +const nonStringInputs = [ + 1, + true, + { a: 1 }, + ['a'] +]; -console.error('Doing the non-strings'); -nonStringInputs.forEach(function(input) { +// zlib.Unzip classes need to get valid data, or else they'll throw. +const unzips = [ + zlib.Unzip(), + zlib.Gunzip(), + zlib.Inflate(), + zlib.InflateRaw() +]; + +nonStringInputs.forEach(common.mustCall((input) => { // zlib.gunzip should not throw an error when called with bad input. assert.doesNotThrow(function() { zlib.gunzip(input, function(err, buffer) { @@ -37,30 +49,12 @@ nonStringInputs.forEach(function(input) { assert.ok(err); }); }); -}); - -console.error('Doing the unzips'); -// zlib.Unzip classes need to get valid data, or else they'll throw. -const unzips = [ zlib.Unzip(), - zlib.Gunzip(), - zlib.Inflate(), - zlib.InflateRaw() ]; -const hadError = []; -unzips.forEach(function(uz, i) { - console.error(`Error for ${uz.constructor.name}`); - uz.on('error', function(er) { - console.error('Error event', er); - hadError[i] = true; - }); +}, nonStringInputs.length)); - uz.on('end', function(er) { - throw new Error(`end event should not be emitted ${uz.constructor.name}`); - }); +unzips.forEach(common.mustCall((uz, i) => { + uz.on('error', common.mustCall()); + uz.on('end', common.mustNotCall); // this will trigger error event uz.write('this is not valid compressed data.'); -}); - -process.on('exit', function() { - assert.deepStrictEqual(hadError, [true, true, true, true], 'expect 4 errors'); -}); +}, unzips.length)); diff --git a/test/parallel/test-zlib-random-byte-pipes.js b/test/parallel/test-zlib-random-byte-pipes.js index f15b31c2f42c96..5eb048101cf513 100644 --- a/test/parallel/test-zlib-random-byte-pipes.js +++ b/test/parallel/test-zlib-random-byte-pipes.js @@ -27,124 +27,119 @@ if (!common.hasCrypto) const assert = require('assert'); const crypto = require('crypto'); const stream = require('stream'); -const util = require('util'); const zlib = require('zlib'); const Stream = stream.Stream; // emit random bytes, and keep a shasum -function RandomReadStream(opt) { - Stream.call(this); +class RandomReadStream extends Stream { + constructor(opt) { + super(); - this.readable = true; - this._paused = false; - this._processing = false; - - this._hasher = crypto.createHash('sha1'); - opt = opt || {}; - - // base block size. - opt.block = opt.block || 256 * 1024; + this.readable = true; + this._paused = false; + this._processing = false; - // total number of bytes to emit - opt.total = opt.total || 256 * 1024 * 1024; - this._remaining = opt.total; + this._hasher = crypto.createHash('sha1'); + opt = opt || {}; - // how variable to make the block sizes - opt.jitter = opt.jitter || 1024; + // base block size. + opt.block = opt.block || 256 * 1024; - this._opt = opt; + // total number of bytes to emit + opt.total = opt.total || 256 * 1024 * 1024; + this._remaining = opt.total; - this._process = this._process.bind(this); + // how variable to make the block sizes + opt.jitter = opt.jitter || 1024; - process.nextTick(this._process); -} + this._opt = opt; -util.inherits(RandomReadStream, Stream); + this._process = this._process.bind(this); -RandomReadStream.prototype.pause = function() { - this._paused = true; - this.emit('pause'); -}; + process.nextTick(this._process); + } -RandomReadStream.prototype.resume = function() { - // console.error("rrs resume"); - this._paused = false; - this.emit('resume'); - this._process(); -}; + pause() { + this._paused = true; + this.emit('pause'); + } -RandomReadStream.prototype._process = function() { - if (this._processing) return; - if (this._paused) return; + resume() { + // console.error("rrs resume"); + this._paused = false; + this.emit('resume'); + this._process(); + } - this._processing = true; + _process() { + if (this._processing) return; + if (this._paused) return; - if (!this._remaining) { - this._hash = this._hasher.digest('hex').toLowerCase().trim(); - this._processing = false; + this._processing = true; - this.emit('end'); - return; - } + if (!this._remaining) { + this._hash = this._hasher.digest('hex').toLowerCase().trim(); + this._processing = false; - // figure out how many bytes to output - // if finished, then just emit end. - let block = this._opt.block; - const jitter = this._opt.jitter; - if (jitter) { - block += Math.ceil(Math.random() * jitter - (jitter / 2)); - } - block = Math.min(block, this._remaining); - const buf = Buffer.allocUnsafe(block); - for (let i = 0; i < block; i++) { - buf[i] = Math.random() * 256; - } + this.emit('end'); + return; + } - this._hasher.update(buf); + // figure out how many bytes to output + // if finished, then just emit end. + let block = this._opt.block; + const jitter = this._opt.jitter; + if (jitter) { + block += Math.ceil(Math.random() * jitter - (jitter / 2)); + } + block = Math.min(block, this._remaining); + const buf = Buffer.allocUnsafe(block); + for (let i = 0; i < block; i++) { + buf[i] = Math.random() * 256; + } - this._remaining -= block; + this._hasher.update(buf); - console.error('block=%d\nremain=%d\n', block, this._remaining); - this._processing = false; + this._remaining -= block; - this.emit('data', buf); - process.nextTick(this._process); -}; + this._processing = false; + this.emit('data', buf); + process.nextTick(this._process); + } +} // a filter that just verifies a shasum -function HashStream() { - Stream.call(this); +class HashStream extends Stream { + constructor() { + super(); + this.readable = this.writable = true; + this._hasher = crypto.createHash('sha1'); + } - this.readable = this.writable = true; - this._hasher = crypto.createHash('sha1'); -} + write(c) { + // Simulate the way that an fs.ReadStream returns false + // on *every* write, only to resume a moment later. + this._hasher.update(c); + process.nextTick(() => this.resume()); + return false; + } + + resume() { + this.emit('resume'); + process.nextTick(() => this.emit('drain')); + } -util.inherits(HashStream, Stream); - -HashStream.prototype.write = function(c) { - // Simulate the way that an fs.ReadStream returns false - // on *every* write like a jerk, only to resume a - // moment later. - this._hasher.update(c); - process.nextTick(this.resume.bind(this)); - return false; -}; - -HashStream.prototype.resume = function() { - this.emit('resume'); - process.nextTick(this.emit.bind(this, 'drain')); -}; - -HashStream.prototype.end = function(c) { - if (c) { - this.write(c); + end(c) { + if (c) { + this.write(c); + } + this._hash = this._hasher.digest('hex').toLowerCase().trim(); + this.emit('data', this._hash); + this.emit('end'); } - this._hash = this._hasher.digest('hex').toLowerCase().trim(); - this.emit('data', this._hash); - this.emit('end'); -}; +} const inp = new RandomReadStream({ total: 1024, block: 256, jitter: 16 }); @@ -154,23 +149,6 @@ const gunz = zlib.createGunzip(); inp.pipe(gzip).pipe(gunz).pipe(out); -inp.on('data', function(c) { - console.error('inp data', c.length); -}); - -gzip.on('data', function(c) { - console.error('gzip data', c.length); -}); - -gunz.on('data', function(c) { - console.error('gunz data', c.length); -}); - -out.on('data', function(c) { - console.error('out data', c.length); -}); - -out.on('data', common.mustCall(function(c) { - console.error('hash=%s', c); +out.on('data', common.mustCall((c) => { assert.strictEqual(c, inp._hash, 'hashes should match'); })); diff --git a/test/parallel/test-zlib-sync-no-event.js b/test/parallel/test-zlib-sync-no-event.js index c6584b43d9ba1c..479aa1c411b096 100644 --- a/test/parallel/test-zlib-sync-no-event.js +++ b/test/parallel/test-zlib-sync-no-event.js @@ -1,20 +1,18 @@ 'use strict'; -require('../common'); +const common = require('../common'); const zlib = require('zlib'); const assert = require('assert'); -const shouldNotBeCalled = () => { throw new Error('unexpected event'); }; - const message = 'Come on, Fhqwhgads.'; +const buffer = Buffer.from(message); const zipper = new zlib.Gzip(); -zipper.on('close', shouldNotBeCalled); +zipper.on('close', common.mustNotCall); -const buffer = Buffer.from(message); const zipped = zipper._processChunk(buffer, zlib.constants.Z_FINISH); const unzipper = new zlib.Gunzip(); -unzipper.on('close', shouldNotBeCalled); +unzipper.on('close', common.mustNotCall); const unzipped = unzipper._processChunk(zipped, zlib.constants.Z_FINISH); assert.notStrictEqual(zipped.toString(), message); diff --git a/test/parallel/test-zlib-write-after-flush.js b/test/parallel/test-zlib-write-after-flush.js index 10eee39951be41..2ba6ba4550f942 100644 --- a/test/parallel/test-zlib-write-after-flush.js +++ b/test/parallel/test-zlib-write-after-flush.js @@ -20,7 +20,7 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; -require('../common'); +const common = require('../common'); const assert = require('assert'); const zlib = require('zlib'); @@ -32,23 +32,14 @@ gzip.pipe(gunz); let output = ''; const input = 'A line of data\n'; gunz.setEncoding('utf8'); -gunz.on('data', function(c) { - output += c; -}); - -process.on('exit', function() { +gunz.on('data', (c) => output += c); +gunz.on('end', common.mustCall(() => { assert.strictEqual(output, input); - - // Make sure that the flush flag was set back to normal assert.strictEqual(gzip._flushFlag, zlib.constants.Z_NO_FLUSH); - - console.log('ok'); -}); +})); // make sure that flush/write doesn't trigger an assert failure -gzip.flush(); write(); -function write() { - gzip.write(input); - gzip.end(); - gunz.read(0); -} +gzip.flush(); +gzip.write(input); +gzip.end(); +gunz.read(0); diff --git a/test/parallel/test-zlib.js b/test/parallel/test-zlib.js index 4dd0f41e648db2..2f358a117286bc 100644 --- a/test/parallel/test-zlib.js +++ b/test/parallel/test-zlib.js @@ -25,7 +25,6 @@ const assert = require('assert'); const zlib = require('zlib'); const path = require('path'); const fs = require('fs'); -const util = require('util'); const stream = require('stream'); let zlibPairs = [ @@ -69,105 +68,104 @@ if (process.env.FAST) { } const tests = {}; -testFiles.forEach(function(file) { +testFiles.forEach(common.mustCall((file) => { tests[file] = fs.readFileSync(path.resolve(common.fixturesDir, file)); -}); +}, testFiles.length)); // stream that saves everything -function BufferStream() { - this.chunks = []; - this.length = 0; - this.writable = true; - this.readable = true; +class BufferStream extends stream.Stream { + constructor() { + super(); + this.chunks = []; + this.length = 0; + this.writable = true; + this.readable = true; + } + + write(c) { + this.chunks.push(c); + this.length += c.length; + return true; + } + + end(c) { + if (c) this.write(c); + // flatten + const buf = Buffer.allocUnsafe(this.length); + let i = 0; + this.chunks.forEach((c) => { + c.copy(buf, i); + i += c.length; + }); + this.emit('data', buf); + this.emit('end'); + return true; + } } -util.inherits(BufferStream, stream.Stream); - -BufferStream.prototype.write = function(c) { - this.chunks.push(c); - this.length += c.length; - return true; -}; - -BufferStream.prototype.end = function(c) { - if (c) this.write(c); - // flatten - const buf = Buffer.allocUnsafe(this.length); - let i = 0; - this.chunks.forEach(function(c) { - c.copy(buf, i); - i += c.length; - }); - this.emit('data', buf); - this.emit('end'); - return true; -}; - - -function SlowStream(trickle) { - this.trickle = trickle; - this.offset = 0; - this.readable = this.writable = true; +class SlowStream extends stream.Stream { + constructor(trickle) { + super(); + this.trickle = trickle; + this.offset = 0; + this.readable = this.writable = true; + } + + write() { + throw new Error('not implemented, just call ss.end(chunk)'); + } + + pause() { + this.paused = true; + this.emit('pause'); + } + + resume() { + const emit = () => { + if (this.paused) return; + if (this.offset >= this.length) { + this.ended = true; + return this.emit('end'); + } + const end = Math.min(this.offset + this.trickle, this.length); + const c = this.chunk.slice(this.offset, end); + this.offset += c.length; + this.emit('data', c); + process.nextTick(emit); + }; + + if (this.ended) return; + this.emit('resume'); + if (!this.chunk) return; + this.paused = false; + emit(); + } + + end(chunk) { + // walk over the chunk in blocks. + this.chunk = chunk; + this.length = chunk.length; + this.resume(); + return this.ended; + } } -util.inherits(SlowStream, stream.Stream); - -SlowStream.prototype.write = function() { - throw new Error('not implemented, just call ss.end(chunk)'); -}; - -SlowStream.prototype.pause = function() { - this.paused = true; - this.emit('pause'); -}; - -SlowStream.prototype.resume = function() { - const emit = () => { - if (this.paused) return; - if (this.offset >= this.length) { - this.ended = true; - return this.emit('end'); - } - const end = Math.min(this.offset + this.trickle, this.length); - const c = this.chunk.slice(this.offset, end); - this.offset += c.length; - this.emit('data', c); - process.nextTick(emit); - }; - - if (this.ended) return; - this.emit('resume'); - if (!this.chunk) return; - this.paused = false; - emit(); -}; - -SlowStream.prototype.end = function(chunk) { - // walk over the chunk in blocks. - this.chunk = chunk; - this.length = chunk.length; - this.resume(); - return this.ended; -}; - // for each of the files, make sure that compressing and // decompressing results in the same data, for every combination // of the options set above. -let failures = 0; -let total = 0; -let done = 0; -Object.keys(tests).forEach(function(file) { +const testKeys = Object.keys(tests); +testKeys.forEach(common.mustCall((file) => { const test = tests[file]; - chunkSize.forEach(function(chunkSize) { - trickle.forEach(function(trickle) { - windowBits.forEach(function(windowBits) { - level.forEach(function(level) { - memLevel.forEach(function(memLevel) { - strategy.forEach(function(strategy) { - zlibPairs.forEach(function(pair) { + chunkSize.forEach(common.mustCall((chunkSize) => { + trickle.forEach(common.mustCall((trickle) => { + windowBits.forEach(common.mustCall((windowBits) => { + level.forEach(common.mustCall((level) => { + memLevel.forEach(common.mustCall((memLevel) => { + strategy.forEach(common.mustCall((strategy) => { + zlibPairs.forEach(common.mustCall((pair) => { const Def = pair[0]; const Inf = pair[1]; const opts = { level: level, @@ -175,57 +173,32 @@ Object.keys(tests).forEach(function(file) { memLevel: memLevel, strategy: strategy }; - total++; - const def = new Def(opts); const inf = new Inf(opts); const ss = new SlowStream(trickle); const buf = new BufferStream(); // verify that the same exact buffer comes out the other end. - buf.on('data', function(c) { + buf.on('data', common.mustCall((c) => { const msg = `${file} ${chunkSize} ${ JSON.stringify(opts)} ${Def.name} -> ${Inf.name}`; - let ok = true; - const testNum = ++done; let i; for (i = 0; i < Math.max(c.length, test.length); i++) { if (c[i] !== test[i]) { - ok = false; - failures++; + assert.fail(msg); break; } } - if (ok) { - console.log(`ok ${testNum} ${msg}`); - } else { - console.log(`not ok ${testNum} msg`); - console.log(' ...'); - console.log(` testfile: ${file}`); - console.log(` type: ${Def.name} -> ${Inf.name}`); - console.log(` position: ${i}`); - console.log(` options: ${JSON.stringify(opts)}`); - console.log(` expect: ${test[i]}`); - console.log(` actual: ${c[i]}`); - console.log(` chunkSize: ${chunkSize}`); - console.log(' ---'); - } - }); + })); // the magic happens here. ss.pipe(def).pipe(inf).pipe(buf); ss.end(test); - }); - }); - }); - }); - }); - }); - }); -}); - -process.on('exit', function(code) { - console.log(`1..${done}`); - assert.strictEqual(done, total, `${total - done} tests left unfinished`); - assert.strictEqual(failures, 0, 'some test failures'); -}); + }, zlibPairs.length)); + }, strategy.length)); + }, memLevel.length)); + }, level.length)); + }, windowBits.length)); + }, trickle.length)); + }, chunkSize.length)); +}, testKeys.length)); From 868b441f3e95a847a28f5990c4a0b623e4ae8e65 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 17 Jul 2017 15:33:46 -0700 Subject: [PATCH 004/231] test: begin normalizing fixtures use MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a new `../common/fixtures' module to begin normalizing `test/fixtures` use. Our test code is a bit inconsistent with regards to use of the fixtures directory. Some code uses `path.join()`, some code uses string concats, some other code uses template strings, etc. In mnay cases, significant duplication of code is seen when accessing fixture files, etc. This updates many (but by no means all) of the tests in the test suite to use the new consistent API. There are still many more to update, which would make an excelent Code-n-Learn exercise. PR-URL: https://github.com/nodejs/node/pull/14332 Reviewed-By: Anna Henningsen Reviewed-By: Gibson Fahnestock Reviewed-By: Colin Ihrig Reviewed-By: Tobias Nießen Reviewed-By: Michaël Zasso --- test/common/README.md | 31 +++++++++++++++++++ test/common/fixtures.js | 28 +++++++++++++++++ test/common/index.js | 4 ++- test/parallel/test-async-wrap-GH13045.js | 8 ++--- test/parallel/test-async-wrap-getasyncid.js | 8 +++-- test/parallel/test-child-process-detached.js | 7 ++--- test/parallel/test-child-process-execfile.js | 4 +-- test/parallel/test-child-process-exit-code.js | 7 ++--- .../parallel/test-child-process-fork-close.js | 3 +- ...child-process-fork-stdio-string-variant.js | 3 +- test/parallel/test-child-process-fork.js | 3 +- test/parallel/test-child-process-fork3.js | 5 +-- test/parallel/test-child-process-ipc.js | 9 +++--- .../test-child-process-send-after-close.js | 5 +-- ...test-child-process-send-returns-boolean.js | 4 +-- .../test-child-process-spawn-typeerror.js | 9 +++--- .../test-child-process-stdout-flush.js | 5 +-- test/parallel/test-cli-eval.js | 3 +- test/parallel/test-cli-syntax.js | 10 +++--- test/parallel/test-crypto-binary-default.js | 25 +++++++-------- test/parallel/test-crypto-certificate.js | 9 +++--- test/parallel/test-crypto-fips.js | 7 +++-- test/parallel/test-crypto-hash.js | 4 +-- test/parallel/test-crypto-rsa-dsa.js | 29 +++++++++-------- test/parallel/test-crypto-sign-verify.js | 15 ++++----- test/parallel/test-crypto-verify-failure.js | 9 +++--- test/parallel/test-crypto.js | 14 ++++----- test/parallel/test-cwd-enoent-preload.js | 3 +- test/parallel/test-delayed-require.js | 4 +-- test/parallel/test-fs-empty-readStream.js | 4 +-- test/parallel/test-fs-fsync.js | 4 +-- test/parallel/test-fs-read-file-sync.js | 6 ++-- test/parallel/test-fs-read-stream-encoding.js | 6 ++-- test/parallel/test-fs-read-stream-fd-leak.js | 6 ++-- test/parallel/test-fs-read-stream.js | 6 ++-- test/parallel/test-fs-read-type.js | 5 +-- test/parallel/test-fs-readfile-empty.js | 7 +++-- test/parallel/test-fs-readfile-error.js | 4 +-- test/parallel/test-fs-readfile-unlink.js | 6 ++-- test/parallel/test-require-dot.js | 9 +++--- test/parallel/test-require-extensions-main.js | 7 +++-- ...ons-same-filename-as-dir-trailing-slash.js | 12 +++++-- ...require-extensions-same-filename-as-dir.js | 16 +++------- test/parallel/test-require-json.js | 6 ++-- test/parallel/test-require-symlink.js | 22 +++++++------ test/parallel/test-signal-unregister.js | 3 +- test/parallel/test-stdio-closed.js | 4 +-- test/parallel/test-stdout-close-catch.js | 4 +-- test/parallel/test-stdout-to-file.js | 6 ++-- test/parallel/test-stream-preprocess.js | 23 +++++--------- test/parallel/test-sync-fileread.js | 8 ++--- test/parallel/test-tls-0-dns-altname.js | 6 ++-- test/parallel/test-tls-addca.js | 6 ++-- test/parallel/test-tls-alert-handling.js | 9 ++---- test/parallel/test-tls-alert.js | 9 ++---- test/parallel/test-tls-alpn-server-client.js | 9 ++---- test/parallel/test-tls-ca-concat.js | 6 ++-- test/parallel/test-tls-cert-chains-concat.js | 6 ++-- test/parallel/test-tls-cert-chains-in-ca.js | 6 ++-- test/parallel/test-tls-client-abort.js | 7 ++--- test/parallel/test-tls-client-destroy-soon.js | 6 ++-- test/parallel/test-tls-client-mindhsize.js | 13 ++++---- test/parallel/test-tls-client-reject.js | 9 +++--- test/parallel/test-tls-client-verify.js | 9 ++---- test/parallel/test-tls-close-error.js | 6 ++-- test/parallel/test-tls-close-notify.js | 6 ++-- test/parallel/test-tls-cnnic-whitelist.js | 9 ++---- test/parallel/test-tls-connect-pipe.js | 7 ++--- test/parallel/test-tls-connect-simple.js | 7 ++--- .../test-tls-connect-stream-writes.js | 9 +++--- .../parallel/test-tls-delayed-attach-error.js | 6 ++-- test/parallel/test-tls-dhe.js | 13 ++++---- test/parallel/test-tls-env-extra-ca.js | 10 +++--- test/parallel/test-tls-no-rsa-key.js | 6 ++-- test/parallel/test-tls-no-sslv3.js | 6 ++-- test/parallel/test-tls-npn-server-client.js | 9 ++---- test/parallel/test-tls-ocsp-callback.js | 18 +++++------ test/parallel/test-tls-on-empty-socket.js | 7 ++--- test/parallel/test-tls-over-http-tunnel.js | 6 ++-- test/parallel/test-tls-passphrase.js | 9 +++--- test/parallel/test-tls-pause.js | 7 ++--- .../test-tls-peer-certificate-encoding.js | 9 +++--- .../test-tls-peer-certificate-multi-keys.js | 7 ++--- test/parallel/test-tls-peer-certificate.js | 6 ++-- test/parallel/test-tls-pfx-gh-5100-regr.js | 6 ++-- test/parallel/test-tls-regr-gh-5108.js | 6 ++-- test/parallel/test-tls-request-timeout.js | 6 ++-- .../test-tls-retain-handle-no-abort.js | 6 ++-- test/parallel/test-tls-securepair-fiftharg.js | 8 ++--- test/parallel/test-tls-securepair-server.js | 7 ++--- .../test-tls-server-connection-server.js | 6 ++-- test/parallel/test-tls-server-verify.js | 9 ++---- test/parallel/test-tls-session-cache.js | 25 ++++++--------- test/parallel/test-tls-set-ciphers.js | 6 ++-- test/parallel/test-tls-set-encoding.js | 6 ++-- test/parallel/test-tls-sni-option.js | 9 ++---- test/parallel/test-tls-sni-server-client.js | 8 ++--- test/parallel/test-tls-socket-close.js | 6 ++-- .../test-tls-socket-default-options.js | 4 +-- test/parallel/test-tls-socket-destroy.js | 7 +++-- .../test-tls-startcom-wosign-whitelist.js | 9 ++---- test/parallel/test-tls-starttls-server.js | 6 ++-- test/parallel/test-tls-ticket-cluster.js | 15 +++------ test/parallel/test-tls-ticket.js | 6 ++-- test/parallel/test-tls-timeout-server-2.js | 6 ++-- test/parallel/test-tls-timeout-server.js | 6 ++-- test/parallel/test-tls-two-cas-one-string.js | 12 +++---- test/parallel/test-tls-wrap-timeout.js | 7 ++--- test/parallel/test-tls-zero-clear-in.js | 8 ++--- test/parallel/test-util-callbackify.js | 8 ++--- test/parallel/test-util-internal.js | 6 ++-- test/parallel/test-vm-debug-context.js | 7 +++-- test/parallel/test-vm-syntax-error-stderr.js | 4 +-- test/parallel/test-whatwg-url-constructor.js | 4 +-- test/parallel/test-whatwg-url-origin.js | 4 +-- test/parallel/test-whatwg-url-parsing.js | 8 ++--- test/parallel/test-whatwg-url-searchparams.js | 4 +-- test/parallel/test-whatwg-url-setters.js | 9 +++--- test/parallel/test-whatwg-url-toascii.js | 4 +-- test/parallel/test-zlib-flush.js | 7 ++--- .../test-zlib-from-concatenated-gzip.js | 6 ++-- test/parallel/test-zlib-from-gzip.js | 5 +-- test/parallel/test-zlib-params.js | 7 ++--- test/parallel/test-zlib.js | 5 ++- test/sequential/test-module-loading.js | 4 ++- 125 files changed, 497 insertions(+), 508 deletions(-) create mode 100644 test/common/fixtures.js diff --git a/test/common/README.md b/test/common/README.md index 1ec603a3bd8a02..aa3fcb3d4a41b5 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -374,6 +374,37 @@ Decrements the `Countdown` counter. Specifies the remaining number of times `Countdown.prototype.dec()` must be called before the callback is invoked. +## Fixtures Module + +The `common/fixtures` module provides convenience methods for working with +files in the `test/fixtures` directory. + +### fixtures.fixturesDir + +* [<String>] + +The absolute path to the `test/fixtures/` directory. + +### fixtures.path(...args) + +* `...args` [<String>] + +Returns the result of `path.join(fixtures.fixturesDir, ...args)`. + +### fixtures.readSync(args[, enc]) + +* `args` [<String>] | [<Array>] + +Returns the result of +`fs.readFileSync(path.join(fixtures.fixturesDir, ...args), 'enc')`. + +### fixtures.readKey(arg[, enc]) + +* `arg` [<String>] + +Returns the result of +`fs.readFileSync(path.join(fixtures.fixturesDir, 'keys', arg), 'enc')`. + ## WPT Module The wpt.js module is a port of parts of diff --git a/test/common/fixtures.js b/test/common/fixtures.js new file mode 100644 index 00000000000000..b4b7c042e805a9 --- /dev/null +++ b/test/common/fixtures.js @@ -0,0 +1,28 @@ +/* eslint-disable required-modules */ +'use strict'; + +const path = require('path'); +const fs = require('fs'); + +const fixturesDir = path.join(__dirname, '..', 'fixtures'); + +function fixturesPath(...args) { + return path.join(fixturesDir, ...args); +} + +function readFixtureSync(args, enc) { + if (Array.isArray(args)) + return fs.readFileSync(fixturesPath(...args), enc); + return fs.readFileSync(fixturesPath(args), enc); +} + +function readFixtureKey(name, enc) { + return fs.readFileSync(fixturesPath('keys', name), enc); +} + +module.exports = { + fixturesDir, + path: fixturesPath, + readSync: readFixtureSync, + readKey: readFixtureKey +}; diff --git a/test/common/index.js b/test/common/index.js index a5ca4cec576e74..9358f86f65f14d 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -29,13 +29,15 @@ const { exec, execSync, spawn, spawnSync } = require('child_process'); const stream = require('stream'); const util = require('util'); const Timer = process.binding('timer_wrap').Timer; +const { fixturesDir } = require('./fixtures'); const testRoot = process.env.NODE_TEST_DIR ? fs.realpathSync(process.env.NODE_TEST_DIR) : path.resolve(__dirname, '..'); const noop = () => {}; -exports.fixturesDir = path.join(__dirname, '..', 'fixtures'); +exports.fixturesDir = fixturesDir; + exports.tmpDirName = 'tmp'; // PORT should match the definition in test/testpy/__init__.py. exports.PORT = +process.env.NODE_COMMON_PORT || 12346; diff --git a/test/parallel/test-async-wrap-GH13045.js b/test/parallel/test-async-wrap-GH13045.js index 1382de8060813a..bb4e1a0c411666 100644 --- a/test/parallel/test-async-wrap-GH13045.js +++ b/test/parallel/test-async-wrap-GH13045.js @@ -8,12 +8,12 @@ if (!common.hasCrypto) const assert = require('assert'); const https = require('https'); -const fs = require('fs'); +const fixtures = require('../common/fixtures'); const serverOptions = { - key: fs.readFileSync(`${common.fixturesDir}/keys/agent1-key.pem`), - cert: fs.readFileSync(`${common.fixturesDir}/keys/agent1-cert.pem`), - ca: fs.readFileSync(`${common.fixturesDir}/keys/ca1-cert.pem`) + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem'), + ca: fixtures.readKey('ca1-cert.pem') }; const server = https.createServer(serverOptions, common.mustCall((req, res) => { diff --git a/test/parallel/test-async-wrap-getasyncid.js b/test/parallel/test-async-wrap-getasyncid.js index ce51408a6b678b..4c1ea8e212e7ef 100644 --- a/test/parallel/test-async-wrap-getasyncid.js +++ b/test/parallel/test-async-wrap-getasyncid.js @@ -5,6 +5,7 @@ const assert = require('assert'); const fs = require('fs'); const net = require('net'); const providers = Object.assign({}, process.binding('async_wrap').Providers); +const fixtures = require('../common/fixtures'); // Make sure that all Providers are tested. { @@ -218,9 +219,10 @@ if (common.hasCrypto) { const TCP = process.binding('tcp_wrap').TCP; const tcp = new TCP(); - const ca = fs.readFileSync(common.fixturesDir + '/test_ca.pem', 'ascii'); - const cert = fs.readFileSync(common.fixturesDir + '/test_cert.pem', 'ascii'); - const key = fs.readFileSync(common.fixturesDir + '/test_key.pem', 'ascii'); + const ca = fixtures.readSync('test_ca.pem', 'ascii'); + const cert = fixtures.readSync('test_cert.pem', 'ascii'); + const key = fixtures.readSync('test_key.pem', 'ascii'); + const credentials = require('tls').createSecureContext({ ca, cert, key }); // TLSWrap is exposed, but needs to be instantiated via tls_wrap.wrap(). diff --git a/test/parallel/test-child-process-detached.js b/test/parallel/test-child-process-detached.js index bd778543aac7e2..f53983c29e791c 100644 --- a/test/parallel/test-child-process-detached.js +++ b/test/parallel/test-child-process-detached.js @@ -20,13 +20,12 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; -const common = require('../common'); +require('../common'); const assert = require('assert'); -const path = require('path'); +const fixtures = require('../common/fixtures'); const spawn = require('child_process').spawn; -const childPath = path.join(common.fixturesDir, - 'parent-process-nonpersistent.js'); +const childPath = fixtures.path('parent-process-nonpersistent.js'); let persistentPid = -1; const child = spawn(process.execPath, [ childPath ]); diff --git a/test/parallel/test-child-process-execfile.js b/test/parallel/test-child-process-execfile.js index f87a4eb1154f2a..62cc7f534dc86b 100644 --- a/test/parallel/test-child-process-execfile.js +++ b/test/parallel/test-child-process-execfile.js @@ -2,10 +2,10 @@ const common = require('../common'); const assert = require('assert'); const execFile = require('child_process').execFile; -const path = require('path'); const uv = process.binding('uv'); +const fixtures = require('../common/fixtures'); -const fixture = path.join(common.fixturesDir, 'exit.js'); +const fixture = fixtures.path('exit.js'); { execFile( diff --git a/test/parallel/test-child-process-exit-code.js b/test/parallel/test-child-process-exit-code.js index 1f2566be36efdd..7f5e54be42f69c 100644 --- a/test/parallel/test-child-process-exit-code.js +++ b/test/parallel/test-child-process-exit-code.js @@ -23,9 +23,9 @@ const common = require('../common'); const assert = require('assert'); const spawn = require('child_process').spawn; -const path = require('path'); +const fixtures = require('../common/fixtures'); -const exitScript = path.join(common.fixturesDir, 'exit.js'); +const exitScript = fixtures.path('exit.js'); const exitChild = spawn(process.argv[0], [exitScript, 23]); exitChild.on('exit', common.mustCall(function(code, signal) { assert.strictEqual(code, 23); @@ -33,8 +33,7 @@ exitChild.on('exit', common.mustCall(function(code, signal) { })); -const errorScript = path.join(common.fixturesDir, - 'child_process_should_emit_error.js'); +const errorScript = fixtures.path('child_process_should_emit_error.js'); const errorChild = spawn(process.argv[0], [errorScript]); errorChild.on('exit', common.mustCall(function(code, signal) { assert.ok(code !== 0); diff --git a/test/parallel/test-child-process-fork-close.js b/test/parallel/test-child-process-fork-close.js index d8e78b4d5bea39..bfaabd3d2f4e03 100644 --- a/test/parallel/test-child-process-fork-close.js +++ b/test/parallel/test-child-process-fork-close.js @@ -23,8 +23,9 @@ const common = require('../common'); const assert = require('assert'); const fork = require('child_process').fork; +const fixtures = require('../common/fixtures'); -const cp = fork(`${common.fixturesDir}/child-process-message-and-exit.js`); +const cp = fork(fixtures.path('child-process-message-and-exit.js')); let gotMessage = false; let gotExit = false; diff --git a/test/parallel/test-child-process-fork-stdio-string-variant.js b/test/parallel/test-child-process-fork-stdio-string-variant.js index 5c69850216a6b4..cb20ed8f70ca72 100644 --- a/test/parallel/test-child-process-fork-stdio-string-variant.js +++ b/test/parallel/test-child-process-fork-stdio-string-variant.js @@ -7,8 +7,9 @@ const common = require('../common'); const assert = require('assert'); const fork = require('child_process').fork; +const fixtures = require('../common/fixtures'); -const childScript = `${common.fixturesDir}/child-process-spawn-node`; +const childScript = fixtures.path('child-process-spawn-node'); const errorRegexp = /^TypeError: Incorrect value of stdio option:/; const malFormedOpts = {stdio: '33'}; const payload = {hello: 'world'}; diff --git a/test/parallel/test-child-process-fork.js b/test/parallel/test-child-process-fork.js index 72654f488ea1fd..04318207a3ea94 100644 --- a/test/parallel/test-child-process-fork.js +++ b/test/parallel/test-child-process-fork.js @@ -24,8 +24,9 @@ const common = require('../common'); const assert = require('assert'); const fork = require('child_process').fork; const args = ['foo', 'bar']; +const fixtures = require('../common/fixtures'); -const n = fork(`${common.fixturesDir}/child-process-spawn-node.js`, args); +const n = fork(fixtures.path('child-process-spawn-node.js'), args); assert.strictEqual(n.channel, n._channel); assert.deepStrictEqual(args, ['foo', 'bar']); diff --git a/test/parallel/test-child-process-fork3.js b/test/parallel/test-child-process-fork3.js index dab689da0cee9f..735a441950423b 100644 --- a/test/parallel/test-child-process-fork3.js +++ b/test/parallel/test-child-process-fork3.js @@ -20,7 +20,8 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; -const common = require('../common'); +require('../common'); const child_process = require('child_process'); +const fixtures = require('../common/fixtures'); -child_process.fork(`${common.fixturesDir}/empty.js`); // should not hang +child_process.fork(fixtures.path('empty.js')); // should not hang diff --git a/test/parallel/test-child-process-ipc.js b/test/parallel/test-child-process-ipc.js index b4a2a5e019e9de..36293cb73c1a7d 100644 --- a/test/parallel/test-child-process-ipc.js +++ b/test/parallel/test-child-process-ipc.js @@ -21,14 +21,13 @@ 'use strict'; -const common = require('../common'); +require('../common'); const assert = require('assert'); -const spawn = require('child_process').spawn; +const { spawn } = require('child_process'); +const fixtures = require('../common/fixtures'); -const path = require('path'); - -const sub = path.join(common.fixturesDir, 'echo.js'); +const sub = fixtures.path('echo.js'); let gotHelloWorld = false; let gotEcho = false; diff --git a/test/parallel/test-child-process-send-after-close.js b/test/parallel/test-child-process-send-after-close.js index 78cf3b6697ea1f..ed84f1464cb7f5 100644 --- a/test/parallel/test-child-process-send-after-close.js +++ b/test/parallel/test-child-process-send-after-close.js @@ -2,8 +2,9 @@ const common = require('../common'); const assert = require('assert'); const cp = require('child_process'); -const path = require('path'); -const fixture = path.join(common.fixturesDir, 'empty.js'); +const fixtures = require('../common/fixtures'); + +const fixture = fixtures.path('empty.js'); const child = cp.fork(fixture); child.on('close', common.mustCall((code, signal) => { diff --git a/test/parallel/test-child-process-send-returns-boolean.js b/test/parallel/test-child-process-send-returns-boolean.js index d986b633d4140b..e273d1c205c3ac 100644 --- a/test/parallel/test-child-process-send-returns-boolean.js +++ b/test/parallel/test-child-process-send-returns-boolean.js @@ -1,11 +1,11 @@ 'use strict'; const common = require('../common'); const assert = require('assert'); -const path = require('path'); const net = require('net'); const { fork, spawn } = require('child_process'); +const fixtures = require('../common/fixtures'); -const emptyFile = path.join(common.fixturesDir, 'empty.js'); +const emptyFile = fixtures.path('empty.js'); const n = fork(emptyFile); diff --git a/test/parallel/test-child-process-spawn-typeerror.js b/test/parallel/test-child-process-spawn-typeerror.js index 93b26107a72f48..d8ae4c758ce3a1 100644 --- a/test/parallel/test-child-process-spawn-typeerror.js +++ b/test/parallel/test-child-process-spawn-typeerror.js @@ -22,17 +22,16 @@ 'use strict'; const common = require('../common'); const assert = require('assert'); -const child_process = require('child_process'); -const spawn = child_process.spawn; -const fork = child_process.fork; -const execFile = child_process.execFile; +const { spawn, fork, execFile } = require('child_process'); +const fixtures = require('../common/fixtures'); const cmd = common.isWindows ? 'rundll32' : 'ls'; const invalidcmd = 'hopefully_you_dont_have_this_on_your_machine'; const invalidArgsMsg = /Incorrect value of args option/; const invalidOptionsMsg = /"options" argument must be an object/; const invalidFileMsg = /^TypeError: "file" argument must be a non-empty string$/; -const empty = `${common.fixturesDir}/empty.js`; + +const empty = fixtures.path('empty.js'); assert.throws(function() { const child = spawn(invalidcmd, 'this is not an array'); diff --git a/test/parallel/test-child-process-stdout-flush.js b/test/parallel/test-child-process-stdout-flush.js index 970a03edeea982..bc549fb6f311ec 100644 --- a/test/parallel/test-child-process-stdout-flush.js +++ b/test/parallel/test-child-process-stdout-flush.js @@ -22,9 +22,10 @@ 'use strict'; const common = require('../common'); const assert = require('assert'); -const path = require('path'); const spawn = require('child_process').spawn; -const sub = path.join(common.fixturesDir, 'print-chars.js'); +const fixtures = require('../common/fixtures'); + +const sub = fixtures.path('print-chars.js'); const n = 500000; diff --git a/test/parallel/test-cli-eval.js b/test/parallel/test-cli-eval.js index 7c7ced62fd51f0..0853d7a29b8ca0 100644 --- a/test/parallel/test-cli-eval.js +++ b/test/parallel/test-cli-eval.js @@ -31,6 +31,7 @@ const common = require('../common'); const assert = require('assert'); const child = require('child_process'); const path = require('path'); +const fixtures = require('../common/fixtures'); const nodejs = `"${process.execPath}"`; if (process.argv.length > 2) { @@ -138,7 +139,7 @@ child.exec(`${nodejs} --use-strict -p process.execArgv`, // Regression test for https://github.com/nodejs/node/issues/3574. { - const emptyFile = path.join(common.fixturesDir, 'empty.js'); + const emptyFile = fixtures.path('empty.js'); child.exec(`${nodejs} -e 'require("child_process").fork("${emptyFile}")'`, common.mustCall((err, stdout, stderr) => { diff --git a/test/parallel/test-cli-syntax.js b/test/parallel/test-cli-syntax.js index 8ccc3fe01736f7..256334f21fd75c 100644 --- a/test/parallel/test-cli-syntax.js +++ b/test/parallel/test-cli-syntax.js @@ -2,8 +2,8 @@ const common = require('../common'); const assert = require('assert'); -const {exec, spawnSync} = require('child_process'); -const path = require('path'); +const { exec, spawnSync } = require('child_process'); +const fixtures = require('../common/fixtures'); const node = process.execPath; @@ -24,7 +24,7 @@ const notFoundRE = /^Error: Cannot find module/m; 'syntax/good_syntax_shebang', 'syntax/illegal_if_not_wrapped.js' ].forEach(function(file) { - file = path.join(common.fixturesDir, file); + file = fixtures.path(file); // loop each possible option, `-c` or `--check` syntaxArgs.forEach(function(args) { @@ -46,7 +46,7 @@ const notFoundRE = /^Error: Cannot find module/m; 'syntax/bad_syntax_shebang.js', 'syntax/bad_syntax_shebang' ].forEach(function(file) { - file = path.join(common.fixturesDir, file); + file = fixtures.path(file); // loop each possible option, `-c` or `--check` syntaxArgs.forEach(function(args) { @@ -73,7 +73,7 @@ const notFoundRE = /^Error: Cannot find module/m; 'syntax/file_not_found.js', 'syntax/file_not_found' ].forEach(function(file) { - file = path.join(common.fixturesDir, file); + file = fixtures.path(file); // loop each possible option, `-c` or `--check` syntaxArgs.forEach(function(args) { diff --git a/test/parallel/test-crypto-binary-default.js b/test/parallel/test-crypto-binary-default.js index 5bb254057b5a7a..21a3233ad3bc43 100644 --- a/test/parallel/test-crypto-binary-default.js +++ b/test/parallel/test-crypto-binary-default.js @@ -32,19 +32,18 @@ if (!common.hasCrypto) const assert = require('assert'); const crypto = require('crypto'); const fs = require('fs'); -const path = require('path'); const tls = require('tls'); +const fixtures = require('../common/fixtures'); const DH_NOT_SUITABLE_GENERATOR = crypto.constants.DH_NOT_SUITABLE_GENERATOR; -const fixtDir = common.fixturesDir; crypto.DEFAULT_ENCODING = 'latin1'; // Test Certificates -const certPem = fs.readFileSync(`${fixtDir}/test_cert.pem`, 'ascii'); -const certPfx = fs.readFileSync(`${fixtDir}/test_cert.pfx`); -const keyPem = fs.readFileSync(`${fixtDir}/test_key.pem`, 'ascii'); -const rsaPubPem = fs.readFileSync(`${fixtDir}/test_rsa_pubkey.pem`, 'ascii'); -const rsaKeyPem = fs.readFileSync(`${fixtDir}/test_rsa_privkey.pem`, 'ascii'); +const certPem = fixtures.readSync('test_cert.pem', 'ascii'); +const certPfx = fixtures.readSync('test_cert.pfx'); +const keyPem = fixtures.readSync('test_key.pem', 'ascii'); +const rsaPubPem = fixtures.readSync('test_rsa_pubkey.pem', 'ascii'); +const rsaKeyPem = fixtures.readSync('test_rsa_privkey.pem', 'ascii'); // PFX tests assert.doesNotThrow(function() { @@ -405,7 +404,7 @@ const h2 = crypto.createHash('sha1').update('Test').update('123').digest('hex'); assert.strictEqual(h1, h2, 'multipled updates'); // Test hashing for binary files -const fn = path.join(fixtDir, 'sample.png'); +const fn = fixtures.path('sample.png'); const sha1Hash = crypto.createHash('sha1'); const fileStream = fs.createReadStream(fn); fileStream.on('data', function(data) { @@ -614,9 +613,8 @@ assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true); // Test RSA signing and verification // { - const privateKey = fs.readFileSync(`${fixtDir}/test_rsa_privkey_2.pem`); - - const publicKey = fs.readFileSync(`${fixtDir}/test_rsa_pubkey_2.pem`); + const privateKey = fixtures.readSync('test_rsa_privkey_2.pem'); + const publicKey = fixtures.readSync('test_rsa_pubkey_2.pem'); const input = 'I AM THE WALRUS'; @@ -644,9 +642,8 @@ assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true); // Test DSA signing and verification // { - const privateKey = fs.readFileSync(`${fixtDir}/test_dsa_privkey.pem`); - - const publicKey = fs.readFileSync(`${fixtDir}/test_dsa_pubkey.pem`); + const privateKey = fixtures.readSync('test_dsa_privkey.pem'); + const publicKey = fixtures.readSync('test_dsa_pubkey.pem'); const input = 'I AM THE WALRUS'; diff --git a/test/parallel/test-crypto-certificate.js b/test/parallel/test-crypto-certificate.js index 61d8ea8193d5bc..5af62b58bf5c95 100644 --- a/test/parallel/test-crypto-certificate.js +++ b/test/parallel/test-crypto-certificate.js @@ -26,15 +26,14 @@ if (!common.hasCrypto) const assert = require('assert'); const crypto = require('crypto'); +const fixtures = require('../common/fixtures'); crypto.DEFAULT_ENCODING = 'buffer'; -const fs = require('fs'); - // Test Certificates -const spkacValid = fs.readFileSync(`${common.fixturesDir}/spkac.valid`); -const spkacFail = fs.readFileSync(`${common.fixturesDir}/spkac.fail`); -const spkacPem = fs.readFileSync(`${common.fixturesDir}/spkac.pem`); +const spkacValid = fixtures.readSync('spkac.valid'); +const spkacFail = fixtures.readSync('spkac.fail'); +const spkacPem = fixtures.readSync('spkac.pem'); const certificate = new crypto.Certificate(); diff --git a/test/parallel/test-crypto-fips.js b/test/parallel/test-crypto-fips.js index 3bba62f4a5d7a2..d72918756a200b 100644 --- a/test/parallel/test-crypto-fips.js +++ b/test/parallel/test-crypto-fips.js @@ -6,13 +6,16 @@ if (!common.hasCrypto) const assert = require('assert'); const spawnSync = require('child_process').spawnSync; const path = require('path'); +const fixtures = require('../common/fixtures'); const FIPS_ENABLED = 1; const FIPS_DISABLED = 0; const FIPS_ERROR_STRING = 'Error: Cannot set FIPS mode'; const OPTION_ERROR_STRING = 'bad option'; -const CNF_FIPS_ON = path.join(common.fixturesDir, 'openssl_fips_enabled.cnf'); -const CNF_FIPS_OFF = path.join(common.fixturesDir, 'openssl_fips_disabled.cnf'); + +const CNF_FIPS_ON = fixtures.path('openssl_fips_enabled.cnf'); +const CNF_FIPS_OFF = fixtures.path('openssl_fips_disabled.cnf'); + let num_children_ok = 0; function compiledWithFips() { diff --git a/test/parallel/test-crypto-hash.js b/test/parallel/test-crypto-hash.js index 1d36753738e95f..0cab2a9ef1e7c1 100644 --- a/test/parallel/test-crypto-hash.js +++ b/test/parallel/test-crypto-hash.js @@ -5,8 +5,8 @@ if (!common.hasCrypto) const assert = require('assert'); const fs = require('fs'); -const path = require('path'); const crypto = require('crypto'); +const fixtures = require('../common/fixtures'); // Test hashing const a1 = crypto.createHash('sha1').update('Test123').digest('hex'); @@ -74,7 +74,7 @@ const h2 = crypto.createHash('sha1').update('Test').update('123').digest('hex'); assert.strictEqual(h1, h2, 'multipled updates'); // Test hashing for binary files -const fn = path.join(common.fixturesDir, 'sample.png'); +const fn = fixtures.path('sample.png'); const sha1Hash = crypto.createHash('sha1'); const fileStream = fs.createReadStream(fn); fileStream.on('data', function(data) { diff --git a/test/parallel/test-crypto-rsa-dsa.js b/test/parallel/test-crypto-rsa-dsa.js index 813df587325ba5..94aa380e829d34 100644 --- a/test/parallel/test-crypto-rsa-dsa.js +++ b/test/parallel/test-crypto-rsa-dsa.js @@ -4,23 +4,23 @@ if (!common.hasCrypto) common.skip('missing crypto'); const assert = require('assert'); -const fs = require('fs'); const crypto = require('crypto'); const constants = crypto.constants; -const fixtDir = common.fixturesDir; + +const fixtures = require('../common/fixtures'); // Test certificates -const certPem = fs.readFileSync(`${fixtDir}/test_cert.pem`, 'ascii'); -const keyPem = fs.readFileSync(`${fixtDir}/test_key.pem`, 'ascii'); -const rsaPubPem = fs.readFileSync(`${fixtDir}/test_rsa_pubkey.pem`, 'ascii'); -const rsaKeyPem = fs.readFileSync(`${fixtDir}/test_rsa_privkey.pem`, 'ascii'); -const rsaKeyPemEncrypted = fs.readFileSync( - `${fixtDir}/test_rsa_privkey_encrypted.pem`, 'ascii'); -const dsaPubPem = fs.readFileSync(`${fixtDir}/test_dsa_pubkey.pem`, 'ascii'); -const dsaKeyPem = fs.readFileSync(`${fixtDir}/test_dsa_privkey.pem`, 'ascii'); -const dsaKeyPemEncrypted = fs.readFileSync( - `${fixtDir}/test_dsa_privkey_encrypted.pem`, 'ascii'); +const certPem = fixtures.readSync('test_cert.pem', 'ascii'); +const keyPem = fixtures.readSync('test_key.pem', 'ascii'); +const rsaPubPem = fixtures.readSync('test_rsa_pubkey.pem', 'ascii'); +const rsaKeyPem = fixtures.readSync('test_rsa_privkey.pem', 'ascii'); +const rsaKeyPemEncrypted = fixtures.readSync('test_rsa_privkey_encrypted.pem', + 'ascii'); +const dsaPubPem = fixtures.readSync('test_dsa_pubkey.pem', 'ascii'); +const dsaKeyPem = fixtures.readSync('test_dsa_privkey.pem', 'ascii'); +const dsaKeyPemEncrypted = fixtures.readSync('test_dsa_privkey_encrypted.pem', + 'ascii'); const decryptError = /^Error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt$/; @@ -174,9 +174,8 @@ assert.throws(() => { // Test RSA signing and verification // { - const privateKey = fs.readFileSync(`${fixtDir}/test_rsa_privkey_2.pem`); - - const publicKey = fs.readFileSync(`${fixtDir}/test_rsa_pubkey_2.pem`); + const privateKey = fixtures.readSync('test_rsa_privkey_2.pem'); + const publicKey = fixtures.readSync('test_rsa_pubkey_2.pem'); const input = 'I AM THE WALRUS'; diff --git a/test/parallel/test-crypto-sign-verify.js b/test/parallel/test-crypto-sign-verify.js index fa805a0a85d072..a24ce866624412 100644 --- a/test/parallel/test-crypto-sign-verify.js +++ b/test/parallel/test-crypto-sign-verify.js @@ -8,10 +8,11 @@ const fs = require('fs'); const path = require('path'); const exec = require('child_process').exec; const crypto = require('crypto'); +const fixtures = require('../common/fixtures'); // Test certificates -const certPem = fs.readFileSync(`${common.fixturesDir}/test_cert.pem`, 'ascii'); -const keyPem = fs.readFileSync(`${common.fixturesDir}/test_key.pem`, 'ascii'); +const certPem = fixtures.readSync('test_cert.pem', 'ascii'); +const keyPem = fixtures.readSync('test_key.pem', 'ascii'); const modSize = 1024; // Test signing and verifying @@ -185,10 +186,7 @@ const modSize = 1024; assert.strictEqual(verified, true, 'verify (PSS)'); } - const vectorfile = path.join(common.fixturesDir, 'pss-vectors.json'); - const examples = JSON.parse(fs.readFileSync(vectorfile, { - encoding: 'utf8' - })); + const examples = JSON.parse(fixtures.readSync('pss-vectors.json', 'utf8')); for (const key in examples) { const example = examples[key]; @@ -246,9 +244,8 @@ const modSize = 1024; if (!common.opensslCli) common.skip('node compiled without OpenSSL CLI.'); - const pubfile = path.join(common.fixturesDir, 'keys/rsa_public_2048.pem'); - const privfile = path.join(common.fixturesDir, 'keys/rsa_private_2048.pem'); - const privkey = fs.readFileSync(privfile); + const pubfile = fixtures.path('keys', 'rsa_public_2048.pem'); + const privkey = fixtures.readKey('rsa_private_2048.pem'); const msg = 'Test123'; const s5 = crypto.createSign('RSA-SHA256') diff --git a/test/parallel/test-crypto-verify-failure.js b/test/parallel/test-crypto-verify-failure.js index 4bd70f388d878f..0fbdb5b82608d9 100644 --- a/test/parallel/test-crypto-verify-failure.js +++ b/test/parallel/test-crypto-verify-failure.js @@ -27,16 +27,15 @@ if (!common.hasCrypto) const crypto = require('crypto'); const tls = require('tls'); +const fixtures = require('../common/fixtures'); crypto.DEFAULT_ENCODING = 'buffer'; -const fs = require('fs'); - -const certPem = fs.readFileSync(`${common.fixturesDir}/test_cert.pem`, 'ascii'); +const certPem = fixtures.readSync('test_cert.pem', 'ascii'); const options = { - key: fs.readFileSync(`${common.fixturesDir}/keys/agent1-key.pem`), - cert: fs.readFileSync(`${common.fixturesDir}/keys/agent1-cert.pem`) + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') }; const server = tls.Server(options, (socket) => { diff --git a/test/parallel/test-crypto.js b/test/parallel/test-crypto.js index d45de9979b96ec..7826d988704583 100644 --- a/test/parallel/test-crypto.js +++ b/test/parallel/test-crypto.js @@ -27,16 +27,16 @@ if (!common.hasCrypto) const assert = require('assert'); const crypto = require('crypto'); -const fs = require('fs'); const tls = require('tls'); +const fixtures = require('../common/fixtures'); crypto.DEFAULT_ENCODING = 'buffer'; // Test Certificates -const caPem = fs.readFileSync(`${common.fixturesDir}/test_ca.pem`, 'ascii'); -const certPem = fs.readFileSync(`${common.fixturesDir}/test_cert.pem`, 'ascii'); -const certPfx = fs.readFileSync(`${common.fixturesDir}/test_cert.pfx`); -const keyPem = fs.readFileSync(`${common.fixturesDir}/test_key.pem`, 'ascii'); +const caPem = fixtures.readSync('test_ca.pem', 'ascii'); +const certPem = fixtures.readSync('test_cert.pem', 'ascii'); +const certPfx = fixtures.readSync('test_cert.pfx'); +const keyPem = fixtures.readSync('test_key.pem', 'ascii'); // 'this' safety // https://github.com/joyent/node/issues/6690 @@ -176,8 +176,8 @@ assert.throws(function() { // $ openssl pkcs8 -topk8 -inform PEM -outform PEM -in mykey.pem \ // -out private_key.pem -nocrypt; // Then open private_key.pem and change its header and footer. - const sha1_privateKey = fs.readFileSync( - `${common.fixturesDir}/test_bad_rsa_privkey.pem`, 'ascii'); + const sha1_privateKey = fixtures.readSync('test_bad_rsa_privkey.pem', + 'ascii'); // this would inject errors onto OpenSSL's error stack crypto.createSign('sha1').sign(sha1_privateKey); }, /asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag/); diff --git a/test/parallel/test-cwd-enoent-preload.js b/test/parallel/test-cwd-enoent-preload.js index 5f22ee1e90bc9e..ec9f1fee754d9c 100644 --- a/test/parallel/test-cwd-enoent-preload.js +++ b/test/parallel/test-cwd-enoent-preload.js @@ -7,9 +7,10 @@ if (common.isSunOS || common.isWindows || common.isAIX) const assert = require('assert'); const fs = require('fs'); const spawn = require('child_process').spawn; +const fixtures = require('../common/fixtures'); const dirname = `${common.tmpDir}/cwd-does-not-exist-${process.pid}`; -const abspathFile = require('path').join(common.fixturesDir, 'a.js'); +const abspathFile = fixtures.path('a.js'); common.refreshTmpDir(); fs.mkdirSync(dirname); process.chdir(dirname); diff --git a/test/parallel/test-delayed-require.js b/test/parallel/test-delayed-require.js index f199ebf108e4f3..8aecb354698185 100644 --- a/test/parallel/test-delayed-require.js +++ b/test/parallel/test-delayed-require.js @@ -21,11 +21,11 @@ 'use strict'; const common = require('../common'); -const path = require('path'); const assert = require('assert'); +const fixtures = require('../common/fixtures'); setTimeout(common.mustCall(function() { - const a = require(path.join(common.fixturesDir, 'a')); + const a = require(fixtures.path('a')); assert.strictEqual(true, 'A' in a); assert.strictEqual('A', a.A()); assert.strictEqual('D', a.D()); diff --git a/test/parallel/test-fs-empty-readStream.js b/test/parallel/test-fs-empty-readStream.js index 73658d97579a09..a5de2ab80bcf10 100644 --- a/test/parallel/test-fs-empty-readStream.js +++ b/test/parallel/test-fs-empty-readStream.js @@ -22,10 +22,10 @@ 'use strict'; const common = require('../common'); const assert = require('assert'); -const path = require('path'); const fs = require('fs'); +const fixtures = require('../common/fixtures'); -const emptyFile = path.join(common.fixturesDir, 'empty.txt'); +const emptyFile = fixtures.path('empty.txt'); fs.open(emptyFile, 'r', common.mustCall((error, fd) => { diff --git a/test/parallel/test-fs-fsync.js b/test/parallel/test-fs-fsync.js index 0a4ebf08e4db39..006ec3fcc05cf6 100644 --- a/test/parallel/test-fs-fsync.js +++ b/test/parallel/test-fs-fsync.js @@ -22,11 +22,11 @@ 'use strict'; const common = require('../common'); const assert = require('assert'); +const fixtures = require('../common/fixtures'); -const path = require('path'); const fs = require('fs'); -const file = path.join(common.fixturesDir, 'a.js'); +const file = fixtures.path('a.js'); fs.open(file, 'a', 0o777, common.mustCall(function(err, fd) { assert.ifError(err); diff --git a/test/parallel/test-fs-read-file-sync.js b/test/parallel/test-fs-read-file-sync.js index 14e5db3827e43e..c8a9941b4990da 100644 --- a/test/parallel/test-fs-read-file-sync.js +++ b/test/parallel/test-fs-read-file-sync.js @@ -20,12 +20,12 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; -const common = require('../common'); +require('../common'); const assert = require('assert'); -const path = require('path'); const fs = require('fs'); +const fixtures = require('../common/fixtures'); -const fn = path.join(common.fixturesDir, 'elipses.txt'); +const fn = fixtures.path('elipses.txt'); const s = fs.readFileSync(fn, 'utf8'); for (let i = 0; i < s.length; i++) { diff --git a/test/parallel/test-fs-read-stream-encoding.js b/test/parallel/test-fs-read-stream-encoding.js index f8442ed548af5e..8eeaee6572bf72 100644 --- a/test/parallel/test-fs-read-stream-encoding.js +++ b/test/parallel/test-fs-read-stream-encoding.js @@ -1,12 +1,12 @@ 'use strict'; -const common = require('../common'); +require('../common'); const assert = require('assert'); const fs = require('fs'); -const path = require('path'); const stream = require('stream'); +const fixtures = require('../common/fixtures'); const encoding = 'base64'; -const example = path.join(common.fixturesDir, 'x.txt'); +const example = fixtures.path('x.txt'); const assertStream = new stream.Writable({ write: function(chunk, enc, next) { const expected = Buffer.from('xyz'); diff --git a/test/parallel/test-fs-read-stream-fd-leak.js b/test/parallel/test-fs-read-stream-fd-leak.js index e6594afefcd58a..28ec7b91b4c35b 100644 --- a/test/parallel/test-fs-read-stream-fd-leak.js +++ b/test/parallel/test-fs-read-stream-fd-leak.js @@ -1,9 +1,9 @@ 'use strict'; -const common = require('../common'); +require('../common'); const assert = require('assert'); const fs = require('fs'); -const path = require('path'); +const fixtures = require('../common/fixtures'); let openCount = 0; const _fsopen = fs.open; @@ -11,7 +11,7 @@ const _fsclose = fs.close; const loopCount = 50; const totalCheck = 50; -const emptyTxt = path.join(common.fixturesDir, 'empty.txt'); +const emptyTxt = fixtures.path('empty.txt'); fs.open = function() { openCount++; diff --git a/test/parallel/test-fs-read-stream.js b/test/parallel/test-fs-read-stream.js index 9ec397c31e5c3b..eb216d2a4daaf7 100644 --- a/test/parallel/test-fs-read-stream.js +++ b/test/parallel/test-fs-read-stream.js @@ -24,10 +24,10 @@ const common = require('../common'); const assert = require('assert'); const fs = require('fs'); -const path = require('path'); +const fixtures = require('../common/fixtures'); -const fn = path.join(common.fixturesDir, 'elipses.txt'); -const rangeFile = path.join(common.fixturesDir, 'x.txt'); +const fn = fixtures.path('elipses.txt'); +const rangeFile = fixtures.path('x.txt'); { let paused = false; diff --git a/test/parallel/test-fs-read-type.js b/test/parallel/test-fs-read-type.js index c6e3e53f054a63..0014affa1f7dde 100644 --- a/test/parallel/test-fs-read-type.js +++ b/test/parallel/test-fs-read-type.js @@ -1,9 +1,10 @@ 'use strict'; const common = require('../common'); const assert = require('assert'); -const path = require('path'); const fs = require('fs'); -const filepath = path.join(common.fixturesDir, 'x.txt'); +const fixtures = require('../common/fixtures'); + +const filepath = fixtures.path('x.txt'); const fd = fs.openSync(filepath, 'r'); const expected = 'xyz\n'; diff --git a/test/parallel/test-fs-readfile-empty.js b/test/parallel/test-fs-readfile-empty.js index ee2f3f722e1378..bbc2e9c81d6251 100644 --- a/test/parallel/test-fs-readfile-empty.js +++ b/test/parallel/test-fs-readfile-empty.js @@ -20,11 +20,12 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; -const common = require('../common'); +require('../common'); const assert = require('assert'); -const path = require('path'); const fs = require('fs'); -const fn = path.join(common.fixturesDir, 'empty.txt'); +const fixtures = require('../common/fixtures'); + +const fn = fixtures.path('empty.txt'); fs.readFile(fn, function(err, data) { assert.ok(data); diff --git a/test/parallel/test-fs-readfile-error.js b/test/parallel/test-fs-readfile-error.js index d9ad834afcdc29..aa6e6bdecb0b08 100644 --- a/test/parallel/test-fs-readfile-error.js +++ b/test/parallel/test-fs-readfile-error.js @@ -28,10 +28,10 @@ if (common.isFreeBSD) const assert = require('assert'); const exec = require('child_process').exec; -const path = require('path'); +const fixtures = require('../common/fixtures'); function test(env, cb) { - const filename = path.join(common.fixturesDir, 'test-fs-readfile-error.js'); + const filename = fixtures.path('test-fs-readfile-error.js'); const execPath = `"${process.execPath}" "${filename}"`; const options = { env: Object.assign(process.env, env) }; exec(execPath, options, common.mustCall((err, stdout, stderr) => { diff --git a/test/parallel/test-fs-readfile-unlink.js b/test/parallel/test-fs-readfile-unlink.js index 1098fb73743b97..9c931c5f4e5195 100644 --- a/test/parallel/test-fs-readfile-unlink.js +++ b/test/parallel/test-fs-readfile-unlink.js @@ -20,11 +20,13 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; -const common = require('../common'); +require('../common'); const assert = require('assert'); const fs = require('fs'); const path = require('path'); -const dirName = path.resolve(common.fixturesDir, 'test-readfile-unlink'); +const fixtures = require('../common/fixtures'); + +const dirName = fixtures.path('test-readfile-unlink'); const fileName = path.resolve(dirName, 'test.bin'); const buf = Buffer.alloc(512 * 1024, 42); diff --git a/test/parallel/test-require-dot.js b/test/parallel/test-require-dot.js index 04e978df56e200..e2202efec1df1a 100644 --- a/test/parallel/test-require-dot.js +++ b/test/parallel/test-require-dot.js @@ -1,15 +1,16 @@ 'use strict'; -const common = require('../common'); +require('../common'); const assert = require('assert'); const m = require('module'); +const fixtures = require('../common/fixtures'); -const a = require(`${common.fixturesDir}/module-require/relative/dot.js`); -const b = require(`${common.fixturesDir}/module-require/relative/dot-slash.js`); +const a = require(fixtures.path('module-require', 'relative', 'dot.js')); +const b = require(fixtures.path('module-require', 'relative', 'dot-slash.js')); assert.strictEqual(a.value, 42); assert.strictEqual(a, b, 'require(".") should resolve like require("./")'); -process.env.NODE_PATH = `${common.fixturesDir}/module-require/relative`; +process.env.NODE_PATH = fixtures.path('module-require', 'relative'); m._initPaths(); const c = require('.'); diff --git a/test/parallel/test-require-extensions-main.js b/test/parallel/test-require-extensions-main.js index cf3d2ee183f50d..40a857b8e63614 100644 --- a/test/parallel/test-require-extensions-main.js +++ b/test/parallel/test-require-extensions-main.js @@ -1,7 +1,10 @@ 'use strict'; +require('../common'); const assert = require('assert'); -const common = require('../common'); -const fixturesRequire = require(`${common.fixturesDir}/require-bin/bin/req.js`); +const fixtures = require('../common/fixtures'); + +const fixturesRequire = + require(fixtures.path('require-bin', 'bin', 'req.js')); assert.strictEqual( fixturesRequire, diff --git a/test/parallel/test-require-extensions-same-filename-as-dir-trailing-slash.js b/test/parallel/test-require-extensions-same-filename-as-dir-trailing-slash.js index 66a58be01c3c4a..f79f07a45d9de4 100644 --- a/test/parallel/test-require-extensions-same-filename-as-dir-trailing-slash.js +++ b/test/parallel/test-require-extensions-same-filename-as-dir-trailing-slash.js @@ -21,10 +21,16 @@ /* eslint-disable max-len */ 'use strict'; -const common = require('../common'); +require('../common'); const assert = require('assert'); -const filePath = '/json-with-directory-name-module/module-stub/one-trailing-slash/two/three.js'; -const content = require(`${common.fixturesDir}${filePath}`); +const fixtures = require('../common/fixtures'); + +const content = + require(fixtures.path('json-with-directory-name-module', + 'module-stub', + 'one-trailing-slash', + 'two', + 'three.js')); assert.notStrictEqual(content.rocko, 'artischocko'); assert.strictEqual(content, 'hello from module-stub!'); diff --git a/test/parallel/test-require-extensions-same-filename-as-dir.js b/test/parallel/test-require-extensions-same-filename-as-dir.js index 546a2cb381d508..1084d1d4581329 100644 --- a/test/parallel/test-require-extensions-same-filename-as-dir.js +++ b/test/parallel/test-require-extensions-same-filename-as-dir.js @@ -20,19 +20,13 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; -const common = require('../common'); +require('../common'); const assert = require('assert'); -const path = require('path'); +const fixtures = require('../common/fixtures'); -const filePath = path.join( - common.fixturesDir, - 'json-with-directory-name-module', - 'module-stub', - 'one', - 'two', - 'three.js' -); -const content = require(filePath); +const content = require(fixtures.path('json-with-directory-name-module', + 'module-stub', 'one', 'two', + 'three.js')); assert.notStrictEqual(content.rocko, 'artischocko'); assert.strictEqual(content, 'hello from module-stub!'); diff --git a/test/parallel/test-require-json.js b/test/parallel/test-require-json.js index 45581b186ca311..608a661b1c8664 100644 --- a/test/parallel/test-require-json.js +++ b/test/parallel/test-require-json.js @@ -20,12 +20,12 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; -const common = require('../common'); -const path = require('path'); +require('../common'); const assert = require('assert'); +const fixtures = require('../common/fixtures'); try { - require(path.join(common.fixturesDir, 'invalid.json')); + require(fixtures.path('invalid.json')); } catch (err) { assert.ok( /test[/\\]fixtures[/\\]invalid\.json: Unexpected string/.test(err.message), diff --git a/test/parallel/test-require-symlink.js b/test/parallel/test-require-symlink.js index 5608df4eaf4823..927afa1a870071 100644 --- a/test/parallel/test-require-symlink.js +++ b/test/parallel/test-require-symlink.js @@ -6,19 +6,22 @@ const path = require('path'); const fs = require('fs'); const { exec, spawn } = require('child_process'); const util = require('util'); +const fixtures = require('../common/fixtures'); common.refreshTmpDir(); -const linkTarget = path.join(common.fixturesDir, - '/module-require-symlink/node_modules/dep2/'); +const linkTarget = fixtures.path('module-require-symlink', + 'node_modules', + 'dep2'); -const linkDir = path.join( - common.fixturesDir, - '/module-require-symlink/node_modules/dep1/node_modules/dep2' -); +const linkDir = fixtures.path('module-require-symlink', + 'node_modules', + 'dep1', + 'node_modules', + 'dep2'); -const linkScriptTarget = path.join(common.fixturesDir, - '/module-require-symlink/symlinked.js'); +const linkScriptTarget = fixtures.path('module-require-symlink', + 'symlinked.js'); const linkScript = path.join(common.tmpDir, 'module-require-symlink.js'); @@ -44,8 +47,7 @@ function test() { fs.symlinkSync(linkScriptTarget, linkScript); // load symlinked-module - const fooModule = - require(path.join(common.fixturesDir, '/module-require-symlink/foo.js')); + const fooModule = require(fixtures.path('/module-require-symlink/foo.js')); assert.strictEqual(fooModule.dep1.bar.version, 'CORRECT_VERSION'); assert.strictEqual(fooModule.dep2.bar.version, 'CORRECT_VERSION'); diff --git a/test/parallel/test-signal-unregister.js b/test/parallel/test-signal-unregister.js index f28c73c0997dd9..2c4d3129426bbb 100644 --- a/test/parallel/test-signal-unregister.js +++ b/test/parallel/test-signal-unregister.js @@ -2,8 +2,9 @@ const common = require('../common'); const assert = require('assert'); const spawn = require('child_process').spawn; +const fixtures = require('../common/fixtures'); -const child = spawn(process.argv[0], [`${common.fixturesDir}/should_exit.js`]); +const child = spawn(process.argv[0], [fixtures.path('should_exit.js')]); child.stdout.once('data', function() { child.kill('SIGINT'); }); diff --git a/test/parallel/test-stdio-closed.js b/test/parallel/test-stdio-closed.js index 2313140a26aea7..7a6625f494d3a6 100644 --- a/test/parallel/test-stdio-closed.js +++ b/test/parallel/test-stdio-closed.js @@ -3,7 +3,7 @@ const common = require('../common'); const assert = require('assert'); const spawn = require('child_process').spawn; const fs = require('fs'); -const path = require('path'); +const fixtures = require('../common/fixtures'); if (common.isWindows) { if (process.argv[2] === 'child') { @@ -13,7 +13,7 @@ if (common.isWindows) { return; } const python = process.env.PYTHON || 'python'; - const script = path.join(common.fixturesDir, 'spawn_closed_stdio.py'); + const script = fixtures.path('spawn_closed_stdio.py'); const proc = spawn(python, [script, process.execPath, __filename, 'child']); proc.on('exit', common.mustCall(function(exitCode) { assert.strictEqual(exitCode, 0); diff --git a/test/parallel/test-stdout-close-catch.js b/test/parallel/test-stdout-close-catch.js index 5f1f52fe514b16..e9b559c9f24cd0 100644 --- a/test/parallel/test-stdout-close-catch.js +++ b/test/parallel/test-stdout-close-catch.js @@ -1,10 +1,10 @@ 'use strict'; const common = require('../common'); const assert = require('assert'); -const path = require('path'); const child_process = require('child_process'); +const fixtures = require('../common/fixtures'); -const testScript = path.join(common.fixturesDir, 'catch-stdout-error.js'); +const testScript = fixtures.path('catch-stdout-error.js'); const cmd = `${JSON.stringify(process.execPath)} ` + `${JSON.stringify(testScript)} | ` + diff --git a/test/parallel/test-stdout-to-file.js b/test/parallel/test-stdout-to-file.js index 131cb320d87fbc..6869fafa1cfaf9 100644 --- a/test/parallel/test-stdout-to-file.js +++ b/test/parallel/test-stdout-to-file.js @@ -4,10 +4,10 @@ const assert = require('assert'); const path = require('path'); const childProcess = require('child_process'); const fs = require('fs'); +const fixtures = require('../common/fixtures'); -const scriptString = path.join(common.fixturesDir, 'print-chars.js'); -const scriptBuffer = path.join(common.fixturesDir, - 'print-chars-from-buffer.js'); +const scriptString = fixtures.path('print-chars.js'); +const scriptBuffer = fixtures.path('print-chars-from-buffer.js'); const tmpFile = path.join(common.tmpDir, 'stdout.txt'); common.refreshTmpDir(); diff --git a/test/parallel/test-stream-preprocess.js b/test/parallel/test-stream-preprocess.js index 9cb4c1d3cca8ae..d42c2fd63ef52f 100644 --- a/test/parallel/test-stream-preprocess.js +++ b/test/parallel/test-stream-preprocess.js @@ -3,32 +3,26 @@ const common = require('../common'); const assert = require('assert'); const fs = require('fs'); -const path = require('path'); const rl = require('readline'); +const fixtures = require('../common/fixtures'); const BOM = '\uFEFF'; // Get the data using a non-stream way to compare with the streamed data. -const modelData = fs.readFileSync( - path.join(common.fixturesDir, 'file-to-read-without-bom.txt'), 'utf8' -); +const modelData = fixtures.readSync('file-to-read-without-bom.txt', 'utf8'); const modelDataFirstCharacter = modelData[0]; // Detect the number of forthcoming 'line' events for mustCall() 'expected' arg. const lineCount = modelData.match(/\n/g).length; // Ensure both without-bom and with-bom test files are textwise equal. -assert.strictEqual( - fs.readFileSync( - path.join(common.fixturesDir, 'file-to-read-with-bom.txt'), 'utf8' - ), - `${BOM}${modelData}` +assert.strictEqual(fixtures.readSync('file-to-read-with-bom.txt', 'utf8'), + `${BOM}${modelData}` ); // An unjustified BOM stripping with a non-BOM character unshifted to a stream. -const inputWithoutBOM = fs.createReadStream( - path.join(common.fixturesDir, 'file-to-read-without-bom.txt'), 'utf8' -); +const inputWithoutBOM = + fs.createReadStream(fixtures.path('file-to-read-without-bom.txt'), 'utf8'); inputWithoutBOM.once('readable', common.mustCall(() => { const maybeBOM = inputWithoutBOM.read(1); @@ -48,9 +42,8 @@ inputWithoutBOM.once('readable', common.mustCall(() => { })); // A justified BOM stripping. -const inputWithBOM = fs.createReadStream( - path.join(common.fixturesDir, 'file-to-read-with-bom.txt'), 'utf8' -); +const inputWithBOM = + fs.createReadStream(fixtures.path('file-to-read-with-bom.txt'), 'utf8'); inputWithBOM.once('readable', common.mustCall(() => { const maybeBOM = inputWithBOM.read(1); diff --git a/test/parallel/test-sync-fileread.js b/test/parallel/test-sync-fileread.js index 6882513c50b950..826f62d220f835 100644 --- a/test/parallel/test-sync-fileread.js +++ b/test/parallel/test-sync-fileread.js @@ -1,9 +1,7 @@ 'use strict'; -const common = require('../common'); +require('../common'); const assert = require('assert'); -const path = require('path'); const fs = require('fs'); +const fixtures = require('../common/fixtures'); -const fixture = path.join(common.fixturesDir, 'x.txt'); - -assert.strictEqual(fs.readFileSync(fixture).toString(), 'xyz\n'); +assert.strictEqual(fs.readFileSync(fixtures.path('x.txt')).toString(), 'xyz\n'); diff --git a/test/parallel/test-tls-0-dns-altname.js b/test/parallel/test-tls-0-dns-altname.js index 0b1c7f6496ce39..5952d6b02cb239 100644 --- a/test/parallel/test-tls-0-dns-altname.js +++ b/test/parallel/test-tls-0-dns-altname.js @@ -28,11 +28,11 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); -const fs = require('fs'); +const fixtures = require('../common/fixtures'); const server = tls.createServer({ - key: fs.readFileSync(`${common.fixturesDir}/0-dns/0-dns-key.pem`), - cert: fs.readFileSync(`${common.fixturesDir}/0-dns/0-dns-cert.pem`) + key: fixtures.readSync(['0-dns', '0-dns-key.pem']), + cert: fixtures.readSync(['0-dns', '0-dns-cert.pem']) }, function(c) { c.once('data', function() { c.destroy(); diff --git a/test/parallel/test-tls-addca.js b/test/parallel/test-tls-addca.js index f3f5e5b8dea107..034334a7e41bf3 100644 --- a/test/parallel/test-tls-addca.js +++ b/test/parallel/test-tls-addca.js @@ -1,14 +1,14 @@ 'use strict'; -const common = require('../common'); +require('../common'); +const fixtures = require('../common/fixtures'); // Adding a CA certificate to contextWithCert should not also add it to // contextWithoutCert. This is tested by trying to connect to a server that // depends on that CA using contextWithoutCert. -const join = require('path').join; const { assert, connect, keys, tls -} = require(join(common.fixturesDir, 'tls-connect')); +} = require(fixtures.path('tls-connect')); const contextWithoutCert = tls.createSecureContext({}); const contextWithCert = tls.createSecureContext({}); diff --git a/test/parallel/test-tls-alert-handling.js b/test/parallel/test-tls-alert-handling.js index 0b3447243db7f3..dfe7ea640db2b1 100644 --- a/test/parallel/test-tls-alert-handling.js +++ b/test/parallel/test-tls-alert-handling.js @@ -7,17 +7,12 @@ if (!common.hasCrypto) if (!common.opensslCli) common.skip('node compiled without OpenSSL CLI.'); -const fs = require('fs'); const net = require('net'); -const path = require('path'); const tls = require('tls'); - -function filenamePEM(n) { - return path.join(common.fixturesDir, 'keys', `${n}.pem`); -} +const fixtures = require('../common/fixtures'); function loadPEM(n) { - return fs.readFileSync(filenamePEM(n)); + return fixtures.readKey(`${n}.pem`); } const opts = { diff --git a/test/parallel/test-tls-alert.js b/test/parallel/test-tls-alert.js index 8aa56eaf755ab6..8e78e748525257 100644 --- a/test/parallel/test-tls-alert.js +++ b/test/parallel/test-tls-alert.js @@ -29,18 +29,13 @@ if (!common.opensslCli) const assert = require('assert'); const { spawn } = require('child_process'); -const fs = require('fs'); -const path = require('path'); const tls = require('tls'); +const fixtures = require('../common/fixtures'); let success = false; -function filenamePEM(n) { - return path.join(common.fixturesDir, 'keys', `${n}.pem`); -} - function loadPEM(n) { - return fs.readFileSync(filenamePEM(n)); + return fixtures.readKey(`${n}.pem`); } const server = tls.Server({ diff --git a/test/parallel/test-tls-alpn-server-client.js b/test/parallel/test-tls-alpn-server-client.js index 56f925b8a97945..e0547300c0a416 100644 --- a/test/parallel/test-tls-alpn-server-client.js +++ b/test/parallel/test-tls-alpn-server-client.js @@ -10,16 +10,11 @@ if (!process.features.tls_alpn || !process.features.tls_npn) { } const assert = require('assert'); -const fs = require('fs'); -const path = require('path'); const tls = require('tls'); - -function filenamePEM(n) { - return path.join(common.fixturesDir, 'keys', `${n}.pem`); -} +const fixtures = require('../common/fixtures'); function loadPEM(n) { - return fs.readFileSync(filenamePEM(n)); + return fixtures.readKey(`${n}.pem`); } const serverIP = common.localhostIPv4; diff --git a/test/parallel/test-tls-ca-concat.js b/test/parallel/test-tls-ca-concat.js index 81559a420cd9e9..3b4f3e7db74115 100644 --- a/test/parallel/test-tls-ca-concat.js +++ b/test/parallel/test-tls-ca-concat.js @@ -1,13 +1,13 @@ 'use strict'; -const common = require('../common'); +require('../common'); +const fixtures = require('../common/fixtures'); // Check ca option can contain concatenated certs by prepending an unrelated // non-CA cert and showing that agent6's CA root is still found. -const join = require('path').join; const { assert, connect, keys -} = require(join(common.fixturesDir, 'tls-connect')); +} = require(fixtures.path('tls-connect')); connect({ client: { diff --git a/test/parallel/test-tls-cert-chains-concat.js b/test/parallel/test-tls-cert-chains-concat.js index a90ed67997ee22..aeb37a8696cf69 100644 --- a/test/parallel/test-tls-cert-chains-concat.js +++ b/test/parallel/test-tls-cert-chains-concat.js @@ -1,13 +1,13 @@ 'use strict'; -const common = require('../common'); +require('../common'); +const fixtures = require('../common/fixtures'); // Check cert chain is received by client, and is completed with the ca cert // known to the client. -const join = require('path').join; const { assert, connect, debug, keys -} = require(join(common.fixturesDir, 'tls-connect')); +} = require(fixtures.path('tls-connect')); // agent6-cert.pem includes cert for agent6 and ca3 connect({ diff --git a/test/parallel/test-tls-cert-chains-in-ca.js b/test/parallel/test-tls-cert-chains-in-ca.js index 03fae36cb74a57..9cd33fcff64145 100644 --- a/test/parallel/test-tls-cert-chains-in-ca.js +++ b/test/parallel/test-tls-cert-chains-in-ca.js @@ -1,13 +1,13 @@ 'use strict'; -const common = require('../common'); +require('../common'); +const fixtures = require('../common/fixtures'); // Check cert chain is received by client, and is completed with the ca cert // known to the client. -const join = require('path').join; const { assert, connect, debug, keys -} = require(join(common.fixturesDir, 'tls-connect')); +} = require(fixtures.path('tls-connect')); // agent6-cert.pem includes cert for agent6 and ca3, split it apart and diff --git a/test/parallel/test-tls-client-abort.js b/test/parallel/test-tls-client-abort.js index 14f96104e16aef..b12c79fb05b9f5 100644 --- a/test/parallel/test-tls-client-abort.js +++ b/test/parallel/test-tls-client-abort.js @@ -26,11 +26,10 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); -const fs = require('fs'); -const path = require('path'); +const fixtures = require('../common/fixtures'); -const cert = fs.readFileSync(path.join(common.fixturesDir, 'test_cert.pem')); -const key = fs.readFileSync(path.join(common.fixturesDir, 'test_key.pem')); +const cert = fixtures.readSync('test_cert.pem'); +const key = fixtures.readSync('test_key.pem'); const conn = tls.connect({cert, key, port: 0}, common.mustNotCall()); conn.on('error', function() { diff --git a/test/parallel/test-tls-client-destroy-soon.js b/test/parallel/test-tls-client-destroy-soon.js index a94e8fbdb98afd..1d49a6094bd7e6 100644 --- a/test/parallel/test-tls-client-destroy-soon.js +++ b/test/parallel/test-tls-client-destroy-soon.js @@ -30,11 +30,11 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); -const fs = require('fs'); +const fixtures = require('../common/fixtures'); const options = { - key: fs.readFileSync(`${common.fixturesDir}/keys/agent2-key.pem`), - cert: fs.readFileSync(`${common.fixturesDir}/keys/agent2-cert.pem`) + key: fixtures.readKey('agent2-key.pem'), + cert: fixtures.readKey('agent2-cert.pem') }; const big = Buffer.alloc(2 * 1024 * 1024, 'Y'); diff --git a/test/parallel/test-tls-client-mindhsize.js b/test/parallel/test-tls-client-mindhsize.js index 230c6e08458cb1..6de8dfb627348a 100644 --- a/test/parallel/test-tls-client-mindhsize.js +++ b/test/parallel/test-tls-client-mindhsize.js @@ -5,18 +5,19 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); -const fs = require('fs'); +const fixtures = require('../common/fixtures'); -const key = fs.readFileSync(`${common.fixturesDir}/keys/agent2-key.pem`); -const cert = fs.readFileSync(`${common.fixturesDir}/keys/agent2-cert.pem`); +const key = fixtures.readKey('agent2-key.pem'); +const cert = fixtures.readKey('agent2-cert.pem'); let nsuccess = 0; let nerror = 0; function loadDHParam(n) { - let path = common.fixturesDir; - if (n !== 'error') path += '/keys'; - return fs.readFileSync(`${path}/dh${n}.pem`); + const params = [`dh${n}.pem`]; + if (n !== 'error') + params.unshift('keys'); + return fixtures.readSync(params); } function test(size, err, next) { diff --git a/test/parallel/test-tls-client-reject.js b/test/parallel/test-tls-client-reject.js index a11e0fe28028ca..955d97da6fb6fc 100644 --- a/test/parallel/test-tls-client-reject.js +++ b/test/parallel/test-tls-client-reject.js @@ -26,12 +26,11 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); -const fs = require('fs'); -const path = require('path'); +const fixtures = require('../common/fixtures'); const options = { - key: fs.readFileSync(path.join(common.fixturesDir, 'test_key.pem')), - cert: fs.readFileSync(path.join(common.fixturesDir, 'test_cert.pem')) + key: fixtures.readSync('test_key.pem'), + cert: fixtures.readSync('test_cert.pem') }; const server = tls.createServer(options, common.mustCall(function(socket) { @@ -70,7 +69,7 @@ function rejectUnauthorized() { function authorized() { const socket = tls.connect(server.address().port, { - ca: [fs.readFileSync(path.join(common.fixturesDir, 'test_cert.pem'))], + ca: [fixtures.readSync('test_cert.pem')], servername: 'localhost' }, common.mustCall(function() { assert(socket.authorized); diff --git a/test/parallel/test-tls-client-verify.js b/test/parallel/test-tls-client-verify.js index 034688a2ed5dfa..180c689ccc9ba9 100644 --- a/test/parallel/test-tls-client-verify.js +++ b/test/parallel/test-tls-client-verify.js @@ -25,9 +25,8 @@ if (!common.hasCrypto) common.skip('missing crypto'); const assert = require('assert'); -const fs = require('fs'); -const path = require('path'); const tls = require('tls'); +const fixtures = require('../common/fixtures'); const hosterr = /Hostname\/IP doesn't match certificate's altnames/; const testCases = @@ -62,13 +61,9 @@ const testCases = } ]; -function filenamePEM(n) { - return path.join(common.fixturesDir, 'keys', `${n}.pem`); -} - function loadPEM(n) { - return fs.readFileSync(filenamePEM(n)); + return fixtures.readKey(`${n}.pem`); } let successfulTests = 0; diff --git a/test/parallel/test-tls-close-error.js b/test/parallel/test-tls-close-error.js index 9973adde122024..de51b4686a93a4 100644 --- a/test/parallel/test-tls-close-error.js +++ b/test/parallel/test-tls-close-error.js @@ -6,11 +6,11 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); -const fs = require('fs'); +const fixtures = require('../common/fixtures'); const server = tls.createServer({ - key: fs.readFileSync(`${common.fixturesDir}/keys/agent1-key.pem`), - cert: fs.readFileSync(`${common.fixturesDir}/keys/agent1-cert.pem`) + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') }, function(c) { }).listen(0, common.mustCall(function() { const c = tls.connect(this.address().port, common.mustNotCall()); diff --git a/test/parallel/test-tls-close-notify.js b/test/parallel/test-tls-close-notify.js index 500aec26c8d200..5ce7145540562d 100644 --- a/test/parallel/test-tls-close-notify.js +++ b/test/parallel/test-tls-close-notify.js @@ -26,11 +26,11 @@ if (!common.hasCrypto) common.skip('missing crypto'); const tls = require('tls'); -const fs = require('fs'); +const fixtures = require('../common/fixtures'); const server = tls.createServer({ - key: fs.readFileSync(`${common.fixturesDir}/keys/agent1-key.pem`), - cert: fs.readFileSync(`${common.fixturesDir}/keys/agent1-cert.pem`) + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') }, function(c) { // Send close-notify without shutting down TCP socket if (c._handle.shutdownSSL() !== 1) diff --git a/test/parallel/test-tls-cnnic-whitelist.js b/test/parallel/test-tls-cnnic-whitelist.js index 84b71c7e64b1be..c269835428e52d 100644 --- a/test/parallel/test-tls-cnnic-whitelist.js +++ b/test/parallel/test-tls-cnnic-whitelist.js @@ -6,16 +6,11 @@ if (!common.hasCrypto) common.skip('missing crypto'); const assert = require('assert'); -const fs = require('fs'); -const path = require('path'); const tls = require('tls'); - -function filenamePEM(n) { - return path.join(common.fixturesDir, 'keys', `${n}.pem`); -} +const fixtures = require('../common/fixtures'); function loadPEM(n) { - return fs.readFileSync(filenamePEM(n)); + return fixtures.readKey(`${n}.pem`); } const testCases = [ diff --git a/test/parallel/test-tls-connect-pipe.js b/test/parallel/test-tls-connect-pipe.js index 8a3abe3312ec2d..f609659d195499 100644 --- a/test/parallel/test-tls-connect-pipe.js +++ b/test/parallel/test-tls-connect-pipe.js @@ -26,12 +26,11 @@ if (!common.hasCrypto) common.skip('missing crypto'); const tls = require('tls'); - -const fs = require('fs'); +const fixtures = require('../common/fixtures'); const options = { - key: fs.readFileSync(`${common.fixturesDir}/keys/agent1-key.pem`), - cert: fs.readFileSync(`${common.fixturesDir}/keys/agent1-cert.pem`) + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') }; common.refreshTmpDir(); diff --git a/test/parallel/test-tls-connect-simple.js b/test/parallel/test-tls-connect-simple.js index d6211d05c8cab1..633529d6d3ccb1 100644 --- a/test/parallel/test-tls-connect-simple.js +++ b/test/parallel/test-tls-connect-simple.js @@ -26,14 +26,13 @@ if (!common.hasCrypto) common.skip('missing crypto'); const tls = require('tls'); - -const fs = require('fs'); +const fixtures = require('../common/fixtures'); let serverConnected = 0; const options = { - key: fs.readFileSync(`${common.fixturesDir}/keys/agent1-key.pem`), - cert: fs.readFileSync(`${common.fixturesDir}/keys/agent1-cert.pem`) + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') }; const server = tls.Server(options, common.mustCall(function(socket) { diff --git a/test/parallel/test-tls-connect-stream-writes.js b/test/parallel/test-tls-connect-stream-writes.js index 4a8b53b4de185b..0c6ae2b660d502 100644 --- a/test/parallel/test-tls-connect-stream-writes.js +++ b/test/parallel/test-tls-connect-stream-writes.js @@ -4,15 +4,14 @@ if (!common.hasCrypto) common.skip('missing crypto'); const assert = require('assert'); -const fs = require('fs'); const tls = require('tls'); const stream = require('stream'); const net = require('net'); +const fixtures = require('../common/fixtures'); -const cert_dir = common.fixturesDir; -const options = { key: fs.readFileSync(`${cert_dir}/test_key.pem`), - cert: fs.readFileSync(`${cert_dir}/test_cert.pem`), - ca: [ fs.readFileSync(`${cert_dir}/test_ca.pem`) ], +const options = { key: fixtures.readSync('test_key.pem'), + cert: fixtures.readSync('test_cert.pem'), + ca: [ fixtures.readSync('test_ca.pem') ], ciphers: 'AES256-GCM-SHA384' }; const content = 'hello world'; const recv_bufs = []; diff --git a/test/parallel/test-tls-delayed-attach-error.js b/test/parallel/test-tls-delayed-attach-error.js index 9d37f15efe4846..94d8e879adcd2d 100644 --- a/test/parallel/test-tls-delayed-attach-error.js +++ b/test/parallel/test-tls-delayed-attach-error.js @@ -5,14 +5,14 @@ if (!common.hasCrypto) common.skip('missing crypto'); const tls = require('tls'); -const fs = require('fs'); const net = require('net'); +const fixtures = require('../common/fixtures'); const bonkers = Buffer.alloc(1024, 42); const options = { - key: fs.readFileSync(`${common.fixturesDir}/keys/agent1-key.pem`), - cert: fs.readFileSync(`${common.fixturesDir}/keys/agent1-cert.pem`) + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') }; const server = net.createServer(common.mustCall(function(c) { diff --git a/test/parallel/test-tls-dhe.js b/test/parallel/test-tls-dhe.js index fe17206fdcdc14..177f6f2eb2db19 100644 --- a/test/parallel/test-tls-dhe.js +++ b/test/parallel/test-tls-dhe.js @@ -31,10 +31,10 @@ if (!common.opensslCli) const assert = require('assert'); const tls = require('tls'); const spawn = require('child_process').spawn; -const fs = require('fs'); +const fixtures = require('../common/fixtures'); -const key = fs.readFileSync(`${common.fixturesDir}/keys/agent2-key.pem`); -const cert = fs.readFileSync(`${common.fixturesDir}/keys/agent2-cert.pem`); +const key = fixtures.readKey('agent2-key.pem'); +const cert = fixtures.readKey('agent2-cert.pem'); let nsuccess = 0; let ntests = 0; const ciphers = 'DHE-RSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'; @@ -44,9 +44,10 @@ common.expectWarning('SecurityWarning', 'DH parameter is less than 2048 bits'); function loadDHParam(n) { - let path = common.fixturesDir; - if (n !== 'error') path += '/keys'; - return fs.readFileSync(`${path}/dh${n}.pem`); + const params = [`dh${n}.pem`]; + if (n !== 'error') + params.unshift('keys'); + return fixtures.readSync(params); } function test(keylen, expectedCipher, cb) { diff --git a/test/parallel/test-tls-env-extra-ca.js b/test/parallel/test-tls-env-extra-ca.js index be7c826b85cc1f..5f011f33382ac6 100644 --- a/test/parallel/test-tls-env-extra-ca.js +++ b/test/parallel/test-tls-env-extra-ca.js @@ -7,10 +7,10 @@ if (!common.hasCrypto) common.skip('missing crypto'); const assert = require('assert'); -const fs = require('fs'); const tls = require('tls'); +const fixtures = require('../common/fixtures'); -const fork = require('child_process').fork; +const { fork } = require('child_process'); if (process.env.CHILD) { const copts = { @@ -24,8 +24,8 @@ if (process.env.CHILD) { } const options = { - key: fs.readFileSync(`${common.fixturesDir}/keys/agent1-key.pem`), - cert: fs.readFileSync(`${common.fixturesDir}/keys/agent1-cert.pem`), + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem'), }; const server = tls.createServer(options, common.mustCall(function(s) { @@ -35,7 +35,7 @@ const server = tls.createServer(options, common.mustCall(function(s) { const env = { CHILD: 'yes', PORT: this.address().port, - NODE_EXTRA_CA_CERTS: `${common.fixturesDir}/keys/ca1-cert.pem`, + NODE_EXTRA_CA_CERTS: fixtures.path('keys', 'ca1-cert.pem') }; fork(__filename, {env: env}).on('exit', common.mustCall(function(status) { diff --git a/test/parallel/test-tls-no-rsa-key.js b/test/parallel/test-tls-no-rsa-key.js index 4551928acdcf38..e3c1b5eda316b3 100644 --- a/test/parallel/test-tls-no-rsa-key.js +++ b/test/parallel/test-tls-no-rsa-key.js @@ -26,11 +26,11 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); -const fs = require('fs'); +const fixtures = require('../common/fixtures'); const options = { - key: fs.readFileSync(`${common.fixturesDir}/keys/ec-key.pem`), - cert: fs.readFileSync(`${common.fixturesDir}/keys/ec-cert.pem`) + key: fixtures.readKey('/ec-key.pem'), + cert: fixtures.readKey('ec-cert.pem') }; const server = tls.createServer(options, function(conn) { diff --git a/test/parallel/test-tls-no-sslv3.js b/test/parallel/test-tls-no-sslv3.js index 0a118dbe7e9d49..9622262f38cbf3 100644 --- a/test/parallel/test-tls-no-sslv3.js +++ b/test/parallel/test-tls-no-sslv3.js @@ -8,11 +8,11 @@ if (common.opensslCli === false) const assert = require('assert'); const tls = require('tls'); -const fs = require('fs'); const spawn = require('child_process').spawn; +const fixtures = require('../common/fixtures'); -const cert = fs.readFileSync(`${common.fixturesDir}/test_cert.pem`); -const key = fs.readFileSync(`${common.fixturesDir}/test_key.pem`); +const cert = fixtures.readSync('test_cert.pem'); +const key = fixtures.readSync('test_key.pem'); const server = tls.createServer({ cert: cert, key: key }, common.mustNotCall()); const errors = []; let stderr = ''; diff --git a/test/parallel/test-tls-npn-server-client.js b/test/parallel/test-tls-npn-server-client.js index 6a528732a3202a..518e881f9c0c15 100644 --- a/test/parallel/test-tls-npn-server-client.js +++ b/test/parallel/test-tls-npn-server-client.js @@ -28,16 +28,11 @@ if (!process.features.tls_npn) common.skip('Skipping because node compiled without NPN feature of OpenSSL.'); const assert = require('assert'); -const fs = require('fs'); -const path = require('path'); const tls = require('tls'); - -function filenamePEM(n) { - return path.join(common.fixturesDir, 'keys', `${n}.pem`); -} +const fixtures = require('../common/fixtures'); function loadPEM(n) { - return fs.readFileSync(filenamePEM(n)); + return fixtures.readKey(`${n}.pem`); } const serverOptions = { diff --git a/test/parallel/test-tls-ocsp-callback.js b/test/parallel/test-tls-ocsp-callback.js index fd1555ca88bbfe..9a6df6fb5b2c5b 100644 --- a/test/parallel/test-tls-ocsp-callback.js +++ b/test/parallel/test-tls-ocsp-callback.js @@ -32,26 +32,22 @@ if (!common.hasCrypto) common.skip('missing crypto'); const tls = require('tls'); +const fixtures = require('../common/fixtures'); const assert = require('assert'); -const fs = require('fs'); -const join = require('path').join; const SSL_OP_NO_TICKET = require('crypto').constants.SSL_OP_NO_TICKET; -const pfx = fs.readFileSync(join(common.fixturesDir, 'keys', 'agent1-pfx.pem')); +const pfx = fixtures.readKey('agent1-pfx.pem'); function test(testOptions, cb) { - const keyFile = join(common.fixturesDir, 'keys', 'agent1-key.pem'); - const certFile = join(common.fixturesDir, 'keys', 'agent1-cert.pem'); - const caFile = join(common.fixturesDir, 'keys', 'ca1-cert.pem'); - const key = fs.readFileSync(keyFile); - const cert = fs.readFileSync(certFile); - const ca = fs.readFileSync(caFile); + const key = fixtures.readKey('agent1-key.pem'); + const cert = fixtures.readKey('agent1-cert.pem'); + const ca = fixtures.readKey('ca1-cert.pem'); const options = { - key: key, - cert: cert, + key, + cert, ca: [ca] }; let requestCount = 0; diff --git a/test/parallel/test-tls-on-empty-socket.js b/test/parallel/test-tls-on-empty-socket.js index 5b66edd0c2076a..87d51a81bbe08e 100644 --- a/test/parallel/test-tls-on-empty-socket.js +++ b/test/parallel/test-tls-on-empty-socket.js @@ -6,15 +6,14 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); - -const fs = require('fs'); const net = require('net'); +const fixtures = require('../common/fixtures'); let out = ''; const server = tls.createServer({ - key: fs.readFileSync(`${common.fixturesDir}/keys/agent1-key.pem`), - cert: fs.readFileSync(`${common.fixturesDir}/keys/agent1-cert.pem`) + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') }, function(c) { c.end('hello'); }).listen(0, function() { diff --git a/test/parallel/test-tls-over-http-tunnel.js b/test/parallel/test-tls-over-http-tunnel.js index 5ff05b8504fdf8..d316a517af8e58 100644 --- a/test/parallel/test-tls-over-http-tunnel.js +++ b/test/parallel/test-tls-over-http-tunnel.js @@ -26,14 +26,14 @@ if (!common.hasCrypto) const assert = require('assert'); const https = require('https'); -const fs = require('fs'); const net = require('net'); const http = require('http'); +const fixtures = require('../common/fixtures'); let gotRequest = false; -const key = fs.readFileSync(`${common.fixturesDir}/keys/agent1-key.pem`); -const cert = fs.readFileSync(`${common.fixturesDir}/keys/agent1-cert.pem`); +const key = fixtures.readKey('agent1-key.pem'); +const cert = fixtures.readKey('agent1-cert.pem'); const options = { key: key, diff --git a/test/parallel/test-tls-passphrase.js b/test/parallel/test-tls-passphrase.js index c8cc06d35e4cd9..df94d08297d9dd 100644 --- a/test/parallel/test-tls-passphrase.js +++ b/test/parallel/test-tls-passphrase.js @@ -26,12 +26,11 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); -const fs = require('fs'); -const path = require('path'); +const fixtures = require('../common/fixtures'); -const passKey = fs.readFileSync(path.join(common.fixturesDir, 'pass-key.pem')); -const rawKey = fs.readFileSync(path.join(common.fixturesDir, 'raw-key.pem')); -const cert = fs.readFileSync(path.join(common.fixturesDir, 'pass-cert.pem')); +const passKey = fixtures.readSync('pass-key.pem'); +const rawKey = fixtures.readSync('raw-key.pem'); +const cert = fixtures.readSync('pass-cert.pem'); assert(Buffer.isBuffer(passKey)); assert(Buffer.isBuffer(cert)); diff --git a/test/parallel/test-tls-pause.js b/test/parallel/test-tls-pause.js index 647b13c62a298c..1572bd2288d421 100644 --- a/test/parallel/test-tls-pause.js +++ b/test/parallel/test-tls-pause.js @@ -26,12 +26,11 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); -const fs = require('fs'); -const path = require('path'); +const fixtures = require('../common/fixtures'); const options = { - key: fs.readFileSync(path.join(common.fixturesDir, 'test_key.pem')), - cert: fs.readFileSync(path.join(common.fixturesDir, 'test_cert.pem')) + key: fixtures.readSync('test_key.pem'), + cert: fixtures.readSync('test_cert.pem') }; const bufSize = 1024 * 1024; diff --git a/test/parallel/test-tls-peer-certificate-encoding.js b/test/parallel/test-tls-peer-certificate-encoding.js index 76781e0953470e..19c7cde42cdc16 100644 --- a/test/parallel/test-tls-peer-certificate-encoding.js +++ b/test/parallel/test-tls-peer-certificate-encoding.js @@ -26,14 +26,13 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); -const fs = require('fs'); const util = require('util'); -const join = require('path').join; +const fixtures = require('../common/fixtures'); const options = { - key: fs.readFileSync(join(common.fixturesDir, 'keys', 'agent5-key.pem')), - cert: fs.readFileSync(join(common.fixturesDir, 'keys', 'agent5-cert.pem')), - ca: [ fs.readFileSync(join(common.fixturesDir, 'keys', 'ca2-cert.pem')) ] + key: fixtures.readKey('agent5-key.pem'), + cert: fixtures.readKey('agent5-cert.pem'), + ca: [ fixtures.readKey('ca2-cert.pem') ] }; const server = tls.createServer(options, function(cleartext) { diff --git a/test/parallel/test-tls-peer-certificate-multi-keys.js b/test/parallel/test-tls-peer-certificate-multi-keys.js index 226772386253f4..b178daa2ba9328 100644 --- a/test/parallel/test-tls-peer-certificate-multi-keys.js +++ b/test/parallel/test-tls-peer-certificate-multi-keys.js @@ -26,13 +26,12 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); -const fs = require('fs'); const util = require('util'); -const join = require('path').join; +const fixtures = require('../common/fixtures'); const options = { - key: fs.readFileSync(join(common.fixturesDir, 'agent.key')), - cert: fs.readFileSync(join(common.fixturesDir, 'multi-alice.crt')) + key: fixtures.readSync('agent.key'), + cert: fixtures.readSync('multi-alice.crt') }; const server = tls.createServer(options, function(cleartext) { diff --git a/test/parallel/test-tls-peer-certificate.js b/test/parallel/test-tls-peer-certificate.js index ba18870d82f633..d2b94d6eaefbd7 100644 --- a/test/parallel/test-tls-peer-certificate.js +++ b/test/parallel/test-tls-peer-certificate.js @@ -20,14 +20,14 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; -const common = require('../common'); +require('../common'); +const fixtures = require('../common/fixtures'); // Verify that detailed getPeerCertificate() return value has all certs. -const join = require('path').join; const { assert, connect, debug, keys -} = require(join(common.fixturesDir, 'tls-connect')); +} = require(fixtures.path('tls-connect')); connect({ client: {rejectUnauthorized: false}, diff --git a/test/parallel/test-tls-pfx-gh-5100-regr.js b/test/parallel/test-tls-pfx-gh-5100-regr.js index ba38f02cdcf540..dc72b42aa0df91 100644 --- a/test/parallel/test-tls-pfx-gh-5100-regr.js +++ b/test/parallel/test-tls-pfx-gh-5100-regr.js @@ -7,11 +7,9 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); -const fs = require('fs'); -const path = require('path'); +const fixtures = require('../common/fixtures'); -const pfx = fs.readFileSync( - path.join(common.fixturesDir, 'keys', 'agent1-pfx.pem')); +const pfx = fixtures.readKey('agent1-pfx.pem'); const server = tls.createServer({ pfx: pfx, diff --git a/test/parallel/test-tls-regr-gh-5108.js b/test/parallel/test-tls-regr-gh-5108.js index 9bb07fe7ca3b3e..402a6014d1396c 100644 --- a/test/parallel/test-tls-regr-gh-5108.js +++ b/test/parallel/test-tls-regr-gh-5108.js @@ -6,11 +6,11 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); -const fs = require('fs'); +const fixtures = require('../common/fixtures'); const options = { - key: fs.readFileSync(`${common.fixturesDir}/keys/agent1-key.pem`), - cert: fs.readFileSync(`${common.fixturesDir}/keys/agent1-cert.pem`) + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') }; diff --git a/test/parallel/test-tls-request-timeout.js b/test/parallel/test-tls-request-timeout.js index 216b69bb21269c..6bbb8432f5ed0b 100644 --- a/test/parallel/test-tls-request-timeout.js +++ b/test/parallel/test-tls-request-timeout.js @@ -26,11 +26,11 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); -const fs = require('fs'); +const fixtures = require('../common/fixtures'); const options = { - key: fs.readFileSync(`${common.fixturesDir}/keys/agent1-key.pem`), - cert: fs.readFileSync(`${common.fixturesDir}/keys/agent1-cert.pem`) + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') }; const server = tls.Server(options, common.mustCall(function(socket) { diff --git a/test/parallel/test-tls-retain-handle-no-abort.js b/test/parallel/test-tls-retain-handle-no-abort.js index 4be64c15969f22..6571aab3a95721 100644 --- a/test/parallel/test-tls-retain-handle-no-abort.js +++ b/test/parallel/test-tls-retain-handle-no-abort.js @@ -6,14 +6,14 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); -const fs = require('fs'); const util = require('util'); +const fixtures = require('../common/fixtures'); const sent = 'hello world'; const serverOptions = { isServer: true, - key: fs.readFileSync(`${common.fixturesDir}/keys/agent1-key.pem`), - cert: fs.readFileSync(`${common.fixturesDir}/keys/agent1-cert.pem`) + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') }; let ssl = null; diff --git a/test/parallel/test-tls-securepair-fiftharg.js b/test/parallel/test-tls-securepair-fiftharg.js index 236f719157f11e..364b70aa76b0ca 100644 --- a/test/parallel/test-tls-securepair-fiftharg.js +++ b/test/parallel/test-tls-securepair-fiftharg.js @@ -5,12 +5,12 @@ if (!common.hasCrypto) common.skip('missing crypto'); const assert = require('assert'); -const fs = require('fs'); const tls = require('tls'); +const fixtures = require('../common/fixtures'); const sslcontext = tls.createSecureContext({ - cert: fs.readFileSync(`${common.fixturesDir}/test_cert.pem`), - key: fs.readFileSync(`${common.fixturesDir}/test_key.pem`) + cert: fixtures.readSync('test_cert.pem'), + key: fixtures.readSync('test_key.pem') }); let catchedServername; @@ -21,7 +21,7 @@ const pair = tls.createSecurePair(sslcontext, true, false, false, { }); // captured traffic from browser's request to https://www.google.com -const sslHello = fs.readFileSync(`${common.fixturesDir}/google_ssl_hello.bin`); +const sslHello = fixtures.readSync('google_ssl_hello.bin'); pair.encrypted.write(sslHello); diff --git a/test/parallel/test-tls-securepair-server.js b/test/parallel/test-tls-securepair-server.js index 6aebc80722120f..5d44356c661e97 100644 --- a/test/parallel/test-tls-securepair-server.js +++ b/test/parallel/test-tls-securepair-server.js @@ -29,13 +29,12 @@ if (!common.opensslCli) const assert = require('assert'); const tls = require('tls'); -const join = require('path').join; const net = require('net'); -const fs = require('fs'); const spawn = require('child_process').spawn; +const fixtures = require('../common/fixtures'); -const key = fs.readFileSync(join(common.fixturesDir, 'agent.key')).toString(); -const cert = fs.readFileSync(join(common.fixturesDir, 'agent.crt')).toString(); +const key = fixtures.readSync('agent.key').toString(); +const cert = fixtures.readSync('agent.crt').toString(); function log(a) { console.error(`***server*** ${a}`); diff --git a/test/parallel/test-tls-server-connection-server.js b/test/parallel/test-tls-server-connection-server.js index 7eef14d23b5371..7fb2c74996ab4b 100644 --- a/test/parallel/test-tls-server-connection-server.js +++ b/test/parallel/test-tls-server-connection-server.js @@ -6,11 +6,11 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); -const fs = require('fs'); +const fixtures = require('../common/fixtures'); const options = { - key: fs.readFileSync(`${common.fixturesDir}/keys/agent1-key.pem`), - cert: fs.readFileSync(`${common.fixturesDir}/keys/agent1-cert.pem`) + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') }; const server = tls.createServer(options, function(s) { diff --git a/test/parallel/test-tls-server-verify.js b/test/parallel/test-tls-server-verify.js index ef575a2e90ada1..f9075539c69a51 100644 --- a/test/parallel/test-tls-server-verify.js +++ b/test/parallel/test-tls-server-verify.js @@ -40,9 +40,8 @@ const assert = require('assert'); const { spawn } = require('child_process'); const { SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION } = require('crypto').constants; -const fs = require('fs'); -const path = require('path'); const tls = require('tls'); +const fixtures = require('../common/fixtures'); const testCases = [{ title: 'Do not request certs. Everyone is unauthorized.', @@ -128,14 +127,12 @@ const testCases = } ]; - function filenamePEM(n) { - return path.join(common.fixturesDir, 'keys', `${n}.pem`); + return fixtures.path('keys', `${n}.pem`); } - function loadPEM(n) { - return fs.readFileSync(filenamePEM(n)); + return fixtures.readKey(`${n}.pem`); } diff --git a/test/parallel/test-tls-session-cache.js b/test/parallel/test-tls-session-cache.js index a6d00d2c07b97d..59822acbf8768e 100644 --- a/test/parallel/test-tls-session-cache.js +++ b/test/parallel/test-tls-session-cache.js @@ -21,6 +21,10 @@ 'use strict'; const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const tls = require('tls'); +const { spawn } = require('child_process'); if (!common.opensslCli) common.skip('node compiled without OpenSSL CLI.'); @@ -28,15 +32,6 @@ if (!common.opensslCli) if (!common.hasCrypto) common.skip('missing crypto'); -const assert = require('assert'); -const tls = require('tls'); -const fs = require('fs'); -const { join } = require('path'); -const { spawn } = require('child_process'); - -const keyFile = join(common.fixturesDir, 'agent.key'); -const certFile = join(common.fixturesDir, 'agent.crt'); - doTest({ tickets: false }, function() { doTest({ tickets: true }, function() { doTest({ tickets: false, invalidSession: true }, function() { @@ -46,11 +41,11 @@ doTest({ tickets: false }, function() { }); function doTest(testOptions, callback) { - const key = fs.readFileSync(keyFile); - const cert = fs.readFileSync(certFile); + const key = fixtures.readSync('agent.key'); + const cert = fixtures.readSync('agent.crt'); const options = { - key: key, - cert: cert, + key, + cert, ca: [cert], requestCert: true, rejectUnauthorized: false @@ -108,8 +103,8 @@ function doTest(testOptions, callback) { '-tls1', '-connect', `localhost:${this.address().port}`, '-servername', 'ohgod', - '-key', join(common.fixturesDir, 'agent.key'), - '-cert', join(common.fixturesDir, 'agent.crt'), + '-key', fixtures.path('agent.key'), + '-cert', fixtures.path('agent.crt'), '-reconnect' ].concat(testOptions.tickets ? [] : '-no_ticket'); diff --git a/test/parallel/test-tls-set-ciphers.js b/test/parallel/test-tls-set-ciphers.js index 5fb437e9bb661a..abb5f21909cc63 100644 --- a/test/parallel/test-tls-set-ciphers.js +++ b/test/parallel/test-tls-set-ciphers.js @@ -31,11 +31,11 @@ if (!common.hasCrypto) const assert = require('assert'); const exec = require('child_process').exec; const tls = require('tls'); -const fs = require('fs'); +const fixtures = require('../common/fixtures'); const options = { - key: fs.readFileSync(`${common.fixturesDir}/keys/agent2-key.pem`), - cert: fs.readFileSync(`${common.fixturesDir}/keys/agent2-cert.pem`), + key: fixtures.readKey('agent2-key.pem'), + cert: fixtures.readKey('agent2-cert.pem'), ciphers: 'AES256-SHA' }; diff --git a/test/parallel/test-tls-set-encoding.js b/test/parallel/test-tls-set-encoding.js index c9ff52b9b2fc05..cf621420774973 100644 --- a/test/parallel/test-tls-set-encoding.js +++ b/test/parallel/test-tls-set-encoding.js @@ -26,11 +26,11 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); -const fs = require('fs'); +const fixtures = require('../common/fixtures'); const options = { - key: fs.readFileSync(`${common.fixturesDir}/keys/agent2-key.pem`), - cert: fs.readFileSync(`${common.fixturesDir}/keys/agent2-cert.pem`) + key: fixtures.readKey('agent2-key.pem'), + cert: fixtures.readKey('agent2-cert.pem') }; // Contains a UTF8 only character diff --git a/test/parallel/test-tls-sni-option.js b/test/parallel/test-tls-sni-option.js index 0cd56ef9781d40..799084f337a61f 100644 --- a/test/parallel/test-tls-sni-option.js +++ b/test/parallel/test-tls-sni-option.js @@ -28,16 +28,11 @@ if (!process.features.tls_sni) common.skip('node compiled without OpenSSL or with old OpenSSL version.'); const assert = require('assert'); -const fs = require('fs'); -const path = require('path'); const tls = require('tls'); - -function filenamePEM(n) { - return path.join(common.fixturesDir, 'keys', `${n}.pem`); -} +const fixtures = require('../common/fixtures'); function loadPEM(n) { - return fs.readFileSync(filenamePEM(n)); + return fixtures.readKey(`${n}.pem`); } const serverOptions = { diff --git a/test/parallel/test-tls-sni-server-client.js b/test/parallel/test-tls-sni-server-client.js index 83fd50c06603d1..867b79e1d01545 100644 --- a/test/parallel/test-tls-sni-server-client.js +++ b/test/parallel/test-tls-sni-server-client.js @@ -28,16 +28,12 @@ if (!process.features.tls_sni) common.skip('node compiled without OpenSSL or with old OpenSSL version.'); const assert = require('assert'); -const fs = require('fs'); -const path = require('path'); const tls = require('tls'); +const fixtures = require('../common/fixtures'); -function filenamePEM(n) { - return path.join(common.fixturesDir, 'keys', `${n}.pem`); -} function loadPEM(n) { - return fs.readFileSync(filenamePEM(n)); + return fixtures.readKey(`${n}.pem`); } const serverOptions = { diff --git a/test/parallel/test-tls-socket-close.js b/test/parallel/test-tls-socket-close.js index f9a77bf4648b46..debaca213bb0e2 100644 --- a/test/parallel/test-tls-socket-close.js +++ b/test/parallel/test-tls-socket-close.js @@ -5,11 +5,11 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); -const fs = require('fs'); const net = require('net'); +const fixtures = require('../common/fixtures'); -const key = fs.readFileSync(`${common.fixturesDir}/keys/agent2-key.pem`); -const cert = fs.readFileSync(`${common.fixturesDir}/keys/agent2-cert.pem`); +const key = fixtures.readKey('agent2-key.pem'); +const cert = fixtures.readKey('agent2-cert.pem'); let tlsSocket; // tls server diff --git a/test/parallel/test-tls-socket-default-options.js b/test/parallel/test-tls-socket-default-options.js index edf16a6bffc78d..b59f948b2d6861 100644 --- a/test/parallel/test-tls-socket-default-options.js +++ b/test/parallel/test-tls-socket-default-options.js @@ -1,13 +1,13 @@ 'use strict'; const common = require('../common'); +const fixtures = require('../common/fixtures'); // Test directly created TLS sockets and options. const assert = require('assert'); -const join = require('path').join; const { connect, keys, tls -} = require(join(common.fixturesDir, 'tls-connect')); +} = require(fixtures.path('tls-connect')); test(undefined, (err) => { assert.strictEqual(err.message, 'unable to verify the first certificate'); diff --git a/test/parallel/test-tls-socket-destroy.js b/test/parallel/test-tls-socket-destroy.js index b101a872c23422..f62b6f905296db 100644 --- a/test/parallel/test-tls-socket-destroy.js +++ b/test/parallel/test-tls-socket-destroy.js @@ -5,12 +5,13 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); -const fs = require('fs'); const net = require('net'); const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +const key = fixtures.readKey('agent1-key.pem'); +const cert = fixtures.readKey('agent1-cert.pem'); -const key = fs.readFileSync(`${common.fixturesDir}/keys/agent1-key.pem`); -const cert = fs.readFileSync(`${common.fixturesDir}/keys/agent1-cert.pem`); const secureContext = tls.createSecureContext({ key, cert }); const server = net.createServer(common.mustCall((conn) => { diff --git a/test/parallel/test-tls-startcom-wosign-whitelist.js b/test/parallel/test-tls-startcom-wosign-whitelist.js index 92d595e80cfae6..86075f6a350872 100644 --- a/test/parallel/test-tls-startcom-wosign-whitelist.js +++ b/test/parallel/test-tls-startcom-wosign-whitelist.js @@ -4,18 +4,13 @@ if (!common.hasCrypto) common.skip('missing crypto'); const assert = require('assert'); -const fs = require('fs'); -const path = require('path'); const tls = require('tls'); +const fixtures = require('../common/fixtures'); let finished = 0; -function filenamePEM(n) { - return path.join(common.fixturesDir, 'keys', `${n}.pem`); -} - function loadPEM(n) { - return fs.readFileSync(filenamePEM(n)); + return fixtures.readKey(`${n}.pem`); } const testCases = [ diff --git a/test/parallel/test-tls-starttls-server.js b/test/parallel/test-tls-starttls-server.js index 28d1db88598cfd..ea55d397317c87 100644 --- a/test/parallel/test-tls-starttls-server.js +++ b/test/parallel/test-tls-starttls-server.js @@ -9,12 +9,12 @@ if (!common.hasCrypto) common.skip('missing crypto'); const assert = require('assert'); -const fs = require('fs'); const net = require('net'); const tls = require('tls'); +const fixtures = require('../common/fixtures'); -const key = fs.readFileSync(`${common.fixturesDir}/keys/agent1-key.pem`); -const cert = fs.readFileSync(`${common.fixturesDir}/keys/agent1-cert.pem`); +const key = fixtures.readKey('agent1-key.pem'); +const cert = fixtures.readKey('agent1-cert.pem'); const server = net.createServer(common.mustCall((s) => { const tlsSocket = new tls.TLSSocket(s, { diff --git a/test/parallel/test-tls-ticket-cluster.js b/test/parallel/test-tls-ticket-cluster.js index 486aef8358715c..a139cccad34075 100644 --- a/test/parallel/test-tls-ticket-cluster.js +++ b/test/parallel/test-tls-ticket-cluster.js @@ -27,8 +27,7 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); const cluster = require('cluster'); -const fs = require('fs'); -const join = require('path').join; +const fixtures = require('../common/fixtures'); const workerCount = 4; const expectedReqCount = 16; @@ -87,14 +86,10 @@ if (cluster.isMaster) { return; } -const keyFile = join(common.fixturesDir, 'agent.key'); -const certFile = join(common.fixturesDir, 'agent.crt'); -const key = fs.readFileSync(keyFile); -const cert = fs.readFileSync(certFile); -const options = { - key: key, - cert: cert -}; +const key = fixtures.readSync('agent.key'); +const cert = fixtures.readSync('agent.crt'); + +const options = { key, cert }; const server = tls.createServer(options, function(c) { if (c.isSessionReused()) { diff --git a/test/parallel/test-tls-ticket.js b/test/parallel/test-tls-ticket.js index bf1ce0d5eb421b..187dd22cee6b76 100644 --- a/test/parallel/test-tls-ticket.js +++ b/test/parallel/test-tls-ticket.js @@ -26,9 +26,9 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); -const fs = require('fs'); const net = require('net'); const crypto = require('crypto'); +const fixtures = require('../common/fixtures'); const keys = crypto.randomBytes(48); const serverLog = []; @@ -42,8 +42,8 @@ function createServer() { let previousKey = null; const server = tls.createServer({ - key: fs.readFileSync(`${common.fixturesDir}/keys/agent1-key.pem`), - cert: fs.readFileSync(`${common.fixturesDir}/keys/agent1-cert.pem`), + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem'), ticketKeys: keys }, function(c) { serverLog.push(id); diff --git a/test/parallel/test-tls-timeout-server-2.js b/test/parallel/test-tls-timeout-server-2.js index 8513b29f2e94cd..d2ffaf799008ff 100644 --- a/test/parallel/test-tls-timeout-server-2.js +++ b/test/parallel/test-tls-timeout-server-2.js @@ -26,11 +26,11 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); -const fs = require('fs'); +const fixtures = require('../common/fixtures'); const options = { - key: fs.readFileSync(`${common.fixturesDir}/keys/agent1-key.pem`), - cert: fs.readFileSync(`${common.fixturesDir}/keys/agent1-cert.pem`) + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') }; const server = tls.createServer(options, common.mustCall(function(cleartext) { diff --git a/test/parallel/test-tls-timeout-server.js b/test/parallel/test-tls-timeout-server.js index a3d9a8164707aa..7ca85f14d7ae33 100644 --- a/test/parallel/test-tls-timeout-server.js +++ b/test/parallel/test-tls-timeout-server.js @@ -27,11 +27,11 @@ if (!common.hasCrypto) const tls = require('tls'); const net = require('net'); -const fs = require('fs'); +const fixtures = require('../common/fixtures'); const options = { - key: fs.readFileSync(`${common.fixturesDir}/keys/agent1-key.pem`), - cert: fs.readFileSync(`${common.fixturesDir}/keys/agent1-cert.pem`), + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem'), handshakeTimeout: 50 }; diff --git a/test/parallel/test-tls-two-cas-one-string.js b/test/parallel/test-tls-two-cas-one-string.js index 897635d7b18c02..bbde78a24ed706 100644 --- a/test/parallel/test-tls-two-cas-one-string.js +++ b/test/parallel/test-tls-two-cas-one-string.js @@ -4,15 +4,13 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); -const fs = require('fs'); const tls = require('tls'); +const fixtures = require('../common/fixtures'); -const keydir = `${common.fixturesDir}/keys`; - -const ca1 = fs.readFileSync(`${keydir}/ca1-cert.pem`, 'utf8'); -const ca2 = fs.readFileSync(`${keydir}/ca2-cert.pem`, 'utf8'); -const cert = fs.readFileSync(`${keydir}/agent3-cert.pem`, 'utf8'); -const key = fs.readFileSync(`${keydir}/agent3-key.pem`, 'utf8'); +const ca1 = fixtures.readKey('ca1-cert.pem', 'utf8'); +const ca2 = fixtures.readKey('ca2-cert.pem', 'utf8'); +const cert = fixtures.readKey('agent3-cert.pem', 'utf8'); +const key = fixtures.readKey('agent3-key.pem', 'utf8'); function test(ca, next) { const server = tls.createServer({ ca, cert, key }, function(conn) { diff --git a/test/parallel/test-tls-wrap-timeout.js b/test/parallel/test-tls-wrap-timeout.js index b4cff368182fa3..d1598ab737f129 100644 --- a/test/parallel/test-tls-wrap-timeout.js +++ b/test/parallel/test-tls-wrap-timeout.js @@ -6,13 +6,12 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); - const net = require('net'); -const fs = require('fs'); +const fixtures = require('../common/fixtures'); const options = { - key: fs.readFileSync(`${common.fixturesDir}/keys/agent1-key.pem`), - cert: fs.readFileSync(`${common.fixturesDir}/keys/agent1-cert.pem`) + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') }; const server = tls.createServer(options, common.mustCall((c) => { diff --git a/test/parallel/test-tls-zero-clear-in.js b/test/parallel/test-tls-zero-clear-in.js index 84c03d292c1dbe..006e756826c7d1 100644 --- a/test/parallel/test-tls-zero-clear-in.js +++ b/test/parallel/test-tls-zero-clear-in.js @@ -26,12 +26,10 @@ if (!common.hasCrypto) common.skip('missing crypto'); const tls = require('tls'); +const fixtures = require('../common/fixtures'); -const fs = require('fs'); -const path = require('path'); - -const cert = fs.readFileSync(path.join(common.fixturesDir, 'test_cert.pem')); -const key = fs.readFileSync(path.join(common.fixturesDir, 'test_key.pem')); +const cert = fixtures.readSync('test_cert.pem'); +const key = fixtures.readSync('test_key.pem'); const server = tls.createServer({ cert: cert, diff --git a/test/parallel/test-util-callbackify.js b/test/parallel/test-util-callbackify.js index 68ffc6e9207e23..44549165c1f268 100644 --- a/test/parallel/test-util-callbackify.js +++ b/test/parallel/test-util-callbackify.js @@ -6,9 +6,9 @@ const common = require('../common'); const assert = require('assert'); const { callbackify } = require('util'); -const { join } = require('path'); const { execFile } = require('child_process'); -const fixtureDir = join(common.fixturesDir, 'uncaught-exceptions'); +const fixtures = require('../common/fixtures'); + const values = [ 'hello world', null, @@ -197,7 +197,7 @@ const values = [ { // Test that callback that throws emits an `uncaughtException` event - const fixture = join(fixtureDir, 'callbackify1.js'); + const fixture = fixtures.path('uncaught-exceptions', 'callbackify1.js'); execFile( process.execPath, [fixture], @@ -214,7 +214,7 @@ const values = [ { // Test that handled `uncaughtException` works and passes rejection reason - const fixture = join(fixtureDir, 'callbackify2.js'); + const fixture = fixtures.path('uncaught-exceptions', 'callbackify2.js'); execFile( process.execPath, [fixture], diff --git a/test/parallel/test-util-internal.js b/test/parallel/test-util-internal.js index ef73c24288f4bd..63e782f15de1e8 100644 --- a/test/parallel/test-util-internal.js +++ b/test/parallel/test-util-internal.js @@ -1,9 +1,9 @@ 'use strict'; // Flags: --expose_internals -const common = require('../common'); -const path = require('path'); +require('../common'); const assert = require('assert'); +const fixtures = require('../common/fixtures'); const binding = process.binding('util'); const kArrowMessagePrivateSymbolIndex = binding['arrow_message_private_symbol']; @@ -54,7 +54,7 @@ assert.strictEqual( let arrowMessage; try { - require(path.join(common.fixturesDir, 'syntax', 'bad_syntax')); + require(fixtures.path('syntax', 'bad_syntax')); } catch (err) { arrowMessage = binding.getHiddenValue(err, kArrowMessagePrivateSymbolIndex); diff --git a/test/parallel/test-vm-debug-context.js b/test/parallel/test-vm-debug-context.js index b558906dca38d2..314c9fa0655841 100644 --- a/test/parallel/test-vm-debug-context.js +++ b/test/parallel/test-vm-debug-context.js @@ -24,7 +24,8 @@ const common = require('../common'); const assert = require('assert'); const vm = require('vm'); -const spawn = require('child_process').spawn; +const { spawn } = require('child_process'); +const fixtures = require('../common/fixtures'); assert.throws(function() { vm.runInDebugContext('*'); @@ -77,7 +78,7 @@ assert.strictEqual(vm.runInDebugContext(undefined), undefined); // Can set listeners and breakpoints on a single line file { const Debug = vm.runInDebugContext('Debug'); - const fn = require(`${common.fixturesDir}/exports-function-with-param`); + const fn = require(fixtures.path('exports-function-with-param')); let called = false; Debug.setListener(function(event, state, data) { @@ -94,7 +95,7 @@ assert.strictEqual(vm.runInDebugContext(undefined), undefined); // See https://github.com/nodejs/node/issues/1190, fatal errors should not // crash the process. -const script = `${common.fixturesDir}/vm-run-in-debug-context.js`; +const script = fixtures.path('vm-run-in-debug-context.js'); let proc = spawn(process.execPath, [script]); const data = []; proc.stdout.on('data', common.mustNotCall()); diff --git a/test/parallel/test-vm-syntax-error-stderr.js b/test/parallel/test-vm-syntax-error-stderr.js index e40edc907c6639..137d5de432b398 100644 --- a/test/parallel/test-vm-syntax-error-stderr.js +++ b/test/parallel/test-vm-syntax-error-stderr.js @@ -1,10 +1,10 @@ 'use strict'; const common = require('../common'); const assert = require('assert'); -const path = require('path'); const child_process = require('child_process'); +const fixtures = require('../common/fixtures'); -const wrong_script = path.join(common.fixturesDir, 'cert.pem'); +const wrong_script = fixtures.path('cert.pem'); const p = child_process.spawn(process.execPath, [ '-e', diff --git a/test/parallel/test-whatwg-url-constructor.js b/test/parallel/test-whatwg-url-constructor.js index 38be3a293b9fcf..16bcac74cc6bcd 100644 --- a/test/parallel/test-whatwg-url-constructor.js +++ b/test/parallel/test-whatwg-url-constructor.js @@ -5,13 +5,13 @@ if (!common.hasIntl) { common.skip('missing Intl'); } -const path = require('path'); +const fixtures = require('../common/fixtures'); const { URL, URLSearchParams } = require('url'); const { test, assert_equals, assert_true, assert_throws } = require('../common/wpt'); const request = { - response: require(path.join(common.fixturesDir, 'url-tests')) + response: require(fixtures.path('url-tests')) }; /* The following tests are copied from WPT. Modifications to them should be diff --git a/test/parallel/test-whatwg-url-origin.js b/test/parallel/test-whatwg-url-origin.js index 274de420bebbf3..8a5ad6d3154f8f 100644 --- a/test/parallel/test-whatwg-url-origin.js +++ b/test/parallel/test-whatwg-url-origin.js @@ -5,12 +5,12 @@ if (!common.hasIntl) { common.skip('missing Intl'); } -const path = require('path'); +const fixtures = require('../common/fixtures'); const URL = require('url').URL; const { test, assert_equals } = require('../common/wpt'); const request = { - response: require(path.join(common.fixturesDir, 'url-tests')) + response: require(fixtures.path('url-tests')) }; /* The following tests are copied from WPT. Modifications to them should be diff --git a/test/parallel/test-whatwg-url-parsing.js b/test/parallel/test-whatwg-url-parsing.js index 9cff28a9061df4..5647068e6853e6 100644 --- a/test/parallel/test-whatwg-url-parsing.js +++ b/test/parallel/test-whatwg-url-parsing.js @@ -7,11 +7,11 @@ if (!common.hasIntl) { } const URL = require('url').URL; -const path = require('path'); const assert = require('assert'); +const fixtures = require('../common/fixtures'); // Tests below are not from WPT. -const tests = require(path.join(common.fixturesDir, 'url-tests')); +const tests = require(fixtures.path('url-tests')); const failureTests = tests.filter((test) => test.failure).concat([ { input: '' }, { input: 'test' }, @@ -44,8 +44,8 @@ for (const test of failureTests) { }); } -const additional_tests = require( - path.join(common.fixturesDir, 'url-tests-additional.js')); +const additional_tests = + require(fixtures.path('url-tests-additional.js')); for (const test of additional_tests) { const url = new URL(test.url); diff --git a/test/parallel/test-whatwg-url-searchparams.js b/test/parallel/test-whatwg-url-searchparams.js index db7f9a491719dc..0b72c08d0b887f 100644 --- a/test/parallel/test-whatwg-url-searchparams.js +++ b/test/parallel/test-whatwg-url-searchparams.js @@ -2,8 +2,8 @@ const common = require('../common'); const assert = require('assert'); -const path = require('path'); const { URL, URLSearchParams } = require('url'); +const fixtures = require('../common/fixtures'); // Tests below are not from WPT. const serialized = 'a=a&a=1&a=true&a=undefined&a=null&a=%EF%BF%BD' + @@ -84,7 +84,7 @@ sp.forEach(function() { m.search = '?a=a&b=b'; assert.strictEqual(sp.toString(), 'a=a&b=b'); -const tests = require(path.join(common.fixturesDir, 'url-searchparams.js')); +const tests = require(fixtures.path('url-searchparams.js')); for (const [input, expected, parsed] of tests) { if (input[0] !== '?') { diff --git a/test/parallel/test-whatwg-url-setters.js b/test/parallel/test-whatwg-url-setters.js index 4aafc176f03fb9..819a835e2ce347 100644 --- a/test/parallel/test-whatwg-url-setters.js +++ b/test/parallel/test-whatwg-url-setters.js @@ -7,14 +7,15 @@ if (!common.hasIntl) { } const assert = require('assert'); -const path = require('path'); const URL = require('url').URL; const { test, assert_equals } = require('../common/wpt'); -const additionalTestCases = require( - path.join(common.fixturesDir, 'url-setter-tests-additional.js')); +const fixtures = require('../common/fixtures'); + +const additionalTestCases = + require(fixtures.path('url-setter-tests-additional.js')); const request = { - response: require(path.join(common.fixturesDir, 'url-setter-tests')) + response: require(fixtures.path('url-setter-tests')) }; /* The following tests are copied from WPT. Modifications to them should be diff --git a/test/parallel/test-whatwg-url-toascii.js b/test/parallel/test-whatwg-url-toascii.js index 5a34b0396495fa..c85b092c1d250c 100644 --- a/test/parallel/test-whatwg-url-toascii.js +++ b/test/parallel/test-whatwg-url-toascii.js @@ -5,12 +5,12 @@ if (!common.hasIntl) { common.skip('missing Intl'); } -const path = require('path'); +const fixtures = require('../common/fixtures'); const { URL } = require('url'); const { test, assert_equals, assert_throws } = require('../common/wpt'); const request = { - response: require(path.join(common.fixturesDir, 'url-toascii')) + response: require(fixtures.path('url-toascii')) }; /* The following tests are copied from WPT. Modifications to them should be diff --git a/test/parallel/test-zlib-flush.js b/test/parallel/test-zlib-flush.js index 0eec5da7dd835e..965cb5c45b13ea 100644 --- a/test/parallel/test-zlib-flush.js +++ b/test/parallel/test-zlib-flush.js @@ -1,11 +1,10 @@ 'use strict'; -const common = require('../common'); +require('../common'); const assert = require('assert'); const zlib = require('zlib'); -const path = require('path'); -const fs = require('fs'); +const fixtures = require('../common/fixtures'); -const file = fs.readFileSync(path.resolve(common.fixturesDir, 'person.jpg')); +const file = fixtures.readSync('person.jpg'); const chunkSize = 16; const opts = { level: 0 }; const deflater = zlib.createDeflate(opts); diff --git a/test/parallel/test-zlib-from-concatenated-gzip.js b/test/parallel/test-zlib-from-concatenated-gzip.js index f59b6796357e3b..7f3942035a7e04 100644 --- a/test/parallel/test-zlib-from-concatenated-gzip.js +++ b/test/parallel/test-zlib-from-concatenated-gzip.js @@ -4,8 +4,8 @@ const common = require('../common'); const assert = require('assert'); const zlib = require('zlib'); -const path = require('path'); const fs = require('fs'); +const fixtures = require('../common/fixtures'); const abcEncoded = zlib.gzipSync('abc'); const defEncoded = zlib.gzipSync('def'); @@ -42,8 +42,8 @@ zlib.unzip(Buffer.concat([ // files that have the "right" magic bytes for starting a new gzip member // in the middle of themselves, even if they are part of a single // regularly compressed member -const pmmFileZlib = path.join(common.fixturesDir, 'pseudo-multimember-gzip.z'); -const pmmFileGz = path.join(common.fixturesDir, 'pseudo-multimember-gzip.gz'); +const pmmFileZlib = fixtures.path('pseudo-multimember-gzip.z'); +const pmmFileGz = fixtures.path('pseudo-multimember-gzip.gz'); const pmmExpected = zlib.inflateSync(fs.readFileSync(pmmFileZlib)); const pmmResultBuffers = []; diff --git a/test/parallel/test-zlib-from-gzip.js b/test/parallel/test-zlib-from-gzip.js index 98a581f01e4c98..7ded110056e36f 100644 --- a/test/parallel/test-zlib-from-gzip.js +++ b/test/parallel/test-zlib-from-gzip.js @@ -27,6 +27,7 @@ const common = require('../common'); const assert = require('assert'); const zlib = require('zlib'); const path = require('path'); +const fixtures = require('../common/fixtures'); common.refreshTmpDir(); @@ -34,8 +35,8 @@ const gunzip = zlib.createGunzip(); const fs = require('fs'); -const fixture = path.resolve(common.fixturesDir, 'person.jpg.gz'); -const unzippedFixture = path.resolve(common.fixturesDir, 'person.jpg'); +const fixture = fixtures.path('person.jpg.gz'); +const unzippedFixture = fixtures.path('person.jpg'); const outputFile = path.resolve(common.tmpDir, 'person.jpg'); const expect = fs.readFileSync(unzippedFixture); const inp = fs.createReadStream(fixture); diff --git a/test/parallel/test-zlib-params.js b/test/parallel/test-zlib-params.js index 40799637ecdac7..293ceecf8fa02a 100644 --- a/test/parallel/test-zlib-params.js +++ b/test/parallel/test-zlib-params.js @@ -1,11 +1,10 @@ 'use strict'; -const common = require('../common'); +require('../common'); const assert = require('assert'); const zlib = require('zlib'); -const path = require('path'); -const fs = require('fs'); +const fixtures = require('../common/fixtures'); -const file = fs.readFileSync(path.resolve(common.fixturesDir, 'person.jpg')); +const file = fixtures.readSync('person.jpg'); const chunkSize = 12 * 1024; const opts = { level: 9, strategy: zlib.constants.Z_DEFAULT_STRATEGY }; const deflater = zlib.createDeflate(opts); diff --git a/test/parallel/test-zlib.js b/test/parallel/test-zlib.js index 2f358a117286bc..e91bd4736bd54c 100644 --- a/test/parallel/test-zlib.js +++ b/test/parallel/test-zlib.js @@ -23,9 +23,8 @@ const common = require('../common'); const assert = require('assert'); const zlib = require('zlib'); -const path = require('path'); -const fs = require('fs'); const stream = require('stream'); +const fixtures = require('../common/fixtures'); let zlibPairs = [ [zlib.Deflate, zlib.Inflate], @@ -69,7 +68,7 @@ if (process.env.FAST) { const tests = {}; testFiles.forEach(common.mustCall((file) => { - tests[file] = fs.readFileSync(path.resolve(common.fixturesDir, file)); + tests[file] = fixtures.readSync(file); }, testFiles.length)); diff --git a/test/sequential/test-module-loading.js b/test/sequential/test-module-loading.js index e59ec247828944..47916d352d729a 100644 --- a/test/sequential/test-module-loading.js +++ b/test/sequential/test-module-loading.js @@ -248,7 +248,9 @@ try { }, {}); assert.deepStrictEqual(children, { - 'common/index.js': {}, + 'common/index.js': { + 'common/fixtures.js': {} + }, 'fixtures/not-main-module.js': {}, 'fixtures/a.js': { 'fixtures/b/c.js': { From d4f2a52953797011928079582500714d8a722917 Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Wed, 9 Aug 2017 07:28:28 +0200 Subject: [PATCH 005/231] test: check crypto before requiring tls module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit test-tls-session-cache currently fails if built --without-ssl: internal/util.js:82 throw new errors.Error('ERR_NO_CRYPTO'); ^ Error [ERR_NO_CRYPTO]: Node.js is not compiled with OpenSSL crypto support at Object.assertCrypto (internal/util.js:82:11) at tls.js:26:14 at NativeModule.compile (bootstrap_node.js:586:7) at Function.NativeModule.require (bootstrap_node.js:531:18) at Function.Module._load (module.js:449:25) at Module.require (module.js:517:17) at require (internal/module.js:11:18) at Object. (/node/test/parallel/test-tls-session-cache.js:26:13) at Module._compile (module.js:573:30) at Object.Module._extensions..js (module.js:584:10) The test has a crypto check but it come after the require of the tls module. This commit moves the crypto check to come before the require of tls and allows the test to pass. PR-URL: https://github.com/nodejs/node/pull/14708 Reviewed-By: Rich Trott Reviewed-By: Sakthipriyan Vairamani Reviewed-By: David Cai Reviewed-By: Richard Lau Reviewed-By: Luigi Pinca Reviewed-By: Tobias Nießen Reviewed-By: Colin Ihrig Reviewed-By: James M Snell --- test/parallel/test-tls-session-cache.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/parallel/test-tls-session-cache.js b/test/parallel/test-tls-session-cache.js index 59822acbf8768e..73ccdd8396cecd 100644 --- a/test/parallel/test-tls-session-cache.js +++ b/test/parallel/test-tls-session-cache.js @@ -21,6 +21,8 @@ 'use strict'; const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); const fixtures = require('../common/fixtures'); const assert = require('assert'); const tls = require('tls'); @@ -29,8 +31,6 @@ const { spawn } = require('child_process'); if (!common.opensslCli) common.skip('node compiled without OpenSSL CLI.'); -if (!common.hasCrypto) - common.skip('missing crypto'); doTest({ tickets: false }, function() { doTest({ tickets: true }, function() { From a3feb54c7f7734cb61ded485850270ca913b75c8 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Sun, 6 Aug 2017 10:48:14 -0700 Subject: [PATCH 006/231] test: make test-tls-alert-handling more strict MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use `common.mustCall()` and `common.mustNotCall()` to more rigorously check that functions (especially no-op error handlers) are called the expected number of times in test-tls-alert-handling. PR-URL: https://github.com/nodejs/node/pull/14650 Reviewed-By: Colin Ihrig Reviewed-By: Tobias Nießen Reviewed-By: Luigi Pinca Reviewed-By: David Cai Reviewed-By: Yuta Hiroto Reviewed-By: James M Snell --- test/parallel/test-tls-alert-handling.js | 49 ++++++++++++++---------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/test/parallel/test-tls-alert-handling.js b/test/parallel/test-tls-alert-handling.js index dfe7ea640db2b1..c686d68a182e3d 100644 --- a/test/parallel/test-tls-alert-handling.js +++ b/test/parallel/test-tls-alert-handling.js @@ -5,33 +5,43 @@ if (!common.hasCrypto) common.skip('missing crypto'); if (!common.opensslCli) - common.skip('node compiled without OpenSSL CLI.'); + common.skip('node compiled without OpenSSL CLI'); const net = require('net'); const tls = require('tls'); const fixtures = require('../common/fixtures'); +let clientClosed = false; +let errorReceived = false; +function canCloseServer() { + return clientClosed && errorReceived; +} + function loadPEM(n) { - return fixtures.readKey(`${n}.pem`); + return fixtures.readKey(`${n}.pem`, 'utf-8'); } const opts = { key: loadPEM('agent2-key'), cert: loadPEM('agent2-cert') }; + const max_iter = 20; let iter = 0; -const server = tls.createServer(opts, function(s) { - s.pipe(s); - s.on('error', function() { - // ignore error - }); +const errorHandler = common.mustCall(() => { + errorReceived = true; + if (canCloseServer()) + server.close(); }); +const server = tls.createServer(opts, common.mustCall(function(s) { + s.pipe(s); + s.on('error', errorHandler); +}, 2)); -server.listen(0, function() { +server.listen(0, common.mustCall(function() { sendClient(); -}); +})); function sendClient() { @@ -45,15 +55,14 @@ function sendClient() { return; } client.end(); - server.close(); }, max_iter)); client.write('a'); - client.on('error', function() { - // ignore error - }); - client.on('close', function() { - server.close(); - }); + client.on('error', common.mustNotCall()); + client.on('close', common.mustCall(function() { + clientClosed = true; + if (canCloseServer()) + server.close(); + })); } @@ -63,11 +72,9 @@ function sendBADTLSRecord() { const client = tls.connect({ socket: socket, rejectUnauthorized: false - }, function() { + }, common.mustCall(function() { socket.write(BAD_RECORD); socket.end(); - }); - client.on('error', function() { - // ignore error - }); + })); + client.on('error', common.mustCall()); } From 4205648216e26846530e9883980d2ccb4ab630f0 Mon Sep 17 00:00:00 2001 From: Griffith Tchenpan Date: Sat, 17 Sep 2016 19:00:53 +1000 Subject: [PATCH 007/231] test: invoke callback with common.mustCall() * invoke callback with `common.mustCall()` in test-crypto-hash * order module declarations aphabetically per test-writing-guide PR-URL: https://github.com/nodejs/node/pull/8597 Reviewed-By: Colin Ihrig Reviewed-By: James M Snell Reviewed-By: Rich Trott --- test/parallel/test-crypto-domain.js | 2 +- test/parallel/test-crypto-hash.js | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/test/parallel/test-crypto-domain.js b/test/parallel/test-crypto-domain.js index d579331287c485..348b55b09d4e17 100644 --- a/test/parallel/test-crypto-domain.js +++ b/test/parallel/test-crypto-domain.js @@ -25,8 +25,8 @@ if (!common.hasCrypto) common.skip('missing crypto'); const assert = require('assert'); -const domain = require('domain'); const crypto = require('crypto'); +const domain = require('domain'); function test(fn) { const ex = new Error('BAM'); diff --git a/test/parallel/test-crypto-hash.js b/test/parallel/test-crypto-hash.js index 0cab2a9ef1e7c1..83b355a2bee181 100644 --- a/test/parallel/test-crypto-hash.js +++ b/test/parallel/test-crypto-hash.js @@ -4,8 +4,9 @@ if (!common.hasCrypto) common.skip('missing crypto'); const assert = require('assert'); -const fs = require('fs'); const crypto = require('crypto'); +const fs = require('fs'); + const fixtures = require('../common/fixtures'); // Test hashing @@ -80,11 +81,11 @@ const fileStream = fs.createReadStream(fn); fileStream.on('data', function(data) { sha1Hash.update(data); }); -fileStream.on('close', function() { +fileStream.on('close', common.mustCall(function() { assert.strictEqual(sha1Hash.digest('hex'), '22723e553129a336ad96e10f6aecdf0f45e4149e', 'Test SHA1 of sample.png'); -}); +})); // Issue #2227: unknown digest method should throw an error. assert.throws(function() { From 136eea4bcbaa5d2be425c93d31b3923791abcc32 Mon Sep 17 00:00:00 2001 From: Mudit Ameta Date: Sun, 16 Jul 2017 11:51:44 -0400 Subject: [PATCH 008/231] os: add CIDR support This patch adds support for CIDR notation to the output of the `networkInterfaces()` method PR-URL: https://github.com/nodejs/node/pull/14307 Fixes: https://github.com/nodejs/node/issues/14006 Reviewed-By: Roman Reiss Reviewed-By: James M Snell Reviewed-By: Refael Ackermann Reviewed-By: Timothy Gu --- doc/api/os.md | 15 ++++++++--- lib/internal/os.js | 41 +++++++++++++++++++++++++++++++ lib/os.js | 18 +++++++++++++- node.gyp | 1 + test/parallel/test-internal-os.js | 32 ++++++++++++++++++++++++ test/parallel/test-os.js | 25 +++++++++++++++++-- 6 files changed, 125 insertions(+), 7 deletions(-) create mode 100644 lib/internal/os.js create mode 100644 test/parallel/test-internal-os.js diff --git a/doc/api/os.md b/doc/api/os.md index 3b4b9526cb48cc..9dc0fcf30877e2 100644 --- a/doc/api/os.md +++ b/doc/api/os.md @@ -253,6 +253,9 @@ The properties available on the assigned network address object include: similar interface that is not remotely accessible; otherwise `false` * `scopeid` {number} The numeric IPv6 scope ID (only specified when `family` is `IPv6`) +* `cidr` {string} The assigned IPv4 or IPv6 address with the routing prefix + in CIDR notation. If the `netmask` is invalid, this property is set + to `null` ```js @@ -263,14 +266,16 @@ The properties available on the assigned network address object include: netmask: '255.0.0.0', family: 'IPv4', mac: '00:00:00:00:00:00', - internal: true + internal: true, + cidr: '127.0.0.1/8' }, { address: '::1', netmask: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', family: 'IPv6', mac: '00:00:00:00:00:00', - internal: true + internal: true, + cidr: '::1/128' } ], eth0: [ @@ -279,14 +284,16 @@ The properties available on the assigned network address object include: netmask: '255.255.255.0', family: 'IPv4', mac: '01:02:03:0a:0b:0c', - internal: false + internal: false, + cidr: '192.168.1.108/24' }, { address: 'fe80::a00:27ff:fe4e:66a1', netmask: 'ffff:ffff:ffff:ffff::', family: 'IPv6', mac: '01:02:03:0a:0b:0c', - internal: false + internal: false, + cidr: 'fe80::a00:27ff:fe4e:66a1/64' } ] } diff --git a/lib/internal/os.js b/lib/internal/os.js new file mode 100644 index 00000000000000..74ed6e767ee16d --- /dev/null +++ b/lib/internal/os.js @@ -0,0 +1,41 @@ +'use strict'; + +function getCIDRSuffix(mask, protocol = 'ipv4') { + const isV6 = protocol === 'ipv6'; + const bitsString = mask + .split(isV6 ? ':' : '.') + .filter((v) => !!v) + .map((v) => pad(parseInt(v, isV6 ? 16 : 10).toString(2), isV6)) + .join(''); + + if (isValidMask(bitsString)) { + return countOnes(bitsString); + } else { + return null; + } +} + +function pad(binaryString, isV6) { + const groupLength = isV6 ? 16 : 8; + const binLen = binaryString.length; + + return binLen < groupLength ? + `${'0'.repeat(groupLength - binLen)}${binaryString}` : binaryString; +} + +function isValidMask(bitsString) { + const firstIndexOfZero = bitsString.indexOf(0); + const lastIndexOfOne = bitsString.lastIndexOf(1); + + return firstIndexOfZero < 0 || firstIndexOfZero > lastIndexOfOne; +} + +function countOnes(bitsString) { + return bitsString + .split('') + .reduce((acc, bit) => acc += parseInt(bit, 10), 0); +} + +module.exports = { + getCIDRSuffix +}; diff --git a/lib/os.js b/lib/os.js index 4a99cab81e3e34..078dba3fcca071 100644 --- a/lib/os.js +++ b/lib/os.js @@ -24,6 +24,7 @@ const pushValToArrayMax = process.binding('util').pushValToArrayMax; const constants = process.binding('constants').os; const deprecate = require('internal/util').deprecate; +const getCIDRSuffix = require('internal/os').getCIDRSuffix; const isWindows = process.platform === 'win32'; const { @@ -121,6 +122,21 @@ function endianness() { } endianness[Symbol.toPrimitive] = () => kEndianness; +function networkInterfaces() { + const interfaceAddresses = getInterfaceAddresses(); + + return Object.entries(interfaceAddresses).reduce((acc, [key, val]) => { + acc[key] = val.map((v) => { + const protocol = v.family.toLowerCase(); + const suffix = getCIDRSuffix(v.netmask, protocol); + const cidr = suffix ? `${v.address}/${suffix}` : null; + + return Object.assign({}, v, { cidr }); + }); + return acc; + }, {}); +} + module.exports = exports = { arch, cpus, @@ -130,7 +146,7 @@ module.exports = exports = { homedir: getHomeDirectory, hostname: getHostname, loadavg, - networkInterfaces: getInterfaceAddresses, + networkInterfaces, platform, release: getOSRelease, tmpdir, diff --git a/node.gyp b/node.gyp index 81f549f8b63f73..040e38223daeda 100644 --- a/node.gyp +++ b/node.gyp @@ -91,6 +91,7 @@ 'lib/internal/linkedlist.js', 'lib/internal/net.js', 'lib/internal/module.js', + 'lib/internal/os.js', 'lib/internal/process/next_tick.js', 'lib/internal/process/promises.js', 'lib/internal/process/stdio.js', diff --git a/test/parallel/test-internal-os.js b/test/parallel/test-internal-os.js new file mode 100644 index 00000000000000..c4014abc5b1c0f --- /dev/null +++ b/test/parallel/test-internal-os.js @@ -0,0 +1,32 @@ +// Flags: --expose-internals +'use strict'; + +require('../common'); +const assert = require('assert'); +const getCIDRSuffix = require('internal/os').getCIDRSuffix; + +const specs = [ + // valid + ['128.0.0.0', 'ipv4', 1], + ['255.0.0.0', 'ipv4', 8], + ['255.255.255.128', 'ipv4', 25], + ['255.255.255.255', 'ipv4', 32], + ['ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', 'ipv6', 128], + ['ffff:ffff:ffff:ffff::', 'ipv6', 64], + ['ffff:ffff:ffff:ff80::', 'ipv6', 57], + // invalid + ['255.0.0.1', 'ipv4', null], + ['255.255.9.0', 'ipv4', null], + ['255.255.1.0', 'ipv4', null], + ['ffff:ffff:43::', 'ipv6', null], + ['ffff:ffff:ffff:1::', 'ipv6', null] +]; + +specs.forEach(([mask, protocol, expectedSuffix]) => { + const actualSuffix = getCIDRSuffix(mask, protocol); + + assert.strictEqual( + actualSuffix, expectedSuffix, + `Mask: ${mask}, expected: ${expectedSuffix}, actual: ${actualSuffix}` + ); +}); diff --git a/test/parallel/test-os.js b/test/parallel/test-os.js index 180d869001f5a6..afff23b2e4f866 100644 --- a/test/parallel/test-os.js +++ b/test/parallel/test-os.js @@ -24,6 +24,7 @@ const common = require('../common'); const assert = require('assert'); const os = require('os'); const path = require('path'); +const { inspect } = require('util'); const is = { string: (value) => { assert.strictEqual(typeof value, 'string'); }, @@ -121,7 +122,7 @@ switch (platform) { const actual = interfaces.lo.filter(filter); const expected = [{ address: '127.0.0.1', netmask: '255.0.0.0', mac: '00:00:00:00:00:00', family: 'IPv4', - internal: true }]; + internal: true, cidr: '127.0.0.1/8' }]; assert.deepStrictEqual(actual, expected); break; } @@ -131,11 +132,31 @@ switch (platform) { const actual = interfaces['Loopback Pseudo-Interface 1'].filter(filter); const expected = [{ address: '127.0.0.1', netmask: '255.0.0.0', mac: '00:00:00:00:00:00', family: 'IPv4', - internal: true }]; + internal: true, cidr: '127.0.0.1/8' }]; assert.deepStrictEqual(actual, expected); break; } } +function flatten(arr) { + return arr.reduce( + (acc, c) => acc.concat(Array.isArray(c) ? flatten(c) : c), + [] + ); +} +const netmaskToCIDRSuffixMap = new Map(Object.entries({ + '255.0.0.0': 8, + '255.255.255.0': 24, + 'ffff:ffff:ffff:ffff::': 64, + 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff': 128 +})); +flatten(Object.values(interfaces)) + .map((v) => ({ v, mask: netmaskToCIDRSuffixMap.get(v.netmask) })) + .forEach(({ v, mask }) => { + assert.ok('cidr' in v, `"cidr" prop not found in ${inspect(v)}`); + if (mask) { + assert.strictEqual(v.cidr, `${v.address}/${mask}`); + } + }); const EOL = os.EOL; assert.ok(EOL.length > 0); From 7f02c36c4fc1b8afa67aa99ef5647330f5d8562b Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Fri, 11 Aug 2017 17:27:44 -0700 Subject: [PATCH 009/231] test: fix test-cluster-send-handle-large-payload On macOS, the parent process might not receive a message if it is sent to soon, and then subsequent messages are also sometimes not received. (Is this a bug or expected operating system behavior like the way a file watcher is returned before it's actually watching the file system on/ macOS?) Send a second message after a delay on macOS. While at it, minor refactoring to the test: * Blank line after loading `common` module per test-writing guide * Wrap arrow function in braces where implicit return is not needed * Remove unnecessary unref in subprocess PR-URL: https://github.com/nodejs/node/pull/14780 Fixes: https://github.com/nodejs/node/issues/14747 Reviewed-By: Anna Henningsen --- .../test-cluster-send-handle-large-payload.js | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/test/parallel/test-cluster-send-handle-large-payload.js b/test/parallel/test-cluster-send-handle-large-payload.js index ba529593e28247..f0ffa63421bff6 100644 --- a/test/parallel/test-cluster-send-handle-large-payload.js +++ b/test/parallel/test-cluster-send-handle-large-payload.js @@ -1,5 +1,6 @@ 'use strict'; const common = require('../common'); + const assert = require('assert'); const cluster = require('cluster'); const net = require('net'); @@ -9,7 +10,7 @@ const payload = 'a'.repeat(800004); if (cluster.isMaster) { const server = net.createServer(); - server.on('connection', common.mustCall((socket) => socket.unref())); + server.on('connection', common.mustCall((socket) => { socket.unref(); })); const worker = cluster.fork(); worker.on('message', common.mustCall(({ payload: received }, handle) => { @@ -31,10 +32,23 @@ if (cluster.isMaster) { process.on('message', common.mustCall(({ payload: received }, handle) => { assert.strictEqual(payload, received); assert(handle instanceof net.Socket); - process.send({ payload }, handle); + + // On macOS, the parent process might not receive a message if it is sent + // to soon, and then subsequent messages are also sometimes not received. + // + // (Is this a bug or expected operating system behavior like the way a file + // watcher is returned before it's actually watching the file system on + // macOS?) + // + // Send a second message after a delay on macOS. + // + // Refs: https://github.com/nodejs/node/issues/14747 + if (common.isOSX) + setTimeout(() => { process.send({ payload }, handle); }, 1000); + else + process.send({ payload }, handle); // Prepare for a clean exit. process.channel.unref(); - handle.unref(); })); } From 4d2aa16d33a6a664972635308ba9b307fb2ba3bb Mon Sep 17 00:00:00 2001 From: Jimmy Thomson Date: Wed, 9 Aug 2017 08:58:10 -0700 Subject: [PATCH 010/231] process: keep process prototype in inheritance chain The global `process` object had its prototype replaced with a fresh object that had `EventEmitter.prototype` as its prototype. With this change, the original `process.constructor.prototype` is modified to have `EventEmitter.prototype` as its prototype, reflecting that `process` objects are also `EventEmitter`s. Fixes: https://github.com/nodejs/node/issues/14699 PR-URL: https://github.com/nodejs/node/pull/14715 Reviewed-By: Anna Henningsen --- lib/internal/bootstrap_node.js | 4 +--- test/parallel/test-process-prototype.js | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index 01a16a9f0c0936..670e35fb31635d 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -14,9 +14,7 @@ process._eventsCount = 0; const origProcProto = Object.getPrototypeOf(process); - Object.setPrototypeOf(process, Object.create(EventEmitter.prototype, { - constructor: Object.getOwnPropertyDescriptor(origProcProto, 'constructor') - })); + Object.setPrototypeOf(origProcProto, EventEmitter.prototype); EventEmitter.call(process); diff --git a/test/parallel/test-process-prototype.js b/test/parallel/test-process-prototype.js index 0a0de8123d127d..6eb442fd96e4d6 100644 --- a/test/parallel/test-process-prototype.js +++ b/test/parallel/test-process-prototype.js @@ -5,6 +5,7 @@ const EventEmitter = require('events'); const proto = Object.getPrototypeOf(process); +assert(process instanceof process.constructor); assert(proto instanceof EventEmitter); const desc = Object.getOwnPropertyDescriptor(proto, 'constructor'); From 9da6c1056caca63daca6b14928da079dc988f548 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Tue, 8 Aug 2017 15:00:17 -0700 Subject: [PATCH 011/231] tools: checkout for unassigned DEP00XX codes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check for `DEP00XX` codes on release build like we do with `REPLACEME` PR-URL: https://github.com/nodejs/node/pull/14702 Reviewed-By: Anna Henningsen Reviewed-By: Tobias Nießen --- Makefile | 5 +++++ doc/releases.md | 9 ++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 652c591e2bfe96..ced319ee68f550 100644 --- a/Makefile +++ b/Makefile @@ -672,6 +672,11 @@ release-only: echo 'Please update REPLACEME in Added: tags in doc/api/*.md (See doc/releases.md)' ; \ exit 1 ; \ fi + @if [ "$(DISTTYPE)" != "nightly" ] && [ "$(DISTTYPE)" != "next-nightly" ] && \ + `grep -q DEP00XX doc/api/deprecations.md`; then \ + echo 'Please update DEP00XX in doc/api/deprecations.md (See doc/releases.md)' ; \ + exit 1 ; \ + fi @if [ "$(shell git status --porcelain | egrep -v '^\?\? ')" = "" ]; then \ exit 0 ; \ else \ diff --git a/doc/releases.md b/doc/releases.md index d2dd317fdb416b..d1eeb05876085c 100644 --- a/doc/releases.md +++ b/doc/releases.md @@ -144,7 +144,7 @@ is shown in **bold** in the index. When updating the index, please make sure to update the display accordingly by removing the bold styling from the previous release. -#### Step 3: Update any REPLACEME tags in the docs +#### Step 3: Update any REPLACEME and DEP00XX tags in the docs If this release includes new APIs then it is necessary to document that they were first added in this version. The relevant commits should already include @@ -154,6 +154,13 @@ were first added in this version. The relevant commits should already include `sed -i "s/REPLACEME/$VERSION/g" doc/api/*.md` or `perl -pi -e "s/REPLACEME/$VERSION/g" doc/api/*.md`. +If this release includes any new deprecations it is necessary to ensure that +those are assigned a proper static deprecation code. These are listed in the +docs (see `doc/api/deprecations.md`) and in the source as `DEP00XX`. The code +must be assigned a number (e.g. `DEP0012`). Note that this assignment should +occur when the PR is landed, but a check will be made when the release built +is run. + ### 4. Create Release Commit The `CHANGELOG.md`, `doc/changelogs/CHANGELOG_*.md`, `src/node_version.h`, and From 27ec693a53fca35b787d4bae4cbb3ad22fee31bc Mon Sep 17 00:00:00 2001 From: Beth Griggs Date: Sat, 12 Aug 2017 13:04:46 +0100 Subject: [PATCH 012/231] test: add missing console.error to exec-maxBuffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the missing console.error to test-child-process-exec-maxBuffer PR-URL: https://github.com/nodejs/node/pull/14796 Reviewed-By: Anna Henningsen Reviewed-By: Vse Mozhet Byt Reviewed-By: Gibson Fahnestock Reviewed-By: David Cai Reviewed-By: Colin Ihrig Reviewed-By: Rich Trott Reviewed-By: Alexey Orlenko Reviewed-By: Tobias Nießen --- test/parallel/test-child-process-exec-maxBuffer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/parallel/test-child-process-exec-maxBuffer.js b/test/parallel/test-child-process-exec-maxBuffer.js index 65674a8503c3d2..2c585abcdf82a8 100644 --- a/test/parallel/test-child-process-exec-maxBuffer.js +++ b/test/parallel/test-child-process-exec-maxBuffer.js @@ -36,7 +36,7 @@ const unicode = '中文测试'; // length = 4, byte length = 12 } { - const cmd = `"${process.execPath}" -e "console.('${unicode}');"`; + const cmd = `"${process.execPath}" -e "console.error('${unicode}');"`; cp.exec(cmd, {maxBuffer: 10}, checkFactory('stderr')); } From 72cc2caf7824779bbae0ff53d305aeba45c64421 Mon Sep 17 00:00:00 2001 From: Saad Quadri Date: Sun, 13 Aug 2017 12:20:17 -0400 Subject: [PATCH 013/231] doc: fix word wrapping for api stability boxes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/14809 Fixes: https://github.com/nodejs/nodejs.org/issues/1337 Reviewed-By: Gibson Fahnestock Reviewed-By: Luigi Pinca Reviewed-By: Refael Ackermann Reviewed-By: James M Snell Reviewed-By: Daijiro Wachi Reviewed-By: Roman Reiss Reviewed-By: Tobias Nießen --- doc/api_assets/style.css | 3 ++- tools/doc/html.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/api_assets/style.css b/doc/api_assets/style.css index 20845970fbce37..3761be4031e178 100644 --- a/doc/api_assets/style.css +++ b/doc/api_assets/style.css @@ -93,7 +93,8 @@ em code { color: white !important; margin: 0 0 1em 0; font-family: "Lato", "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Verdana, Tahoma, sans-serif; - font-weight: 700; + padding: 1em; + line-height: 1.5; } .api_stability * { diff --git a/tools/doc/html.js b/tools/doc/html.js index a48b04b379da93..b0a3c13c69953d 100644 --- a/tools/doc/html.js +++ b/tools/doc/html.js @@ -400,7 +400,7 @@ function parseAPIHeader(text) { text = text.replace( STABILITY_TEXT_REG_EXP, - `
$1 $2$3
` + `` ); return text; } From 9589641c5cdf16b94edde59c137c59f51299bb50 Mon Sep 17 00:00:00 2001 From: Pini Houri Date: Fri, 11 Aug 2017 00:12:28 +0300 Subject: [PATCH 014/231] http2: Expose Http2ServerRequest/Response In order for express (and possibly other libraries) to get and use the Http2ServerRequest/Response - expose them in the http2 exports. Same as is done in http module. PR-URL: https://github.com/nodejs/node/pull/14690 Ref: https://github.com/expressjs/express/issues/3390 Fixes: https://github.com/nodejs/node/issues/14672 Reviewed-By: Benjamin Gruenbaum Reviewed-By: James M Snell Reviewed-By: Matteo Collina --- lib/http2.js | 8 ++++++-- lib/internal/http2/compat.js | 6 +++++- lib/internal/http2/core.js | 9 +++++++-- .../test-http2-request-response-proto.js | 19 +++++++++++++++++++ 4 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 test/parallel/test-http2-request-response-proto.js diff --git a/lib/http2.js b/lib/http2.js index e964abf589d0eb..ab9e33b2a2a859 100644 --- a/lib/http2.js +++ b/lib/http2.js @@ -13,7 +13,9 @@ const { getUnpackedSettings, createServer, createSecureServer, - connect + connect, + Http2ServerRequest, + Http2ServerResponse, } = require('internal/http2/core'); module.exports = { @@ -23,5 +25,7 @@ module.exports = { getUnpackedSettings, createServer, createSecureServer, - connect + connect, + Http2ServerResponse, + Http2ServerRequest, }; diff --git a/lib/internal/http2/compat.js b/lib/internal/http2/compat.js index 100e08a25df92b..c2ada307dda675 100644 --- a/lib/internal/http2/compat.js +++ b/lib/internal/http2/compat.js @@ -573,4 +573,8 @@ function onServerStream(stream, headers, flags) { server.emit('request', request, response); } -module.exports = { onServerStream }; +module.exports = { + onServerStream, + Http2ServerRequest, + Http2ServerResponse, +}; diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index c3681383f87e80..92d8e6077c9c75 100755 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -15,7 +15,10 @@ const fs = require('fs'); const errors = require('internal/errors'); const { Duplex } = require('stream'); const { URL } = require('url'); -const { onServerStream } = require('internal/http2/compat'); +const { onServerStream, + Http2ServerRequest, + Http2ServerResponse, +} = require('internal/http2/compat'); const { utcDate } = require('internal/http'); const { _connectionListener: httpConnectionListener } = require('http'); const { isUint8Array } = process.binding('util'); @@ -2552,7 +2555,9 @@ module.exports = { getUnpackedSettings, createServer, createSecureServer, - connect + connect, + Http2ServerRequest, + Http2ServerResponse }; /* eslint-enable no-use-before-define */ diff --git a/test/parallel/test-http2-request-response-proto.js b/test/parallel/test-http2-request-response-proto.js new file mode 100644 index 00000000000000..3bbf8e86434489 --- /dev/null +++ b/test/parallel/test-http2-request-response-proto.js @@ -0,0 +1,19 @@ +// Flags: --expose-http2 +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); + +const { + Http2ServerRequest, + Http2ServerResponse, +} = http2; + +const protoRequest = Object.create(Http2ServerRequest.prototype); +const protoResponse = Object.create(Http2ServerResponse.prototype); + +assert.strictEqual(protoRequest instanceof Http2ServerRequest, true); +assert.strictEqual(protoResponse instanceof Http2ServerResponse, true); From 4dd095c982ef0af2901cf0b7a65a17b5d9254159 Mon Sep 17 00:00:00 2001 From: Runite618 Date: Sun, 13 Aug 2017 20:23:20 +0100 Subject: [PATCH 015/231] test: refactor async-hooks/test-httparser tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CRLF variable was defined but only used on line 22 so the variable was deleted and placed inside line 22 as a string literal. This was in file test-httpparser.request.js On line 46 there's a function declared that takes 3 arguments but none of them are ever used so removed. This is in file test-httpparser.response.js PR-URL: https://github.com/nodejs/node/pull/14818 Reviewed-By: Vse Mozhet Byt Reviewed-By: Alexey Orlenko Reviewed-By: Gireesh Punathil Reviewed-By: Tobias Nießen Reviewed-By: Luigi Pinca Reviewed-By: Rich Trott Reviewed-By: Refael Ackermann Reviewed-By: James M Snell --- test/async-hooks/test-httpparser.request.js | 3 +-- test/async-hooks/test-httpparser.response.js | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/test/async-hooks/test-httpparser.request.js b/test/async-hooks/test-httpparser.request.js index ca15172de19817..c6e18be5a63d38 100644 --- a/test/async-hooks/test-httpparser.request.js +++ b/test/async-hooks/test-httpparser.request.js @@ -9,7 +9,6 @@ const { checkInvocations } = require('./hook-checks'); const binding = process.binding('http_parser'); const HTTPParser = binding.HTTPParser; -const CRLF = '\r\n'; const REQUEST = HTTPParser.REQUEST; const kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0; @@ -19,7 +18,7 @@ const hooks = initHooks(); hooks.enable(); const request = Buffer.from( - 'GET /hello HTTP/1.1' + CRLF + CRLF + 'GET /hello HTTP/1.1\r\n\r\n' ); const parser = new HTTPParser(REQUEST); diff --git a/test/async-hooks/test-httpparser.response.js b/test/async-hooks/test-httpparser.response.js index 5723ac6fbdca53..d56e3e1fecae2d 100644 --- a/test/async-hooks/test-httpparser.response.js +++ b/test/async-hooks/test-httpparser.response.js @@ -43,7 +43,7 @@ function onheadersComplete() { 'when onheadersComplete called'); } -function onbody(buf, start, len) { +function onbody() { checkInvocations(httpparser, { init: 1, before: 2, after: 1 }, 'when onbody called'); tick(1, common.mustCall(tick1)); From 0dc9d284a447d5a4a69f06566fe85a674118f3d9 Mon Sep 17 00:00:00 2001 From: Kyle Farnung Date: Mon, 7 Aug 2017 14:07:19 -0700 Subject: [PATCH 016/231] doc: added napi_get_value_string_latin1 * Reordered string functions alphabetically * Fixed a typo in napi_get_value_string_utf8 PR-URL: https://github.com/nodejs/node/pull/14678 Fixes: https://github.com/nodejs/node/issues/14397 Refs: https://github.com/nodejs/node/issues/14256 Reviewed-By: Refael Ackermann Reviewed-By: Colin Ihrig Reviewed-By: Anna Henningsen Reviewed-By: Michael Dawson Reviewed-By: James M Snell Reviewed-By: Timothy Gu --- doc/api/n-api.md | 73 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 23 deletions(-) diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 109674a4bf4b06..4397af96898c50 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -1462,51 +1462,51 @@ The JavaScript Number type is described in [Section 6.1.6](https://tc39.github.io/ecma262/#sec-ecmascript-language-types-number-type) of the ECMAScript Language Specification. -#### *napi_create_string_utf16* +#### *napi_create_string_latin1* ```C -napi_status napi_create_string_utf16(napi_env env, - const char16_t* str, - size_t length, - napi_value* result) +NAPI_EXTERN napi_status napi_create_string_latin1(napi_env env, + const char* str, + size_t length, + napi_value* result); ``` - `[in] env`: The environment that the API is invoked under. -- `[in] str`: Character buffer representing a UTF16-LE-encoded string. -- `[in] length`: The length of the string in two-byte code units, or -1 if -it is null-terminated. +- `[in] str`: Character buffer representing a ISO-8859-1-encoded string. +- `[in] length`: The length of the string in bytes, or -1 if it is +null-terminated. - `[out] result`: A `napi_value` representing a JavaScript String. Returns `napi_ok` if the API succeeded. -This API creates a JavaScript String object from a UTF16-LE-encoded C string +This API creates a JavaScript String object from a ISO-8859-1-encoded C string. The JavaScript String type is described in [Section 6.1.4](https://tc39.github.io/ecma262/#sec-ecmascript-language-types-string-type) of the ECMAScript Language Specification. -#### *napi_create_string_latin1* +#### *napi_create_string_utf16* ```C -NAPI_EXTERN napi_status napi_create_string_latin1(napi_env env, - const char* str, - size_t length, - napi_value* result); +napi_status napi_create_string_utf16(napi_env env, + const char16_t* str, + size_t length, + napi_value* result) ``` - `[in] env`: The environment that the API is invoked under. -- `[in] str`: Character buffer representing a latin1-encoded string. -- `[in] length`: The length of the string in bytes, or -1 if it is -null-terminated. +- `[in] str`: Character buffer representing a UTF16-LE-encoded string. +- `[in] length`: The length of the string in two-byte code units, or -1 if +it is null-terminated. - `[out] result`: A `napi_value` representing a JavaScript String. Returns `napi_ok` if the API succeeded. -This API creates a JavaScript String object from a latin1-encoded C string. +This API creates a JavaScript String object from a UTF16-LE-encoded C string The JavaScript String type is described in [Section 6.1.4](https://tc39.github.io/ecma262/#sec-ecmascript-language-types-string-type) @@ -1795,6 +1795,33 @@ is passed in it returns `napi_number_expected`. This API returns the C int64 primitive equivalent of the given JavaScript Number +#### *napi_get_value_string_latin1* + +```C +NAPI_EXTERN napi_status napi_get_value_string_latin1(napi_env env, + napi_value value, + char* buf, + size_t bufsize, + size_t* result) +``` + +- `[in] env`: The environment that the API is invoked under. +- `[in] value`: `napi_value` representing JavaScript string. +- `[in] buf`: Buffer to write the ISO-8859-1-encoded string into. If NULL is +passed in, the length of the string (in bytes) is returned. +- `[in] bufsize`: Size of the destination buffer. +- `[out] result`: Number of bytes copied into the buffer including the null +terminator. If the buffer size is insufficient, the string will be truncated +including a null terminator. + +Returns `napi_ok` if the API succeeded. If a non-String `napi_value` +is passed in it returns `napi_string_expected`. + +This API returns the ISO-8859-1-encoded string corresponding the value passed +in. + #### *napi_get_value_string_utf8* -* `request` {http.ClientRequest} +* `request` {http.IncomingMessage} * `response` {http.ServerResponse} Emitted each time a request with an HTTP `Expect` header is received, where the @@ -1224,8 +1231,8 @@ Example: ```js const http = require('http'); const server = http.createServer((req, res) => { - const ip = req.socket.remoteAddress; - const port = req.socket.remotePort; + const ip = res.socket.remoteAddress; + const port = res.socket.remotePort; res.end(`Your IP address is ${ip} and your source port is ${port}.`); }).listen(3000); ``` @@ -1883,6 +1890,7 @@ const req = http.request(options, (res) => { [`TypeError`]: errors.html#errors_class_typeerror [`URL`]: url.html#url_the_whatwg_url_api [`agent.createConnection()`]: #http_agent_createconnection_options_callback +[`agent.getName()`]: #http_agent_getname_options [`destroy()`]: #http_agent_destroy [`http.Agent`]: #http_class_http_agent [`http.ClientRequest`]: #http_class_http_clientrequest @@ -1898,6 +1906,7 @@ const req = http.request(options, (res) => { [`net.Server`]: net.html#net_class_net_server [`net.Socket`]: net.html#net_class_net_socket [`net.createConnection()`]: net.html#net_net_createconnection_options_connectlistener +[`request.end()`]: #http_request_end_data_encoding_callback [`request.socket`]: #http_request_socket [`request.socket.getPeerCertificate()`]: tls.html#tls_tlssocket_getpeercertificate_detailed [`request.write(data, encoding)`]: #http_request_write_chunk_encoding_callback From c5380c83c64f6a2fcf451315ffea402adc009965 Mon Sep 17 00:00:00 2001 From: Jon Moss Date: Fri, 18 Aug 2017 14:36:33 -0400 Subject: [PATCH 043/231] doc: add missing word PR-URL: https://github.com/nodejs/node/pull/14924 Reviewed-By: Anna Henningsen Reviewed-By: Luigi Pinca Reviewed-By: Rich Trott Reviewed-By: Colin Ihrig Reviewed-By: James M Snell --- BUILDING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILDING.md b/BUILDING.md index 47630ddf150216..bd2218673b62b1 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -134,7 +134,7 @@ This will build Node.js first (if necessary) and then use it to build the docs: $ make doc ``` -If you have an existing Node.js you can build just the docs with: +If you have an existing Node.js build, you can build just the docs with: ```console $ NODE=/path/to/node make doc-only From e4ea45412e87ee3254183e6659ec4522abac2ac0 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Wed, 16 Aug 2017 13:15:04 -0700 Subject: [PATCH 044/231] tools: remove stray package-lock.json file Before the ESLint updates were automated, a stray package-lock.json file was accidentally introduced in the tools directory. This change removes it. PR-URL: https://github.com/nodejs/node/pull/14873 Reviewed-By: Jeremiah Senkpiel Reviewed-By: Alexey Orlenko Reviewed-By: Ruben Bridgewater Reviewed-By: James M Snell Reviewed-By: Colin Ihrig Reviewed-By: Timothy Gu Reviewed-By: Refael Ackermann --- tools/package-lock.json | 792 ---------------------------------------- tools/update-eslint.sh | 4 +- 2 files changed, 2 insertions(+), 794 deletions(-) delete mode 100644 tools/package-lock.json diff --git a/tools/package-lock.json b/tools/package-lock.json deleted file mode 100644 index cd42a9cbe93a6a..00000000000000 --- a/tools/package-lock.json +++ /dev/null @@ -1,792 +0,0 @@ -{ - "lockfileVersion": 1, - "dependencies": { - "acorn": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.0.3.tgz", - "integrity": "sha1-xGDfCEkUY/AozLguqzcwvwEIez0=" - }, - "acorn-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", - "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", - "dependencies": { - "acorn": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=" - } - } - }, - "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=" - }, - "ajv-keywords": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", - "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=" - }, - "ansi-escapes": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-2.0.0.tgz", - "integrity": "sha1-W65SvkJIeN2Xg+iRDj/Cki6DyBs=" - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "argparse": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", - "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=" - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=" - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" - }, - "babel-code-frame": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz", - "integrity": "sha1-AnYgvuVnqIwyVhV05/0IAdMxGOQ=" - }, - "bail": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.1.tgz", - "integrity": "sha1-kSV53os5Gq3zxf30zSoPwiXfO8I=" - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "brace-expansion": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", - "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=" - }, - "caller-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", - "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=" - }, - "callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=" - }, - "ccount": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.1.tgz", - "integrity": "sha1-ZlaHlFFowhjsd/9hpBVa4AInqWw=" - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=" - }, - "character-entities": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.0.tgz", - "integrity": "sha1-poPiz3Xb6LFxljUxNk5Y4YobFV8=" - }, - "character-entities-html4": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.0.tgz", - "integrity": "sha1-GrCFUdPOH6HfCNAPucod77FHoGw=" - }, - "character-entities-legacy": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.0.tgz", - "integrity": "sha1-sYqtmPa3vMZGweTIH58ZVjdqVho=" - }, - "character-reference-invalid": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.0.tgz", - "integrity": "sha1-3smtHfufjQa0/NqircPE/ZevHmg=" - }, - "circular-json": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.1.tgz", - "integrity": "sha1-vos2rvzN6LPKeqLWr8B6NyQsDS0=" - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=" - }, - "cli-width": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.1.0.tgz", - "integrity": "sha1-sjTKIJsp72b8UY2bmNWEewDt8Ao=" - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" - }, - "collapse-white-space": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.2.tgz", - "integrity": "sha1-nEY/ucbRkNLcriGjVqAbyunu720=" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "concat-stream": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", - "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "debug": { - "version": "2.6.8", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", - "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=" - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" - }, - "del": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=" - }, - "doctrine": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz", - "integrity": "sha1-xz2NKQnSIpHhoAejlYBNqLZl/mM=" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "eslint": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.1.0.tgz", - "integrity": "sha1-u7VaKCIO4Itp2pVU1FprLr/X2RM=" - }, - "eslint-plugin-markdown": { - "version": "1.0.0-beta.7", - "resolved": "https://registry.npmjs.org/eslint-plugin-markdown/-/eslint-plugin-markdown-1.0.0-beta.7.tgz", - "integrity": "sha1-Euc6QSfEpLedlm+fR1hR3Q949+c=" - }, - "eslint-scope": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", - "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=" - }, - "espree": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/espree/-/espree-3.4.3.tgz", - "integrity": "sha1-KRC1zNSc6JPC//+qtP2LOjG4I3Q=" - }, - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" - }, - "esquery": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz", - "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=" - }, - "esrecurse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz", - "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=" - }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" - }, - "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" - }, - "external-editor": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.0.4.tgz", - "integrity": "sha1-HtkZnanL/i7y96MbL96LDRI2iXI=" - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=" - }, - "file-entry-cache": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", - "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=" - }, - "flat-cache": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.2.tgz", - "integrity": "sha1-+oZxTnLCHbiGAXYezy9VXRq8a5Y=" - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "function-bind": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz", - "integrity": "sha1-FhdnFMgBeY5Ojyz391KUZ7tKV3E=" - }, - "generate-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", - "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=" - }, - "generate-object-property": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", - "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=" - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==" - }, - "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==" - }, - "globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=" - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" - }, - "has": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", - "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=" - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=" - }, - "iconv-lite": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz", - "integrity": "sha512-sr1ZQph3UwHTR0XftSbK85OvBbxe/abLGzEnPENCQwmHf7sck8Oyu4ob3LgBxWWxRoM+QszeUyl7jbqapu2TqA==" - }, - "ignore": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.3.tgz", - "integrity": "sha1-QyNS5XrM2HqzEQ6C0/6g5HgSFW0=" - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=" - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "inquirer": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.1.1.tgz", - "integrity": "sha512-H50sHQwgvvaTBd3HpKMVtL/u6LoHDvYym51gd7bGQe/+9HkCE+J0/3N5FJLfd6O6oz44hHewC2Pc2LodzWVafQ==" - }, - "is-alphabetical": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.0.tgz", - "integrity": "sha1-4lRMEwWCVfIUTLdXBmzTNCocjEY=" - }, - "is-alphanumerical": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.0.tgz", - "integrity": "sha1-4GSS5xnBvxXewjnk8a9fZ7TW578=" - }, - "is-decimal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.0.tgz", - "integrity": "sha1-lAV5tupjxigICmnmK9qIyEcLT+A=" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "is-hexadecimal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.0.tgz", - "integrity": "sha1-XEWXcdKvmi45Ungf1U/LG8/kETw=" - }, - "is-my-json-valid": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz", - "integrity": "sha1-8Hndm/2uZe4gOKrorLyGqxCeNpM=" - }, - "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=" - }, - "is-path-in-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", - "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=" - }, - "is-path-inside": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz", - "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=" - }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" - }, - "is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=" - }, - "is-resolvable": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz", - "integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "js-tokens": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.1.tgz", - "integrity": "sha1-COnxMkhKLEWjCQfp3E1VZ7fxFNc=" - }, - "js-yaml": { - "version": "3.8.4", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.8.4.tgz", - "integrity": "sha1-UgtFZPhlc7qWZir4Woyvp7S1pvY=" - }, - "jschardet": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jschardet/-/jschardet-1.4.2.tgz", - "integrity": "sha1-KqEH8UKvQSHRRWWdRPUIMJYeaZo=" - }, - "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=" - }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" - }, - "jsonpointer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", - "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=" - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=" - }, - "lodash": { - "version": "4.17.4", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" - }, - "longest-streak": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-1.0.0.tgz", - "integrity": "sha1-0GWXxNTDG1LMsfXY+P5xSOr9aWU=" - }, - "markdown-table": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-0.4.0.tgz", - "integrity": "sha1-iQwsGzv+g/sA5BKbjkz+ZFJw+dE=" - }, - "mimic-fn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.1.0.tgz", - "integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg=" - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==" - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=" - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=" - }, - "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=" - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "parse-entities": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.1.1.tgz", - "integrity": "sha1-gRLYhHExnyerrk1klksSL+ThuJA=" - }, - "parse5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-2.2.3.tgz", - "integrity": "sha1-DE/EHBAAxea5PUiwP4CDg3g06fY=" - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=" - }, - "pluralize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-4.0.0.tgz", - "integrity": "sha1-WbcIwcAZCi9pLxx2GMRGsFL9F2I=" - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" - }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" - }, - "progress": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", - "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=" - }, - "readable-stream": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz", - "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=" - }, - "remark": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/remark/-/remark-5.1.0.tgz", - "integrity": "sha1-y0Y709vLS5l5STXu4c9x16jjBow=" - }, - "remark-parse": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-1.1.0.tgz", - "integrity": "sha1-w8oQ+ajaBGFcKPCapOMEUQUm7CE=" - }, - "remark-stringify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-1.1.0.tgz", - "integrity": "sha1-pxBeJbnuK/mkm3XSxCPxGwauIJI=" - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" - }, - "require-uncached": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", - "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=" - }, - "resolve-from": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", - "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=" - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=" - }, - "rimraf": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", - "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=" - }, - "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=" - }, - "rx-lite": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", - "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=" - }, - "rx-lite-aggregates": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", - "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=" - }, - "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" - }, - "slice-ansi": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", - "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=" - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==" - }, - "string-width": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.0.0.tgz", - "integrity": "sha1-Y1xUNsxypuDDh87KJ41OLuxSaH4=" - }, - "stringify-entities": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-1.3.1.tgz", - "integrity": "sha1-sVDsLXKsTBtfMktR+2soyc3/BYw=" - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=" - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - }, - "table": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.1.tgz", - "integrity": "sha1-qBFsEz+sLGH0pCCrbN9cTWHw5DU=" - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, - "tmp": { - "version": "0.0.31", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz", - "integrity": "sha1-jzirlDjhcxXl29izZX6L+yd65Kc=" - }, - "trim": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", - "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=" - }, - "trim-trailing-lines": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.0.tgz", - "integrity": "sha1-eu+7eAjfnWafbaLkOMrIxGradoQ=" - }, - "trough": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.0.tgz", - "integrity": "sha1-a97f5/KqSabzxDIldodVWVfzQv0=" - }, - "tryit": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz", - "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=" - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=" - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" - }, - "unherit": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.0.tgz", - "integrity": "sha1-a5qu379z3xdWrZ4xbdmBiFhAzX0=" - }, - "unified": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/unified/-/unified-4.2.1.tgz", - "integrity": "sha1-dv9Dqo2kMPbn5KVchOusKtLPzS4=" - }, - "unist-util-remove-position": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.1.tgz", - "integrity": "sha1-WoXBVV/BugwQG4ZwfRXlD6TIcbs=" - }, - "unist-util-visit": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.1.3.tgz", - "integrity": "sha1-7CaOcxudJ3p5pbWqBkOZDkBdYAs=" - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "vfile": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-1.4.0.tgz", - "integrity": "sha1-wP1vpIT43r23cfaMMe112I2pf+c=" - }, - "vfile-location": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.1.tgz", - "integrity": "sha1-C/iBb3MrD4vZAqVv2kxiyOk13FI=" - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "write": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=" - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" - } - } -} diff --git a/tools/update-eslint.sh b/tools/update-eslint.sh index 3616d62db334d2..7de687f715cf25 100755 --- a/tools/update-eslint.sh +++ b/tools/update-eslint.sh @@ -13,10 +13,10 @@ mkdir eslint-tmp cd eslint-tmp npm init --yes -npm install --global-style --no-bin-links --production eslint@latest +npm install --global-style --no-bin-links --production --no-package-lock eslint@latest cd node_modules/eslint -npm install --no-bin-links --production eslint-plugin-markdown@next +npm install --no-bin-links --production --no-package-lock eslint-plugin-markdown@next cd ../.. # Install dmn if it is not in path. From 081c3e107deba2659c24472d2e8c6a28e1fbbda7 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 16 Aug 2017 09:34:37 -0700 Subject: [PATCH 045/231] src: minor cleanup for node_revert Make the revert related functions inline to eliminate the need for node_revert.cc, prefix the constants and the def, other misc cleanup PR-URL: https://github.com/nodejs/node/pull/14864 Reviewed-By: Anna Henningsen --- node.gyp | 2 -- src/node.cc | 7 +++-- src/node_config.cc | 1 - src/node_revert.cc | 53 ------------------------------------- src/node_revert.h | 65 ++++++++++++++++++++++++++++++---------------- 5 files changed, 48 insertions(+), 80 deletions(-) delete mode 100644 src/node_revert.cc diff --git a/node.gyp b/node.gyp index 25c3b35163961d..b9b2a881a1eb93 100644 --- a/node.gyp +++ b/node.gyp @@ -190,7 +190,6 @@ 'src/node_main.cc', 'src/node_os.cc', 'src/node_platform.cc', - 'src/node_revert.cc', 'src/node_serdes.cc', 'src/node_url.cc', 'src/node_util.cc', @@ -646,7 +645,6 @@ '<(OBJ_PATH)<(OBJ_SEPARATOR)string_search.<(OBJ_SUFFIX)', '<(OBJ_PATH)<(OBJ_SEPARATOR)stream_base.<(OBJ_SUFFIX)', '<(OBJ_PATH)<(OBJ_SEPARATOR)node_constants.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)node_revert.<(OBJ_SUFFIX)', '<(OBJ_TRACING_PATH)<(OBJ_SEPARATOR)agent.<(OBJ_SUFFIX)', '<(OBJ_TRACING_PATH)<(OBJ_SEPARATOR)node_trace_buffer.<(OBJ_SUFFIX)', '<(OBJ_TRACING_PATH)<(OBJ_SEPARATOR)node_trace_writer.<(OBJ_SUFFIX)', diff --git a/src/node.cc b/src/node.cc index 46c68f40250dd6..3c9a6e6ce65743 100644 --- a/src/node.cc +++ b/src/node.cc @@ -184,6 +184,9 @@ static bool trace_enabled = false; static std::string trace_enabled_categories; // NOLINT(runtime/string) static bool abort_on_uncaught_exception = false; +// Bit flag used to track security reverts (see node_revert.h) +unsigned int reverted = 0; + #if defined(NODE_HAVE_I18N_SUPPORT) // Path to ICU data (for i18n / Intl) std::string icu_data_dir; // NOLINT(runtime/string) @@ -3437,11 +3440,11 @@ void SetupProcessObject(Environment* env, // --security-revert flags #define V(code, _, __) \ do { \ - if (IsReverted(REVERT_ ## code)) { \ + if (IsReverted(SECURITY_REVERT_ ## code)) { \ READONLY_PROPERTY(process, "REVERT_" #code, True(env->isolate())); \ } \ } while (0); - REVERSIONS(V) + SECURITY_REVERSIONS(V) #undef V size_t exec_path_len = 2 * PATH_MAX; diff --git a/src/node_config.cc b/src/node_config.cc index 041e18f6b76ff9..02701259c77ba9 100644 --- a/src/node_config.cc +++ b/src/node_config.cc @@ -6,7 +6,6 @@ #include "util-inl.h" #include "node_debug_options.h" - namespace node { using v8::Boolean; diff --git a/src/node_revert.cc b/src/node_revert.cc deleted file mode 100644 index 9d029a3592fd0c..00000000000000 --- a/src/node_revert.cc +++ /dev/null @@ -1,53 +0,0 @@ -#include "node_revert.h" -#include -#include - -namespace node { - -unsigned int reverted = 0; - -static const char* RevertMessage(const unsigned int cve) { -#define V(code, label, msg) case REVERT_ ## code: return label ": " msg; - switch (cve) { - REVERSIONS(V) - default: - return "Unknown"; - } -#undef V -} - -void Revert(const unsigned int cve) { - reverted |= 1 << cve; - printf("SECURITY WARNING: Reverting %s\n", RevertMessage(cve)); -} - -void Revert(const char* cve) { -#define V(code, label, _) \ - do { \ - if (strcmp(cve, label) == 0) { \ - Revert(static_cast(REVERT_ ## code)); \ - return; \ - } \ - } while (0); - REVERSIONS(V) -#undef V - printf("Error: Attempt to revert an unknown CVE [%s]\n", cve); - exit(12); -} - -bool IsReverted(const unsigned int cve) { - return reverted & (1 << cve); -} - -bool IsReverted(const char * cve) { -#define V(code, label, _) \ - do { \ - if (strcmp(cve, label) == 0) \ - return IsReverted(static_cast(REVERT_ ## code)); \ - } while (0); - REVERSIONS(V) - return false; -#undef V -} - -} // namespace node diff --git a/src/node_revert.h b/src/node_revert.h index b4c3633e947a6e..c26bb677818cfa 100644 --- a/src/node_revert.h +++ b/src/node_revert.h @@ -6,40 +6,61 @@ #include "node.h" /** - * Note that it is expected for this list to vary across specific LTS and - * Stable versions! Only CVE's whose fixes require *breaking* changes within - * a given LTS or Stable may be added to this list, and only with CTC - * consensus. + * Note that it is expected for this list to vary across specific LTS and + * Stable versions! Only CVE's whose fixes require *breaking* changes within + * a given LTS or Stable may be added to this list, and only with CTC + * consensus. * * For *master* this list should always be empty! - * **/ -#define REVERSIONS(XX) -// XX(CVE_2016_PEND, "CVE-2016-PEND", "Vulnerability Title") - namespace node { -typedef enum { -#define V(code, _, __) REVERT_ ## code, - REVERSIONS(V) -#undef V -} reversions_t; +#define SECURITY_REVERSIONS(XX) +// XX(CVE_2016_PEND, "CVE-2016-PEND", "Vulnerability Title") +enum reversion { +#define V(code, ...) SECURITY_REVERT_##code, + SECURITY_REVERSIONS(V) +#undef V +}; -/* A bit field for tracking the active reverts */ extern unsigned int reverted; -/* Revert the given CVE (see reversions_t enum) */ -void Revert(const unsigned int cve); +inline const char* RevertMessage(const reversion cve) { +#define V(code, label, msg) case SECURITY_REVERT_##code: return label ": " msg; + switch (cve) { + SECURITY_REVERSIONS(V) + default: + return "Unknown"; + } +#undef V +} -/* Revert the given CVE by label */ -void Revert(const char* cve); +inline void Revert(const reversion cve) { + reverted |= 1 << cve; + printf("SECURITY WARNING: Reverting %s\n", RevertMessage(cve)); +} -/* true if the CVE has been reverted **/ -bool IsReverted(const unsigned int cve); +inline void Revert(const char* cve) { +#define V(code, label, _) \ + if (strcmp(cve, label) == 0) return Revert(SECURITY_REVERT_##code); + SECURITY_REVERSIONS(V) +#undef V + printf("Error: Attempt to revert an unknown CVE [%s]\n", cve); + exit(12); +} -/* true if the CVE has been reverted **/ -bool IsReverted(const char * cve); +inline bool IsReverted(const reversion cve) { + return reverted & (1 << cve); +} + +inline bool IsReverted(const char* cve) { +#define V(code, label, _) \ + if (strcmp(cve, label) == 0) return IsReverted(SECURITY_REVERT_##code); + SECURITY_REVERSIONS(V) + return false; +#undef V +} } // namespace node From bfa3cbe158334e1611ee2f72717b1b1d9b59ddc8 Mon Sep 17 00:00:00 2001 From: George Sapkin Date: Wed, 16 Aug 2017 17:06:45 +0200 Subject: [PATCH 046/231] doc: remove redundant only from doc/api/stream.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/14858 Reviewed-By: Evan Lucas Reviewed-By: Yuta Hiroto Reviewed-By: Tobias Nießen Reviewed-By: Vse Mozhet Byt Reviewed-By: James M Snell --- doc/api/stream.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/api/stream.md b/doc/api/stream.md index 3663fe2d780830..aa95d2e4e4cd6a 100644 --- a/doc/api/stream.md +++ b/doc/api/stream.md @@ -1383,7 +1383,7 @@ resource. [`writable._write()`][stream-_write]. *Note*: This function MUST NOT be called by application code directly. It -should be implemented by child classes, and called only by the internal Writable +should be implemented by child classes, and called by the internal Writable class methods only. The `callback` method must be called to signal either that the write completed @@ -1418,7 +1418,7 @@ user programs. argument) to be invoked when processing is complete for the supplied chunks. *Note*: This function MUST NOT be called by application code directly. It -should be implemented by child classes, and called only by the internal Writable +should be implemented by child classes, and called by the internal Writable class methods only. The `writable._writev()` method may be implemented in addition to @@ -1573,7 +1573,7 @@ const myReadable = new Readable({ * `size` {number} Number of bytes to read asynchronously *Note*: This function MUST NOT be called by application code directly. It -should be implemented by child classes, and called only by the internal Readable +should be implemented by child classes, and called by the internal Readable class methods only. All Readable stream implementations must provide an implementation of the @@ -1974,7 +1974,7 @@ after all data has been output, which occurs after the callback in argument and data) to be called when remaining data has been flushed. *Note*: This function MUST NOT be called by application code directly. It -should be implemented by child classes, and called only by the internal Readable +should be implemented by child classes, and called by the internal Readable class methods only. In some cases, a transform operation may need to emit an additional bit of @@ -2009,7 +2009,7 @@ user programs. processed. *Note*: This function MUST NOT be called by application code directly. It -should be implemented by child classes, and called only by the internal Readable +should be implemented by child classes, and called by the internal Readable class methods only. All Transform stream implementations must provide a `_transform()` From 762155578ac16520cfbdb5719e3c484779d23fc2 Mon Sep 17 00:00:00 2001 From: Beth Griggs Date: Fri, 18 Aug 2017 13:26:16 +0100 Subject: [PATCH 047/231] test: remove erroneous assert message from test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes the incorrect 'exit successfully' message from test when the exit code is 0. PR-URL: https://github.com/nodejs/node/pull/14918 Reviewed-By: Colin Ihrig Reviewed-By: Rich Trott Reviewed-By: James M Snell Reviewed-By: Tobias Nießen Reviewed-By: Refael Ackermann Reviewed-By: Luigi Pinca --- test/parallel/test-process-external-stdio-close-spawn.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/parallel/test-process-external-stdio-close-spawn.js b/test/parallel/test-process-external-stdio-close-spawn.js index b1ab52ff8a9d9c..93d7f795bd74ce 100644 --- a/test/parallel/test-process-external-stdio-close-spawn.js +++ b/test/parallel/test-process-external-stdio-close-spawn.js @@ -22,7 +22,7 @@ if (process.argv[2] === 'child') { }); child.on('close', common.mustCall((exitCode, signal) => { - assert.strictEqual(exitCode, 0, 'exit successfully'); + assert.strictEqual(exitCode, 0); assert.strictEqual(signal, null); })); From 205d5f674ae260dd3fc753d27673aed1b59f75ee Mon Sep 17 00:00:00 2001 From: Aleh Zasypkin Date: Wed, 16 Aug 2017 19:32:54 +0200 Subject: [PATCH 048/231] doc,fs: rename defaultEncoding option to encoding The documentation for `fs.createWriteStream()` references a `defaultEncoding` as possible options property, but in reality `encoding` property is expected and properly handled. This fix updates the documentation to rename the `defaultEncoding` property to `encoding`. PR-URL: https://github.com/nodejs/node/pull/14867 Fixes: https://github.com/nodejs/node/issues/14611 Reviewed-By: Luigi Pinca Reviewed-By: Refael Ackermann Reviewed-By: Matteo Collina Reviewed-By: James M Snell --- doc/api/fs.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/api/fs.md b/doc/api/fs.md index 6aeee5d55e412f..8496211be9666d 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -835,7 +835,7 @@ changes: * `path` {string|Buffer|URL} * `options` {string|Object} * `flags` {string} - * `defaultEncoding` {string} + * `encoding` {string} * `fd` {integer} * `mode` {integer} * `autoClose` {boolean} @@ -848,7 +848,7 @@ Returns a new [`WriteStream`][] object. (See [Writable Stream][]). ```js const defaults = { flags: 'w', - defaultEncoding: 'utf8', + encoding: 'utf8', fd: null, mode: 0o666, autoClose: true @@ -858,7 +858,7 @@ const defaults = { `options` may also include a `start` option to allow writing data at some position past the beginning of the file. Modifying a file rather than replacing it may require a `flags` mode of `r+` rather than the -default mode `w`. The `defaultEncoding` can be any one of those accepted by +default mode `w`. The `encoding` can be any one of those accepted by [`Buffer`][]. If `autoClose` is set to true (default behavior) on `error` or `end` From c0312dc7817490fa4ded2c158e2eb292d9c42d61 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Mon, 14 Aug 2017 15:38:28 -0700 Subject: [PATCH 049/231] test: make timers-blocking-callback more reliable test-timers-blocking-callback may fail erroneously on resource-constrained machines due to the timing nature of the test. There is likely no way around the timing issue. This change tries to decrease the probability of the test failing erroneously by having it retry a small number of times on failure. Tested on 0.10.38 (which has a bug that this test was written for) and (modifying the test slightly to remove ES6 stuff) the test still seems to fail 100% of the time there, which is what we want/expect. PR-URL: https://github.com/nodejs/node/pull/14831 Fixes: https://github.com/nodejs/node/issues/14792 Reviewed-By: Refael Ackermann Reviewed-By: Jeremiah Senkpiel Reviewed-By: James M Snell --- .../test-timers-blocking-callback.js | 69 ++++++++++++++----- 1 file changed, 51 insertions(+), 18 deletions(-) diff --git a/test/sequential/test-timers-blocking-callback.js b/test/sequential/test-timers-blocking-callback.js index 73b0f13997716d..22697cbd780238 100644 --- a/test/sequential/test-timers-blocking-callback.js +++ b/test/sequential/test-timers-blocking-callback.js @@ -1,8 +1,9 @@ 'use strict'; /* - * This is a regression test for https://github.com/joyent/node/issues/15447 - * and https://github.com/joyent/node/issues/9333. + * This is a regression test for + * https://github.com/nodejs/node-v0.x-archive/issues/15447 and + * and https://github.com/nodejs/node-v0.x-archive/issues/9333. * * When a timer is added in another timer's callback, its underlying timer * handle was started with a timeout that was actually incorrect. @@ -28,9 +29,15 @@ const Timer = process.binding('timer_wrap').Timer; const TIMEOUT = 100; -let nbBlockingCallbackCalls = 0; -let latestDelay = 0; -let timeCallbackScheduled = 0; +let nbBlockingCallbackCalls; +let latestDelay; +let timeCallbackScheduled; + +// These tests are timing dependent so they may fail even when the bug is +// not present (if the host is sufficiently busy that the timers are delayed +// significantly). However, they fail 100% of the time when the bug *is* +// present, so to increase reliability, allow for a small number of retries. +let retries = 2; function initTest() { nbBlockingCallbackCalls = 0; @@ -38,7 +45,7 @@ function initTest() { timeCallbackScheduled = 0; } -function blockingCallback(callback) { +function blockingCallback(retry, callback) { ++nbBlockingCallbackCalls; if (nbBlockingCallbackCalls > 1) { @@ -47,8 +54,14 @@ function blockingCallback(callback) { // to fire, they shouldn't generally be more than 100% late in this case. // But they are guaranteed to be at least 100ms late given the bug in // https://github.com/nodejs/node-v0.x-archive/issues/15447 and - // https://github.com/nodejs/node-v0.x-archive/issues/9333.. - assert(latestDelay < TIMEOUT * 2); + // https://github.com/nodejs/node-v0.x-archive/issues/9333. + if (latestDelay >= TIMEOUT * 2) { + if (retries > 0) { + retries--; + return retry(callback); + } + assert.fail(`timeout delayed by more than 100% (${latestDelay}ms)`); + } if (callback) return callback(); } else { @@ -56,25 +69,45 @@ function blockingCallback(callback) { common.busyLoop(TIMEOUT); timeCallbackScheduled = Timer.now(); - setTimeout(blockingCallback.bind(null, callback), TIMEOUT); + setTimeout(blockingCallback.bind(null, retry, callback), TIMEOUT); } } -const testAddingTimerToEmptyTimersList = common.mustCall(function(callback) { +function testAddingTimerToEmptyTimersList(callback) { initTest(); // Call setTimeout just once to make sure the timers list is // empty when blockingCallback is called. - setTimeout(blockingCallback.bind(null, callback), TIMEOUT); -}); + setTimeout( + blockingCallback.bind(null, testAddingTimerToEmptyTimersList, callback), + TIMEOUT + ); +} + +function testAddingTimerToNonEmptyTimersList() { + // If both timers fail and attempt a retry, only actually do anything for one + // of them. + let retryOK = true; + const retry = () => { + if (retryOK) + testAddingTimerToNonEmptyTimersList(); + retryOK = false; + }; -const testAddingTimerToNonEmptyTimersList = common.mustCall(function() { initTest(); // Call setTimeout twice with the same timeout to make // sure the timers list is not empty when blockingCallback is called. - setTimeout(blockingCallback, TIMEOUT); - setTimeout(blockingCallback, TIMEOUT); -}); + setTimeout( + blockingCallback.bind(null, retry), + TIMEOUT + ); + setTimeout( + blockingCallback.bind(null, retry), + TIMEOUT + ); +} // Run the test for the empty timers list case, and then for the non-empty -// timers list one -testAddingTimerToEmptyTimersList(testAddingTimerToNonEmptyTimersList); +// timers list one. +testAddingTimerToEmptyTimersList( + common.mustCall(testAddingTimerToNonEmptyTimersList) +); From 96d95d4fed691a51a3b156e5e631e9c2f9ded7f0 Mon Sep 17 00:00:00 2001 From: Anand Suresh Date: Wed, 16 Aug 2017 15:02:10 -0700 Subject: [PATCH 050/231] doc: minor fixes to http/2 docs PR-URL: https://github.com/nodejs/node/pull/14877 Reviewed-By: James M Snell Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig Reviewed-By: Luigi Pinca --- doc/api/http2.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/api/http2.md b/doc/api/http2.md index eaf88483cd0bd4..13a5750158fa2f 100755 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -192,7 +192,7 @@ added: v8.4.0 --> The `'remoteSettings'` event is emitted when a new SETTINGS frame is received -from the connected peer. When invoked, the handle function will receive a copy +from the connected peer. When invoked, the handler function will receive a copy of the remote settings. ```js @@ -1725,7 +1725,7 @@ those to lower-case (e.g. `content-type`) upon transmission. Header field-names *must only* contain one or more of the following ASCII characters: `a`-`z`, `A`-`Z`, `0`-`9`, `!`, `#`, `$`, `%`, `&`, `'`, `*`, `+`, -`-`, `.`, `^`, `_`, `` (backtick), `|`, and `~`. +`-`, `.`, `^`, `_`, `` ` `` (backtick), `|`, and `~`. Using invalid characters within an HTTP header field name will cause the stream to be closed with a protocol error being reported. @@ -1883,12 +1883,12 @@ const server = createSecureServer( function onRequest(req, res) { // detects if it is a HTTPS request or HTTP/2 - const { socket: { alpnProtocol } } = request.httpVersion === '2.0' ? - request.stream.session : request; - response.writeHead(200, { 'content-type': 'application/json' }); - response.end(JSON.stringify({ + const { socket: { alpnProtocol } } = req.httpVersion === '2.0' ? + req.stream.session : req; + res.writeHead(200, { 'content-type': 'application/json' }); + res.end(JSON.stringify({ alpnProtocol, - httpVersion: request.httpVersion + httpVersion: req.httpVersion })); } ``` From 8f61bf2cda7b91d78709d2021495d731c9c025b6 Mon Sep 17 00:00:00 2001 From: Michael Albert Date: Tue, 15 Aug 2017 01:23:22 +0200 Subject: [PATCH 051/231] test: increase coverage for http2.connect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added checks for connecting using https and an unsupported protocol. PR-URL: https://github.com/nodejs/node/pull/14832 Reviewed-By: James M Snell Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig Reviewed-By: Yuta Hiroto Reviewed-By: Tobias Nießen --- test/parallel/test-http2-connect.js | 62 +++++++++++++++++++---------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/test/parallel/test-http2-connect.js b/test/parallel/test-http2-connect.js index 087f942932c519..60777dadeb8e1e 100644 --- a/test/parallel/test-http2-connect.js +++ b/test/parallel/test-http2-connect.js @@ -1,31 +1,49 @@ // Flags: --expose-http2 'use strict'; -const { mustCall, hasCrypto, skip } = require('../common'); +const { mustCall, hasCrypto, skip, expectsError } = require('../common'); if (!hasCrypto) skip('missing crypto'); -const { doesNotThrow } = require('assert'); +const { doesNotThrow, throws } = require('assert'); const { createServer, connect } = require('http2'); +{ + const server = createServer(); + server.listen(0, mustCall(() => { + const authority = `http://localhost:${server.address().port}`; + const options = {}; + const listener = () => mustCall(); -const server = createServer(); -server.listen(0, mustCall(() => { - const authority = `http://localhost:${server.address().port}`; - const options = {}; - const listener = () => mustCall(); + const clients = new Set(); + doesNotThrow(() => clients.add(connect(authority))); + doesNotThrow(() => clients.add(connect(authority, options))); + doesNotThrow(() => clients.add(connect(authority, options, listener()))); + doesNotThrow(() => clients.add(connect(authority, listener()))); - const clients = new Set(); - doesNotThrow(() => clients.add(connect(authority))); - doesNotThrow(() => clients.add(connect(authority, options))); - doesNotThrow(() => clients.add(connect(authority, options, listener()))); - doesNotThrow(() => clients.add(connect(authority, listener()))); + for (const client of clients) { + client.once('connect', mustCall((headers) => { + client.destroy(); + clients.delete(client); + if (clients.size === 0) { + server.close(); + } + })); + } + })); +} - for (const client of clients) { - client.once('connect', mustCall((headers) => { - client.destroy(); - clients.delete(client); - if (clients.size === 0) { - server.close(); - } - })); - } -})); +// check for https as protocol +{ + const authority = 'https://localhost'; + doesNotThrow(() => connect(authority)); +} + +// check for error for an invalid protocol (not http or https) +{ + const authority = 'ssh://localhost'; + throws(() => { + connect(authority); + }, expectsError({ + code: 'ERR_HTTP2_UNSUPPORTED_PROTOCOL', + type: Error + })); +} From de10c0f515ed69286f4e8d45ac98963717d5f4d8 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 16 Aug 2017 21:21:29 -0300 Subject: [PATCH 052/231] util: fix inspect array w. negative maxArrayLength MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/14880 Reviewed-By: Timothy Gu Reviewed-By: Alexey Orlenko Reviewed-By: James M Snell Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig Reviewed-By: Tobias Nießen --- lib/util.js | 7 ++++--- test/parallel/test-util-inspect.js | 4 ++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/util.js b/lib/util.js index 94d7b828bd2845..b1225365d365c3 100644 --- a/lib/util.js +++ b/lib/util.js @@ -682,11 +682,12 @@ function formatObject(ctx, value, recurseTimes, visibleKeys, keys) { function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { + const maxLength = Math.min(Math.max(0, ctx.maxArrayLength), value.length); var output = []; let visibleLength = 0; let index = 0; for (const elem of keys) { - if (visibleLength === ctx.maxArrayLength) + if (visibleLength === maxLength) break; // Symbols might have been added to the keys if (typeof elem !== 'string') @@ -701,7 +702,7 @@ function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { const message = `<${emptyItems} empty item${ending}>`; output.push(ctx.stylize(message, 'undefined')); index = i; - if (++visibleLength === ctx.maxArrayLength) + if (++visibleLength === maxLength) break; } output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, @@ -709,7 +710,7 @@ function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { visibleLength++; index++; } - if (index < value.length && visibleLength !== ctx.maxArrayLength) { + if (index < value.length && visibleLength !== maxLength) { const len = value.length - index; const ending = len > 1 ? 's' : ''; const message = `<${len} empty item${ending}>`; diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 8cbd6a36227f0f..42717ee79dc84c 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -970,6 +970,10 @@ if (typeof Symbol !== 'undefined') { { const x = new Array(101).fill(); assert(!util.inspect(x, { maxArrayLength: 101 }).endsWith('1 more item ]')); + assert.strictEqual( + util.inspect(x, { maxArrayLength: -1 }), + '[ ... 101 more items ]' + ); } { From 838d3fef72f26bb8734aacc4d885949319f3eb3f Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Tue, 15 Aug 2017 09:17:02 +0200 Subject: [PATCH 053/231] build: don't add libraries when --enable-static Currently when building with --enabled-static the cctest target will include libraries to be linked regardless. This commit adds a condition to only add the libraries when dynamically linking. This commit re-adds the previously reverted commit (be63c26e8c "build: don't add libraries when --enable-static"). The reason that commit failed was a mistake on my part where I moved the the list into a variable (thinking that it was a simple change that would not affect anything) and not re-running CI. But the list itself contains variables used to enable differences in operating systems/compilers where the object file locations vary. PR-URL: https://github.com/nodejs/node/pull/14912 Reviewed-By: Ben Noordhuis Reviewed-By: Refael Ackermann --- node.gyp | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/node.gyp b/node.gyp index b9b2a881a1eb93..3d70a80197c140 100644 --- a/node.gyp +++ b/node.gyp @@ -631,26 +631,6 @@ '<(SHARED_INTERMEDIATE_DIR)', # for node_natives.h ], - 'libraries': [ - '<(OBJ_GEN_PATH)<(OBJ_SEPARATOR)node_javascript.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)node_debug_options.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)async-wrap.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)env.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)node.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)node_buffer.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)node_i18n.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)node_url.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)util.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)string_bytes.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)string_search.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)stream_base.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)node_constants.<(OBJ_SUFFIX)', - '<(OBJ_TRACING_PATH)<(OBJ_SEPARATOR)agent.<(OBJ_SUFFIX)', - '<(OBJ_TRACING_PATH)<(OBJ_SEPARATOR)node_trace_buffer.<(OBJ_SUFFIX)', - '<(OBJ_TRACING_PATH)<(OBJ_SEPARATOR)node_trace_writer.<(OBJ_SUFFIX)', - '<(OBJ_TRACING_PATH)<(OBJ_SEPARATOR)trace_event.<(OBJ_SUFFIX)', - ], - 'defines': [ 'NODE_WANT_INTERNALS=1' ], 'sources': [ @@ -667,6 +647,27 @@ ], 'conditions': [ + ['node_target_type!="static_library"', { + 'libraries': [ + '<(OBJ_GEN_PATH)<(OBJ_SEPARATOR)node_javascript.<(OBJ_SUFFIX)', + '<(OBJ_PATH)<(OBJ_SEPARATOR)node_debug_options.<(OBJ_SUFFIX)', + '<(OBJ_PATH)<(OBJ_SEPARATOR)async-wrap.<(OBJ_SUFFIX)', + '<(OBJ_PATH)<(OBJ_SEPARATOR)env.<(OBJ_SUFFIX)', + '<(OBJ_PATH)<(OBJ_SEPARATOR)node.<(OBJ_SUFFIX)', + '<(OBJ_PATH)<(OBJ_SEPARATOR)node_buffer.<(OBJ_SUFFIX)', + '<(OBJ_PATH)<(OBJ_SEPARATOR)node_i18n.<(OBJ_SUFFIX)', + '<(OBJ_PATH)<(OBJ_SEPARATOR)node_url.<(OBJ_SUFFIX)', + '<(OBJ_PATH)<(OBJ_SEPARATOR)util.<(OBJ_SUFFIX)', + '<(OBJ_PATH)<(OBJ_SEPARATOR)string_bytes.<(OBJ_SUFFIX)', + '<(OBJ_PATH)<(OBJ_SEPARATOR)string_search.<(OBJ_SUFFIX)', + '<(OBJ_PATH)<(OBJ_SEPARATOR)stream_base.<(OBJ_SUFFIX)', + '<(OBJ_PATH)<(OBJ_SEPARATOR)node_constants.<(OBJ_SUFFIX)', + '<(OBJ_TRACING_PATH)<(OBJ_SEPARATOR)agent.<(OBJ_SUFFIX)', + '<(OBJ_TRACING_PATH)<(OBJ_SEPARATOR)node_trace_buffer.<(OBJ_SUFFIX)', + '<(OBJ_TRACING_PATH)<(OBJ_SEPARATOR)node_trace_writer.<(OBJ_SUFFIX)', + '<(OBJ_TRACING_PATH)<(OBJ_SEPARATOR)trace_event.<(OBJ_SUFFIX)', + ], + }], ['v8_enable_inspector==1', { 'sources': [ 'test/cctest/test_inspector_socket.cc', From 8f3537f66a59e31ca31a30ba02c5aa1891cc7640 Mon Sep 17 00:00:00 2001 From: Jon Moss Date: Fri, 18 Aug 2017 18:48:45 -0400 Subject: [PATCH 054/231] build: allow proper generation of html docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `gen-doc` always calls `gen-json`, which means it's impossible to generate html docs. Changed this to pass in the command the user wants to run. PR-URL: https://github.com/nodejs/node/pull/14932 Fixes: https://github.com/nodejs/node/issues/14930 Reviewed-By: Refael Ackermann Reviewed-By: Tobias Nießen --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 5c856e6a2d2657..5b4f6d87f96cd6 100644 --- a/Makefile +++ b/Makefile @@ -503,14 +503,14 @@ gen-doc = \ else \ cd tools/doc && node ../../$(NPM) install; \ fi;\ - [ -x $(NODE) ] && $(NODE) $(gen-json) || node + [ -x $(NODE) ] && $(NODE) $(1) || node $(1) out/doc/api/%.json: doc/api/%.md - $(gen-doc) $(gen-json) + $(call gen-doc, $(gen-json)) # check if ./node is actually set, else use user pre-installed binary out/doc/api/%.html: doc/api/%.md - $(gen-doc) $(gen-html) + $(call gen-doc, $(gen-html)) docopen: $(apidocs_html) @$(PYTHON) -mwebbrowser file://$(PWD)/out/doc/api/all.html From 4570fa16c74e0a4c47f8fa05a3eead27904ac7b3 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Fri, 18 Aug 2017 04:34:05 +0200 Subject: [PATCH 055/231] src: remove extra copy from Copy() in node_url.cc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The was copying the whole array and the strings in it without any benefit. PR-URL: https://github.com/nodejs/node/pull/14907 Reviewed-By: James M Snell Reviewed-By: Tobias Nießen Reviewed-By: Timothy Gu Reviewed-By: Colin Ihrig --- src/node_url.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node_url.cc b/src/node_url.cc index 01e46eb7643e53..dd3da1133ebf2a 100644 --- a/src/node_url.cc +++ b/src/node_url.cc @@ -1007,7 +1007,7 @@ static inline void Copy(Environment* env, } static inline Local Copy(Environment* env, - std::vector vec) { + const std::vector& vec) { Isolate* isolate = env->isolate(); Local ary = Array::New(isolate, vec.size()); for (size_t n = 0; n < vec.size(); n++) From 992d1dd95617d75ea7a623156544c1f90a574286 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 16 Aug 2017 11:46:55 +0200 Subject: [PATCH 056/231] src: detect nul bytes in InternalModuleReadFile() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Throw an exception when the path contains nul bytes, don't abort. Fixes: https://github.com/nodejs/node/issues/13787 PR-URL: https://github.com/nodejs/node/pull/14854 Reviewed-By: James M Snell Reviewed-By: Tobias Nießen Reviewed-By: Timothy Gu Reviewed-By: Colin Ihrig Reviewed-By: Anna Henningsen --- src/node_file.cc | 3 +++ test/parallel/test-require-nul.js | 9 +++++++++ 2 files changed, 12 insertions(+) create mode 100644 test/parallel/test-require-nul.js diff --git a/src/node_file.cc b/src/node_file.cc index beaf581afca0ff..9e0553a7a579a0 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -503,6 +503,9 @@ static void InternalModuleReadFile(const FunctionCallbackInfo& args) { CHECK(args[0]->IsString()); node::Utf8Value path(env->isolate(), args[0]); + if (strlen(*path) != path.length()) + return; // Contains a nul byte. + uv_fs_t open_req; const int fd = uv_fs_open(loop, &open_req, *path, O_RDONLY, 0, nullptr); uv_fs_req_cleanup(&open_req); diff --git a/test/parallel/test-require-nul.js b/test/parallel/test-require-nul.js new file mode 100644 index 00000000000000..0c5cb7018d47fc --- /dev/null +++ b/test/parallel/test-require-nul.js @@ -0,0 +1,9 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +// Nul bytes should throw, not abort. +assert.throws(() => require('\u0000ab'), /Cannot find module '\u0000ab'/); +assert.throws(() => require('a\u0000b'), /Cannot find module 'a\u0000b'/); +assert.throws(() => require('ab\u0000'), /Cannot find module 'ab\u0000'/); From 6c93d01fbadf53deb17fdd9d26de3095444fad43 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Fri, 18 Aug 2017 23:09:14 -0700 Subject: [PATCH 057/231] doc: remove `you` and fixup note in stream.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/14938 Reviewed-By: Timothy Gu Reviewed-By: Ruben Bridgewater Reviewed-By: Luigi Pinca Reviewed-By: Rich Trott Reviewed-By: Colin Ihrig Reviewed-By: Yuta Hiroto Reviewed-By: Tobias Nießen Reviewed-By: Refael Ackermann --- doc/api/stream.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api/stream.md b/doc/api/stream.md index aa95d2e4e4cd6a..ffe0df4a20b73b 100644 --- a/doc/api/stream.md +++ b/doc/api/stream.md @@ -1445,9 +1445,9 @@ added: v8.0.0 --> * `callback` {Function} Call this function (optionally with an error - argument) when you are done writing any remaining data. + argument) when finished writing any remaining data. -Note: `_final()` **must not** be called directly. It MAY be implemented +The `_final()` method **must not** be called directly. It may be implemented by child classes, and if so, will be called by the internal Writable class methods only. From daae6bc652cf24c82431497a90fae543ab9e401b Mon Sep 17 00:00:00 2001 From: sharababy Date: Thu, 17 Aug 2017 13:35:28 +0530 Subject: [PATCH 058/231] doc: fixed link definitions in http2.md footer PR-URL: https://github.com/nodejs/node/pull/14946 Fixes: https://github.com/nodejs/node/issues/14857 Reviewed-By: Refael Ackermann Reviewed-By: James M Snell --- doc/api/http2.md | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/doc/api/http2.md b/doc/api/http2.md index 13a5750158fa2f..9d779eecca98b6 100755 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -2576,25 +2576,37 @@ given newly created [`Http2Stream`] on `Http2ServerRespose`. The callback will be called with an error with code `ERR_HTTP2_STREAM_CLOSED` if the stream is closed. -[HTTP/2]: https://tools.ietf.org/html/rfc7540 +[ALPN negotiation]: #http2_alpn_negotiation +[Compatibility API]: #http2_compatibility_api [HTTP/1]: http.html +[HTTP/2]: https://tools.ietf.org/html/rfc7540 [HTTPS]: https.html -[`net.Socket`]: net.html -[`tls.TLSSocket`]: tls.html -[`tls.createServer()`]: tls.html#tls_tls_createserver_options_secureconnectionlistener -[`ClientHttp2Stream`]: #http2_class_clienthttp2stream -[Compatibility API]: #http2_compatibility_api -[ALPN negotiation]: #http2_alpn_negotiation -[`Duplex`]: stream.html#stream_class_stream_duplex [Headers Object]: #http2_headers_object -[`Http2Stream`]: #http2_class_http2stream [Http2Session and Sockets]: #http2_http2sesion_and_sockets -[`ServerHttp2Stream`]: #http2_class_serverhttp2stream +[Readable Stream]: stream.html#stream_class_stream_readable [Settings Object]: #http2_settings_object [Using options.selectPadding]: #http2_using_options_selectpadding -[error code]: #error_codes -[`'unknownProtocol'`]: #http2_event_unknownprotocol +[Writable Stream]: stream.html#stream_writable_streams [`'request'`]: #http2_event_request -[Readable Stream]: stream.html#stream_class_stream_readable +[`'unknownProtocol'`]: #http2_event_unknownprotocol +[`ClientHttp2Stream`]: #http2_class_clienthttp2stream +[`Duplex`]: stream.html#stream_class_stream_duplex +[`EventEmitter`]: events.html#events_class_eventemitter +[`Http2Stream`]: #http2_class_http2stream +[`ServerHttp2Stream`]: #http2_class_serverhttp2stream [`ServerRequest`]: #http2_class_server_request +[`TypeError`]: errors.html#errors_class_typeerror +[`http2.SecureServer`]: #http2_class_http2secureserver +[`http2.Server`]: #http2_class_http2server +[`net.Socket`]: net.html#net_class_net_socket +[`request.socket.getPeerCertificate()`]: tls.html#tls_tlssocket_getpeercertificate_detailed +[`response.end()`]: #http2_response_end_data_encoding_callback +[`response.setHeader()`]: #http2_response_setheader_name_value +[`response.socket`]: #http2_response_socket +[`response.write()`]: #http2_response_write_chunk_encoding_callback +[`response.write(data, encoding)`]: http.html#http_response_write_chunk_encoding_callback +[`response.writeHead()`]: #http2_response_writehead_statuscode_statusmessage_headers [`stream.pushStream()`]: #http2_stream-pushstream +[`tls.TLSSocket`]: tls.html#tls_class_tls_tlssocket +[`tls.createServer()`]: tls.html#tls_tls_createserver_options_secureconnectionlistener +[error code]: #error_codes From 7f1ea7c3afce6104ef15a7d5617124de7008dc7b Mon Sep 17 00:00:00 2001 From: Abhishek Raj Date: Fri, 18 Aug 2017 13:04:22 +0530 Subject: [PATCH 059/231] benchmark: removed unused arguments from callbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removed unused arguments 'buf' and 'rinfo' from the callbacks. PR-URL: https://github.com/nodejs/node/pull/14919 Reviewed-By: Rich Trott Reviewed-By: James M Snell Reviewed-By: Tobias Nießen Reviewed-By: Luigi Pinca Reviewed-By: Ruben Bridgewater --- benchmark/dgram/multi-buffer.js | 2 +- benchmark/dgram/offset-length.js | 2 +- benchmark/dgram/single-buffer.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/benchmark/dgram/multi-buffer.js b/benchmark/dgram/multi-buffer.js index 6a7fc9bfaf83ee..a0285c8c59e015 100644 --- a/benchmark/dgram/multi-buffer.js +++ b/benchmark/dgram/multi-buffer.js @@ -64,7 +64,7 @@ function server() { }, dur * 1000); }); - socket.on('message', function(buf, rinfo) { + socket.on('message', function() { received++; }); diff --git a/benchmark/dgram/offset-length.js b/benchmark/dgram/offset-length.js index b897707ded5e58..0445f7b70bec8b 100644 --- a/benchmark/dgram/offset-length.js +++ b/benchmark/dgram/offset-length.js @@ -56,7 +56,7 @@ function server() { }, dur * 1000); }); - socket.on('message', function(buf, rinfo) { + socket.on('message', function() { received++; }); diff --git a/benchmark/dgram/single-buffer.js b/benchmark/dgram/single-buffer.js index 8b81d7fbfc0794..e5fcac63f640fc 100644 --- a/benchmark/dgram/single-buffer.js +++ b/benchmark/dgram/single-buffer.js @@ -56,7 +56,7 @@ function server() { }, dur * 1000); }); - socket.on('message', function(buf, rinfo) { + socket.on('message', function() { received++; }); From e244f8433e0ecd03f2effa1bc4b87329ac1a729d Mon Sep 17 00:00:00 2001 From: cjihrig Date: Sun, 20 Aug 2017 00:04:43 -0400 Subject: [PATCH 060/231] test: update windows module load error message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit libuv 1.14.0 includes a fix for the "%1 is not a valid Win32 application" error message. Refs: https://github.com/nodejs/node/pull/14866 PR-URL: https://github.com/nodejs/node/pull/14950 Reviewed-By: Richard Lau Reviewed-By: Tobias Nießen Reviewed-By: Gibson Fahnestock Reviewed-By: Refael Ackermann Reviewed-By: James M Snell --- test/parallel/test-module-loading-error.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/parallel/test-module-loading-error.js b/test/parallel/test-module-loading-error.js index 0816c5f94a5ec5..68f45d774e5ce0 100644 --- a/test/parallel/test-module-loading-error.js +++ b/test/parallel/test-module-loading-error.js @@ -25,7 +25,7 @@ const assert = require('assert'); const { execSync } = require('child_process'); const errorMessagesByPlatform = { - win32: ['%1 is not a valid Win32 application'], + win32: ['is not a valid Win32 application'], linux: ['file too short', 'Exec format error'], sunos: ['unknown file type', 'not an ELF file'], darwin: ['file too short'], From a6344d5a83cca4a0dc9335d45560a4ffd79ccf08 Mon Sep 17 00:00:00 2001 From: Gabriel Schulhof Date: Thu, 27 Jul 2017 20:59:29 +0300 Subject: [PATCH 061/231] n-api: add ability to remove a wrapping Calling napi_wrap() twice on the same object has the result of returning napi_invalid_arg on the second call. However, sometimes it is necessary to replace the native pointer associated with a given object. This new API allows one to remove an existing pointer, returning the object to its pristine, non-wrapped state. PR-URL: https://github.com/nodejs/node/pull/14658 Reviewed-By: Michael Dawson Fixes: https://github.com/nodejs/abi-stable-node/issues/266 --- doc/api/n-api.md | 30 ++++++- src/node_api.cc | 87 +++++++++++++++----- src/node_api.h | 3 + test/addons-napi/test_general/test.js | 30 ++++++- test/addons-napi/test_general/test_general.c | 53 ++++++++++++ 5 files changed, 178 insertions(+), 25 deletions(-) diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 4397af96898c50..31103d75a9e1dd 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -3152,7 +3152,9 @@ Afterward, additional manipulation of the wrapper's prototype chain may cause *Note*: Calling `napi_wrap()` a second time on an object that already has a native instance associated with it by virtue of a previous call to -`napi_wrap()` will cause an error to be returned. +`napi_wrap()` will cause an error to be returned. If you wish to associate +another native instance with the given object, call `napi_remove_wrap()` on it +first. ### *napi_unwrap* +```C +napi_status napi_remove_wrap(napi_env env, + napi_value js_object, + void** result); +``` + + - `[in] env`: The environment that the API is invoked under. + - `[in] js_object`: The object associated with the native instance. + - `[out] result`: Pointer to the wrapped native instance. + +Returns `napi_ok` if the API succeeded. + +Retrieves a native instance that was previously wrapped in the JavaScript +object `js_object` using `napi_wrap()` and removes the wrapping, thereby +restoring the JavaScript object's prototype chain. If a finalize callback was +associated with the wrapping, it will no longer be called when the JavaScript +object becomes garbage-collected. + ## Asynchronous Operations Addon modules often need to leverage async helpers from libuv as part of their diff --git a/src/node_api.cc b/src/node_api.cc index b84a33e510f264..bec98e07ce95d1 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -674,6 +674,8 @@ v8::Local CreateAccessorCallbackData(napi_env env, return cbdata; } +int kWrapperFields = 3; + // Pointer used to identify items wrapped by N-API. Used by FindWrapper and // napi_wrap(). const char napi_wrap_name[] = "N-API Wrapper"; @@ -682,7 +684,8 @@ const char napi_wrap_name[] = "N-API Wrapper"; // wrapper would be the first in the chain, but it is OK for other objects to // be inserted in the prototype chain. bool FindWrapper(v8::Local obj, - v8::Local* result = nullptr) { + v8::Local* result = nullptr, + v8::Local* parent = nullptr) { v8::Local wrapper = obj; do { @@ -690,8 +693,11 @@ bool FindWrapper(v8::Local obj, if (proto.IsEmpty() || !proto->IsObject()) { return false; } + if (parent != nullptr) { + *parent = wrapper; + } wrapper = proto.As(); - if (wrapper->InternalFieldCount() == 2) { + if (wrapper->InternalFieldCount() == kWrapperFields) { v8::Local external = wrapper->GetInternalField(1); if (external->IsExternal() && external.As()->Value() == v8impl::napi_wrap_name) { @@ -745,6 +751,29 @@ napi_env GetEnv(v8::Local context) { return result; } +napi_status Unwrap(napi_env env, + napi_value js_object, + void** result, + v8::Local* wrapper, + v8::Local* parent = nullptr) { + CHECK_ARG(env, js_object); + CHECK_ARG(env, result); + + v8::Local value = v8impl::V8LocalValueFromJsValue(js_object); + RETURN_STATUS_IF_FALSE(env, value->IsObject(), napi_invalid_arg); + v8::Local obj = value.As(); + + RETURN_STATUS_IF_FALSE( + env, v8impl::FindWrapper(obj, wrapper, parent), napi_invalid_arg); + + v8::Local unwrappedValue = (*wrapper)->GetInternalField(0); + RETURN_STATUS_IF_FALSE(env, unwrappedValue->IsExternal(), napi_invalid_arg); + + *result = unwrappedValue.As()->Value(); + + return napi_ok; +} + } // end of namespace v8impl // Intercepts the Node-V8 module registration callback. Converts parameters @@ -2266,62 +2295,78 @@ napi_status napi_wrap(napi_env env, // Create a wrapper object with an internal field to hold the wrapped pointer // and a second internal field to identify the owner as N-API. v8::Local wrapper_template; - ENV_OBJECT_TEMPLATE(env, wrap, wrapper_template, 2); + ENV_OBJECT_TEMPLATE(env, wrap, wrapper_template, v8impl::kWrapperFields); auto maybe_object = wrapper_template->NewInstance(context); CHECK_MAYBE_EMPTY(env, maybe_object, napi_generic_failure); - v8::Local wrapper = maybe_object.ToLocalChecked(); - wrapper->SetInternalField(1, v8::External::New(isolate, - reinterpret_cast(const_cast(v8impl::napi_wrap_name)))); // Store the pointer as an external in the wrapper. wrapper->SetInternalField(0, v8::External::New(isolate, native_object)); + wrapper->SetInternalField(1, v8::External::New(isolate, + reinterpret_cast(const_cast(v8impl::napi_wrap_name)))); // Insert the wrapper into the object's prototype chain. v8::Local proto = obj->GetPrototype(); CHECK(wrapper->SetPrototype(context, proto).FromJust()); CHECK(obj->SetPrototype(context, wrapper).FromJust()); + v8impl::Reference* reference = nullptr; if (result != nullptr) { // The returned reference should be deleted via napi_delete_reference() // ONLY in response to the finalize callback invocation. (If it is deleted // before then, then the finalize callback will never be invoked.) // Therefore a finalize callback is required when returning a reference. CHECK_ARG(env, finalize_cb); - v8impl::Reference* reference = v8impl::Reference::New( + reference = v8impl::Reference::New( env, obj, 0, false, finalize_cb, native_object, finalize_hint); *result = reinterpret_cast(reference); } else if (finalize_cb != nullptr) { // Create a self-deleting reference just for the finalize callback. - v8impl::Reference::New( + reference = v8impl::Reference::New( env, obj, 0, true, finalize_cb, native_object, finalize_hint); } + if (reference != nullptr) { + wrapper->SetInternalField(2, v8::External::New(isolate, reference)); + } + return GET_RETURN_STATUS(env); } -napi_status napi_unwrap(napi_env env, napi_value js_object, void** result) { +napi_status napi_unwrap(napi_env env, napi_value obj, void** result) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. CHECK_ENV(env); - CHECK_ARG(env, js_object); - CHECK_ARG(env, result); - - v8::Local value = v8impl::V8LocalValueFromJsValue(js_object); - RETURN_STATUS_IF_FALSE(env, value->IsObject(), napi_invalid_arg); - v8::Local obj = value.As(); + v8::Local wrapper; + return napi_set_last_error(env, v8impl::Unwrap(env, obj, result, &wrapper)); +} +napi_status napi_remove_wrap(napi_env env, napi_value obj, void** result) { + NAPI_PREAMBLE(env); v8::Local wrapper; - RETURN_STATUS_IF_FALSE( - env, v8impl::FindWrapper(obj, &wrapper), napi_invalid_arg); + v8::Local parent; + napi_status status = v8impl::Unwrap(env, obj, result, &wrapper, &parent); + if (status != napi_ok) { + return napi_set_last_error(env, status); + } - v8::Local unwrappedValue = wrapper->GetInternalField(0); - RETURN_STATUS_IF_FALSE(env, unwrappedValue->IsExternal(), napi_invalid_arg); + v8::Local external = wrapper->GetInternalField(2); + if (external->IsExternal()) { + v8impl::Reference::Delete( + static_cast(external.As()->Value())); + } - *result = unwrappedValue.As()->Value(); + if (!parent.IsEmpty()) { + v8::Maybe maybe = parent->SetPrototype( + env->isolate->GetCurrentContext(), wrapper->GetPrototype()); + CHECK_MAYBE_NOTHING(env, maybe, napi_generic_failure); + if (!maybe.FromMaybe(false)) { + return napi_set_last_error(env, napi_generic_failure); + } + } - return napi_clear_last_error(env); + return GET_RETURN_STATUS(env); } napi_status napi_create_external(napi_env env, diff --git a/src/node_api.h b/src/node_api.h index 0cf0ba046997d3..e52e2016d733b9 100644 --- a/src/node_api.h +++ b/src/node_api.h @@ -362,6 +362,9 @@ NAPI_EXTERN napi_status napi_wrap(napi_env env, NAPI_EXTERN napi_status napi_unwrap(napi_env env, napi_value js_object, void** result); +NAPI_EXTERN napi_status napi_remove_wrap(napi_env env, + napi_value js_object, + void** result); NAPI_EXTERN napi_status napi_create_external(napi_env env, void* data, napi_finalize finalize_cb, diff --git a/test/addons-napi/test_general/test.js b/test/addons-napi/test_general/test.js index 8e0740a68c45c6..484707e868db58 100644 --- a/test/addons-napi/test_general/test.js +++ b/test/addons-napi/test_general/test.js @@ -1,4 +1,5 @@ 'use strict'; +// Flags: --expose-gc const common = require('../../common'); const test_general = require(`./build/${common.buildType}/test_general`); @@ -56,10 +57,37 @@ assert.strictEqual(release, process.release.name); // for null assert.strictEqual(test_general.testNapiTypeof(null), 'null'); -const x = {}; +// Ensure that garbage collecting an object with a wrapped native item results +// in the finalize callback being called. +let w = {}; +test_general.wrap(w, []); +w = null; +global.gc(); +assert.strictEqual(test_general.derefItemWasCalled(), true, + 'deref_item() was called upon garbage collecting a ' + + 'wrapped object'); // Assert that wrapping twice fails. +const x = {}; test_general.wrap(x, 25); assert.throws(function() { test_general.wrap(x, 'Blah'); }, Error); + +// Ensure that wrapping, removing the wrap, and then wrapping again works. +const y = {}; +test_general.wrap(y, -12); +test_general.removeWrap(y); +assert.doesNotThrow(function() { + test_general.wrap(y, 're-wrap!'); +}, Error, 'Wrapping twice succeeds if a remove_wrap() separates the instances'); + +// Ensure that removing a wrap and garbage collecting does not fire the +// finalize callback. +let z = {}; +test_general.testFinalizeWrap(z); +test_general.removeWrap(z); +z = null; +global.gc(); +assert.strictEqual(test_general.finalizeWasCalled(), false, + 'finalize callback was not called upon garbage collection'); diff --git a/test/addons-napi/test_general/test_general.c b/test/addons-napi/test_general/test_general.c index da347bd68bcc9a..ecec3e014ba0b1 100644 --- a/test/addons-napi/test_general/test_general.c +++ b/test/addons-napi/test_general/test_general.c @@ -138,17 +138,29 @@ napi_value testNapiTypeof(napi_env env, napi_callback_info info) { return result; } +static bool deref_item_called = false; static void deref_item(napi_env env, void* data, void* hint) { (void) hint; + deref_item_called = true; NAPI_CALL_RETURN_VOID(env, napi_delete_reference(env, (napi_ref)data)); } +napi_value deref_item_was_called(napi_env env, napi_callback_info info) { + napi_value it_was_called; + + NAPI_CALL(env, napi_get_boolean(env, deref_item_called, &it_was_called)); + + return it_was_called; +} + napi_value wrap(napi_env env, napi_callback_info info) { size_t argc = 2; napi_value argv[2]; napi_ref payload; + deref_item_called = false; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL)); NAPI_CALL(env, napi_create_reference(env, argv[1], 1, &payload)); NAPI_CALL(env, napi_wrap(env, argv[0], payload, deref_item, NULL, NULL)); @@ -156,6 +168,43 @@ napi_value wrap(napi_env env, napi_callback_info info) { return NULL; } +napi_value remove_wrap(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value wrapped; + void* data; + + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &wrapped, NULL, NULL)); + NAPI_CALL(env, napi_remove_wrap(env, wrapped, &data)); + if (data != NULL) { + NAPI_CALL(env, napi_delete_reference(env, (napi_ref)data)); + } + + return NULL; +} + +static bool finalize_called = false; +static void test_finalize(napi_env env, void* data, void* hint) { + finalize_called = true; +} + +napi_value test_finalize_wrap(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value js_object; + + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &js_object, NULL, NULL)); + NAPI_CALL(env, napi_wrap(env, js_object, NULL, test_finalize, NULL, NULL)); + + return NULL; +} + +napi_value finalize_was_called(napi_env env, napi_callback_info info) { + napi_value it_was_called; + + NAPI_CALL(env, napi_get_boolean(env, finalize_called, &it_was_called)); + + return it_was_called; +} + void Init(napi_env env, napi_value exports, napi_value module, void* priv) { napi_property_descriptor descriptors[] = { DECLARE_NAPI_PROPERTY("testStrictEquals", testStrictEquals), @@ -169,6 +218,10 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { DECLARE_NAPI_PROPERTY("testNapiErrorCleanup", testNapiErrorCleanup), DECLARE_NAPI_PROPERTY("testNapiTypeof", testNapiTypeof), DECLARE_NAPI_PROPERTY("wrap", wrap), + DECLARE_NAPI_PROPERTY("removeWrap", remove_wrap), + DECLARE_NAPI_PROPERTY("testFinalizeWrap", test_finalize_wrap), + DECLARE_NAPI_PROPERTY("finalizeWasCalled", finalize_was_called), + DECLARE_NAPI_PROPERTY("derefItemWasCalled", deref_item_was_called), }; NAPI_CALL_RETURN_VOID(env, napi_define_properties( From 9f46bde4406d24c001d9c84ce7284391165df167 Mon Sep 17 00:00:00 2001 From: Matt Loring Date: Sat, 19 Aug 2017 10:17:09 -0700 Subject: [PATCH 062/231] deps: backport d727680 from V8 upstream MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Original commit message: [d8] Bring PredictablePlatform in line with default platform This removes a lot of special handling for the predictable platform. Instead of executing spawned foreground and background tasks immediately (i.e. inside the scope that spawns the tasks), just add both to the foreground task queue. This avoids existing special handling for predictable mode in wasm async compilation, and should fix current failures on the predictable bot. BUG=v8:6427 Change-Id: Idbaa764a3dc8c230c29f3937d885e12174691ac4 Reviewed-on: https://chromium-review.googlesource.com/509694 Reviewed-by: Jochen Eisinger Commit-Queue: Clemens Hammacher Cr-Commit-Position: refs/heads/master@{#45538} PR-URL: https://github.com/nodejs/node/pull/14947 Reviewed-By: Refael Ackermann Reviewed-By: James M Snell Reviewed-By: Nikolai Vavilov Reviewed-By: Michaël Zasso --- deps/v8/src/d8.cc | 179 +++++++++----------- deps/v8/src/heap/page-parallel-job.h | 2 +- deps/v8/src/libplatform/default-platform.cc | 14 +- deps/v8/src/wasm/wasm-module.cc | 23 +-- 4 files changed, 95 insertions(+), 123 deletions(-) diff --git a/deps/v8/src/d8.cc b/deps/v8/src/d8.cc index d92f8e0b05b06f..1bb5300ce98b6e 100644 --- a/deps/v8/src/d8.cc +++ b/deps/v8/src/d8.cc @@ -191,76 +191,65 @@ class MockArrayBufferAllocator : public ArrayBufferAllocatorBase { } }; - -// Predictable v8::Platform implementation. All background and foreground -// tasks are run immediately, delayed tasks are not executed at all. +// Predictable v8::Platform implementation. Background tasks and idle tasks are +// disallowed, and the time reported by {MonotonicallyIncreasingTime} is +// deterministic. class PredictablePlatform : public Platform { - public: - PredictablePlatform() {} - - void CallOnBackgroundThread(Task* task, - ExpectedRuntime expected_runtime) override { - task->Run(); - delete task; - } - - void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override { - task->Run(); - delete task; - } - - void CallDelayedOnForegroundThread(v8::Isolate* isolate, Task* task, - double delay_in_seconds) override { - delete task; - } - - void CallIdleOnForegroundThread(v8::Isolate* isolate, - IdleTask* task) override { - UNREACHABLE(); - } - - bool IdleTasksEnabled(v8::Isolate* isolate) override { return false; } - - double MonotonicallyIncreasingTime() override { - return synthetic_time_in_sec_ += 0.00001; - } - - v8::TracingController* GetTracingController() override { - return platform_->GetTracingController(); - } - - using Platform::AddTraceEvent; - uint64_t AddTraceEvent(char phase, const uint8_t* categoryEnabledFlag, - const char* name, const char* scope, uint64_t id, - uint64_t bind_id, int numArgs, const char** argNames, - const uint8_t* argTypes, const uint64_t* argValues, - unsigned int flags) override { - return 0; - } - - void UpdateTraceEventDuration(const uint8_t* categoryEnabledFlag, - const char* name, uint64_t handle) override {} - - const uint8_t* GetCategoryGroupEnabled(const char* name) override { - static uint8_t no = 0; - return &no; - } - - const char* GetCategoryGroupName( - const uint8_t* categoryEnabledFlag) override { - static const char* dummy = "dummy"; - return dummy; - } - - private: - double synthetic_time_in_sec_ = 0.0; - - DISALLOW_COPY_AND_ASSIGN(PredictablePlatform); +public: + explicit PredictablePlatform(std::unique_ptr platform) + : platform_(std::move(platform)) { + DCHECK_NOT_NULL(platform_); + } + + void CallOnBackgroundThread(Task* task, + ExpectedRuntime expected_runtime) override { + // It's not defined when background tasks are being executed, so we can just + // execute them right away. + task->Run(); + delete task; + } + + void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override { + platform_->CallOnForegroundThread(isolate, task); + } + + void CallDelayedOnForegroundThread(v8::Isolate* isolate, Task* task, + double delay_in_seconds) override { + platform_->CallDelayedOnForegroundThread(isolate, task, delay_in_seconds); + } + + void CallIdleOnForegroundThread(Isolate* isolate, IdleTask* task) override { + UNREACHABLE(); + } + + bool IdleTasksEnabled(Isolate* isolate) override { return false; } + + double MonotonicallyIncreasingTime() override { + return synthetic_time_in_sec_ += 0.00001; + } + + v8::TracingController* GetTracingController() override { + return platform_->GetTracingController(); + } + + Platform* platform() const { return platform_.get(); } + +private: + double synthetic_time_in_sec_ = 0.0; + std::unique_ptr platform_; + + DISALLOW_COPY_AND_ASSIGN(PredictablePlatform); }; v8::Platform* g_platform = NULL; +v8::Platform* GetDefaultPlatform() { + return i::FLAG_verify_predictable + ? static_cast(g_platform)->platform() + : g_platform; +} + static Local Throw(Isolate* isolate, const char* message) { return isolate->ThrowException( String::NewFromUtf8(isolate, message, NewStringType::kNormal) @@ -1389,8 +1378,6 @@ void Shell::Quit(const v8::FunctionCallbackInfo& args) { const_cast*>(&args)); } -// Note that both WaitUntilDone and NotifyDone are no-op when -// --verify-predictable. See comment in Shell::EnsureEventLoopInitialized. void Shell::WaitUntilDone(const v8::FunctionCallbackInfo& args) { SetWaitUntilDone(args.GetIsolate(), true); } @@ -2767,13 +2754,8 @@ void Shell::CollectGarbage(Isolate* isolate) { } void Shell::EnsureEventLoopInitialized(Isolate* isolate) { - // When using PredictablePlatform (i.e. FLAG_verify_predictable), - // we don't need event loop support, because tasks are completed - // immediately - both background and foreground ones. - if (!i::FLAG_verify_predictable) { - v8::platform::EnsureEventLoopInitialized(g_platform, isolate); - SetWaitUntilDone(isolate, false); - } + v8::platform::EnsureEventLoopInitialized(GetDefaultPlatform(), isolate); + SetWaitUntilDone(isolate, false); } void Shell::SetWaitUntilDone(Isolate* isolate, bool value) { @@ -2792,29 +2774,32 @@ bool Shell::IsWaitUntilDone(Isolate* isolate) { } void Shell::CompleteMessageLoop(Isolate* isolate) { - // See comment in EnsureEventLoopInitialized. - if (i::FLAG_verify_predictable) return; + Platform* platform = GetDefaultPlatform(); while (v8::platform::PumpMessageLoop( - g_platform, isolate, + platform, isolate, Shell::IsWaitUntilDone(isolate) ? platform::MessageLoopBehavior::kWaitForWork : platform::MessageLoopBehavior::kDoNotWait)) { isolate->RunMicrotasks(); } - v8::platform::RunIdleTasks(g_platform, isolate, - 50.0 / base::Time::kMillisecondsPerSecond); + if (platform->IdleTasksEnabled(isolate)) { + v8::platform::RunIdleTasks(platform, isolate, + 50.0 / base::Time::kMillisecondsPerSecond); + } } void Shell::EmptyMessageQueues(Isolate* isolate) { - if (i::FLAG_verify_predictable) return; + Platform* platform = GetDefaultPlatform(); // Pump the message loop until it is empty. while (v8::platform::PumpMessageLoop( - g_platform, isolate, platform::MessageLoopBehavior::kDoNotWait)) { + platform, isolate, platform::MessageLoopBehavior::kDoNotWait)) { isolate->RunMicrotasks(); } // Run the idle tasks. - v8::platform::RunIdleTasks(g_platform, isolate, - 50.0 / base::Time::kMillisecondsPerSecond); + if (platform->IdleTasksEnabled(isolate)) { + v8::platform::RunIdleTasks(platform, isolate, + 50.0 / base::Time::kMillisecondsPerSecond); + } } class Serializer : public ValueSerializer::Delegate { @@ -3067,8 +3052,19 @@ int Shell::Main(int argc, char* argv[]) { if (!SetOptions(argc, argv)) return 1; v8::V8::InitializeICUDefaultLocation(argv[0], options.icu_data_file); + v8::platform::InProcessStackDumping in_process_stack_dumping = + options.disable_in_process_stack_traces + ? v8::platform::InProcessStackDumping::kDisabled + : v8::platform::InProcessStackDumping::kEnabled; + + g_platform = v8::platform::CreateDefaultPlatform( + 0, v8::platform::IdleTaskSupport::kEnabled, in_process_stack_dumping); + if (i::FLAG_verify_predictable) { + g_platform = new PredictablePlatform(std::unique_ptr(g_platform)); + } + platform::tracing::TracingController* tracing_controller = nullptr; - if (options.trace_enabled) { + if (options.trace_enabled && !i::FLAG_verify_predictable) { trace_file.open("v8_trace.json"); tracing_controller = new platform::tracing::TracingController(); platform::tracing::TraceBuffer* trace_buffer = @@ -3076,20 +3072,9 @@ int Shell::Main(int argc, char* argv[]) { platform::tracing::TraceBuffer::kRingBufferChunks, platform::tracing::TraceWriter::CreateJSONTraceWriter(trace_file)); tracing_controller->Initialize(trace_buffer); + platform::SetTracingController(g_platform, tracing_controller); } - v8::platform::InProcessStackDumping in_process_stack_dumping = - options.disable_in_process_stack_traces - ? v8::platform::InProcessStackDumping::kDisabled - : v8::platform::InProcessStackDumping::kEnabled; - - g_platform = i::FLAG_verify_predictable - ? new PredictablePlatform() - : v8::platform::CreateDefaultPlatform( - 0, v8::platform::IdleTaskSupport::kEnabled, - in_process_stack_dumping, - tracing_controller); - v8::V8::InitializePlatform(g_platform); v8::V8::Initialize(); if (options.natives_blob || options.snapshot_blob) { @@ -3136,6 +3121,7 @@ int Shell::Main(int argc, char* argv[]) { } Isolate* isolate = Isolate::New(create_params); + D8Console console(isolate); { Isolate::Scope scope(isolate); @@ -3205,9 +3191,6 @@ int Shell::Main(int argc, char* argv[]) { V8::Dispose(); V8::ShutdownPlatform(); delete g_platform; - if (i::FLAG_verify_predictable) { - delete tracing_controller; - } return result; } diff --git a/deps/v8/src/heap/page-parallel-job.h b/deps/v8/src/heap/page-parallel-job.h index eb215efbb489fe..939bdb3b3b2925 100644 --- a/deps/v8/src/heap/page-parallel-job.h +++ b/deps/v8/src/heap/page-parallel-job.h @@ -69,7 +69,7 @@ class PageParallelJob { void Run(int num_tasks, Callback per_task_data_callback) { if (num_items_ == 0) return; DCHECK_GE(num_tasks, 1); - uint32_t task_ids[kMaxNumberOfTasks]; + CancelableTaskManager::Id task_ids[kMaxNumberOfTasks]; const int max_num_tasks = Min( kMaxNumberOfTasks, static_cast( diff --git a/deps/v8/src/libplatform/default-platform.cc b/deps/v8/src/libplatform/default-platform.cc index f873a7bb62ee6e..34cda33b4349cc 100644 --- a/deps/v8/src/libplatform/default-platform.cc +++ b/deps/v8/src/libplatform/default-platform.cc @@ -47,25 +47,25 @@ v8::Platform* CreateDefaultPlatform(int thread_pool_size, bool PumpMessageLoop(v8::Platform* platform, v8::Isolate* isolate, MessageLoopBehavior behavior) { - return reinterpret_cast(platform)->PumpMessageLoop( - isolate, behavior); + return static_cast(platform)->PumpMessageLoop(isolate, + behavior); } void EnsureEventLoopInitialized(v8::Platform* platform, v8::Isolate* isolate) { - return reinterpret_cast(platform) - ->EnsureEventLoopInitialized(isolate); + return static_cast(platform)->EnsureEventLoopInitialized( + isolate); } void RunIdleTasks(v8::Platform* platform, v8::Isolate* isolate, double idle_time_in_seconds) { - reinterpret_cast(platform)->RunIdleTasks( - isolate, idle_time_in_seconds); + static_cast(platform)->RunIdleTasks(isolate, + idle_time_in_seconds); } void SetTracingController( v8::Platform* platform, v8::platform::tracing::TracingController* tracing_controller) { - return reinterpret_cast(platform)->SetTracingController( + return static_cast(platform)->SetTracingController( tracing_controller); } diff --git a/deps/v8/src/wasm/wasm-module.cc b/deps/v8/src/wasm/wasm-module.cc index bd4495573568f8..5b8020b4f5cb08 100644 --- a/deps/v8/src/wasm/wasm-module.cc +++ b/deps/v8/src/wasm/wasm-module.cc @@ -2695,10 +2695,6 @@ void wasm::AsyncInstantiate(Isolate* isolate, Handle promise, // foreground task. All other tasks (e.g. decoding and validating, the majority // of the work of compilation) can be background tasks. // TODO(wasm): factor out common parts of this with the synchronous pipeline. -// -// Note: In predictable mode, DoSync and DoAsync execute the referenced function -// immediately before returning. Thus we handle the predictable mode specially, -// e.g. when we synchronizing tasks or when we delete the AyncCompileJob. class AsyncCompileJob { // TODO(ahaas): Fix https://bugs.chromium.org/p/v8/issues/detail?id=6263 to // make sure that d8 does not shut down before the AsyncCompileJob is @@ -2761,14 +2757,14 @@ class AsyncCompileJob { RejectPromise(isolate_, context_, thrower, module_promise_); // The AsyncCompileJob is finished, we resolved the promise, we do not need // the data anymore. We can delete the AsyncCompileJob object. - if (!FLAG_verify_predictable) delete this; + delete this; } void AsyncCompileSucceeded(Handle result) { ResolvePromise(isolate_, context_, module_promise_, result); // The AsyncCompileJob is finished, we resolved the promise, we do not need // the data anymore. We can delete the AsyncCompileJob object. - if (!FLAG_verify_predictable) delete this; + delete this; } enum TaskType { SYNC, ASYNC }; @@ -2975,9 +2971,7 @@ class AsyncCompileJob { // TODO(ahaas): Limit the number of outstanding compilation units to be // finished to reduce memory overhead. } - // Special handling for predictable mode, see above. - if (!FLAG_verify_predictable) - job_->helper_->module_->pending_tasks.get()->Signal(); + job_->helper_->module_->pending_tasks.get()->Signal(); } }; @@ -3026,12 +3020,9 @@ class AsyncCompileJob { // Bump next_unit_, such that background tasks stop processing the queue. job_->helper_->next_unit_.SetValue( job_->helper_->compilation_units_.size()); - // Special handling for predictable mode, see above. - if (!FLAG_verify_predictable) { - for (size_t i = 0; i < job_->num_background_tasks_; ++i) { - // We wait for it to finish. - job_->helper_->module_->pending_tasks.get()->Wait(); - } + for (size_t i = 0; i < job_->num_background_tasks_; ++i) { + // We wait for it to finish. + job_->helper_->module_->pending_tasks.get()->Wait(); } if (thrower_.error()) { job_->DoSync(std::move(thrower_)); @@ -3194,8 +3185,6 @@ void wasm::AsyncCompile(Isolate* isolate, Handle promise, auto job = new AsyncCompileJob(isolate, std::move(copy), bytes.length(), handle(isolate->context()), promise); job->Start(); - // Special handling for predictable mode, see above. - if (FLAG_verify_predictable) delete job; } Handle wasm::CompileLazy(Isolate* isolate) { From 6179c2764af81412131985979901ac4933ddd8d8 Mon Sep 17 00:00:00 2001 From: Daniel Taveras Date: Thu, 27 Jul 2017 21:05:01 -0400 Subject: [PATCH 063/231] doc: fix doc for napi_get_value_string_utf8 The API for napi_get_value_string_utf8() appears to have been previously changed. This improves the doc reflect the current design. PR-URL: https://github.com/nodejs/node/pull/14529 Fixes: https://github.com/nodejs/node/issues/14398 Reviewed-By: Colin Ihrig Reviewed-By: James M Snell Reviewed-By: Michael Dawson --- doc/api/n-api.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 31103d75a9e1dd..d26ff1a6685a3e 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -1811,10 +1811,10 @@ NAPI_EXTERN napi_status napi_get_value_string_latin1(napi_env env, - `[in] value`: `napi_value` representing JavaScript string. - `[in] buf`: Buffer to write the ISO-8859-1-encoded string into. If NULL is passed in, the length of the string (in bytes) is returned. -- `[in] bufsize`: Size of the destination buffer. -- `[out] result`: Number of bytes copied into the buffer including the null -terminator. If the buffer size is insufficient, the string will be truncated -including a null terminator. +- `[in] bufsize`: Size of the destination buffer. When this value is +insufficient, the returned string will be truncated. +- `[out] result`: Number of bytes copied into the buffer, excluding the null +terminator. Returns `napi_ok` if the API succeeded. If a non-String `napi_value` is passed in it returns `napi_string_expected`. @@ -1837,11 +1837,11 @@ napi_status napi_get_value_string_utf8(napi_env env, - `[in] env`: The environment that the API is invoked under. - `[in] value`: `napi_value` representing JavaScript string. - `[in] buf`: Buffer to write the UTF8-encoded string into. If NULL is passed -in, the length of the string (in bytes) is returned. -- `[in] bufsize`: Size of the destination buffer. -- `[out] result`: Number of bytes copied into the buffer including the null -terminator. If the buffer size is insufficient, the string will be truncated -including a null terminator. + in, the length of the string (in bytes) is returned. +- `[in] bufsize`: Size of the destination buffer. When this value is +insufficient, the returned string will be truncated. +- `[out] result`: Number of bytes copied into the buffer, excluding the null +terminator. Returns `napi_ok` if the API succeeded. If a non-String `napi_value` is passed in it returns `napi_string_expected`. @@ -1864,10 +1864,10 @@ napi_status napi_get_value_string_utf16(napi_env env, - `[in] value`: `napi_value` representing JavaScript string. - `[in] buf`: Buffer to write the UTF16-LE-encoded string into. If NULL is passed in, the length of the string (in 2-byte code units) is returned. -- `[in] bufsize`: Size of the destination buffer. -- `[out] result`: Number of 2-byte code units copied into the buffer including -the null terminator. If the buffer size is insufficient, the string will be -truncated including a null terminator. +- `[in] bufsize`: Size of the destination buffer. When this value is +insufficient, the returned string will be truncated. +- `[out] result`: Number of 2-byte code units copied into the buffer, excluding the null +terminator. Returns `napi_ok` if the API succeeded. If a non-String `napi_value` is passed in it returns `napi_string_expected`. From 78b2bc77f2861fee92210cc4db463c50b6308946 Mon Sep 17 00:00:00 2001 From: RefinedSoftwareLLC Date: Tue, 22 Aug 2017 09:57:37 -0600 Subject: [PATCH 064/231] doc: update http2.md example code `require('http');` should be `require('http2');` PR-URL: https://github.com/nodejs/node/pull/14979 Reviewed-By: James M Snell Reviewed-By: Luigi Pinca Reviewed-By: Colin Ihrig Reviewed-By: Anna Henningsen --- doc/api/http2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/http2.md b/doc/api/http2.md index 9d779eecca98b6..d9b8aa97b0e19e 100755 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -893,7 +893,7 @@ invoked with two arguments: an Object containing the received For example: ```js -const http2 = require('http'); +const http2 = require('http2'); const client = http2.connect('https://localhost'); const req = client.request({ ':path': '/' }); req.on('response', (headers, flags) => { From e187c98186b3908a5c799ab818292061824b4efc Mon Sep 17 00:00:00 2001 From: Julien Gilli Date: Tue, 22 Aug 2017 14:40:28 -0700 Subject: [PATCH 065/231] doc: remove misterdjules from the CTC members list PR-URL: https://github.com/nodejs/node/pull/1498 Reviewed-By: Colin Ihrig Reviewed-By: Rich Trott --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index db86dd3b7a1f13..76c46c10d7547b 100644 --- a/README.md +++ b/README.md @@ -206,8 +206,6 @@ more information about the governance of the Node.js project, see **Matteo Collina** <matteo.collina@gmail.com> (he/him) * [mhdawson](https://github.com/mhdawson) - **Michael Dawson** <michael_dawson@ca.ibm.com> (he/him) -* [misterdjules](https://github.com/misterdjules) - -**Julien Gilli** <jgilli@nodejs.org> * [mscdex](https://github.com/mscdex) - **Brian White** <mscdex@mscdex.net> * [MylesBorins](https://github.com/MylesBorins) - From 6421a9cb9a9d291caad9de30600ddd8c7af7a390 Mon Sep 17 00:00:00 2001 From: Ankit Parashar Date: Fri, 18 Aug 2017 14:40:53 +0530 Subject: [PATCH 066/231] test: remove unused arguments from function Removed the unused arguments of functions defined in file test/parallel/test-http-parser.js. PR-URL: https://github.com/nodejs/node/pull/14931 Reviewed-By: Rich Trott Reviewed-By: Yuta Hiroto Reviewed-By: Refael Ackermann Reviewed-By: Luigi Pinca Reviewed-By: Colin Ihrig Reviewed-By: James M Snell Reviewed-By: Alexey Orlenko --- test/parallel/test-http-parser.js | 45 +++++++++++-------------------- 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/test/parallel/test-http-parser.js b/test/parallel/test-http-parser.js index efffbc63f231d5..81aadf26169179 100644 --- a/test/parallel/test-http-parser.js +++ b/test/parallel/test-http-parser.js @@ -52,7 +52,7 @@ function newParser(type) { parser.url += url; }; - parser[kOnHeadersComplete] = function(info) { + parser[kOnHeadersComplete] = function() { }; parser[kOnBody] = common.mustNotCall('kOnBody should not be called'); @@ -94,8 +94,7 @@ function expectBody(expected) { const request = Buffer.from('GET /hello HTTP/1.1\r\n\r\n'); const onHeadersComplete = (versionMajor, versionMinor, headers, - method, url, statusCode, statusMessage, - upgrade, shouldKeepAlive) => { + method, url) => { assert.strictEqual(versionMajor, 1); assert.strictEqual(versionMinor, 1); assert.strictEqual(method, methods.indexOf('GET')); @@ -111,7 +110,7 @@ function expectBody(expected) { // thrown from parser.execute() // - parser[kOnHeadersComplete] = function(info) { + parser[kOnHeadersComplete] = function() { throw new Error('hello world'); }; @@ -136,8 +135,7 @@ function expectBody(expected) { ); const onHeadersComplete = (versionMajor, versionMinor, headers, - method, url, statusCode, statusMessage, - upgrade, shouldKeepAlive) => { + method, url, statusCode, statusMessage) => { assert.strictEqual(method, undefined); assert.strictEqual(versionMajor, 1); assert.strictEqual(versionMinor, 1); @@ -165,8 +163,7 @@ function expectBody(expected) { 'HTTP/1.0 200 Connection established\r\n\r\n'); const onHeadersComplete = (versionMajor, versionMinor, headers, - method, url, statusCode, statusMessage, - upgrade, shouldKeepAlive) => { + method, url, statusCode, statusMessage) => { assert.strictEqual(versionMajor, 1); assert.strictEqual(versionMinor, 0); assert.strictEqual(method, undefined); @@ -199,15 +196,14 @@ function expectBody(expected) { let seen_body = false; - const onHeaders = (headers, url) => { + const onHeaders = (headers) => { assert.ok(seen_body); // trailers should come after the body assert.deepStrictEqual(headers, ['Vary', '*', 'Content-Type', 'text/plain']); }; const onHeadersComplete = (versionMajor, versionMinor, headers, - method, url, statusCode, statusMessage, - upgrade, shouldKeepAlive) => { + method, url) => { assert.strictEqual(method, methods.indexOf('POST')); assert.strictEqual(url || parser.url, '/it'); assert.strictEqual(versionMajor, 1); @@ -242,8 +238,7 @@ function expectBody(expected) { ); const onHeadersComplete = (versionMajor, versionMinor, headers, - method, url, statusCode, statusMessage, - upgrade, shouldKeepAlive) => { + method) => { assert.strictEqual(method, methods.indexOf('GET')); assert.strictEqual(versionMajor, 1); assert.strictEqual(versionMinor, 0); @@ -272,8 +267,7 @@ function expectBody(expected) { ); const onHeadersComplete = (versionMajor, versionMinor, headers, - method, url, statusCode, statusMessage, - upgrade, shouldKeepAlive) => { + method, url) => { assert.strictEqual(method, methods.indexOf('GET')); assert.strictEqual(url || parser.url, '/foo/bar/baz?quux=42#1337'); assert.strictEqual(versionMajor, 1); @@ -307,8 +301,7 @@ function expectBody(expected) { ); const onHeadersComplete = (versionMajor, versionMinor, headers, - method, url, statusCode, statusMessage, - upgrade, shouldKeepAlive) => { + method, url) => { assert.strictEqual(method, methods.indexOf('POST')); assert.strictEqual(url || parser.url, '/it'); assert.strictEqual(versionMajor, 1); @@ -346,8 +339,7 @@ function expectBody(expected) { ); const onHeadersComplete = (versionMajor, versionMinor, headers, - method, url, statusCode, statusMessage, - upgrade, shouldKeepAlive) => { + method, url) => { assert.strictEqual(method, methods.indexOf('POST')); assert.strictEqual(url || parser.url, '/it'); assert.strictEqual(versionMajor, 1); @@ -385,8 +377,7 @@ function expectBody(expected) { ); const onHeadersComplete = (versionMajor, versionMinor, headers, - method, url, statusCode, statusMessage, - upgrade, shouldKeepAlive) => { + method, url) => { assert.strictEqual(method, methods.indexOf('POST')); assert.strictEqual(url || parser.url, '/it'); assert.strictEqual(versionMajor, 1); @@ -445,8 +436,7 @@ function expectBody(expected) { function test(a, b) { const onHeadersComplete = (versionMajor, versionMinor, headers, - method, url, statusCode, statusMessage, - upgrade, shouldKeepAlive) => { + method, url) => { assert.strictEqual(method, methods.indexOf('POST')); assert.strictEqual(url || parser.url, '/helpme'); assert.strictEqual(versionMajor, 1); @@ -503,8 +493,7 @@ function expectBody(expected) { ); const onHeadersComplete = (versionMajor, versionMinor, headers, - method, url, statusCode, statusMessage, - upgrade, shouldKeepAlive) => { + method, url) => { assert.strictEqual(method, methods.indexOf('POST')); assert.strictEqual(url || parser.url, '/it'); assert.strictEqual(versionMajor, 1); @@ -557,8 +546,7 @@ function expectBody(expected) { ); const onHeadersComplete1 = (versionMajor, versionMinor, headers, - method, url, statusCode, statusMessage, - upgrade, shouldKeepAlive) => { + method, url) => { assert.strictEqual(method, methods.indexOf('PUT')); assert.strictEqual(url, '/this'); assert.strictEqual(versionMajor, 1); @@ -569,8 +557,7 @@ function expectBody(expected) { }; const onHeadersComplete2 = (versionMajor, versionMinor, headers, - method, url, statusCode, statusMessage, - upgrade, shouldKeepAlive) => { + method, url) => { assert.strictEqual(method, methods.indexOf('POST')); assert.strictEqual(url, '/that'); assert.strictEqual(versionMajor, 1); From a8648e287cd471fca68990d2d071644b56c21e19 Mon Sep 17 00:00:00 2001 From: James Kyle Date: Tue, 22 Aug 2017 09:39:17 +1000 Subject: [PATCH 067/231] doc: link to correct "OS Constants" heading in docs PR-URL: https://github.com/nodejs/node/pull/14969 Reviewed-By: Anna Henningsen Reviewed-By: James M Snell Reviewed-By: Colin Ihrig Reviewed-By: Luigi Pinca --- doc/api/os.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/os.md b/doc/api/os.md index 9dc0fcf30877e2..bb4ffdc3766c01 100644 --- a/doc/api/os.md +++ b/doc/api/os.md @@ -46,7 +46,7 @@ added: v6.3.0 Returns an object containing commonly used operating system specific constants for error codes, process signals, and so on. The specific constants currently -defined are described in [OS Constants][]. +defined are described in [OS Constants](#os_os_constants_1). ## os.cpus() Use OpenSSL's default CA store or use bundled Mozilla CA store as supplied by -current NodeJS version. The default store is selectable at build-time. +current Node.js version. The default store is selectable at build-time. Using OpenSSL store allows for external modifications of the store. For most Linux and BSD distributions, this store is maintained by the distribution maintainers and system administrators. OpenSSL CA store location is dependent on configuration of the OpenSSL library but this can be altered at runtime using -environmental variables. +environment variables. -The bundled CA store, as supplied by NodeJS, is a snapshot of Mozilla CA store +The bundled CA store, as supplied by Node.js, is a snapshot of Mozilla CA store that is fixed at release time. It is identical on all supported platforms. See `SSL_CERT_DIR` and `SSL_CERT_FILE`. diff --git a/doc/api/intl.md b/doc/api/intl.md index ad7b670fae38d2..c7ac7fd2b68cd2 100644 --- a/doc/api/intl.md +++ b/doc/api/intl.md @@ -112,7 +112,7 @@ at runtime so that the JS methods would work for all ICU locales. Assuming the data file is stored at `/some/directory`, it can be made available to ICU through either: -* The [`NODE_ICU_DATA`][] environmental variable: +* The [`NODE_ICU_DATA`][] environment variable: ```shell env NODE_ICU_DATA=/some/directory node diff --git a/doc/api/repl.md b/doc/api/repl.md index f276b965bdfeb2..dcb2df5e2cb932 100644 --- a/doc/api/repl.md +++ b/doc/api/repl.md @@ -499,7 +499,7 @@ by the `NODE_REPL_HISTORY` variable, as documented in the ### Using the Node.js REPL with advanced line-editors -For advanced line-editors, start Node.js with the environmental variable +For advanced line-editors, start Node.js with the environment variable `NODE_NO_READLINE=1`. This will start the main and debugger REPL in canonical terminal settings, which will allow use with `rlwrap`. diff --git a/doc/node.1 b/doc/node.1 index cf79ce33f929df..36c44d6b2cf8c9 100644 --- a/doc/node.1 +++ b/doc/node.1 @@ -223,15 +223,15 @@ used to enable FIPS-compliant crypto if Node.js is built with .TP .BR \-\-use\-openssl\-ca,\-\-use\-bundled\-ca Use OpenSSL's default CA store or use bundled Mozilla CA store as supplied by -current NodeJS version. The default store is selectable at build-time. +current Node.js version. The default store is selectable at build-time. Using OpenSSL store allows for external modifications of the store. For most Linux and BSD distributions, this store is maintained by the distribution maintainers and system administrators. OpenSSL CA store location is dependent on configuration of the OpenSSL library but this can be altered at runtime using -environmental variables. +environment variables. -The bundled CA store, as supplied by NodeJS, is a snapshot of Mozilla CA store +The bundled CA store, as supplied by Node.js, is a snapshot of Mozilla CA store that is fixed at release time. It is identical on all supported platforms. See \fBSSL_CERT_DIR\fR and \fBSSL_CERT_FILE\fR. From d4c2eba3761f284bbf2929bef50074ce0a045261 Mon Sep 17 00:00:00 2001 From: Mohd Maqbool Alam Date: Tue, 22 Aug 2017 06:24:17 +0530 Subject: [PATCH 104/231] test: remove unused function args PR-URL: https://github.com/nodejs/node/pull/14971 Reviewed-By: Rich Trott Reviewed-By: Yuta Hiroto Reviewed-By: David Cai Reviewed-By: Daniel Bevenius Reviewed-By: James M Snell --- test/async-hooks/test-disable-in-init.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/async-hooks/test-disable-in-init.js b/test/async-hooks/test-disable-in-init.js index 21588bf504651f..b7ab31e6d97dc2 100644 --- a/test/async-hooks/test-disable-in-init.js +++ b/test/async-hooks/test-disable-in-init.js @@ -7,7 +7,7 @@ const fs = require('fs'); let nestedCall = false; async_hooks.createHook({ - init: common.mustCall(function(id, type) { + init: common.mustCall(function() { nestedHook.disable(); if (!nestedCall) { nestedCall = true; From f1284d32a5ba4d84ab1b4a3168148aac188a2068 Mon Sep 17 00:00:00 2001 From: Jon Moss Date: Fri, 18 Aug 2017 16:59:10 -0400 Subject: [PATCH 105/231] test: simplify test-tls-client-default-ciphers PR-URL: https://github.com/nodejs/node/pull/14928 Ref: https://github.com/nodejs/node/issues/12376 Reviewed-By: James M Snell Reviewed-By: Rich Trott Reviewed-By: Refael Ackermann Reviewed-By: Colin Ihrig Reviewed-By: Luigi Pinca --- test/parallel/test-tls-client-default-ciphers.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/test/parallel/test-tls-client-default-ciphers.js b/test/parallel/test-tls-client-default-ciphers.js index 0630fe947e7f50..1596c8e981fbff 100644 --- a/test/parallel/test-tls-client-default-ciphers.js +++ b/test/parallel/test-tls-client-default-ciphers.js @@ -37,11 +37,8 @@ function test1() { throw new Done(); }; - try { - tls.connect(common.PORT); - } catch (e) { - assert(e instanceof Done); - } + assert.throws(tls.connect, Done); + assert.strictEqual(ciphers, tls.DEFAULT_CIPHERS); } test1(); From 3f7bdc5ab72a77a54476a5d3150742fc9cbebe42 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Thu, 17 Aug 2017 14:57:35 +0200 Subject: [PATCH 106/231] deps: cherry-pick e020aae394 from V8 upstream MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Original commit message: Work around glibc thread-local storage bug glibc before 2.17 has a bug that makes it impossible to execute binaries that have single-byte thread-local variables: % node --version node: error while loading shared libraries: cannot allocate memory in static TLS block Work around that by making the one instance in the V8 code base an int. See: https://sourceware.org/bugzilla/show_bug.cgi?id=14898 See: https://github.com/nodesource/distributions/issues/513 See: https://github.com/nodejs/build/pull/809 Change-Id: Iefd8009100cd93e26cf8dc5dc03f2d622b423385 Reviewed-on: https://chromium-review.googlesource.com/612351 Commit-Queue: Ben Noordhuis Reviewed-by: Eric Holk Cr-Commit-Position: refs/heads/master@{#47400} PR-URL: https://github.com/nodejs/node/pull/14913 Ref: https://github.com/nodejs/build/pull/809 Reviewed-By: Ben Noordhuis Reviewed-By: Michaël Zasso Reviewed-By: Nikolai Vavilov Reviewed-By: Colin Ihrig Reviewed-By: Michael Dawson Reviewed-By: James M Snell --- deps/v8/src/trap-handler/handler-shared.cc | 9 ++++++++- deps/v8/src/trap-handler/trap-handler.h | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/deps/v8/src/trap-handler/handler-shared.cc b/deps/v8/src/trap-handler/handler-shared.cc index 7b399f5eeac1be..d1b549a1701900 100644 --- a/deps/v8/src/trap-handler/handler-shared.cc +++ b/deps/v8/src/trap-handler/handler-shared.cc @@ -23,7 +23,14 @@ namespace v8 { namespace internal { namespace trap_handler { -THREAD_LOCAL bool g_thread_in_wasm_code = false; +// We declare this as int rather than bool as a workaround for a glibc bug, in +// which the dynamic loader cannot handle executables whose TLS area is only +// 1 byte in size; see https://sourceware.org/bugzilla/show_bug.cgi?id=14898. +THREAD_LOCAL int g_thread_in_wasm_code = false; + +static_assert(sizeof(g_thread_in_wasm_code) > 1, + "sizeof(thread_local_var) must be > 1, see " + "https://sourceware.org/bugzilla/show_bug.cgi?id=14898"); size_t gNumCodeObjects = 0; CodeProtectionInfoListEntry* gCodeObjects = nullptr; diff --git a/deps/v8/src/trap-handler/trap-handler.h b/deps/v8/src/trap-handler/trap-handler.h index 5494c5fdb312f3..ed9459918b7b36 100644 --- a/deps/v8/src/trap-handler/trap-handler.h +++ b/deps/v8/src/trap-handler/trap-handler.h @@ -65,7 +65,7 @@ inline bool UseTrapHandler() { return FLAG_wasm_trap_handler && V8_TRAP_HANDLER_SUPPORTED; } -extern THREAD_LOCAL bool g_thread_in_wasm_code; +extern THREAD_LOCAL int g_thread_in_wasm_code; inline bool IsThreadInWasm() { return g_thread_in_wasm_code; } From 503370e2d3fbf14abcd46aa14a6b105d750afc0c Mon Sep 17 00:00:00 2001 From: Gabriel Schulhof Date: Thu, 24 Aug 2017 13:33:26 +0300 Subject: [PATCH 107/231] n-api: implement promise Promise is implemented as a pair of objects. `napi_create_promise()` returns both a JavaScript promise and a newly allocated "deferred" in its out-params. The deferred is linked to the promise such that the deferred can be passed to `napi_resolve_deferred()` or `napi_reject_deferred()` to reject/resolve the promise. `napi_is_promise()` can be used to check if a `napi_value` is a native promise - that is, a promise created by the underlying engine, rather than a pure JS implementation of a promise. PR-URL: https://github.com/nodejs/node/pull/14365 Fixes: https://github.com/nodejs/abi-stable-node/issues/242 Reviewed-By: Anna Henningsen Reviewed-By: James M Snell Reviewed-By: Benjamin Gruenbaum Reviewed-By: Timothy Gu --- doc/api/n-api.md | 138 +++++++++++++++++++ src/node_api.cc | 78 +++++++++++ src/node_api.h | 14 ++ src/node_api_types.h | 1 + test/addons-napi/test_promise/binding.gyp | 8 ++ test/addons-napi/test_promise/test.js | 60 ++++++++ test/addons-napi/test_promise/test_promise.c | 60 ++++++++ 7 files changed, 359 insertions(+) create mode 100644 test/addons-napi/test_promise/binding.gyp create mode 100644 test/addons-napi/test_promise/test.js create mode 100644 test/addons-napi/test_promise/test_promise.c diff --git a/doc/api/n-api.md b/doc/api/n-api.md index d26ff1a6685a3e..e41465475ea72a 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -42,6 +42,7 @@ The documentation for N-API is structured as follows: * [Working with JavaScript Functions][] * [Object Wrap][] * [Asynchronous Operations][] +* [Promises][] The N-API is a C API that ensures ABI stability across Node.js versions and different compiler levels. However, we also understand that a C++ @@ -3395,6 +3396,142 @@ support it: +## Promises + +N-API provides facilities for creating `Promise` objects as described in +[Section 25.4][] of the ECMA specification. It implements promises as a pair of +objects. When a promise is created by `napi_create_promise()`, a "deferred" +object is created and returned alongside the `Promise`. The deferred object is +bound to the created `Promise` and is the only means to resolve or reject the +`Promise` using `napi_resolve_deferred()` or `napi_reject_deferred()`. The +deferred object that is created by `napi_create_promise()` is freed by +`napi_resolve_deferred()` or `napi_reject_deferred()`. The `Promise` object may +be returned to JavaScript where it can be used in the usual fashion. + +For example, to create a promise and pass it to an asynchronous worker: +```c +napi_deferred deferred; +napi_value promise; +napi_status status; + +// Create the promise. +status = napi_create_promise(env, &deferred, &promise); +if (status != napi_ok) return NULL; + +// Pass the deferred to a function that performs an asynchronous action. +do_something_asynchronous(deferred); + +// Return the promise to JS +return promise; +``` + +The above function `do_something_asynchronous()` would perform its asynchronous +action and then it would resolve or reject the deferred, thereby concluding the +promise and freeing the deferred: +```c +napi_deferred deferred; +napi_value undefined; +napi_status status; + +// Create a value with which to conclude the deferred. +status = napi_get_undefined(env, &undefined); +if (status != napi_ok) return NULL; + +// Resolve or reject the promise associated with the deferred depending on +// whether the asynchronous action succeeded. +if (asynchronous_action_succeeded) { + status = napi_resolve_deferred(env, deferred, undefined); +} else { + status = napi_reject_deferred(env, deferred, undefined); +} +if (status != napi_ok) return NULL; + +// At this point the deferred has been freed, so we should assign NULL to it. +deferred = NULL; +``` + +### napi_create_promise + +```C +NAPI_EXTERN napi_status napi_create_promise(napi_env env, + napi_deferred* deferred, + napi_value* promise); +``` + +- `[in] env`: The environment that the API is invoked under. +- `[out] deferred`: A newly created deferred object which can later be passed to +`napi_resolve_deferred()` or `napi_reject_deferred()` to resolve resp. reject +the associated promise. +- `[out] promise`: The JavaScript promise associated with the deferred object. + +Returns `napi_ok` if the API succeeded. + +This API creates a deferred object and a JavaScript promise. + +### napi_resolve_deferred + +```C +NAPI_EXTERN napi_status napi_resolve_deferred(napi_env env, + napi_deferred deferred, + napi_value resolution); +``` + +- `[in] env`: The environment that the API is invoked under. +- `[in] deferred`: The deferred object whose associated promise to resolve. +- `[in] resolution`: The value with which to resolve the promise. + +This API resolves a JavaScript promise by way of the deferred object +with which it is associated. Thus, it can only be used to resolve JavaScript +promises for which the corresponding deferred object is available. This +effectively means that the promise must have been created using +`napi_create_promise()` and the deferred object returned from that call must +have been retained in order to be passed to this API. + +The deferred object is freed upon successful completion. + +### napi_reject_deferred + +```C +NAPI_EXTERN napi_status napi_reject_deferred(napi_env env, + napi_deferred deferred, + napi_value rejection); +``` + +- `[in] env`: The environment that the API is invoked under. +- `[in] deferred`: The deferred object whose associated promise to resolve. +- `[in] rejection`: The value with which to reject the promise. + +This API rejects a JavaScript promise by way of the deferred object +with which it is associated. Thus, it can only be used to reject JavaScript +promises for which the corresponding deferred object is available. This +effectively means that the promise must have been created using +`napi_create_promise()` and the deferred object returned from that call must +have been retained in order to be passed to this API. + +The deferred object is freed upon successful completion. + +### napi_is_promise + +```C +NAPI_EXTERN napi_status napi_is_promise(napi_env env, + napi_value promise, + bool* is_promise); +``` + +- `[in] env`: The environment that the API is invoked under. +- `[in] promise`: The promise to examine +- `[out] is_promise`: Flag indicating whether `promise` is a native promise +object - that is, a promise object created by the underlying engine. + +[Promises]: #n_api_promises [Asynchronous Operations]: #n_api_asynchronous_operations [Basic N-API Data Types]: #n_api_basic_n_api_data_types [ECMAScript Language Specification]: https://tc39.github.io/ecma262/ @@ -3406,6 +3543,7 @@ support it: [Section 9.1.6]: https://tc39.github.io/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-defineownproperty-p-desc [Section 12.5.5]: https://tc39.github.io/ecma262/#sec-typeof-operator [Section 24.3]: https://tc39.github.io/ecma262/#sec-dataview-objects +[Section 25.4]: https://tc39.github.io/ecma262/#sec-promise-objects [Working with JavaScript Functions]: #n_api_working_with_javascript_functions [Working with JavaScript Properties]: #n_api_working_with_javascript_properties [Working with JavaScript Values]: #n_api_working_with_javascript_values diff --git a/src/node_api.cc b/src/node_api.cc index bec98e07ce95d1..7a2b5bc48e1237 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -218,6 +218,14 @@ V8EscapableHandleScopeFromJsEscapableHandleScope( static_assert(sizeof(v8::Local) == sizeof(napi_value), "Cannot convert between v8::Local and napi_value"); +napi_deferred JsDeferredFromV8Persistent(v8::Persistent* local) { + return reinterpret_cast(local); +} + +v8::Persistent* V8PersistentFromJsDeferred(napi_deferred local) { + return reinterpret_cast*>(local); +} + napi_value JsValueFromV8LocalValue(v8::Local local) { return reinterpret_cast(*local); } @@ -774,6 +782,33 @@ napi_status Unwrap(napi_env env, return napi_ok; } +napi_status ConcludeDeferred(napi_env env, + napi_deferred deferred, + napi_value result, + bool is_resolved) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Local context = env->isolate->GetCurrentContext(); + v8::Persistent* deferred_ref = + V8PersistentFromJsDeferred(deferred); + v8::Local v8_deferred = + v8::Local::New(env->isolate, *deferred_ref); + + auto v8_resolver = v8::Local::Cast(v8_deferred); + + v8::Maybe success = is_resolved ? + v8_resolver->Resolve(context, v8impl::V8LocalValueFromJsValue(result)) : + v8_resolver->Reject(context, v8impl::V8LocalValueFromJsValue(result)); + + deferred_ref->Reset(); + delete deferred_ref; + + RETURN_STATUS_IF_FALSE(env, success.FromMaybe(false), napi_generic_failure); + + return GET_RETURN_STATUS(env); +} + } // end of namespace v8impl // Intercepts the Node-V8 module registration callback. Converts parameters @@ -3332,3 +3367,46 @@ napi_status napi_cancel_async_work(napi_env env, napi_async_work work) { return napi_clear_last_error(env); } + +NAPI_EXTERN napi_status napi_create_promise(napi_env env, + napi_deferred* deferred, + napi_value* promise) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, deferred); + CHECK_ARG(env, promise); + + auto maybe = v8::Promise::Resolver::New(env->isolate->GetCurrentContext()); + CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure); + + auto v8_resolver = maybe.ToLocalChecked(); + auto v8_deferred = new v8::Persistent(); + v8_deferred->Reset(env->isolate, v8_resolver); + + *deferred = v8impl::JsDeferredFromV8Persistent(v8_deferred); + *promise = v8impl::JsValueFromV8LocalValue(v8_resolver->GetPromise()); + return GET_RETURN_STATUS(env); +} + +NAPI_EXTERN napi_status napi_resolve_deferred(napi_env env, + napi_deferred deferred, + napi_value resolution) { + return v8impl::ConcludeDeferred(env, deferred, resolution, true); +} + +NAPI_EXTERN napi_status napi_reject_deferred(napi_env env, + napi_deferred deferred, + napi_value resolution) { + return v8impl::ConcludeDeferred(env, deferred, resolution, false); +} + +NAPI_EXTERN napi_status napi_is_promise(napi_env env, + napi_value promise, + bool* is_promise) { + CHECK_ENV(env); + CHECK_ARG(env, promise); + CHECK_ARG(env, is_promise); + + *is_promise = v8impl::V8LocalValueFromJsValue(promise)->IsPromise(); + + return napi_clear_last_error(env); +} diff --git a/src/node_api.h b/src/node_api.h index e52e2016d733b9..6a4b2941879ff0 100644 --- a/src/node_api.h +++ b/src/node_api.h @@ -543,6 +543,20 @@ NAPI_EXTERN napi_status napi_get_node_version(napi_env env, const napi_node_version** version); +// Promises +NAPI_EXTERN napi_status napi_create_promise(napi_env env, + napi_deferred* deferred, + napi_value* promise); +NAPI_EXTERN napi_status napi_resolve_deferred(napi_env env, + napi_deferred deferred, + napi_value resolution); +NAPI_EXTERN napi_status napi_reject_deferred(napi_env env, + napi_deferred deferred, + napi_value rejection); +NAPI_EXTERN napi_status napi_is_promise(napi_env env, + napi_value promise, + bool* is_promise); + EXTERN_C_END #endif // SRC_NODE_API_H_ diff --git a/src/node_api_types.h b/src/node_api_types.h index 0bdc377c8fc6a0..ac8482bf9dcf9d 100644 --- a/src/node_api_types.h +++ b/src/node_api_types.h @@ -17,6 +17,7 @@ typedef struct napi_handle_scope__ *napi_handle_scope; typedef struct napi_escapable_handle_scope__ *napi_escapable_handle_scope; typedef struct napi_callback_info__ *napi_callback_info; typedef struct napi_async_work__ *napi_async_work; +typedef struct napi_deferred__ *napi_deferred; typedef enum { napi_default = 0, diff --git a/test/addons-napi/test_promise/binding.gyp b/test/addons-napi/test_promise/binding.gyp new file mode 100644 index 00000000000000..bf266f93db74be --- /dev/null +++ b/test/addons-napi/test_promise/binding.gyp @@ -0,0 +1,8 @@ +{ + "targets": [ + { + "target_name": "test_promise", + "sources": [ "test_promise.c" ] + } + ] +} diff --git a/test/addons-napi/test_promise/test.js b/test/addons-napi/test_promise/test.js new file mode 100644 index 00000000000000..4c2a2e5e76c9fb --- /dev/null +++ b/test/addons-napi/test_promise/test.js @@ -0,0 +1,60 @@ +'use strict'; + +const common = require('../../common'); +const test_promise = require(`./build/${common.buildType}/test_promise`); +const assert = require('assert'); + +let expected_result, promise; + +// A resolution +expected_result = 42; +promise = test_promise.createPromise(); +promise.then( + common.mustCall(function(result) { + assert.strictEqual(result, expected_result, + 'promise resolved as expected'); + }), + common.mustNotCall()); +test_promise.concludeCurrentPromise(expected_result, true); + +// A rejection +expected_result = 'It\'s not you, it\'s me.'; +promise = test_promise.createPromise(); +promise.then( + common.mustNotCall(), + common.mustCall(function(result) { + assert.strictEqual(result, expected_result, + 'promise rejected as expected'); + })); +test_promise.concludeCurrentPromise(expected_result, false); + +// Chaining +promise = test_promise.createPromise(); +promise.then( + common.mustCall(function(result) { + assert.strictEqual(result, 'chained answer', + 'resolving with a promise chains properly'); + }), + common.mustNotCall()); +test_promise.concludeCurrentPromise(Promise.resolve('chained answer'), true); + +assert.strictEqual(test_promise.isPromise(promise), true, + 'natively created promise is recognized as a promise'); + +assert.strictEqual(test_promise.isPromise(Promise.reject(-1)), true, + 'Promise created with JS is recognized as a promise'); + +assert.strictEqual(test_promise.isPromise(2.4), false, + 'Number is recognized as not a promise'); + +assert.strictEqual(test_promise.isPromise('I promise!'), false, + 'String is recognized as not a promise'); + +assert.strictEqual(test_promise.isPromise(undefined), false, + 'undefined is recognized as not a promise'); + +assert.strictEqual(test_promise.isPromise(null), false, + 'null is recognized as not a promise'); + +assert.strictEqual(test_promise.isPromise({}), false, + 'an object is recognized as not a promise'); diff --git a/test/addons-napi/test_promise/test_promise.c b/test/addons-napi/test_promise/test_promise.c new file mode 100644 index 00000000000000..dc617f4592e520 --- /dev/null +++ b/test/addons-napi/test_promise/test_promise.c @@ -0,0 +1,60 @@ +#include +#include "../common.h" + +napi_deferred deferred = NULL; + +napi_value createPromise(napi_env env, napi_callback_info info) { + napi_value promise; + + // We do not overwrite an existing deferred. + if (deferred != NULL) { + return NULL; + } + + NAPI_CALL(env, napi_create_promise(env, &deferred, &promise)); + + return promise; +} + +napi_value concludeCurrentPromise(napi_env env, napi_callback_info info) { + napi_value argv[2]; + size_t argc = 2; + bool resolution; + + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL)); + NAPI_CALL(env, napi_get_value_bool(env, argv[1], &resolution)); + if (resolution) { + NAPI_CALL(env, napi_resolve_deferred(env, deferred, argv[0])); + } else { + NAPI_CALL(env, napi_reject_deferred(env, deferred, argv[0])); + } + + deferred = NULL; + + return NULL; +} + +napi_value isPromise(napi_env env, napi_callback_info info) { + napi_value promise, result; + size_t argc = 1; + bool is_promise; + + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &promise, NULL, NULL)); + NAPI_CALL(env, napi_is_promise(env, promise, &is_promise)); + NAPI_CALL(env, napi_get_boolean(env, is_promise, &result)); + + return result; +} + +void Init(napi_env env, napi_value exports, napi_value module, void* priv) { + napi_property_descriptor descriptors[] = { + DECLARE_NAPI_PROPERTY("createPromise", createPromise), + DECLARE_NAPI_PROPERTY("concludeCurrentPromise", concludeCurrentPromise), + DECLARE_NAPI_PROPERTY("isPromise", isPromise), + }; + + NAPI_CALL_RETURN_VOID(env, napi_define_properties( + env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); +} + +NAPI_MODULE(addon, Init) From a06d1295c50b64ba17cb1ec6162df8338948c008 Mon Sep 17 00:00:00 2001 From: Sam Roberts Date: Wed, 23 Aug 2017 14:04:33 -0700 Subject: [PATCH 108/231] doc: crypto.randomBytes does not block when async It may not return random bytes right away, but when called asynchronously it will not block. PR-URL: https://github.com/nodejs/node/pull/14993 Reviewed-By: James M Snell Reviewed-By: Ben Noordhuis Reviewed-By: Luigi Pinca Reviewed-By: Colin Ihrig --- doc/api/crypto.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/api/crypto.md b/doc/api/crypto.md index 07245e8cdc1075..5ae5c28851df07 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -1713,7 +1713,8 @@ console.log( `${buf.length} bytes of random data: ${buf.toString('hex')}`); ``` -The `crypto.randomBytes()` method will block until there is sufficient entropy. +The `crypto.randomBytes()` method will not complete until there is +sufficient entropy available. This should normally never take longer than a few milliseconds. The only time when generating the random bytes may conceivably block for a longer period of time is right after boot, when the whole system is still low on entropy. From 6bb8133638f010efc3ce76dff9fcf531c1e446bc Mon Sep 17 00:00:00 2001 From: Sam Roberts Date: Wed, 23 Aug 2017 13:35:30 -0700 Subject: [PATCH 109/231] doc: sort bottom-of-file dns markdown links PR-URL: https://github.com/nodejs/node/pull/14992 Reviewed-By: Anna Henningsen Reviewed-By: James M Snell Reviewed-By: Luigi Pinca Reviewed-By: Daniel Bevenius Reviewed-By: Colin Ihrig --- doc/api/dns.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/api/dns.md b/doc/api/dns.md index 46927e5f528e0c..be8944736804be 100644 --- a/doc/api/dns.md +++ b/doc/api/dns.md @@ -644,10 +644,12 @@ They do not use the same set of configuration files than what [`dns.lookup()`][] uses. For instance, _they do not use the configuration from `/etc/hosts`_. [`Error`]: errors.html#errors_class_error +[`dns.getServers()`]: #dns_dns_getservers [`dns.lookup()`]: #dns_dns_lookup_hostname_options_callback [`dns.resolve()`]: #dns_dns_resolve_hostname_rrtype_callback [`dns.resolve4()`]: #dns_dns_resolve4_hostname_options_callback [`dns.resolve6()`]: #dns_dns_resolve6_hostname_options_callback +[`dns.resolveAny()`]: #dns_dns_resolveany_hostname_callback [`dns.resolveCname()`]: #dns_dns_resolvecname_hostname_callback [`dns.resolveMx()`]: #dns_dns_resolvemx_hostname_callback [`dns.resolveNaptr()`]: #dns_dns_resolvenaptr_hostname_callback @@ -656,13 +658,11 @@ uses. For instance, _they do not use the configuration from `/etc/hosts`_. [`dns.resolveSoa()`]: #dns_dns_resolvesoa_hostname_callback [`dns.resolveSrv()`]: #dns_dns_resolvesrv_hostname_callback [`dns.resolveTxt()`]: #dns_dns_resolvetxt_hostname_callback -[`dns.resolveAny()`]: #dns_dns_resolveany_hostname_callback -[`dns.getServers()`]: #dns_dns_getservers -[`dns.setServers()`]: #dns_dns_setservers_servers [`dns.reverse()`]: #dns_dns_reverse_ip_callback +[`dns.setServers()`]: #dns_dns_setservers_servers +[`util.promisify()`]: util.html#util_util_promisify_original [DNS error codes]: #dns_error_codes [Implementation considerations section]: #dns_implementation_considerations +[rfc5952]: https://tools.ietf.org/html/rfc5952#section-6 [supported `getaddrinfo` flags]: #dns_supported_getaddrinfo_flags [the official libuv documentation]: http://docs.libuv.org/en/latest/threadpool.html -[`util.promisify()`]: util.html#util_util_promisify_original -[rfc5952]: https://tools.ietf.org/html/rfc5952#section-6 From 449549bc4fa642745291e5011fe52b876453eff8 Mon Sep 17 00:00:00 2001 From: Sam Roberts Date: Wed, 23 Aug 2017 14:59:06 -0700 Subject: [PATCH 110/231] doc: threadpool size, and APIs using the pool Not knowing which APIs use libuv's threadpool can lead to surprising performance problems. Document the APIs, and also document UV_THREADPOOL_SIZE, which can be used to fix problems. PR-URL: https://github.com/nodejs/node/pull/14995 Reviewed-By: Brian White Reviewed-By: James M Snell --- doc/api/cli.md | 25 +++++++++++++++++++++++++ doc/api/crypto.md | 13 +++++++++++++ doc/api/dns.md | 22 +++++++++++++--------- doc/api/fs.md | 8 ++++++++ doc/api/zlib.md | 7 +++++++ 5 files changed, 66 insertions(+), 9 deletions(-) diff --git a/doc/api/cli.md b/doc/api/cli.md index edfacd94c829e6..fae7de7b7a6f2e 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -568,6 +568,30 @@ appended to if it does. If an error occurs while attempting to write the warning to the file, the warning will be written to stderr instead. This is equivalent to using the `--redirect-warnings=file` command-line flag. +### `UV_THREADPOOL_SIZE=size` + +Set the number of threads used in libuv's threadpool to `size` threads. + +Asynchronous system APIs are used by Node.js whenever possible, but where they +do not exist, libuv's threadpool is used to create asynchronous node APIs based +on synchronous system APIs. Node.js APIs that use the threadpool are: + +- all `fs` APIs, other than the file watcher APIs and those that are explicitly + synchronous +- `crypto.pbkdf2()` +- `crypto.randomBytes()`, unless it is used without a callback +- `crypto.randomFill()` +- `dns.lookup()` +- all `zlib` APIs, other than those that are explicitly synchronous + +Because libuv's threadpool has a fixed size, it means that if for whatever +reason any of these APIs takes a long time, other (seemingly unrelated) APIs +that run in libuv's threadpool will experience degraded performance. In order to +mitigate this issue, one potential solution is to increase the size of libuv's +threadpool by setting the `'UV_THREADPOOL_SIZE'` environment variable to a value +greater than `4` (its current default value). For more information, see the +[libuv threadpool documentation][]. + [`--openssl-config`]: #cli_openssl_config_file [Buffer]: buffer.html#buffer_buffer [Chrome Debugging Protocol]: https://chromedevtools.github.io/debugger-protocol-viewer @@ -575,3 +599,4 @@ equivalent to using the `--redirect-warnings=file` command-line flag. [SlowBuffer]: buffer.html#buffer_class_slowbuffer [debugger]: debugger.html [emit_warning]: process.html#process_process_emitwarning_warning_type_code_ctor +[libuv threadpool documentation]: http://docs.libuv.org/en/latest/threadpool.html diff --git a/doc/api/crypto.md b/doc/api/crypto.md index 5ae5c28851df07..6c83aeaff8353d 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -1555,6 +1555,10 @@ crypto.pbkdf2('secret', 'salt', 100000, 512, 'sha512', (err, derivedKey) => { An array of supported digest functions can be retrieved using [`crypto.getHashes()`][]. +Note that this API uses libuv's threadpool, which can have surprising and +negative performance implications for some applications, see the +[`UV_THREADPOOL_SIZE`][] documentation for more information. + ### crypto.pbkdf2Sync(password, salt, iterations, keylen, digest) + +The `'socketError'` event is emitted when a `'socketError'` event is emitted by +an `Http2Session` associated with the server. + #### Event: 'sessionError' -The `'socketError'` event is emitted when a `'socketError'` event is emitted by -an `Http2Session` associated with the server. +* `socket` {http2.ServerHttp2Stream} + +If an `ServerHttp2Stream` emits an `'error'` event, it will be forwarded here. +The stream will already be destroyed when this event is triggered. #### Event: 'stream' + Node.js Addons are dynamically-linked shared objects, written in C++, that can be loaded into Node.js using the [`require()`][require] function, and used just as if they were an ordinary Node.js module. They are used primarily to diff --git a/doc/api/assert.md b/doc/api/assert.md index 2fc83307dfa78f..c2dff91d0391c2 100644 --- a/doc/api/assert.md +++ b/doc/api/assert.md @@ -1,5 +1,7 @@ # Assert + + > Stability: 2 - Stable The `assert` module provides a simple set of assertion tests that can be used to diff --git a/doc/api/buffer.md b/doc/api/buffer.md index 0301eadd3bebdc..363b10bc664b9f 100644 --- a/doc/api/buffer.md +++ b/doc/api/buffer.md @@ -1,5 +1,7 @@ # Buffer + + > Stability: 2 - Stable Prior to the introduction of [`TypedArray`] in ECMAScript 2015 (ES6), the diff --git a/doc/api/child_process.md b/doc/api/child_process.md index bf748a6675f136..c7109901f4fd34 100755 --- a/doc/api/child_process.md +++ b/doc/api/child_process.md @@ -1,5 +1,7 @@ # Child Process + + > Stability: 2 - Stable The `child_process` module provides the ability to spawn child processes in diff --git a/doc/api/cli.md b/doc/api/cli.md index fae7de7b7a6f2e..e3727c0b248fb7 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -1,5 +1,6 @@ # Command Line Options + Node.js comes with a variety of CLI options. These options expose built-in diff --git a/doc/api/cluster.md b/doc/api/cluster.md index 278bd31e6fd8ad..4d56218b1b6b65 100644 --- a/doc/api/cluster.md +++ b/doc/api/cluster.md @@ -1,5 +1,7 @@ # Cluster + + > Stability: 2 - Stable A single instance of Node.js runs in a single thread. To take advantage of diff --git a/doc/api/console.md b/doc/api/console.md index 048948d31b0970..2d8c44581a813f 100644 --- a/doc/api/console.md +++ b/doc/api/console.md @@ -1,5 +1,7 @@ # Console + + > Stability: 2 - Stable The `console` module provides a simple debugging console that is similar to the diff --git a/doc/api/crypto.md b/doc/api/crypto.md index 6c83aeaff8353d..8c7f3131c6bf3e 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -1,5 +1,7 @@ # Crypto + + > Stability: 2 - Stable The `crypto` module provides cryptographic functionality that includes a set of diff --git a/doc/api/debugger.md b/doc/api/debugger.md index 1a2070d3f451b3..e16c83122e2b0b 100644 --- a/doc/api/debugger.md +++ b/doc/api/debugger.md @@ -1,5 +1,7 @@ # Debugger + + > Stability: 2 - Stable diff --git a/doc/api/dgram.md b/doc/api/dgram.md index 22fb2b5bbf1506..3f5664d8fc9090 100644 --- a/doc/api/dgram.md +++ b/doc/api/dgram.md @@ -1,5 +1,7 @@ # UDP / Datagram Sockets + + > Stability: 2 - Stable diff --git a/doc/api/dns.md b/doc/api/dns.md index 4248bdf43a1fbd..5e9fc97bbc48c2 100644 --- a/doc/api/dns.md +++ b/doc/api/dns.md @@ -1,5 +1,7 @@ # DNS + + > Stability: 2 - Stable The `dns` module contains functions belonging to two different categories: diff --git a/doc/api/documentation.md b/doc/api/documentation.md index a12f00e1d63bb8..802bf3613f9b55 100644 --- a/doc/api/documentation.md +++ b/doc/api/documentation.md @@ -1,5 +1,6 @@ # About this Documentation + The goal of this documentation is to comprehensively explain the Node.js diff --git a/doc/api/domain.md b/doc/api/domain.md index 102ac8ec7c354d..a4a31d4fecd1f2 100644 --- a/doc/api/domain.md +++ b/doc/api/domain.md @@ -7,6 +7,8 @@ changes: the first promise of a chain was created. --> + + > Stability: 0 - Deprecated **This module is pending deprecation**. Once a replacement API has been diff --git a/doc/api/errors.md b/doc/api/errors.md index 93d6a25173cc17..3a9a303d6e78da 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -1,5 +1,6 @@ # Errors + Applications running in Node.js will generally experience four categories of diff --git a/doc/api/events.md b/doc/api/events.md index ff6fbe9bb9affb..99c2e1514b43a3 100644 --- a/doc/api/events.md +++ b/doc/api/events.md @@ -1,5 +1,7 @@ # Events + + > Stability: 2 - Stable diff --git a/doc/api/fs.md b/doc/api/fs.md index 64ecae3e01b5f0..2613419df17f9b 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -1,5 +1,7 @@ # File System + + > Stability: 2 - Stable diff --git a/doc/api/globals.md b/doc/api/globals.md index f3a2fc63779b89..a2e5b5fc898686 100644 --- a/doc/api/globals.md +++ b/doc/api/globals.md @@ -1,5 +1,6 @@ # Global Objects + These objects are available in all modules. The following variables may appear diff --git a/doc/api/http.md b/doc/api/http.md index 7ed787240e447c..2994fa48173fc4 100644 --- a/doc/api/http.md +++ b/doc/api/http.md @@ -1,5 +1,7 @@ # HTTP + + > Stability: 2 - Stable To use the HTTP server and client one must `require('http')`. diff --git a/doc/api/https.md b/doc/api/https.md index f6c56ef8ed7efb..3ff97bf446b667 100644 --- a/doc/api/https.md +++ b/doc/api/https.md @@ -1,5 +1,7 @@ # HTTPS + + > Stability: 2 - Stable HTTPS is the HTTP protocol over TLS/SSL. In Node.js this is implemented as a diff --git a/doc/api/modules.md b/doc/api/modules.md index 13e3731cae749d..afddbc14c4f387 100644 --- a/doc/api/modules.md +++ b/doc/api/modules.md @@ -1,5 +1,7 @@ # Modules + + > Stability: 2 - Stable diff --git a/doc/api/net.md b/doc/api/net.md index 686ee300ccf527..36280c06493350 100644 --- a/doc/api/net.md +++ b/doc/api/net.md @@ -1,5 +1,7 @@ # Net + + > Stability: 2 - Stable The `net` module provides an asynchronous network API for creating stream-based diff --git a/doc/api/os.md b/doc/api/os.md index bb4ffdc3766c01..28eff6a13f0b60 100644 --- a/doc/api/os.md +++ b/doc/api/os.md @@ -1,5 +1,7 @@ # OS + + > Stability: 2 - Stable The `os` module provides a number of operating system-related utility methods. diff --git a/doc/api/path.md b/doc/api/path.md index f951a4ab8a5b1a..f2015db47048d7 100644 --- a/doc/api/path.md +++ b/doc/api/path.md @@ -1,5 +1,7 @@ # Path + + > Stability: 2 - Stable The `path` module provides utilities for working with file and directory paths. diff --git a/doc/api/process.md b/doc/api/process.md index 2a85a551c9e032..9dae7693313826 100644 --- a/doc/api/process.md +++ b/doc/api/process.md @@ -1,5 +1,6 @@ # Process + The `process` object is a `global` that provides information about, and control diff --git a/doc/api/punycode.md b/doc/api/punycode.md index b88a89832641ea..03ee3d62ebfd67 100644 --- a/doc/api/punycode.md +++ b/doc/api/punycode.md @@ -6,6 +6,8 @@ changes: description: Accessing this module will now emit a deprecation warning. --> + + > Stability: 0 - Deprecated **The version of the punycode module bundled in Node.js is being deprecated**. diff --git a/doc/api/querystring.md b/doc/api/querystring.md index c6b89235c14d43..5bd4f1cce192a7 100644 --- a/doc/api/querystring.md +++ b/doc/api/querystring.md @@ -1,5 +1,7 @@ # Query String + + > Stability: 2 - Stable diff --git a/doc/api/readline.md b/doc/api/readline.md index 085ac885401c89..603a5ec1888394 100644 --- a/doc/api/readline.md +++ b/doc/api/readline.md @@ -1,5 +1,7 @@ # Readline + + > Stability: 2 - Stable The `readline` module provides an interface for reading data from a [Readable][] diff --git a/doc/api/repl.md b/doc/api/repl.md index dcb2df5e2cb932..04e885a99dbfe1 100644 --- a/doc/api/repl.md +++ b/doc/api/repl.md @@ -1,5 +1,7 @@ # REPL + + > Stability: 2 - Stable The `repl` module provides a Read-Eval-Print-Loop (REPL) implementation that diff --git a/doc/api/stream.md b/doc/api/stream.md index 23b8aa55e1356e..efa69532ef81f8 100644 --- a/doc/api/stream.md +++ b/doc/api/stream.md @@ -1,5 +1,7 @@ # Stream + + > Stability: 2 - Stable A stream is an abstract interface for working with streaming data in Node.js. diff --git a/doc/api/string_decoder.md b/doc/api/string_decoder.md index 5757ba6e2b3440..cde81e6ae5ec2b 100644 --- a/doc/api/string_decoder.md +++ b/doc/api/string_decoder.md @@ -1,5 +1,7 @@ # String Decoder + + > Stability: 2 - Stable The `string_decoder` module provides an API for decoding `Buffer` objects into diff --git a/doc/api/synopsis.md b/doc/api/synopsis.md index e8fa77eee47e9e..3d680c33b554ba 100644 --- a/doc/api/synopsis.md +++ b/doc/api/synopsis.md @@ -1,5 +1,6 @@ # Usage + `node [options] [v8 options] [script.js | -e "script" | - ] [arguments]` diff --git a/doc/api/timers.md b/doc/api/timers.md index 8abcdcb5cb6890..09502dee1003c8 100644 --- a/doc/api/timers.md +++ b/doc/api/timers.md @@ -1,5 +1,7 @@ # Timers + + > Stability: 2 - Stable The `timer` module exposes a global API for scheduling functions to diff --git a/doc/api/tls.md b/doc/api/tls.md index 5df8c6af5e6b79..e18bbb62b1908f 100644 --- a/doc/api/tls.md +++ b/doc/api/tls.md @@ -1,5 +1,7 @@ # TLS (SSL) + + > Stability: 2 - Stable The `tls` module provides an implementation of the Transport Layer Security diff --git a/doc/api/tty.md b/doc/api/tty.md index 963de892cbc0fd..2950eb6db1a396 100644 --- a/doc/api/tty.md +++ b/doc/api/tty.md @@ -1,5 +1,7 @@ # TTY + + > Stability: 2 - Stable The `tty` module provides the `tty.ReadStream` and `tty.WriteStream` classes. diff --git a/doc/api/url.md b/doc/api/url.md index cb2a3965f5eee4..632eef82e4435e 100644 --- a/doc/api/url.md +++ b/doc/api/url.md @@ -1,5 +1,7 @@ # URL + + > Stability: 2 - Stable The `url` module provides utilities for URL resolution and parsing. It can be diff --git a/doc/api/util.md b/doc/api/util.md index 076fbc479dc695..ce56c50104dbc4 100644 --- a/doc/api/util.md +++ b/doc/api/util.md @@ -1,5 +1,7 @@ # Util + + > Stability: 2 - Stable The `util` module is primarily designed to support the needs of Node.js' own diff --git a/doc/api/v8.md b/doc/api/v8.md index 3a3e5f664a14e8..634d3199a1a012 100644 --- a/doc/api/v8.md +++ b/doc/api/v8.md @@ -1,5 +1,7 @@ # V8 + + The `v8` module exposes APIs that are specific to the version of [V8][] built into the Node.js binary. It can be accessed using: diff --git a/doc/api/vm.md b/doc/api/vm.md index 42046e01191b51..dff10b17cf20fd 100644 --- a/doc/api/vm.md +++ b/doc/api/vm.md @@ -1,5 +1,7 @@ # VM (Executing JavaScript) + + > Stability: 2 - Stable diff --git a/doc/api/zlib.md b/doc/api/zlib.md index 5ceb1414287886..f480306d2584e9 100644 --- a/doc/api/zlib.md +++ b/doc/api/zlib.md @@ -1,5 +1,7 @@ # Zlib + + > Stability: 2 - Stable The `zlib` module provides compression functionality implemented using Gzip and diff --git a/doc/api_assets/style.css b/doc/api_assets/style.css index 3761be4031e178..6d764fd88916c7 100644 --- a/doc/api_assets/style.css +++ b/doc/api_assets/style.css @@ -81,6 +81,61 @@ em code { #gtoc { font-size: .8em; + margin-bottom: 1em; +} + +#gtoc ul { + list-style: none; + margin-left: 0; +} + +#gtoc li { + display: inline; +} + +li.version-picker { + position: relative; +} + +li.version-picker:hover > ol { + display: block; +} + +li.version-picker a span { + font-size: .7em; +} + +ol.version-picker { + background: #fff; + border: 1px #43853d solid; + border-radius: 2px; + display: none; + list-style: none; + position: absolute; + right: -2px; + width: 101%; +} + +#gtoc ol.version-picker li { + display: block; +} + +ol.version-picker li a { + border-radius: 0; + display: block; + margin: 0; + padding: .1em; + padding-left: 1em; +} + +ol.version-picker li:first-child a { + border-top-right-radius: 1px; + border-top-left-radius: 1px; +} + +ol.version-picker li:last-child a { + border-bottom-right-radius: 1px; + border-bottom-left-radius: 1px; } .line { @@ -507,6 +562,9 @@ th > *:last-child, td > *:last-child { #content { font-size: 3.5em; } + #gtoc { + font-size: 0.6em; + } } @media print { diff --git a/doc/template.html b/doc/template.html index 572197beff44fe..d65b56ca5e80ad 100644 --- a/doc/template.html +++ b/doc/template.html @@ -23,11 +23,21 @@

Node.js __VERSION__ Documentation

-

- Index | - View on single page | - View as JSON -

+

diff --git a/tools/doc/html.js b/tools/doc/html.js index b0a3c13c69953d..c55772aa053cc9 100644 --- a/tools/doc/html.js +++ b/tools/doc/html.js @@ -31,6 +31,7 @@ const typeParser = require('./type-parser.js'); module.exports = toHTML; const STABILITY_TEXT_REG_EXP = /(.*:)\s*(\d)([\s\S]*)/; +const DOC_CREATED_REG_EXP = //; // customized heading without id attribute const renderer = new marked.Renderer(); @@ -52,13 +53,17 @@ const gtocPath = path.resolve(path.join( )); var gtocLoading = null; var gtocData = null; +var docCreated = null; +var nodeVersion = null; /** * opts: input, filename, template, nodeVersion. */ function toHTML(opts, cb) { const template = opts.template; - const nodeVersion = opts.nodeVersion || process.version; + + nodeVersion = opts.nodeVersion || process.version; + docCreated = opts.input.match(DOC_CREATED_REG_EXP); if (gtocData) { return onGtocLoaded(); @@ -157,6 +162,8 @@ function render(opts, cb) { ); } + template = template.replace(/__ALTDOCS__/, altDocs(filename)); + // content has to be the last thing we do with // the lexed tokens, because it's destructive. const content = marked.parser(lexed); @@ -188,6 +195,50 @@ function replaceInText(text) { return linkJsTypeDocs(linkManPages(text)); } +function altDocs(filename) { + let html = ''; + + if (!docCreated) { + console.error(`Failed to add alternative version links to ${filename}`); + return html; + } + + function lte(v) { + const ns = v.num.split('.'); + if (docCreated[1] > +ns[0]) + return false; + if (docCreated[1] < +ns[0]) + return true; + return docCreated[2] <= +ns[1]; + } + + const versions = [ + { num: '8.x' }, + { num: '7.x' }, + { num: '6.x', lts: true }, + { num: '5.x' }, + { num: '4.x', lts: true }, + { num: '0.12.x' }, + { num: '0.10.x' } + ]; + + const host = 'https://nodejs.org'; + const href = (v) => `${host}/docs/latest-v${v.num}/api/${filename}.html`; + + function li(v, i) { + let html = `
  • ${v.num}`; + + if (v.lts) + html += ' LTS'; + + return html + '
  • '; + } + + const lis = (vs) => vs.filter(lte).map(li).join('\n'); + + return `
      ${lis(versions)}
    `; +} + // handle general body-text replacements // for example, link man page references to the actual page function parseText(lexed) { From bc250a1e3882517a241b318a38e55a9662e882e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= Date: Mon, 28 Aug 2017 16:57:33 +0200 Subject: [PATCH 123/231] tools: fix linter error in html.js PR-URL: https://github.com/nodejs/node/pull/15063 Reviewed-By: Colin Ihrig Reviewed-By: Rich Trott Reviewed-By: Yuta Hiroto --- tools/doc/html.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tools/doc/html.js b/tools/doc/html.js index c55772aa053cc9..3373750a8d299b 100644 --- a/tools/doc/html.js +++ b/tools/doc/html.js @@ -196,11 +196,9 @@ function replaceInText(text) { } function altDocs(filename) { - let html = ''; - if (!docCreated) { console.error(`Failed to add alternative version links to ${filename}`); - return html; + return ''; } function lte(v) { From 7174dc2e8a5f4486ebc795eb06a184b1bf7445b1 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 19 Aug 2017 02:47:27 -0300 Subject: [PATCH 124/231] assert: handle sparse arrays in deepStrictEqual MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Detect sparse array ends and add a fail early path for unequal array length. PR-URL: https://github.com/nodejs/node/pull/15027 Reviewed-By: Rich Trott Reviewed-By: Yuta Hiroto Reviewed-By: Colin Ihrig Reviewed-By: James M Snell Reviewed-By: Tobias Nießen --- lib/assert.js | 9 ++++++++- test/parallel/test-assert-deep.js | 3 +++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/assert.js b/lib/assert.js index 6e4b9effbe5549..e120a981346640 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -178,7 +178,14 @@ function strictDeepEqual(actual, expected) { if (Object.getPrototypeOf(actual) !== Object.getPrototypeOf(expected)) { return false; } - if (isObjectOrArrayTag(actualTag)) { + if (actualTag === '[object Array]') { + // Check for sparse arrays and general fast path + if (actual.length !== expected.length) + return false; + // Skip testing the part below and continue in the callee function. + return; + } + if (actualTag === '[object Object]') { // Skip testing the part below and continue in the callee function. return; } diff --git a/test/parallel/test-assert-deep.js b/test/parallel/test-assert-deep.js index c240d1a33efd9f..068950a33d3c39 100644 --- a/test/parallel/test-assert-deep.js +++ b/test/parallel/test-assert-deep.js @@ -404,4 +404,7 @@ assertOnlyDeepEqual( assertDeepAndStrictEqual(m3, m4); } +assertDeepAndStrictEqual([1, , , 3], [1, , , 3]); +assertOnlyDeepEqual([1, , , 3], [1, , , 3, , , ]); + /* eslint-enable */ From 859abe5169a3aed4df241ce76f5b839b59fe21e4 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Thu, 24 Aug 2017 15:23:33 -0700 Subject: [PATCH 125/231] meta: considerations for new core modules PR-URL: https://github.com/nodejs/node/pull/15022 Reviewed-By: Rich Trott Reviewed-By: Colin Ihrig Reviewed-By: Anna Henningsen Reviewed-By: Luigi Pinca Reviewed-By: Ruben Bridgewater Reviewed-By: Matteo Collina Reviewed-By: Yuta Hiroto Reviewed-By: Andreas Madsen --- COLLABORATOR_GUIDE.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/COLLABORATOR_GUIDE.md b/COLLABORATOR_GUIDE.md index bb012ba90e06ae..cad067dd330798 100644 --- a/COLLABORATOR_GUIDE.md +++ b/COLLABORATOR_GUIDE.md @@ -263,6 +263,32 @@ multiple commits. Commit metadata and the reason for the revert should be appended. Commit message rules about line length and subsystem can be ignored. A Pull Request should be raised and approved like any other change. +### Introducing New Modules + +Semver-minor commits that introduce new core modules should be treated with +extra care. + +The name of the new core module should not conflict with any existing +module in the ecosystem unless a written agreement with the owner of those +modules is reached to transfer ownership. + +If the new module name is free, a Collaborator should register a placeholder +in the module registry as soon as possible, linking to the pull request that +introduces the new core module. + +Pull requests introducing new core modules: + +* Must be left open for at least one week for review. +* Must be labeled using the `ctc-review` label. +* Must have signoff from at least two CTC members. + +New core modules must be landed with a [Stability Index][] of Experimental, +and must remain Experimental until a semver-major release. + +For new modules that involve significant effort, non-trivial additions to +Node.js or significant new capabilities, an [Enhancement Proposal][] is +recommended but not required. + ### Deprecations Deprecation refers to the identification of Public APIs that should no longer @@ -642,3 +668,5 @@ release. This process of making a release will be a collaboration between the LTS working group and the Release team. [backporting guide]: doc/guides/backporting-to-release-lines.md +[Stability Index]: https://github.com/nodejs/node/pull/doc/api/documentation.md#stability-index +[Enhancement Proposal]: https://github.com/nodejs/node-eps From e7838d7077e85be1ac313146f362226ac0ca58fd Mon Sep 17 00:00:00 2001 From: Ruslan Iusupov Date: Mon, 28 Aug 2017 17:22:11 +0200 Subject: [PATCH 126/231] doc: add 8.4.0 link to CHANGELOG.md PR-URL: https://github.com/nodejs/node/pull/15064 Reviewed-By: Colin Ihrig Reviewed-By: Yuta Hiroto Reviewed-By: Anna Henningsen Reviewed-By: Evan Lucas Reviewed-By: Luigi Pinca Reviewed-By: Benjamin Gruenbaum Reviewed-By: James M Snell --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba634b10e3f358..c56fbfa3aaef98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,8 @@ release. -8.3.0
    +8.4.0
    +8.3.0
    8.2.1
    8.2.0
    8.1.4
    From b946693f4b325babb3c606fa09ca4d7431495e82 Mon Sep 17 00:00:00 2001 From: Alexey Orlenko Date: Sun, 27 Aug 2017 09:16:07 +0300 Subject: [PATCH 127/231] build: fix indentation in node.gyp One line in node.gyp was indented using a mix of a tab and spaces, convert it to all spaces. PR-URL: https://github.com/nodejs/node/pull/15051 Reviewed-By: Colin Ihrig Reviewed-By: Luigi Pinca Reviewed-By: Yuta Hiroto Reviewed-By: Daniel Bevenius Reviewed-By: Refael Ackermann Reviewed-By: James M Snell --- node.gyp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node.gyp b/node.gyp index 3d70a80197c140..5bec65caddc084 100644 --- a/node.gyp +++ b/node.gyp @@ -649,7 +649,7 @@ 'conditions': [ ['node_target_type!="static_library"', { 'libraries': [ - '<(OBJ_GEN_PATH)<(OBJ_SEPARATOR)node_javascript.<(OBJ_SUFFIX)', + '<(OBJ_GEN_PATH)<(OBJ_SEPARATOR)node_javascript.<(OBJ_SUFFIX)', '<(OBJ_PATH)<(OBJ_SEPARATOR)node_debug_options.<(OBJ_SUFFIX)', '<(OBJ_PATH)<(OBJ_SEPARATOR)async-wrap.<(OBJ_SUFFIX)', '<(OBJ_PATH)<(OBJ_SEPARATOR)env.<(OBJ_SUFFIX)', From 01846a06c2d16e93435050aaaba981c295800cf6 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 21 Aug 2017 22:44:50 -0700 Subject: [PATCH 128/231] meta: merge TSC and CTC back into a single body PR-URL: https://github.com/nodejs/node/pull/14973 Reviewed-By: Gireesh Punathil Reviewed-By: Matteo Collina Reviewed-By: Evan Lucas Reviewed-By: Rich Trott Reviewed-By: Myles Borins Reviewed-By: Ali Ijaz Sheikh Reviewed-By: Shigeki Ohtsu Reviewed-By: Fedor Indutny --- COLLABORATOR_GUIDE.md | 48 +++++++-------- GOVERNANCE.md | 132 ++++++++++++++---------------------------- README.md | 20 ++++--- doc/onboarding.md | 6 +- src/node_revert.h | 2 +- 5 files changed, 82 insertions(+), 126 deletions(-) diff --git a/COLLABORATOR_GUIDE.md b/COLLABORATOR_GUIDE.md index cad067dd330798..5aa0fb49cd68a6 100644 --- a/COLLABORATOR_GUIDE.md +++ b/COLLABORATOR_GUIDE.md @@ -8,7 +8,7 @@ - [Internal vs. Public API](#internal-vs-public-api) - [Breaking Changes](#breaking-changes) - [Deprecations](#deprecations) - - [Involving the CTC](#involving-the-ctc) + - [Involving the TSC](#involving-the-TSC) * [Landing Pull Requests](#landing-pull-requests) - [Technical HOWTO](#technical-howto) - [I Just Made a Mistake](#i-just-made-a-mistake) @@ -30,7 +30,7 @@ pull requests to the Node.js project. Collaborators should feel free to take full responsibility for managing issues and pull requests they feel qualified to handle, as long as this is done while being mindful of these guidelines, the -opinions of other Collaborators and guidance of the CTC. +opinions of other Collaborators and guidance of the TSC. Collaborators may **close** any issue or pull request they believe is not relevant for the future of the Node.js project. Where this is @@ -46,7 +46,7 @@ necessary. All modifications to the Node.js code and documentation should be performed via GitHub pull requests, including modifications by -Collaborators and CTC members. +Collaborators and TSC members. All pull requests must be reviewed and accepted by a Collaborator with sufficient expertise who is able to take full responsibility for the @@ -70,16 +70,16 @@ For non-breaking changes, if there is no disagreement amongst Collaborators, a pull request may be landed given appropriate review. Where there is discussion amongst Collaborators, consensus should be sought if possible. The lack of consensus may indicate the need to -elevate discussion to the CTC for resolution (see below). +elevate discussion to the TSC for resolution (see below). Breaking changes (that is, pull requests that require an increase in the major version number, known as `semver-major` changes) must be -elevated for review by the CTC. This does not necessarily mean that the -PR must be put onto the CTC meeting agenda. If multiple CTC members +elevated for review by the TSC. This does not necessarily mean that the +PR must be put onto the TSC meeting agenda. If multiple TSC members approve (`LGTM`) the PR and no Collaborators oppose the PR, it can be -landed. Where there is disagreement among CTC members or objections +landed. Where there is disagreement among TSC members or objections from one or more Collaborators, `semver-major` pull requests should be -put on the CTC meeting agenda. +put on the TSC meeting agenda. All bugfixes require a test case which demonstrates the defect. The test should *fail* before the change, and *pass* after the change. @@ -150,7 +150,7 @@ Exception to each of these points can be made if use or behavior of a given internal API can be demonstrated to be sufficiently relied upon by the Node.js ecosystem such that any changes would cause too much breakage. The threshold for what qualifies as "too much breakage" is to be decided on a case-by-case -basis by the CTC. +basis by the TSC. If it is determined that a currently undocumented object, property, method, argument, or event *should* be documented, then a pull request adding the @@ -171,7 +171,7 @@ making and reviewing such changes. Before landing such commits, an effort must be made to determine the potential impact of the change in the ecosystem by analyzing current use and by validating such changes through ecosystem testing using the [Canary in the Goldmine](https://github.com/nodejs/citgm) -tool. If a change cannot be made without ecosystem breakage, then CTC review is +tool. If a change cannot be made without ecosystem breakage, then TSC review is required before landing the change as anything less than semver-major. If a determination is made that a particular internal API (for instance, an @@ -183,7 +183,7 @@ breaking changes are made. ### Breaking Changes Backwards-incompatible changes may land on the master branch at any time after -sufficient review by collaborators and approval of at least two CTC members. +sufficient review by collaborators and approval of at least two TSC members. Examples of breaking changes include, but are not necessarily limited to, removal or redefinition of existing API arguments, changing return values @@ -209,7 +209,7 @@ Exception to this rule is given in the following cases: Such changes *must* be handled as semver-major changes but MAY be landed without a [Deprecation cycle](#deprecation-cycle). -From time-to-time, in particularly exceptional cases, the CTC may be asked to +From time-to-time, in particularly exceptional cases, the TSC may be asked to consider and approve additional exceptions to this rule. Purely additive changes (e.g. adding new events to EventEmitter @@ -244,7 +244,7 @@ Specifically: * Resolving critical security issues. * Fixing a critical bug (e.g. fixing a memory leak) requires a breaking change. - * There is CTC consensus that the change is required. + * There is TSC consensus that the change is required. * If a breaking commit does accidentally land in a Current or LTS branch, an attempt to fix the issue will be made before the next release; If no fix is provided then the commit will be reverted. @@ -320,7 +320,7 @@ operation of running code and therefore should not be viewed as breaking changes. Runtime Deprecations and End-of-life APIs (internal or public) *must* be -handled as semver-major changes unless there is CTC consensus to land the +handled as semver-major changes unless there is TSC consensus to land the deprecation as a semver-minor. All Documentation-Only and Runtime deprecations will be assigned a unique @@ -346,10 +346,10 @@ request adding the deprecation lands in master). All deprecations included in a Node.js release should be listed prominently in the "Notable Changes" section of the release notes. -### Involving the CTC +### Involving the TSC -Collaborators may opt to elevate pull requests or issues to the CTC for -discussion by assigning the `ctc-review` label. This should be done +Collaborators may opt to elevate pull requests or issues to the TSC for +discussion by assigning the `tsc-review` label. This should be done where a pull request: - has a significant impact on the codebase, @@ -357,7 +357,7 @@ where a pull request: - has failed to reach consensus amongst the Collaborators who are actively participating in the discussion. -The CTC should serve as the final arbiter where required. +The TSC should serve as the final arbiter where required. ## Landing Pull Requests @@ -567,7 +567,7 @@ git push upstream master ### I Just Made a Mistake -* Ping a CTC member. +* Ping a TSC member. * `#node-dev` on freenode * With `git`, there's a way to override remote trees by force pushing (`git push -f`). This should generally be seen as forbidden (since @@ -596,9 +596,9 @@ Once a Current branch enters LTS, changes in that branch are limited to bug fixes, security updates, possible npm updates, documentation updates, and certain performance improvements that can be demonstrated to not break existing applications. Semver-minor changes are only permitted if required for bug fixes -and then only on a case-by-case basis with LTS WG and possibly Core Technical -Committee (CTC) review. Semver-major changes are permitted only if required for -security related fixes. +and then only on a case-by-case basis with LTS WG and possibly Technical +Steering Committee (TSC) review. Semver-major changes are permitted only if +required for security related fixes. Once a Current branch moves into Maintenance mode, only **critical** bugs, **critical** security fixes, and documentation updates will be permitted. @@ -606,7 +606,7 @@ Once a Current branch moves into Maintenance mode, only **critical** bugs, #### Landing semver-minor commits in LTS The default policy is to not land semver-minor or higher commits in any LTS -branch. However, the LTS WG or CTC can evaluate any individual semver-minor +branch. However, the LTS WG or TSC can evaluate any individual semver-minor commit and decide whether a special exception ought to be made. It is expected that such exceptions would be evaluated, in part, on the scope and impact of the changes on the code, the risk to ecosystem stability @@ -616,7 +616,7 @@ commit will have for the ecosystem. Any collaborator who feels a semver-minor commit should be landed in an LTS branch should attach the `lts-agenda` label to the pull request. The LTS WG will discuss the issue and, if necessary, will escalate the issue up to the -CTC for further discussion. +TSC for further discussion. #### How are LTS Branches Managed? diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 27c0372ac3e0d0..20b75bd9718be2 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -1,14 +1,15 @@ # Node.js Project Governance -The Node.js project is governed by its Collaborators, including a Core Technical -Committee (CTC) which is responsible for high-level guidance of the project. +The Node.js project is governed by its Collaborators, including a Technical +Steering Committee (TSC) which is responsible for high-level guidance of the +project. ## Collaborators The [nodejs/node](https://github.com/nodejs/node) GitHub repository is -maintained by Collaborators who are added by the CTC on an ongoing basis. +maintained by Collaborators who are added by the TSC on an ongoing basis. -Individuals identified by the CTC as making significant and valuable +Individuals identified by the TSC as making significant and valuable contributions across any Node.js repository may be made Collaborators and given commit access to the project. Activities taken into consideration include (but are not limited to) the quality of: @@ -22,7 +23,7 @@ are not limited to) the quality of: * other participation in the wider Node.js community If individuals making valuable contributions do not believe they have been -considered for commit access, they may log an issue or contact a CTC member +considered for commit access, they may log an issue or contact a TSC member directly. Modifications of the contents of the nodejs/node repository are made on @@ -40,13 +41,13 @@ be accepted unless: * Discussions and/or additional changes result in no Collaborators objecting to the change. Previously-objecting Collaborators do not necessarily have to sign-off on the change, but they should not be opposed to it. -* The change is escalated to the CTC and the CTC votes to approve the change. +* The change is escalated to the TSC and the TSC votes to approve the change. This should only happen if disagreements between Collaborators cannot be resolved through discussion. Collaborators may opt to elevate significant or controversial modifications to -the CTC by assigning the `ctc-review` label to a pull request or issue. The -CTC should serve as the final arbiter where required. +the TSC by assigning the `tsc-review` label to a pull request or issue. The +TSC should serve as the final arbiter where required. * [Current list of Collaborators](./README.md#current-project-team-members) * [A guide for Collaborators](./COLLABORATOR_GUIDE.md) @@ -61,13 +62,13 @@ Typical activities of a Collaborator include: * participation in working groups * merging pull requests -The CTC periodically reviews the Collaborator list to identify inactive +The TSC periodically reviews the Collaborator list to identify inactive Collaborators. Past Collaborators are typically given _Emeritus_ status. Emeriti -may request that the CTC restore them to active status. +may request that the TSC restore them to active status. -## Core Technical Committee +## Technical Steering Committee -The Core Technical Committee (CTC) has final authority over this project +The Technical Steering Committee (TSC) has final authority over this project including: * Technical direction @@ -77,59 +78,19 @@ including: * Conduct guidelines * Maintaining the list of additional Collaborators -* [Current list of CTC members](./README.md#current-project-team-members) +* [Current list of TSC members](./README.md#current-project-team-members) -## CTC Membership +The operations of the TSC are governed by the [TSC Charter][] as approved by +the Node.js Foundation Board of Directors. -CTC seats are not time-limited. There is no fixed size of the CTC. The CTC -should be of such a size as to ensure adequate coverage of important areas of -expertise balanced with the ability to make decisions efficiently. +### TSC Meetings -There is no specific set of requirements or qualifications for CTC -membership beyond these rules. - -The CTC may add additional members to the CTC by a standard CTC motion. - -When a CTC member's participation in [CTC activities](#ctc-activities) has -become minimal for a sustained period of time, the CTC will request that the -member either indicate an intention to increase participation or voluntarily -resign. - -CTC members may only be removed by voluntary resignation or through a standard -CTC motion. - -Changes to CTC membership should be posted in the agenda, and may be -suggested as any other agenda item (see [CTC Meetings](#ctc-meetings) below). - -No more than 1/3 of the CTC members may be affiliated with the same -employer. If removal or resignation of a CTC member, or a change of -employment by a CTC member, creates a situation where more than 1/3 of -the CTC membership shares an employer, then the situation must be -immediately remedied by the resignation or removal of one or more CTC -members affiliated with the over-represented employer(s). - -### CTC Activities - -Typical activities of a CTC member include: - -* attending the weekly meeting -* commenting on the weekly CTC meeting issue and issues labeled `ctc-review` -* participating in CTC email threads -* volunteering for tasks that arise from CTC meetings and related discussions -* other activities (beyond those typical of Collaborators) that facilitate the - smooth day-to-day operation of the Node.js project - -Note that CTC members are also Collaborators and therefore typically perform -Collaborator activities as well. - -### CTC Meetings - -The CTC meets weekly in a voice conference call. The meeting is run by a -designated meeting chair approved by the CTC. Each meeting is streamed on +The TSC meets regularly in a voice conference call. The meeting is run by a +designated meeting chair approved by the TSC. Each meeting is streamed on YouTube. -Items are added to the CTC agenda which are considered contentious or -are modifications of governance, contribution policy, CTC membership, +Items are added to the TSC agenda which are considered contentious or +are modifications of governance, contribution policy, TSC membership, or release process. The intention of the agenda is not to approve or review all patches. @@ -137,49 +98,40 @@ That should happen continuously on GitHub and be handled by the larger group of Collaborators. Any community member or contributor can ask that something be reviewed -by the CTC by logging a GitHub issue. Any Collaborator, CTC member, or the -meeting chair can bring the issue to the CTC's attention by applying the -`ctc-review` label. If consensus-seeking among CTC members fails for a -particular issue, it may be added to the CTC meeting agenda by adding the -`ctc-agenda` label. - -Prior to each CTC meeting, the meeting chair will share the agenda with -members of the CTC. CTC members can also add items to the agenda at the -beginning of each meeting. The meeting chair and the CTC cannot veto or remove +by the TSC by logging a GitHub issue. Any Collaborator, TSC member, or the +meeting chair can bring the issue to the TSC's attention by applying the +`tsc-review` label. If consensus-seeking among TSC members fails for a +particular issue, it may be added to the TSC meeting agenda by adding the +`tsc-agenda` label. + +Prior to each TSC meeting, the meeting chair will share the agenda with +members of the TSC. TSC members can also add items to the agenda at the +beginning of each meeting. The meeting chair and the TSC cannot veto or remove items. -The CTC may invite persons or representatives from certain projects to -participate in a non-voting capacity. +The TSC may invite additional persons to participate in a non-voting capacity. The meeting chair is responsible for ensuring that minutes are taken and that a pull request with the minutes is submitted after the meeting. Due to the challenges of scheduling a global meeting with participants in -several timezones, the CTC will seek to resolve as many agenda items as possible +several timezones, the TSC will seek to resolve as many agenda items as possible outside of meetings using -[the CTC issue tracker](https://github.com/nodejs/CTC/issues). The process in +[the TSC issue tracker](https://github.com/nodejs/TSC/issues). The process in the issue tracker is: -* A CTC member opens an issue explaining the proposal/issue and @-mentions - @nodejs/ctc. -* After 72 hours, if there are two or more `LGTM`s from other CTC members and no - explicit opposition from other CTC members, then the proposal is approved. -* If there are any CTC members objecting, then a conversation ensues until +* A TSC member opens an issue explaining the proposal/issue and @-mentions + @nodejs/tsc. +* After 72 hours, if there are two or more `LGTM`s from other TSC members and no + explicit opposition from other TSC members, then the proposal is approved. +* If there are any TSC members objecting, then a conversation ensues until either the proposal is dropped or the objecting members are persuaded. If there is an extended impasse, a motion for a vote may be made. ## Consensus Seeking Process -The CTC follows a -[Consensus Seeking](http://en.wikipedia.org/wiki/Consensus-seeking_decision-making) -decision making model. - -When an agenda item has appeared to reach a consensus, the meeting chair will -ask "Does anyone object?" as a final call for dissent from the consensus. +The TSC follows a [Consensus Seeking][] decision making model as described by +the [TSC Charter][]. -If an agenda item cannot reach a consensus, a CTC member can call for either a -closing vote or a vote to table the issue to the next meeting. All votes -(including votes to close or table) pass if and only if more than 50% of the CTC -members (excluding individuals who explicitly abstain) vote in favor. For -example, if there are 20 CTC members, and 5 of those members indicate that they -abstain, then 8 votes in favor are required for a resolution to pass. +[TSC Charter]: https://github.com/nodejs/TSC/blob/master/TSC-Charter.md +[Consensus Seeking]: http://en.wikipedia.org/wiki/Consensus-seeking_decision-making diff --git a/README.md b/README.md index 76c46c10d7547b..385a5cef9b4e21 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ If you need help using or installing Node.js, please use the * [Building Node.js](#building-nodejs) * [Security](#security) * [Current Project Team Members](#current-project-team-members) - * [CTC (Core Technical Committee)](#ctc-core-technical-committee) + * [TSC (Technical Steering Committee)](#tsc-technical-steering-committee) * [Collaborators](#collaborators) * [Release Team](#release-team) @@ -55,9 +55,9 @@ If you need help using or installing Node.js, please use the channel. _Please note that unofficial resources are neither managed by (nor necessarily -endorsed by) the Node.js TSC/CTC. Specifically, such resources are not +endorsed by) the Node.js TSC. Specifically, such resources are not currently covered by the [Node.js Moderation Policy][] and the selection and -actions of resource operators/moderators are not subject to TSC/CTC oversight._ +actions of resource operators/moderators are not subject to TSC oversight._ ## Release Types @@ -176,11 +176,11 @@ handling your report. ## Current Project Team Members The Node.js project team comprises a group of core collaborators and a sub-group -that forms the _Core Technical Committee_ (CTC) which governs the project. For -more information about the governance of the Node.js project, see +that forms the _Technical Steering Committee_ (TSC) which governs the project. +For more information about the governance of the Node.js project, see [GOVERNANCE.md](./GOVERNANCE.md). -### CTC (Core Technical Committee) +### TSC (Technical Steering Committee) * [addaleax](https://github.com/addaleax) - **Anna Henningsen** <anna@addaleax.net> (she/her) @@ -200,6 +200,8 @@ more information about the governance of the Node.js project, see **Fedor Indutny** <fedor.indutny@gmail.com> * [jasnell](https://github.com/jasnell) - **James M Snell** <jasnell@gmail.com> (he/him) +* [joshgav](https://github.com/joshgav) - +**Josh Gavant** <josh.gavant@outlook.com> * [joyeecheung](https://github.com/joyeecheung) - **Joyee Cheung** <joyeec9h3@gmail.com> (she/her) * [mcollina](https://github.com/mcollina) - @@ -225,7 +227,7 @@ more information about the governance of the Node.js project, see * [Trott](https://github.com/Trott) - **Rich Trott** <rtrott@gmail.com> (he/him) -### CTC Emeriti +### TSC Emeriti * [chrisdickinson](https://github.com/chrisdickinson) - **Chris Dickinson** <christopher.s.dickinson@gmail.com> @@ -235,6 +237,8 @@ more information about the governance of the Node.js project, see **Alexis Campailla** <orangemocha@nodejs.org> * [piscisaureus](https://github.com/piscisaureus) - **Bert Belder** <bertbelder@gmail.com> +* [nebrius](https://github.com/nebrius) - +**Bryan Hughes** <bryan@nebri.us> ### Collaborators @@ -508,7 +512,7 @@ Previous releases may also have been signed with one of the following GPG keys: ### Working Groups Information on the current Node.js Working Groups can be found in the -[CTC repository](https://github.com/nodejs/CTC/blob/master/WORKING_GROUPS.md). +[TSC repository](https://github.com/nodejs/TSC/blob/master/WORKING_GROUPS.md). [npm]: https://www.npmjs.com [Website]: https://nodejs.org/en/ diff --git a/doc/onboarding.md b/doc/onboarding.md index e1e10d88b30243..2702c9b993768d 100644 --- a/doc/onboarding.md +++ b/doc/onboarding.md @@ -43,7 +43,7 @@ onboarding session. * Use [https://github.com/notifications](https://github.com/notifications) or set up email * Watching the main repo will flood your inbox (several hundred notifications on typical weekdays), so be prepared - * `#node-dev` on [webchat.freenode.net](https://webchat.freenode.net/) is the best place to interact with the CTC / other Collaborators + * `#node-dev` on [webchat.freenode.net](https://webchat.freenode.net/) is the best place to interact with the TSC / other Collaborators * If there are any questions after the session, a good place to ask is there! * Presence is not mandatory, but please drop a note there if force-pushing to `master` @@ -67,7 +67,7 @@ onboarding session. * [**See "Labels"**](./onboarding-extras.md#labels) * There is [a bot](https://github.com/nodejs-github-bot/github-bot) that applies subsystem labels (for example, `doc`, `test`, `assert`, or `buffer`) so that we know what parts of the code base the pull request modifies. It is not perfect, of course. Feel free to apply relevant labels and remove irrelevant labels from pull requests and issues. - * Use the `ctc-review` label if a topic is controversial or isn't coming to + * Use the `tsc-review` label if a topic is controversial or isn't coming to a conclusion after an extended time. * `semver-{minor,major}`: * If a change has the remote *chance* of breaking something, use the `semver-major` label @@ -166,7 +166,7 @@ onboarding session. * Almost any mistake you could make can be fixed or reverted. * The existing Collaborators trust you and are grateful for your help! * Other repositories: - * [https://github.com/nodejs/CTC](https://github.com/nodejs/CTC) + * [https://github.com/nodejs/TSC](https://github.com/nodejs/TSC) * [https://github.com/nodejs/build](https://github.com/nodejs/build) * [https://github.com/nodejs/nodejs.org](https://github.com/nodejs/nodejs.org) * [https://github.com/nodejs/readable-stream](https://github.com/nodejs/readable-stream) diff --git a/src/node_revert.h b/src/node_revert.h index c26bb677818cfa..c5963afeafd027 100644 --- a/src/node_revert.h +++ b/src/node_revert.h @@ -8,7 +8,7 @@ /** * Note that it is expected for this list to vary across specific LTS and * Stable versions! Only CVE's whose fixes require *breaking* changes within - * a given LTS or Stable may be added to this list, and only with CTC + * a given LTS or Stable may be added to this list, and only with TSC * consensus. * * For *master* this list should always be empty! From 296729c41e16ee8b0cc284acbdbbc86d188d4ee5 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Mon, 14 Aug 2017 18:12:30 +0200 Subject: [PATCH 129/231] deps: cherry-pick 0ef4a0c64b6 from c-ares upstream Original commit message: gethostbyaddr: fail with `ECANCELLED` for `ares_cancel()` When `ares_cancel()` was invoked, `ares_gethostbyaddr()` queries would fail with `ENOTFOUND` instead of `ECANCELLED`. It seems appropriate to treat `ares_cancel()` like `ares_destroy()`, but I would appreciate review of the correctness of this change. Ref: https://github.com/nodejs/node/issues/14814 Fixes: https://github.com/nodejs/node/issues/14814 PR-URL: https://github.com/nodejs/node/pull/15023 Reviewed-By: James M Snell Reviewed-By: Colin Ihrig --- deps/cares/src/ares_gethostbyaddr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/cares/src/ares_gethostbyaddr.c b/deps/cares/src/ares_gethostbyaddr.c index 9258919a385699..a0a90f6bb1712b 100644 --- a/deps/cares/src/ares_gethostbyaddr.c +++ b/deps/cares/src/ares_gethostbyaddr.c @@ -157,7 +157,7 @@ static void addr_callback(void *arg, int status, int timeouts, } end_aquery(aquery, status, host); } - else if (status == ARES_EDESTRUCTION) + else if (status == ARES_EDESTRUCTION || status == ARES_ECANCELLED) end_aquery(aquery, status, NULL); else next_lookup(aquery); From 458b8ab5dfd16664e5e1275bfb4aad6cf990dfd3 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Fri, 25 Aug 2017 00:30:53 +0200 Subject: [PATCH 130/231] test: add regression test for 14814 Ref: https://github.com/nodejs/node/issues/14814 PR-URL: https://github.com/nodejs/node/pull/15023 Reviewed-By: James M Snell Reviewed-By: Colin Ihrig --- .../test-dns-cancel-reverse-lookup.js | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 test/parallel/test-dns-cancel-reverse-lookup.js diff --git a/test/parallel/test-dns-cancel-reverse-lookup.js b/test/parallel/test-dns-cancel-reverse-lookup.js new file mode 100644 index 00000000000000..0918178e1257ba --- /dev/null +++ b/test/parallel/test-dns-cancel-reverse-lookup.js @@ -0,0 +1,29 @@ +'use strict'; +const common = require('../common'); +const dnstools = require('../common/dns'); +const { Resolver } = require('dns'); +const assert = require('assert'); +const dgram = require('dgram'); + +const server = dgram.createSocket('udp4'); +const resolver = new Resolver(); + +server.bind(0, common.mustCall(() => { + resolver.setServers([`127.0.0.1:${server.address().port}`]); + resolver.reverse('123.45.67.89', common.mustCall((err, res) => { + assert.strictEqual(err.code, 'ECANCELLED'); + assert.strictEqual(err.errno, 'ECANCELLED'); + assert.strictEqual(err.syscall, 'getHostByAddr'); + assert.strictEqual(err.hostname, '123.45.67.89'); + server.close(); + })); +})); + +server.on('message', common.mustCall((msg, { address, port }) => { + const parsed = dnstools.parseDNSPacket(msg); + const domain = parsed.questions[0].domain; + assert.strictEqual(domain, '89.67.45.123.in-addr.arpa'); + + // Do not send a reply. + resolver.cancel(); +})); From 7050608593cee4377aa57debb8010c1b8839ec86 Mon Sep 17 00:00:00 2001 From: XadillaX Date: Sun, 6 Aug 2017 14:16:05 +0800 Subject: [PATCH 131/231] test: fix hijackStdout behavior in console `console.log` and some other function will swallow the exception in `stdout.write`. So an asynchronous exception is needed, or `common.hijackStdout` won't detect some exception. Refs: https://github.com/nodejs/node/blob/v8.2.1/lib/console.js#L87 PR-URL: https://github.com/nodejs/node/pull/14647 Reviewed-By: James M Snell Reviewed-By: Refael Ackermann Reviewed-By: Yuta Hiroto Reviewed-By: Ruben Bridgewater Reviewed-By: Joyee Cheung --- test/common/index.js | 7 ++++++- test/parallel/test-common.js | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/test/common/index.js b/test/common/index.js index b276b8ff1a9a3b..f4631f4a0b14fd 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -803,7 +803,12 @@ function hijackStdWritable(name, listener) { stream.writeTimes = 0; stream.write = function(data, callback) { - listener(data); + try { + listener(data); + } catch (e) { + process.nextTick(() => { throw e; }); + } + _write.call(stream, data, callback); stream.writeTimes++; }; diff --git a/test/parallel/test-common.js b/test/parallel/test-common.js index 819cd815538b43..50832b3343cf86 100644 --- a/test/parallel/test-common.js +++ b/test/parallel/test-common.js @@ -108,3 +108,26 @@ const HIJACK_TEST_ARRAY = [ 'foo\n', 'bar\n', 'baz\n' ]; common[`restoreStd${txt}`](); assert.strictEqual(originalWrite, stream.write); }); + +// hijackStderr and hijackStdout again +// for console +[[ 'err', 'error' ], [ 'out', 'log' ]].forEach(([ type, method ]) => { + common[`hijackStd${type}`](common.mustCall(function(data) { + assert.strictEqual(data, 'test\n'); + + // throw an error + throw new Error(`console ${type} error`); + })); + + console[method]('test'); + common[`restoreStd${type}`](); +}); + +let uncaughtTimes = 0; +process.on('uncaughtException', common.mustCallAtLeast(function(e) { + assert.strictEqual(uncaughtTimes < 2, true); + assert.strictEqual(e instanceof Error, true); + assert.strictEqual( + e.message, + `console ${([ 'err', 'out' ])[uncaughtTimes++]} error`); +}, 2)); From 2610ae326f7f4a76146f8dd92994606163ccd612 Mon Sep 17 00:00:00 2001 From: AJ Jordan Date: Sat, 26 Aug 2017 18:20:34 -0400 Subject: [PATCH 132/231] doc: clarify http.get data consumption requirement With the previous wording, I read this sentence as meaning, "you _must_ use the http.get callback mechanism and cannot register a listener on the returned http.ClientRequest object." This is obviously not the intention, so adjust the sentence to make this clearer. PR-URL: https://github.com/nodejs/node/pull/15049 Reviewed-By: Luigi Pinca Reviewed-By: James M Snell --- doc/api/http.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api/http.md b/doc/api/http.md index 2994fa48173fc4..e646e291ac724c 100644 --- a/doc/api/http.md +++ b/doc/api/http.md @@ -1695,8 +1695,8 @@ changes: Since most requests are GET requests without bodies, Node.js provides this convenience method. The only difference between this method and [`http.request()`][] is that it sets the method to GET and calls `req.end()` -automatically. Note that response data must be consumed in the callback -for reasons stated in [`http.ClientRequest`][] section. +automatically. Note that the callback must take care to consume the response +data for reasons stated in [`http.ClientRequest`][] section. The `callback` is invoked with a single argument that is an instance of [`http.IncomingMessage`][] From ebb3c2ce6f31c485aa134a2e5c58080b6f3a1212 Mon Sep 17 00:00:00 2001 From: Richard Lau Date: Fri, 25 Aug 2017 17:00:07 +0100 Subject: [PATCH 133/231] build: add npx to zip and 7z packages Copy npx and npx.cmd from `deps\npm\bin` for consistency with .msi package. PR-URL: https://github.com/nodejs/node/pull/15033 Refs: https://github.com/nodejs/node/pull/14235 Reviewed-By: Gibson Fahnestock Reviewed-By: James M Snell --- vcbuild.bat | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/vcbuild.bat b/vcbuild.bat index 8073a40bef521a..6dd053999d55e2 100644 --- a/vcbuild.bat +++ b/vcbuild.bat @@ -298,6 +298,10 @@ copy /Y ..\deps\npm\bin\npm node-v%FULLVERSION%-win-%target_arch%\ > nul if errorlevel 1 echo Cannot copy npm && goto package_error copy /Y ..\deps\npm\bin\npm.cmd node-v%FULLVERSION%-win-%target_arch%\ > nul if errorlevel 1 echo Cannot copy npm.cmd && goto package_error +copy /Y ..\deps\npm\bin\npx node-v%FULLVERSION%-win-%target_arch%\ > nul +if errorlevel 1 echo Cannot copy npx && goto package_error +copy /Y ..\deps\npm\bin\npx.cmd node-v%FULLVERSION%-win-%target_arch%\ > nul +if errorlevel 1 echo Cannot copy npx.cmd && goto package_error copy /Y ..\tools\msvs\nodevars.bat node-v%FULLVERSION%-win-%target_arch%\ > nul if errorlevel 1 echo Cannot copy nodevars.bat && goto package_error if not defined noetw ( From 5cb6500de96d8acfd5f5d304f678393bba67980a Mon Sep 17 00:00:00 2001 From: Anatoli Papirovski Date: Fri, 25 Aug 2017 13:19:28 -0400 Subject: [PATCH 134/231] test: increase coverage for http2 response headers Expanded an existing test for setting pseudo-headers on response to include all pseudo-headers, not just :status. PR-URL: https://github.com/nodejs/node/pull/15035 Refs: https://github.com/nodejs/node/issues/14985 Reviewed-By: Colin Ihrig Reviewed-By: James M Snell Reviewed-By: Yuta Hiroto Reviewed-By: Timothy Gu Reviewed-By: Ruben Bridgewater --- ...est-http2-compat-serverresponse-headers.js | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/test/parallel/test-http2-compat-serverresponse-headers.js b/test/parallel/test-http2-compat-serverresponse-headers.js index 7a9b7d66405704..512a14c1c0cf8f 100644 --- a/test/parallel/test-http2-compat-serverresponse-headers.js +++ b/test/parallel/test-http2-compat-serverresponse-headers.js @@ -42,13 +42,20 @@ server.listen(0, common.mustCall(function() { response.removeHeader(denormalised); assert.strictEqual(response.hasHeader(denormalised), false); - assert.throws(function() { - response.setHeader(':status', 'foobar'); - }, common.expectsError({ - code: 'ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED', - type: Error, - message: 'Cannot set HTTP/2 pseudo-headers' - })); + [ + ':status', + ':method', + ':path', + ':authority', + ':scheme' + ].forEach((header) => assert.throws( + () => response.setHeader(header, 'foobar'), + common.expectsError({ + code: 'ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED', + type: Error, + message: 'Cannot set HTTP/2 pseudo-headers' + }) + )); assert.throws(function() { response.setHeader(real, null); }, common.expectsError({ From 5b13add02851f275e1e11bbcfe6e99333091767e Mon Sep 17 00:00:00 2001 From: Simon Brewster Date: Thu, 24 Aug 2017 09:54:16 +0200 Subject: [PATCH 135/231] test: remove unused param in test-graph.pipe Refs: https://twitter.com/NodeTodo/status/900502354834800645 PR-URL: https://github.com/nodejs/node/pull/15007 Reviewed-By: Yuta Hiroto Reviewed-By: Vse Mozhet Byt Reviewed-By: James M Snell Reviewed-By: Refael Ackermann Reviewed-By: Luigi Pinca --- test/async-hooks/test-graph.pipe.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/async-hooks/test-graph.pipe.js b/test/async-hooks/test-graph.pipe.js index 69d403a34e4c23..162fb3639bfbfc 100644 --- a/test/async-hooks/test-graph.pipe.js +++ b/test/async-hooks/test-graph.pipe.js @@ -14,7 +14,7 @@ sleep .on('exit', common.mustCall(onsleepExit)) .on('close', common.mustCall(onsleepClose)); -function onsleepExit(code) {} +function onsleepExit() {} function onsleepClose() {} From 700d576962f21b6f65e66b2016dd1a005e76ccc4 Mon Sep 17 00:00:00 2001 From: Trevor Norris Date: Thu, 17 Aug 2017 16:44:27 -0600 Subject: [PATCH 136/231] async_hooks: emitAfter correctly on fatalException Previously calling emitAfter() from _fatalException would skipt the first asyncId. Instead use the size() of the std::stack to determine how many times to loop and call emitAfter(). PR-URL: https://github.com/nodejs/node/pull/14914 Reviewed-By: James M Snell Reviewed-By: Refael Ackermann --- lib/internal/bootstrap_node.js | 5 +-- src/async-wrap.cc | 8 ++++ src/async-wrap.h | 1 + src/env-inl.h | 4 ++ src/env.h | 1 + .../test-emit-after-uncaught-exception.js | 40 +++++++++++++++++++ 6 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 test/parallel/test-emit-after-uncaught-exception.js diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index 9e26080987183e..e11df46e7f8053 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -347,7 +347,7 @@ // Arrays containing hook flags and ids for async_hook calls. const { async_hook_fields, async_uid_fields } = async_wrap; // Internal functions needed to manipulate the stack. - const { clearIdStack, popAsyncIds } = async_wrap; + const { clearIdStack, asyncIdStackSize } = async_wrap; const { kAfter, kCurrentAsyncId, kInitTriggerId } = async_wrap.constants; process._fatalException = function(er) { @@ -384,8 +384,7 @@ do { NativeModule.require('async_hooks').emitAfter( async_uid_fields[kCurrentAsyncId]); - // popAsyncIds() returns true if there are more ids on the stack. - } while (popAsyncIds(async_uid_fields[kCurrentAsyncId])); + } while (asyncIdStackSize() > 0); // Or completely empty the id stack. } else { clearIdStack(); diff --git a/src/async-wrap.cc b/src/async-wrap.cc index b6588a20ad4071..3228299233e468 100644 --- a/src/async-wrap.cc +++ b/src/async-wrap.cc @@ -474,6 +474,13 @@ void AsyncWrap::PopAsyncIds(const FunctionCallbackInfo& args) { } +void AsyncWrap::AsyncIdStackSize(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + args.GetReturnValue().Set( + static_cast(env->async_hooks()->stack_size())); +} + + void AsyncWrap::ClearIdStack(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); env->async_hooks()->clear_id_stack(); @@ -503,6 +510,7 @@ void AsyncWrap::Initialize(Local target, env->SetMethod(target, "setupHooks", SetupHooks); env->SetMethod(target, "pushAsyncIds", PushAsyncIds); env->SetMethod(target, "popAsyncIds", PopAsyncIds); + env->SetMethod(target, "asyncIdStackSize", AsyncIdStackSize); env->SetMethod(target, "clearIdStack", ClearIdStack); env->SetMethod(target, "addIdToDestroyList", QueueDestroyId); env->SetMethod(target, "enablePromiseHook", EnablePromiseHook); diff --git a/src/async-wrap.h b/src/async-wrap.h index ffdf8358747f12..5bc48d7faf0c30 100644 --- a/src/async-wrap.h +++ b/src/async-wrap.h @@ -102,6 +102,7 @@ class AsyncWrap : public BaseObject { static void GetAsyncId(const v8::FunctionCallbackInfo& args); static void PushAsyncIds(const v8::FunctionCallbackInfo& args); static void PopAsyncIds(const v8::FunctionCallbackInfo& args); + static void AsyncIdStackSize(const v8::FunctionCallbackInfo& args); static void ClearIdStack(const v8::FunctionCallbackInfo& args); static void AsyncReset(const v8::FunctionCallbackInfo& args); static void QueueDestroyId(const v8::FunctionCallbackInfo& args); diff --git a/src/env-inl.h b/src/env-inl.h index 888dd807c11e15..b651d969a3d7e8 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -166,6 +166,10 @@ inline bool Environment::AsyncHooks::pop_ids(double async_id) { return !ids_stack_.empty(); } +inline size_t Environment::AsyncHooks::stack_size() { + return ids_stack_.size(); +} + inline void Environment::AsyncHooks::clear_id_stack() { while (!ids_stack_.empty()) ids_stack_.pop(); diff --git a/src/env.h b/src/env.h index 80ef32b896d98b..47a6487b77dbe6 100644 --- a/src/env.h +++ b/src/env.h @@ -396,6 +396,7 @@ class Environment { inline void push_ids(double async_id, double trigger_id); inline bool pop_ids(double async_id); + inline size_t stack_size(); inline void clear_id_stack(); // Used in fatal exceptions. // Used to propagate the trigger_id to the constructor of any newly created diff --git a/test/parallel/test-emit-after-uncaught-exception.js b/test/parallel/test-emit-after-uncaught-exception.js new file mode 100644 index 00000000000000..368099d48355be --- /dev/null +++ b/test/parallel/test-emit-after-uncaught-exception.js @@ -0,0 +1,40 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const async_hooks = require('async_hooks'); + +const id_obj = {}; +let collect = true; + +const hook = async_hooks.createHook({ + before(id) { if (collect) id_obj[id] = true; }, + after(id) { delete id_obj[id]; }, +}).enable(); + +process.once('uncaughtException', common.mustCall((er) => { + assert.strictEqual(er.message, 'bye'); + collect = false; +})); + +setImmediate(common.mustCall(() => { + process.nextTick(common.mustCall(() => { + assert.strictEqual(Object.keys(id_obj).length, 0); + hook.disable(); + })); + + // Create a stack of async ids that will need to be emitted in the case of + // an uncaught exception. + const ar1 = new async_hooks.AsyncResource('Mine'); + ar1.emitBefore(); + + const ar2 = new async_hooks.AsyncResource('Mine'); + ar2.emitBefore(); + + throw new Error('bye'); + + // TODO(trevnorris): This test shows that the after() hooks are always called + // correctly, but it doesn't solve where the emitDestroy() is missed because + // of the uncaught exception. Simple solution is to always call emitDestroy() + // before the emitAfter(), but how to codify this? +})); From 29f9101a0f8a10e6d492e56e506f483f4e3c5d3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= Date: Thu, 6 Jul 2017 17:14:16 +0200 Subject: [PATCH 137/231] path: fix normalize on directories with two dots PR-URL: https://github.com/nodejs/node/pull/14107 Fixes: https://github.com/nodejs/node/issues/14105 Reviewed-By: Refael Ackermann --- lib/path.js | 14 ++++++++++++-- test/parallel/test-path.js | 10 ++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/path.js b/lib/path.js index 3fab7f79d746ec..56a7c4f36e35ca 100644 --- a/lib/path.js +++ b/lib/path.js @@ -35,6 +35,7 @@ function normalizeStringWin32(path, allowAboveRoot) { var lastSlash = -1; var dots = 0; var code; + var isAboveRoot = false; for (var i = 0; i <= path.length; ++i) { if (i < path.length) code = path.charCodeAt(i); @@ -46,7 +47,7 @@ function normalizeStringWin32(path, allowAboveRoot) { if (lastSlash === i - 1 || dots === 1) { // NOOP } else if (lastSlash !== i - 1 && dots === 2) { - if (res.length < 2 || + if (res.length < 2 || !isAboveRoot || res.charCodeAt(res.length - 1) !== 46/*.*/ || res.charCodeAt(res.length - 2) !== 46/*.*/) { if (res.length > 2) { @@ -63,12 +64,14 @@ function normalizeStringWin32(path, allowAboveRoot) { res = res.slice(0, j); lastSlash = i; dots = 0; + isAboveRoot = false; continue; } } else if (res.length === 2 || res.length === 1) { res = ''; lastSlash = i; dots = 0; + isAboveRoot = false; continue; } } @@ -77,12 +80,14 @@ function normalizeStringWin32(path, allowAboveRoot) { res += '\\..'; else res = '..'; + isAboveRoot = true; } } else { if (res.length > 0) res += '\\' + path.slice(lastSlash + 1, i); else res = path.slice(lastSlash + 1, i); + isAboveRoot = false; } lastSlash = i; dots = 0; @@ -101,6 +106,7 @@ function normalizeStringPosix(path, allowAboveRoot) { var lastSlash = -1; var dots = 0; var code; + var isAboveRoot = false; for (var i = 0; i <= path.length; ++i) { if (i < path.length) code = path.charCodeAt(i); @@ -112,7 +118,7 @@ function normalizeStringPosix(path, allowAboveRoot) { if (lastSlash === i - 1 || dots === 1) { // NOOP } else if (lastSlash !== i - 1 && dots === 2) { - if (res.length < 2 || + if (res.length < 2 || !isAboveRoot || res.charCodeAt(res.length - 1) !== 46/*.*/ || res.charCodeAt(res.length - 2) !== 46/*.*/) { if (res.length > 2) { @@ -129,12 +135,14 @@ function normalizeStringPosix(path, allowAboveRoot) { res = res.slice(0, j); lastSlash = i; dots = 0; + isAboveRoot = false; continue; } } else if (res.length === 2 || res.length === 1) { res = ''; lastSlash = i; dots = 0; + isAboveRoot = false; continue; } } @@ -143,12 +151,14 @@ function normalizeStringPosix(path, allowAboveRoot) { res += '/..'; else res = '..'; + isAboveRoot = true; } } else { if (res.length > 0) res += '/' + path.slice(lastSlash + 1, i); else res = path.slice(lastSlash + 1, i); + isAboveRoot = false; } lastSlash = i; dots = 0; diff --git a/test/parallel/test-path.js b/test/parallel/test-path.js index 3e4f2b43e4c21e..8b9f1600202d4c 100644 --- a/test/parallel/test-path.js +++ b/test/parallel/test-path.js @@ -432,6 +432,11 @@ assert.strictEqual(path.win32.normalize('C:..\\..\\abc\\..\\def'), 'C:..\\..\\def'); assert.strictEqual(path.win32.normalize('C:\\.'), 'C:\\'); assert.strictEqual(path.win32.normalize('file:stream'), 'file:stream'); +assert.strictEqual(path.win32.normalize('bar\\foo..\\..\\'), 'bar\\'); +assert.strictEqual(path.win32.normalize('bar\\foo..\\..'), 'bar'); +assert.strictEqual(path.win32.normalize('bar\\foo..\\..\\baz'), 'bar\\baz'); +assert.strictEqual(path.win32.normalize('bar\\foo..\\'), 'bar\\foo..\\'); +assert.strictEqual(path.win32.normalize('bar\\foo..'), 'bar\\foo..'); assert.strictEqual(path.posix.normalize('./fixtures///b/../b/c.js'), 'fixtures/b/c.js'); @@ -441,6 +446,11 @@ assert.strictEqual(path.posix.normalize('a//b//./c'), 'a/b/c'); assert.strictEqual(path.posix.normalize('a//b//.'), 'a/b'); assert.strictEqual(path.posix.normalize('/a/b/c/../../../x/y/z'), '/x/y/z'); assert.strictEqual(path.posix.normalize('///..//./foo/.//bar'), '/foo/bar'); +assert.strictEqual(path.posix.normalize('bar/foo../../'), 'bar/'); +assert.strictEqual(path.posix.normalize('bar/foo../..'), 'bar'); +assert.strictEqual(path.posix.normalize('bar/foo../../baz'), 'bar/baz'); +assert.strictEqual(path.posix.normalize('bar/foo../'), 'bar/foo../'); +assert.strictEqual(path.posix.normalize('bar/foo..'), 'bar/foo..'); // path.resolve tests From 76780445b381e1b7af298b50b8e572fdd5af51dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Sch=C3=A4r?= Date: Wed, 30 Aug 2017 15:59:19 +0200 Subject: [PATCH 138/231] doc: remove braces which shouldn't be there PR-URL: https://github.com/nodejs/node/pull/15094 Reviewed-By: Vse Mozhet Byt Reviewed-By: Luigi Pinca Reviewed-By: James M Snell --- doc/api/http2.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/api/http2.md b/doc/api/http2.md index 57e1b74e7e9edc..72ca5cc806950c 100755 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -757,7 +757,7 @@ added: v8.4.0 Shortcut for `http2stream.rstStream()` using error code `0x00` (No Error). -#### http2stream.rstWithProtocolError() { +#### http2stream.rstWithProtocolError() @@ -766,7 +766,7 @@ added: v8.4.0 Shortcut for `http2stream.rstStream()` using error code `0x01` (Protocol Error). -#### http2stream.rstWithCancel() { +#### http2stream.rstWithCancel() @@ -775,7 +775,7 @@ added: v8.4.0 Shortcut for `http2stream.rstStream()` using error code `0x08` (Cancel). -#### http2stream.rstWithRefuse() { +#### http2stream.rstWithRefuse() @@ -784,7 +784,7 @@ added: v8.4.0 Shortcut for `http2stream.rstStream()` using error code `0x07` (Refused Stream). -#### http2stream.rstWithInternalError() { +#### http2stream.rstWithInternalError() From 9129057e038eefb587b4b46c26551873d9a08b11 Mon Sep 17 00:00:00 2001 From: Jackson Tian Date: Thu, 11 Aug 2016 10:47:58 +0800 Subject: [PATCH 139/231] lib: clean up usage of threw Use try/catch to instead of threw. PR-URL: https://github.com/nodejs/node/pull/10534 Reviewed-By: Ruben Bridgewater Reviewed-By: James M Snell Reviewed-By: Colin Ihrig --- lib/internal/bootstrap_node.js | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index e11df46e7f8053..3c608fccd1ed78 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -430,20 +430,14 @@ } function tryGetCwd(path) { - var threw = true; - var cwd; try { - cwd = process.cwd(); - threw = false; - } finally { - if (threw) { - // getcwd(3) can fail if the current working directory has been deleted. - // Fall back to the directory name of the (absolute) executable path. - // It's not really correct but what are the alternatives? - return path.dirname(process.execPath); - } + return process.cwd(); + } catch (ex) { + // getcwd(3) can fail if the current working directory has been deleted. + // Fall back to the directory name of the (absolute) executable path. + // It's not really correct but what are the alternatives? + return path.dirname(process.execPath); } - return cwd; } function evalScript(name) { From cfbf5057d6c02cb4eba6a404ccda67dc07709ba6 Mon Sep 17 00:00:00 2001 From: Anatoli Papirovski Date: Sun, 27 Aug 2017 15:17:05 -0400 Subject: [PATCH 140/231] test: add http2 test for method CONNECT Adds test case for default handling of method CONNECT, as well as the ability to bind a connect listener and handle the request. PR-URL: https://github.com/nodejs/node/pull/15052 Refs: https://github.com/nodejs/node/issues/14985 Reviewed-By: James M Snell --- .../test-http2-compat-method-connect.js | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 test/parallel/test-http2-compat-method-connect.js diff --git a/test/parallel/test-http2-compat-method-connect.js b/test/parallel/test-http2-compat-method-connect.js new file mode 100644 index 00000000000000..8e6202317aafc3 --- /dev/null +++ b/test/parallel/test-http2-compat-method-connect.js @@ -0,0 +1,41 @@ +// Flags: --expose-http2 +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); + +const server = http2.createServer(common.mustNotCall()); + +server.listen(0, common.mustCall(() => testMethodConnect(2))); + +server.once('connect', common.mustCall((req, res) => { + assert.strictEqual(req.headers[':method'], 'CONNECT'); + res.statusCode = 405; + res.end(); +})); + +function testMethodConnect(testsToRun) { + if (!testsToRun) { + return server.close(); + } + + const port = server.address().port; + const client = http2.connect(`http://localhost:${port}`); + const req = client.request({ + ':method': 'CONNECT', + ':authority': `localhost:${port}` + }); + + req.on('response', common.mustCall((headers) => { + assert.strictEqual(headers[':status'], 405); + })); + req.resume(); + req.on('end', common.mustCall(() => { + client.destroy(); + testMethodConnect(testsToRun - 1); + })); + req.end(); +} From d176a1854714abbc8037122b4f74804871037ce8 Mon Sep 17 00:00:00 2001 From: Anatoli Papirovski Date: Sun, 27 Aug 2017 08:41:31 -0400 Subject: [PATCH 141/231] test: add a test for Expect & checkExpectation New test case for Expect header & checkExpectation event based on the existing http test case. PR-URL: https://github.com/nodejs/node/pull/15040 Refs: https://github.com/nodejs/node/issues/14985 Reviewed-By: James M Snell Reviewed-By: Yuta Hiroto Reviewed-By: Colin Ihrig --- .../test-http2-compat-expect-handling.js | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 test/parallel/test-http2-compat-expect-handling.js diff --git a/test/parallel/test-http2-compat-expect-handling.js b/test/parallel/test-http2-compat-expect-handling.js new file mode 100644 index 00000000000000..774d276b64e4ae --- /dev/null +++ b/test/parallel/test-http2-compat-expect-handling.js @@ -0,0 +1,46 @@ +// Flags: --expose-http2 +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); + +const expectValue = 'meoww'; + +const server = http2.createServer(common.mustNotCall()); + +server.once('checkExpectation', common.mustCall((req, res) => { + assert.strictEqual(req.headers['expect'], expectValue); + res.statusCode = 417; + res.end(); +})); + +server.listen(0, common.mustCall(() => nextTest(2))); + +function nextTest(testsToRun) { + if (!testsToRun) { + return server.close(); + } + + const port = server.address().port; + const client = http2.connect(`http://localhost:${port}`); + const req = client.request({ + ':path': '/', + ':method': 'GET', + ':scheme': 'http', + ':authority': `localhost:${port}`, + expect: expectValue + }); + + req.on('response', common.mustCall((headers) => { + assert.strictEqual(headers[':status'], 417); + req.resume(); + })); + + req.on('end', common.mustCall(() => { + client.destroy(); + nextTest(testsToRun - 1); + })); +} From f612a6dd5cb17d8a6bd6b6332348b94839cf2ed0 Mon Sep 17 00:00:00 2001 From: Anatoli Papirovski Date: Wed, 30 Aug 2017 10:12:24 -0400 Subject: [PATCH 142/231] http2: handle 100-continue flow & writeContinue Adds an implementation for writeContinue based on the h2 spec & the existing http implementation. ClientHttp2Stream now also emits a continue event when it receives headers with :status 100. Includes two test cases for default server continue behaviour and for the exposed checkContinue listener. PR-URL: https://github.com/nodejs/node/pull/15039 Reviewed-By: Matteo Collina Reviewed-By: James M Snell --- doc/api/http2.md | 62 +++++++++++++- lib/internal/http2/compat.js | 11 ++- lib/internal/http2/core.js | 8 ++ ...test-http2-compat-expect-continue-check.js | 81 +++++++++++++++++++ .../test-http2-compat-expect-continue.js | 66 +++++++++++++++ 5 files changed, 223 insertions(+), 5 deletions(-) create mode 100644 test/parallel/test-http2-compat-expect-continue-check.js create mode 100644 test/parallel/test-http2-compat-expect-continue.js diff --git a/doc/api/http2.md b/doc/api/http2.md index 72ca5cc806950c..8d521705a7fa03 100755 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -849,6 +849,15 @@ used exclusively on HTTP/2 Clients. `Http2Stream` instances on the client provide events such as `'response'` and `'push'` that are only relevant on the client. +#### Event: 'continue' + + +Emitted when the server sends a `100 Continue` status, usually because +the request contained `Expect: 100-continue`. This is an instruction that +the client should send the request body. + #### Event: 'headers' + +* `request` {http2.Http2ServerRequest} +* `response` {http2.Http2ServerResponse} + +If a [`'request'`][] listener is registered or [`'http2.createServer()'`][] is +supplied a callback function, the `'checkContinue'` event is emitted each time +a request with an HTTP `Expect: 100-continue` is received. If this event is +not listened for, the server will automatically respond with a status +`100 Continue` as appropriate. + +Handling this event involves calling [`response.writeContinue()`][] if the client +should continue to send the request body, or generating an appropriate HTTP +response (e.g. 400 Bad Request) if the client should not continue to send the +request body. + +Note that when this event is emitted and handled, the [`'request'`][] event will +not be emitted. + ### Class: Http2SecureServer +#### Event: 'checkContinue' + + +* `request` {http2.Http2ServerRequest} +* `response` {http2.Http2ServerResponse} + +If a [`'request'`][] listener is registered or [`'http2.createSecureServer()'`][] +is supplied a callback function, the `'checkContinue'` event is emitted each +time a request with an HTTP `Expect: 100-continue` is received. If this event +is not listened for, the server will automatically respond with a status +`100 Continue` as appropriate. + +Handling this event involves calling [`response.writeContinue()`][] if the client +should continue to send the request body, or generating an appropriate HTTP +response (e.g. 400 Bad Request) if the client should not continue to send the +request body. + +Note that when this event is emitted and handled, the [`'request'`][] event will +not be emitted. + ### http2.createServer(options[, onRequestHandler]) -Throws an error as the `'continue'` flow is not current implemented. Added for -parity with [HTTP/1](). +Sends a status `100 Continue` to the client, indicating that the request body +should be sent. See the [`'checkContinue'`][] event on `Http2Server` and +`Http2SecureServer`. ### response.writeHead(statusCode[, statusMessage][, headers]) +```C +NAPI_EXTERN napi_status napi_adjust_external_memory(napi_env env, + int64_t change_in_bytes, + int64_t* result); +``` + +- `[in] env`: The environment that the API is invoked under. +- `[in] change_in_bytes`: The change in externally allocated memory that is +kept alive by JavaScript objects. +- `[out] result`: The adjusted value + +Returns `napi_ok` if the API succeeded. + +This function gives V8 an indication of the amount of externally allocated +memory that is kept alive by JavaScript objects (i.e. a JavaScript object +that points to its own memory allocated by a native module). Registering +externally allocated memory will trigger global garbage collections more +often than it would otherwise. + ## Promises diff --git a/src/node_api.cc b/src/node_api.cc index 7a2b5bc48e1237..16549120b20737 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -3213,6 +3213,19 @@ napi_status napi_get_node_version(napi_env env, return napi_clear_last_error(env); } +napi_status napi_adjust_external_memory(napi_env env, + int64_t change_in_bytes, + int64_t* adjusted_value) { + CHECK_ENV(env); + CHECK_ARG(env, &change_in_bytes); + CHECK_ARG(env, adjusted_value); + + *adjusted_value = env->isolate->AdjustAmountOfExternalAllocatedMemory( + change_in_bytes); + + return napi_clear_last_error(env); +} + namespace uvimpl { static napi_status ConvertUVErrorCode(int code) { diff --git a/src/node_api.h b/src/node_api.h index 6a4b2941879ff0..702ddf2d9e6a0e 100644 --- a/src/node_api.h +++ b/src/node_api.h @@ -557,6 +557,11 @@ NAPI_EXTERN napi_status napi_is_promise(napi_env env, napi_value promise, bool* is_promise); +// Memory management +NAPI_EXTERN napi_status napi_adjust_external_memory(napi_env env, + int64_t change_in_bytes, + int64_t* adjusted_value); + EXTERN_C_END #endif // SRC_NODE_API_H_ diff --git a/test/addons-napi/test_general/test.js b/test/addons-napi/test_general/test.js index 484707e868db58..e9e2b9614ca34c 100644 --- a/test/addons-napi/test_general/test.js +++ b/test/addons-napi/test_general/test.js @@ -91,3 +91,8 @@ z = null; global.gc(); assert.strictEqual(test_general.finalizeWasCalled(), false, 'finalize callback was not called upon garbage collection'); + +// test napi_adjust_external_memory +const adjustedValue = test_general.testAdjustExternalMemory(); +assert.strictEqual(typeof adjustedValue, 'number'); +assert(adjustedValue > 0); diff --git a/test/addons-napi/test_general/test_general.c b/test/addons-napi/test_general/test_general.c index ecec3e014ba0b1..ea1f2ece0a5d30 100644 --- a/test/addons-napi/test_general/test_general.c +++ b/test/addons-napi/test_general/test_general.c @@ -205,6 +205,16 @@ napi_value finalize_was_called(napi_env env, napi_callback_info info) { return it_was_called; } +napi_value testAdjustExternalMemory(napi_env env, napi_callback_info info) { + napi_value result; + int64_t adjustedValue; + + NAPI_CALL(env, napi_adjust_external_memory(env, 1, &adjustedValue)); + NAPI_CALL(env, napi_create_double(env, adjustedValue, &result)); + + return result; +} + void Init(napi_env env, napi_value exports, napi_value module, void* priv) { napi_property_descriptor descriptors[] = { DECLARE_NAPI_PROPERTY("testStrictEquals", testStrictEquals), @@ -222,6 +232,7 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { DECLARE_NAPI_PROPERTY("testFinalizeWrap", test_finalize_wrap), DECLARE_NAPI_PROPERTY("finalizeWasCalled", finalize_was_called), DECLARE_NAPI_PROPERTY("derefItemWasCalled", deref_item_was_called), + DECLARE_NAPI_PROPERTY("testAdjustExternalMemory", testAdjustExternalMemory) }; NAPI_CALL_RETURN_VOID(env, napi_define_properties( From ea0a8820414aa9a38a442500842e77386d06ea90 Mon Sep 17 00:00:00 2001 From: Jackson Tian Date: Sun, 18 Jun 2017 22:48:39 +0800 Subject: [PATCH 145/231] lib: remove the invalid command line options The option --remote_debugging_server and --debug-agent are not supported now. PR-URL: https://github.com/nodejs/node/pull/13764 Reviewed-By: Colin Ihrig Reviewed-By: Anna Henningsen Reviewed-By: James M Snell Reviewed-By: Michael Dawson Reviewed-By: Ruben Bridgewater --- lib/internal/bootstrap_node.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index 3c608fccd1ed78..1d559a2e34b84b 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -119,14 +119,6 @@ NativeModule.require('node-inspect/lib/_inspect').start(); }); - } else if (process.argv[1] === '--remote_debugging_server') { - // Start the debugging server - NativeModule.require('internal/inspector/remote_debugging_server'); - - } else if (process.argv[1] === '--debug-agent') { - // Start the debugger agent - NativeModule.require('_debug_agent').start(); - } else if (process.profProcess) { NativeModule.require('internal/v8_prof_processor'); From 6de4e10c7a98313985fe79dad9156d6fe8baaa81 Mon Sep 17 00:00:00 2001 From: Roy Marples Date: Sun, 16 Jul 2017 21:44:11 +0100 Subject: [PATCH 146/231] build: add NetBSD support to opensslconf.h Simplify the BSD list by defining OPENSSL_BSD if using a matching BSD platform. Add NetBSD to the list and update documentation. PR-URL: https://github.com/nodejs/node/pull/14313 Reviewed-By: Ben Noordhuis Reviewed-By: James M Snell --- deps/openssl/config/opensslconf.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/deps/openssl/config/opensslconf.h b/deps/openssl/config/opensslconf.h index 9b20fb6485aa84..1c89babcf6c864 100644 --- a/deps/openssl/config/opensslconf.h +++ b/deps/openssl/config/opensslconf.h @@ -37,6 +37,8 @@ | solaris | x64 | solaris64-x86_64-gcc | o | | freebsd | ia32 | BSD-x86 | o | | freebsd | x64 | BSD-x86_64 | o | + | netbsd | ia32 | BSD-x86 | o | + | netbsd | x64 | BSD-x86_64 | o | | openbsd | ia32 | BSD-x86 | - | | openbsd | x64 | BSD-x86_64 | - | | others | others | linux-elf | - | @@ -51,6 +53,7 @@ | mac | __APPLE__ && __MACH__ | | solaris | __sun | | freebsd | __FreeBSD__ | + | netbsd | __NetBSD__ | | openbsd | __OpenBSD__ | | linux (not andorid)| __linux__ && !__ANDROID__ | | android | __ANDROID__ | @@ -94,6 +97,11 @@ # define OPENSSL_LINUX 1 #endif +#undef OPENSSL_BSD +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +# define OPENSSL_BSD 1 +#endif + #if defined(OPENSSL_LINUX) && defined(__i386__) # include "./archs/linux-elf/opensslconf.h" #elif defined(OPENSSL_LINUX) && defined(__ILP32__) @@ -112,9 +120,9 @@ # include "./archs/VC-WIN32/opensslconf.h" #elif defined(_WIN32) && defined(_M_X64) # include "./archs/VC-WIN64A/opensslconf.h" -#elif (defined(__FreeBSD__) || defined(__OpenBSD__)) && defined(__i386__) +#elif defined(OPENSSL_BSD) && defined(__i386__) # include "./archs/BSD-x86/opensslconf.h" -#elif (defined(__FreeBSD__) || defined(__OpenBSD__)) && defined(__x86_64__) +#elif defined(OPENSSL_BSD) && defined(__x86_64__) # include "./archs/BSD-x86_64/opensslconf.h" #elif defined(__sun) && defined(__i386__) # include "./archs/solaris-x86-gcc/opensslconf.h" From 959b270fe11a9abcc5fed5787d6ed4758f09fa26 Mon Sep 17 00:00:00 2001 From: Jon Moss Date: Wed, 30 Aug 2017 15:12:31 -0400 Subject: [PATCH 147/231] doc: /s/SHASUM256/SHASUMS256 As an example, `curl https://nodejs.org/dist/v8.4.0/SHASUM256.txt` will return a 404 right now. PR-URL: https://github.com/nodejs/node/pull/15101 Reviewed-By: Benjamin Gruenbaum Reviewed-By: James M Snell Reviewed-By: Ruben Bridgewater Reviewed-By: Luigi Pinca Reviewed-By: Refael Ackermann --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 385a5cef9b4e21..081a0e5757060d 100644 --- a/README.md +++ b/README.md @@ -114,11 +114,11 @@ documentation of the latest stable version. ### Verifying Binaries -Current, LTS and Nightly download directories all contain a _SHASUM256.txt_ +Current, LTS and Nightly download directories all contain a _SHASUMS256.txt_ file that lists the SHA checksums for each file available for download. -The _SHASUM256.txt_ can be downloaded using curl. +The _SHASUMS256.txt_ can be downloaded using curl. ```console $ curl -O https://nodejs.org/dist/vx.y.z/SHASUMS256.txt @@ -135,10 +135,10 @@ _(Where "node-vx.y.z.tar.gz" is the name of the file you have downloaded)_ Additionally, Current and LTS releases (not Nightlies) have GPG signed -copies of SHASUM256.txt files available as SHASUM256.txt.asc. You can use +copies of SHASUMS256.txt files available as SHASUMS256.txt.asc. You can use `gpg` to verify that the file has not been tampered with. -To verify a SHASUM256.txt.asc, you will first need to import all of +To verify a SHASUMS256.txt.asc, you will first need to import all of the GPG keys of individuals authorized to create releases. They are listed at the bottom of this README under [Release Team](#release-team). Use a command such as this to import the keys: From 1d97ff4800b2b9cd612b44429823ab32190310e6 Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Tue, 20 Jun 2017 08:30:47 +0200 Subject: [PATCH 148/231] tools: add eslint rule for hasCrypto checking The motivation for this commit is to pick up early on missing checks for crypto support (when Node is built --without-ssl). There are currently usages of common.hasCrypto which are not just for detecting if crypto support is available and then skip the test in question. For these case we still want to have a lint error generated which can then be disabled using an ESLint comment. PR-URL: https://github.com/nodejs/node/pull/13813 Reviewed-By: James M Snell Reviewed-By: Teddy Katz --- test/.eslintrc.yaml | 1 + test/common/index.js | 2 +- test/parallel/test-async-wrap-getasyncid.js | 6 +- test/parallel/test-buffer-alloc.js | 2 +- test/parallel/test-buffer-concat.js | 1 + test/parallel/test-http2-noflag.js | 2 +- tools/eslint-rules/crypto-check.js | 83 +++++++++++++++++++++ tools/eslint-rules/rules-utils.js | 61 +++++++++++++++ 8 files changed, 152 insertions(+), 6 deletions(-) create mode 100644 tools/eslint-rules/crypto-check.js create mode 100644 tools/eslint-rules/rules-utils.js diff --git a/test/.eslintrc.yaml b/test/.eslintrc.yaml index aeaf09fb0ff732..b571d3a1b6e0a9 100644 --- a/test/.eslintrc.yaml +++ b/test/.eslintrc.yaml @@ -10,5 +10,6 @@ rules: prefer-assert-iferror: error prefer-assert-methods: error prefer-common-mustnotcall: error + crypto-check: error ## common module is mandatory in tests required-modules: [error, common] diff --git a/test/common/index.js b/test/common/index.js index f4631f4a0b14fd..fa5573b2e6f1e0 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -19,7 +19,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -/* eslint-disable required-modules */ +/* eslint-disable required-modules, crypto-check */ 'use strict'; const path = require('path'); const fs = require('fs'); diff --git a/test/parallel/test-async-wrap-getasyncid.js b/test/parallel/test-async-wrap-getasyncid.js index 4c1ea8e212e7ef..95317f711c9907 100644 --- a/test/parallel/test-async-wrap-getasyncid.js +++ b/test/parallel/test-async-wrap-getasyncid.js @@ -81,14 +81,14 @@ function testInitialized(req, ctor_name) { } -if (common.hasCrypto) { +if (common.hasCrypto) { // eslint-disable-line crypto-check const tls = require('tls'); // SecurePair testInitialized(tls.createSecurePair().ssl, 'Connection'); } -if (common.hasCrypto) { +if (common.hasCrypto) { // eslint-disable-line crypto-check const crypto = require('crypto'); // The handle for PBKDF2 and RandomBytes isn't returned by the function call, @@ -215,7 +215,7 @@ if (common.hasCrypto) { } -if (common.hasCrypto) { +if (common.hasCrypto) { // eslint-disable-line crypto-check const TCP = process.binding('tcp_wrap').TCP; const tcp = new TCP(); diff --git a/test/parallel/test-buffer-alloc.js b/test/parallel/test-buffer-alloc.js index f5042c24076a75..f86f31ec68811f 100644 --- a/test/parallel/test-buffer-alloc.js +++ b/test/parallel/test-buffer-alloc.js @@ -905,7 +905,7 @@ assert.throws(() => Buffer.from('', 'buffer'), } } -if (common.hasCrypto) { +if (common.hasCrypto) { // eslint-disable-line crypto-check // Test truncation after decode const crypto = require('crypto'); diff --git a/test/parallel/test-buffer-concat.js b/test/parallel/test-buffer-concat.js index ea5ffaec684eb6..158d30b2435df9 100644 --- a/test/parallel/test-buffer-concat.js +++ b/test/parallel/test-buffer-concat.js @@ -61,6 +61,7 @@ function assertWrongList(value) { }); } +// eslint-disable-next-line crypto-check const random10 = common.hasCrypto ? require('crypto').randomBytes(10) : Buffer.alloc(10, 1); diff --git a/test/parallel/test-http2-noflag.js b/test/parallel/test-http2-noflag.js index a1e0e8b72c79e9..903fd649c5e458 100644 --- a/test/parallel/test-http2-noflag.js +++ b/test/parallel/test-http2-noflag.js @@ -4,5 +4,5 @@ require('../common'); const assert = require('assert'); -assert.throws(() => require('http2'), +assert.throws(() => require('http2'), // eslint-disable-line crypto-check /^Error: Cannot find module 'http2'$/); diff --git a/tools/eslint-rules/crypto-check.js b/tools/eslint-rules/crypto-check.js new file mode 100644 index 00000000000000..b1b2a03f50e3b6 --- /dev/null +++ b/tools/eslint-rules/crypto-check.js @@ -0,0 +1,83 @@ +/** + * @fileoverview Check that common.hasCrypto is used if crypto, tls, + * https, or http2 modules are required. + * + * This rule can be ignored using // eslint-disable-line crypto-check + * + * @author Daniel Bevenius + */ +'use strict'; + +const utils = require('./rules-utils.js'); + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ +const msg = 'Please add a hasCrypto check to allow this test to be skipped ' + + 'when Node is built "--without-ssl".'; + +module.exports = function(context) { + const missingCheckNodes = []; + const requireNodes = []; + var hasSkipCall = false; + + function testCryptoUsage(node) { + if (utils.isRequired(node, ['crypto', 'tls', 'https', 'http2'])) { + requireNodes.push(node); + } + } + + function testIfStatement(node) { + if (node.test.argument === undefined) { + return; + } + if (isCryptoCheck(node.test.argument)) { + checkCryptoCall(node); + } + } + + function isCryptoCheck(node) { + return utils.usesCommonProperty(node, ['hasCrypto', 'hasFipsCrypto']); + } + + function checkCryptoCall(node) { + if (utils.inSkipBlock(node)) { + hasSkipCall = true; + } else { + missingCheckNodes.push(node); + } + } + + function testMemberExpression(node) { + if (isCryptoCheck(node)) { + checkCryptoCall(node); + } + } + + function reportIfMissingCheck(node) { + if (hasSkipCall) { + return; + } + + if (requireNodes.length > 0) { + if (missingCheckNodes.length > 0) { + report(missingCheckNodes); + } else { + report(requireNodes); + } + } + } + + function report(nodes) { + nodes.forEach((node) => { + context.report(node, msg); + }); + } + + return { + 'CallExpression': (node) => testCryptoUsage(node), + 'IfStatement:exit': (node) => testIfStatement(node), + 'MemberExpression:exit': (node) => testMemberExpression(node), + 'Program:exit': (node) => reportIfMissingCheck(node) + }; +}; diff --git a/tools/eslint-rules/rules-utils.js b/tools/eslint-rules/rules-utils.js new file mode 100644 index 00000000000000..e3e5e6e5ef9718 --- /dev/null +++ b/tools/eslint-rules/rules-utils.js @@ -0,0 +1,61 @@ +/** + * Utility functions common to ESLint rules. + */ +'use strict'; + +/** + * Returns true if any of the passed in modules are used in + * require calls. + */ +module.exports.isRequired = function(node, modules) { + return node.callee.name === 'require' && + modules.includes(node.arguments[0].value); +}; + +/** + * Returns true is the node accesses any property in the properties + * array on the 'common' object. + */ +module.exports.usesCommonProperty = function(node, properties) { + if (node.name) { + return properties.includes(node.name); + } + if (node.property) { + return properties.includes(node.property.name); + } + return false; +}; + +/** + * Returns true if the passed in node is inside an if statement block, + * and the block also has a call to skip. + */ +module.exports.inSkipBlock = function(node) { + var hasSkipBlock = false; + if (node.test && + node.test.type === 'UnaryExpression' && + node.test.operator === '!') { + const consequent = node.consequent; + if (consequent.body) { + consequent.body.some(function(expressionStatement) { + if (hasSkip(expressionStatement.expression)) { + return hasSkipBlock = true; + } + return false; + }); + } else { + if (hasSkip(consequent.expression)) { + hasSkipBlock = true; + } + } + } + return hasSkipBlock; +}; + +function hasSkip(expression) { + return expression && + expression.callee && + (expression.callee.name === 'skip' || + expression.callee.property && + expression.callee.property.name === 'skip'); +} From 87e44d865124124924c025bb395d3790b792a3ee Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Tue, 20 Jun 2017 08:30:47 +0200 Subject: [PATCH 149/231] tools: add eslint rule for inspector checking The motivation for this commit is to pick up early on missing checks for inspector support (when Node is built --without-inspector). PR-URL: https://github.com/nodejs/node/pull/13813 Reviewed-By: James M Snell Reviewed-By: Teddy Katz --- test/.eslintrc.yaml | 1 + tools/eslint-rules/inspector-check.js | 43 +++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 tools/eslint-rules/inspector-check.js diff --git a/test/.eslintrc.yaml b/test/.eslintrc.yaml index b571d3a1b6e0a9..58e072c1951e27 100644 --- a/test/.eslintrc.yaml +++ b/test/.eslintrc.yaml @@ -11,5 +11,6 @@ rules: prefer-assert-methods: error prefer-common-mustnotcall: error crypto-check: error + inspector-check: error ## common module is mandatory in tests required-modules: [error, common] diff --git a/tools/eslint-rules/inspector-check.js b/tools/eslint-rules/inspector-check.js new file mode 100644 index 00000000000000..f225b34cb6b0ca --- /dev/null +++ b/tools/eslint-rules/inspector-check.js @@ -0,0 +1,43 @@ +/** + * @fileoverview Check that common.skipIfInspectorDisabled is used if + * the inspector module is required. + * @author Daniel Bevenius + */ +'use strict'; + +const utils = require('./rules-utils.js'); + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ +const msg = 'Please add a skipIfInspectorDisabled() call to allow this ' + + 'test to be skippped when Node is built \'--without-inspector\'.'; + +module.exports = function(context) { + var usesInspector = false; + var hasInspectorCheck = false; + + function testInspectorUsage(context, node) { + if (utils.isRequired(node, ['inspector'])) { + usesInspector = true; + } + } + + function checkMemberExpression(context, node) { + if (utils.usesCommonProperty(node, ['skipIfInspectorDisabled'])) { + hasInspectorCheck = true; + } + } + + function reportIfMissing(context, node) { + if (usesInspector && !hasInspectorCheck) { + context.report(node, msg); + } + } + + return { + 'CallExpression': (node) => testInspectorUsage(context, node), + 'MemberExpression': (node) => checkMemberExpression(context, node), + 'Program:exit': (node) => reportIfMissing(context, node) + }; +}; From 605d625e62667cea845393ed5ee50a2102cabc68 Mon Sep 17 00:00:00 2001 From: Jackson Tian Date: Fri, 26 May 2017 01:38:29 +0800 Subject: [PATCH 150/231] lib: simplify the readonly properties of icu Call Object.defineProperty() twice to set readonly property is unnecessary. PR-URL: https://github.com/nodejs/node/pull/13221 Reviewed-By: James M Snell Reviewed-By: Daijiro Wachi Reviewed-By: Ruben Bridgewater --- lib/internal/bootstrap_node.js | 21 +++------------------ test/parallel/test-process-versions.js | 6 ++++++ 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index 1d559a2e34b84b..ad579ab2b549fc 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -395,28 +395,13 @@ // of possible types. const versionTypes = icu.getVersion().split(','); - function makeGetter(name) { - return () => { - // With an argument, getVersion(type) returns - // the actual version string. - const version = icu.getVersion(name); - // Replace the current getter with a new property. - delete process.versions[name]; - Object.defineProperty(process.versions, name, { - value: version, - writable: false, - enumerable: true - }); - return version; - }; - } - for (var n = 0; n < versionTypes.length; n++) { var name = versionTypes[n]; + const version = icu.getVersion(name); Object.defineProperty(process.versions, name, { - configurable: true, + writable: false, enumerable: true, - get: makeGetter(name) + value: version }); } } diff --git a/test/parallel/test-process-versions.js b/test/parallel/test-process-versions.js index e4da5380217e9a..65634ece6ce8e7 100644 --- a/test/parallel/test-process-versions.js +++ b/test/parallel/test-process-versions.js @@ -32,3 +32,9 @@ assert(commonTemplate.test(process.versions.zlib)); assert(/^\d+\.\d+\.\d+(?:\.\d+)?(?: \(candidate\))?$/ .test(process.versions.v8)); assert(/^\d+$/.test(process.versions.modules)); + +for (let i = 0; i < expected_keys.length; i++) { + const key = expected_keys[i]; + const descriptor = Object.getOwnPropertyDescriptor(process.versions, key); + assert.strictEqual(descriptor.writable, false); +} From 2aec977fa26d12119c3672d1ce779d8aadde526b Mon Sep 17 00:00:00 2001 From: Anatoli Papirovski Date: Wed, 30 Aug 2017 14:21:08 -0400 Subject: [PATCH 151/231] util: remove duplicate code in format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit util.format contains an idential if statement within each branch of switch. Move it before the switch statement for cleaner code and better performance. PR-URL: https://github.com/nodejs/node/pull/15098 Reviewed-By: Michaël Zasso Reviewed-By: Ruben Bridgewater Reviewed-By: Benjamin Gruenbaum Reviewed-By: Refael Ackermann Reviewed-By: James M Snell Reviewed-By: Luigi Pinca Reviewed-By: Evan Lucas Reviewed-By: Colin Ihrig --- lib/util.js | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/lib/util.js b/lib/util.js index b1225365d365c3..03561bcb0914f6 100644 --- a/lib/util.js +++ b/lib/util.js @@ -110,51 +110,35 @@ function format(f) { ++i; continue; } + if (lastPos < i) + str += f.slice(lastPos, i); switch (f.charCodeAt(i + 1)) { case 100: // 'd' - if (lastPos < i) - str += f.slice(lastPos, i); str += Number(arguments[a++]); break; case 105: // 'i' - if (lastPos < i) - str += f.slice(lastPos, i); str += parseInt(arguments[a++]); break; case 102: // 'f' - if (lastPos < i) - str += f.slice(lastPos, i); str += parseFloat(arguments[a++]); break; case 106: // 'j' - if (lastPos < i) - str += f.slice(lastPos, i); str += tryStringify(arguments[a++]); break; case 115: // 's' - if (lastPos < i) - str += f.slice(lastPos, i); str += String(arguments[a++]); break; case 79: // 'O' - if (lastPos < i) - str += f.slice(lastPos, i); str += inspect(arguments[a++]); break; case 111: // 'o' - if (lastPos < i) - str += f.slice(lastPos, i); str += inspect(arguments[a++], { showHidden: true, depth: 4, showProxy: true }); break; case 37: // '%' - if (lastPos < i) - str += f.slice(lastPos, i); str += '%'; break; default: // any other character is not a correct placeholder - if (lastPos < i) - str += f.slice(lastPos, i); str += '%'; lastPos = i = i + 1; continue; From bcf0e5d67619df698f697496555126034291f7bc Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 19 Aug 2017 02:39:03 -0300 Subject: [PATCH 152/231] assert: handle errors properly with deep*Equal PR-URL: https://github.com/nodejs/node/pull/15001 Reviewed-By: Rich Trott Reviewed-By: Benjamin Gruenbaum Reviewed-By: Refael Ackermann Reviewed-By: James M Snell --- doc/api/assert.md | 7 +++++-- lib/assert.js | 11 +++++++++++ test/parallel/test-assert-deep.js | 28 ++++++++++++++++++++-------- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/doc/api/assert.md b/doc/api/assert.md index c2dff91d0391c2..9c5c142a96e937 100644 --- a/doc/api/assert.md +++ b/doc/api/assert.md @@ -47,12 +47,12 @@ Only [enumerable "own" properties][] are considered. The non-enumerable properties — for such checks, consider using [`assert.deepStrictEqual()`][] instead. This can lead to some potentially surprising results. For example, the following example does not -throw an `AssertionError` because the properties on the [`Error`][] object are +throw an `AssertionError` because the properties on the [`RegExp`][] object are not enumerable: ```js // WARNING: This does not throw an AssertionError! -assert.deepEqual(Error('a'), Error('b')); +assert.deepEqual(/a/gi, new Date()); ``` An exception is made for [`Map`][] and [`Set`][]. Maps and Sets have their @@ -104,6 +104,9 @@ parameter is undefined, a default error message is assigned. - `algorithm` {string} - `password` {string | Buffer | TypedArray | DataView} +- `options` {Object} [`stream.transform` options][] Creates and returns a `Cipher` object that uses the given `algorithm` and -`password`. +`password`. Optional `options` argument controls stream behavior. The `algorithm` is dependent on OpenSSL, examples are `'aes192'`, etc. On recent OpenSSL releases, `openssl list-cipher-algorithms` will display the @@ -1202,13 +1203,14 @@ In line with OpenSSL's recommendation to use pbkdf2 instead of their own using [`crypto.pbkdf2()`][] and to use [`crypto.createCipheriv()`][] to create the `Cipher` object. -### crypto.createCipheriv(algorithm, key, iv) +### crypto.createCipheriv(algorithm, key, iv[, options]) - `algorithm` {string} - `key` {string | Buffer | TypedArray | DataView} - `iv` {string | Buffer | TypedArray | DataView} +- `options` {Object} [`stream.transform` options][] Creates and returns a `Cipher` object, with the given `algorithm`, `key` and -initialization vector (`iv`). +initialization vector (`iv`). Optional `options` argument controls stream behavior. The `algorithm` is dependent on OpenSSL, examples are `'aes192'`, etc. On recent OpenSSL releases, `openssl list-cipher-algorithms` will display the @@ -1236,15 +1238,16 @@ value. Returns a `tls.SecureContext`, as-if [`tls.createSecureContext()`][] had been called. -### crypto.createDecipher(algorithm, password) +### crypto.createDecipher(algorithm, password[, options]) - `algorithm` {string} - `password` {string | Buffer | TypedArray | DataView} +- `options` {Object} [`stream.transform` options][] Creates and returns a `Decipher` object that uses the given `algorithm` and -`password` (key). +`password` (key). Optional `options` argument controls stream behavior. The implementation of `crypto.createDecipher()` derives keys using the OpenSSL function [`EVP_BytesToKey`][] with the digest algorithm set to MD5, one @@ -1258,16 +1261,18 @@ In line with OpenSSL's recommendation to use pbkdf2 instead of their own using [`crypto.pbkdf2()`][] and to use [`crypto.createDecipheriv()`][] to create the `Decipher` object. -### crypto.createDecipheriv(algorithm, key, iv) +### crypto.createDecipheriv(algorithm, key, iv[, options]) - `algorithm` {string} - `key` {string | Buffer | TypedArray | DataView} - `iv` {string | Buffer | TypedArray | DataView} +- `options` {Object} [`stream.transform` options][] Creates and returns a `Decipher` object that uses the given `algorithm`, `key` -and initialization vector (`iv`). +and initialization vector (`iv`). Optional `options` argument controls stream +behavior. The `algorithm` is dependent on OpenSSL, examples are `'aes192'`, etc. On recent OpenSSL releases, `openssl list-cipher-algorithms` will display the @@ -1335,14 +1340,16 @@ predefined curve specified by the `curveName` string. Use OpenSSL releases, `openssl ecparam -list_curves` will also display the name and description of each available elliptic curve. -### crypto.createHash(algorithm) +### crypto.createHash(algorithm[, options]) - `algorithm` {string} +- `options` {Object} [`stream.transform` options][] Creates and returns a `Hash` object that can be used to generate hash digests -using the given `algorithm`. +using the given `algorithm`. Optional `options` argument controls stream +behavior. The `algorithm` is dependent on the available algorithms supported by the version of OpenSSL on the platform. Examples are `'sha256'`, `'sha512'`, etc. @@ -1369,14 +1376,16 @@ input.on('readable', () => { }); ``` -### crypto.createHmac(algorithm, key) +### crypto.createHmac(algorithm, key[, options]) - `algorithm` {string} - `key` {string | Buffer | TypedArray | DataView} +- `options` {Object} [`stream.transform` options][] Creates and returns an `Hmac` object that uses the given `algorithm` and `key`. +Optional `options` argument controls stream behavior. The `algorithm` is dependent on the available algorithms supported by the version of OpenSSL on the platform. Examples are `'sha256'`, `'sha512'`, etc. @@ -1405,25 +1414,29 @@ input.on('readable', () => { }); ``` -### crypto.createSign(algorithm) +### crypto.createSign(algorithm[, options]) - `algorithm` {string} +- `options` {Object} [`stream.Writable` options][] Creates and returns a `Sign` object that uses the given `algorithm`. Use [`crypto.getHashes()`][] to obtain an array of names of the available -signing algorithms. +signing algorithms. Optional `options` argument controls the +`stream.Writable` behavior. -### crypto.createVerify(algorithm) +### crypto.createVerify(algorithm[, options]) - `algorithm` {string} +- `options` {Object} [`stream.Writable` options][] Creates and returns a `Verify` object that uses the given algorithm. Use [`crypto.getHashes()`][] to obtain an array of names of the available -signing algorithms. +signing algorithms. Optional `options` argument controls the +`stream.Writable` behavior. ### crypto.getCiphers() +```C +NAPI_EXTERN napi_status napi_run_script(napi_env env, + napi_value script, + napi_value* result); +``` + +- `[in] env`: The environment that the API is invoked under. +- `[in] script`: A JavaScript string containing the script to execute. +- `[out] result`: The value resulting from having executed the script. + [Promises]: #n_api_promises [Asynchronous Operations]: #n_api_asynchronous_operations [Basic N-API Data Types]: #n_api_basic_n_api_data_types @@ -3565,6 +3585,7 @@ object - that is, a promise object created by the underlying engine. [Native Abstractions for Node.js]: https://github.com/nodejs/nan [Object Lifetime Management]: #n_api_object_lifetime_management [Object Wrap]: #n_api_object_wrap +[Script Execution]: #n_api_script_execution [Section 9.1.6]: https://tc39.github.io/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-defineownproperty-p-desc [Section 12.5.5]: https://tc39.github.io/ecma262/#sec-typeof-operator [Section 24.3]: https://tc39.github.io/ecma262/#sec-dataview-objects diff --git a/src/node_api.cc b/src/node_api.cc index 16549120b20737..132d90505a8718 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -3423,3 +3423,30 @@ NAPI_EXTERN napi_status napi_is_promise(napi_env env, return napi_clear_last_error(env); } + +NAPI_EXTERN napi_status napi_run_script(napi_env env, + napi_value script, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, script); + CHECK_ARG(env, result); + + v8::Local v8_script = v8impl::V8LocalValueFromJsValue(script); + + if (!v8_script->IsString()) { + return napi_set_last_error(env, napi_string_expected); + } + + v8::Local context = env->isolate->GetCurrentContext(); + + auto maybe_script = v8::Script::Compile(context, + v8::Local::Cast(v8_script)); + CHECK_MAYBE_EMPTY(env, maybe_script, napi_generic_failure); + + auto script_result = + maybe_script.ToLocalChecked()->Run(context); + CHECK_MAYBE_EMPTY(env, script_result, napi_generic_failure); + + *result = v8impl::JsValueFromV8LocalValue(script_result.ToLocalChecked()); + return GET_RETURN_STATUS(env); +} diff --git a/src/node_api.h b/src/node_api.h index 702ddf2d9e6a0e..807595777dd947 100644 --- a/src/node_api.h +++ b/src/node_api.h @@ -562,6 +562,11 @@ NAPI_EXTERN napi_status napi_adjust_external_memory(napi_env env, int64_t change_in_bytes, int64_t* adjusted_value); +// Runnig a script +NAPI_EXTERN napi_status napi_run_script(napi_env env, + napi_value script, + napi_value* result); + EXTERN_C_END #endif // SRC_NODE_API_H_ diff --git a/test/addons-napi/test_general/testNapiRun.js b/test/addons-napi/test_general/testNapiRun.js new file mode 100644 index 00000000000000..d7534ecf9c3912 --- /dev/null +++ b/test/addons-napi/test_general/testNapiRun.js @@ -0,0 +1,12 @@ +'use strict'; + +const common = require('../../common'); +const assert = require('assert'); + +// addon is referenced through the eval expression in testFile +// eslint-disable-next-line no-unused-vars +const addon = require(`./build/${common.buildType}/test_general`); + +assert.strictEqual(addon.testNapiRun('(41.92 + 0.08);'), 42, + 'napi_run_script() works correctly'); +assert.throws(() => addon.testNapiRun({ abc: 'def' }), /string was expected/); diff --git a/test/addons-napi/test_general/test_general.c b/test/addons-napi/test_general/test_general.c index ea1f2ece0a5d30..287c1d7cb8cb34 100644 --- a/test/addons-napi/test_general/test_general.c +++ b/test/addons-napi/test_general/test_general.c @@ -1,4 +1,5 @@ #include +#include #include "../common.h" napi_value testStrictEquals(napi_env env, napi_callback_info info) { @@ -215,12 +216,24 @@ napi_value testAdjustExternalMemory(napi_env env, napi_callback_info info) { return result; } +napi_value testNapiRun(napi_env env, napi_callback_info info) { + napi_value script, result; + size_t argc = 1; + + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &script, NULL, NULL)); + + NAPI_CALL(env, napi_run_script(env, script, &result)); + + return result; +} + void Init(napi_env env, napi_value exports, napi_value module, void* priv) { napi_property_descriptor descriptors[] = { DECLARE_NAPI_PROPERTY("testStrictEquals", testStrictEquals), DECLARE_NAPI_PROPERTY("testGetPrototype", testGetPrototype), DECLARE_NAPI_PROPERTY("testGetVersion", testGetVersion), DECLARE_NAPI_PROPERTY("testGetNodeVersion", testGetNodeVersion), + DECLARE_NAPI_PROPERTY("testNapiRun", testNapiRun), DECLARE_NAPI_PROPERTY("doInstanceOf", doInstanceOf), DECLARE_NAPI_PROPERTY("getUndefined", getUndefined), DECLARE_NAPI_PROPERTY("getNull", getNull), From fadccbaa174c0eddd750536571b4bb0841f926eb Mon Sep 17 00:00:00 2001 From: Trevor Norris Date: Wed, 9 Aug 2017 00:02:20 -0600 Subject: [PATCH 192/231] async_wrap: return undefined if domain is disposed v8::MaybeLocal::ToLocalChecked() will abort on an empty handle. AsyncWrap::MakeCallback returns an empty handle if the domain is disposed, but this should not cause the process to abort. So instead return v8::Undefined() and allow the error to be handled normally. PR-URL: https://github.com/nodejs/node/pull/14722 Reviewed-By: Anna Henningsen Reviewed-By: Refael Ackermann --- src/async-wrap.cc | 5 ++-- .../test-domain-abort-when-disposed.js | 25 +++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 test/parallel/test-domain-abort-when-disposed.js diff --git a/src/async-wrap.cc b/src/async-wrap.cc index 3228299233e468..8f513ba4524795 100644 --- a/src/async-wrap.cc +++ b/src/async-wrap.cc @@ -54,6 +54,7 @@ using v8::String; using v8::Symbol; using v8::TryCatch; using v8::Uint32Array; +using v8::Undefined; using v8::Value; using AsyncHooks = node::Environment::AsyncHooks; @@ -696,7 +697,7 @@ MaybeLocal AsyncWrap::MakeCallback(const Local cb, get_trigger_id()); if (!PreCallbackExecution(this, true)) { - return MaybeLocal(); + return Undefined(env()->isolate()); } // Finally... Get to running the user's callback. @@ -707,7 +708,7 @@ MaybeLocal AsyncWrap::MakeCallback(const Local cb, } if (!PostCallbackExecution(this, true)) { - return Local(); + return Undefined(env()->isolate()); } exec_scope.Dispose(); diff --git a/test/parallel/test-domain-abort-when-disposed.js b/test/parallel/test-domain-abort-when-disposed.js new file mode 100644 index 00000000000000..3a02b1e94c1b11 --- /dev/null +++ b/test/parallel/test-domain-abort-when-disposed.js @@ -0,0 +1,25 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const domain = require('domain'); + +// These were picked arbitrarily and are only used to trigger arync_hooks. +const JSStream = process.binding('js_stream').JSStream; +const Socket = require('net').Socket; + +const handle = new JSStream(); +handle.domain = domain.create(); +handle.domain.dispose(); + +handle.close = () => {}; +handle.isAlive = () => { throw new Error(); }; + +const s = new Socket({ handle }); + +// When AsyncWrap::MakeCallback() returned an empty handle the +// MaybeLocal::ToLocalChecked() call caused the process to abort. By returning +// v8::Undefined() it allows the error to propagate to the 'error' event. +s.on('error', common.mustCall((e) => { + assert.strictEqual(e.code, 'EINVAL'); +})); From 3e73ea874551d93fcf6b921b03385116ade92b43 Mon Sep 17 00:00:00 2001 From: Trevor Norris Date: Wed, 2 Aug 2017 01:34:59 -0600 Subject: [PATCH 193/231] async_hooks: improve comments and function names * Reword several of the comments to be more descriptive. * Rename functions to better indicate what they are doing. * Change AsyncHooks::uid_fields_ to be a fixed array instead of a pointer. * Define regex early so line ends before 80 columns. * Remove obsolete comments. * Rename AsyncHooks::InitScope::uid_fields_ to uid_fields_ptr_ because using the same name as AsyncHooks::uid_fields_ was confusing. * Place variables that are used to store the new set of hooks if another hook is enabled/disabled during hook execution into an object to act as a namespace. PR-URL: https://github.com/nodejs/node/pull/14722 Reviewed-By: Anna Henningsen Reviewed-By: Refael Ackermann --- lib/async_hooks.js | 190 ++++++++++++++++++++++++--------------------- src/env-inl.h | 6 +- src/env.h | 10 +-- 3 files changed, 109 insertions(+), 97 deletions(-) diff --git a/lib/async_hooks.js b/lib/async_hooks.js index 51039e9f36d24d..38bdbb0306b6be 100644 --- a/lib/async_hooks.js +++ b/lib/async_hooks.js @@ -2,39 +2,58 @@ const internalUtil = require('internal/util'); const async_wrap = process.binding('async_wrap'); -/* Both these arrays are used to communicate between JS and C++ with as little - * overhead as possible. +/* async_hook_fields is a Uint32Array wrapping the uint32_t array of + * Environment::AsyncHooks::fields_[]. Each index tracks the number of active + * hooks for each type. * - * async_hook_fields is a Uint32Array() that communicates the number of each - * type of active hooks of each type and wraps the uin32_t array of - * node::Environment::AsyncHooks::fields_. - * - * async_uid_fields is a Float64Array() that contains the async/trigger ids for - * several operations. These fields are as follows: - * kCurrentAsyncId: The async id of the current execution stack. - * kCurrentTriggerId: The trigger id of the current execution stack. - * kAsyncUidCntr: Counter that tracks the unique ids given to new resources. - * kInitTriggerId: Written to just before creating a new resource, so the - * constructor knows what other resource is responsible for its init(). - * Used this way so the trigger id doesn't need to be passed to every - * resource's constructor. + * async_uid_fields is a Float64Array wrapping the double array of + * Environment::AsyncHooks::uid_fields_[]. Each index contains the ids for the + * various asynchronous states of the application. These are: + * kCurrentAsyncId: The async_id assigned to the resource responsible for the + * current execution stack. + * kCurrentTriggerId: The trigger_async_id of the resource responsible for the + * current execution stack. + * kAsyncUidCntr: Incremental counter tracking the next assigned async_id. + * kInitTriggerId: Written immediately before a resource's constructor that + * sets the value of the init()'s triggerAsyncId. The order of retrieving + * the triggerAsyncId value is passing directly to the constructor -> value + * set in kInitTriggerId -> executionAsyncId of the current resource. */ const { async_hook_fields, async_uid_fields } = async_wrap; -// Used to change the state of the async id stack. +// Store the pair executionAsyncId and triggerAsyncId in a std::stack on +// Environment::AsyncHooks::ids_stack_ tracks the resource responsible for the +// current execution stack. This is unwound as each resource exits. In the case +// of a fatal exception this stack is emptied after calling each hook's after() +// callback. const { pushAsyncIds, popAsyncIds } = async_wrap; -// Array of all AsyncHooks that will be iterated whenever an async event fires. -// Using var instead of (preferably const) in order to assign -// tmp_active_hooks_array if a hook is enabled/disabled during hook execution. -var active_hooks_array = []; -// Use a counter to track whether a hook callback is currently being processed. -// Used to make sure active_hooks_array isn't altered in mid execution if -// another hook is added or removed. A counter is used to track nested calls. -var processing_hook = 0; -// Use to temporarily store and updated active_hooks_array if the user enables -// or disables a hook while hooks are being processed. -var tmp_active_hooks_array = null; -// Keep track of the field counts held in tmp_active_hooks_array. -var tmp_async_hook_fields = null; +// For performance reasons, only track Proimses when a hook is enabled. +const { enablePromiseHook, disablePromiseHook } = async_wrap; +// Properties in active_hooks are used to keep track of the set of hooks being +// executed in case another hook is enabled/disabled. The new set of hooks is +// then restored once the active set of hooks is finished executing. +const active_hooks = { + // Array of all AsyncHooks that will be iterated whenever an async event + // fires. Using var instead of (preferably const) in order to assign + // active_hooks.tmp_array if a hook is enabled/disabled during hook + // execution. + array: [], + // Use a counter to track nested calls of async hook callbacks and make sure + // the active_hooks.array isn't altered mid execution. + call_depth: 0, + // Use to temporarily store and updated active_hooks.array if the user + // enables or disables a hook while hooks are being processed. If a hook is + // enabled() or disabled() during hook execution then the current set of + // active hooks is duplicated and set equal to active_hooks.tmp_array. Any + // subsequent changes are on the duplicated array. When all hooks have + // completed executing active_hooks.tmp_array is assigned to + // active_hooks.array. + tmp_array: null, + // Keep track of the field counts held in active_hooks.tmp_array. Because the + // async_hook_fields can't be reassigned, store each uint32 in an array that + // is written back to async_hook_fields when active_hooks.array is restored. + tmp_fields: null +}; + // Each constant tracks how many callbacks there are for any given step of // async execution. These are tracked so if the user didn't include callbacks @@ -43,6 +62,9 @@ const { kInit, kBefore, kAfter, kDestroy, kTotals, kCurrentAsyncId, kCurrentTriggerId, kAsyncUidCntr, kInitTriggerId } = async_wrap.constants; +// Symbols used to store the respective ids on both AsyncResource instances and +// internal resources. They will also be assigned to arbitrary objects passed +// in by the user that take place of internally constructed objects. const { async_id_symbol, trigger_id_symbol } = async_wrap; // Used in AsyncHook and AsyncResource. @@ -54,6 +76,9 @@ const emitBeforeNative = emitHookFactory(before_symbol, 'emitBeforeNative'); const emitAfterNative = emitHookFactory(after_symbol, 'emitAfterNative'); const emitDestroyNative = emitHookFactory(destroy_symbol, 'emitDestroyNative'); +// TODO(refack): move to node-config.cc +const abort_regex = /^--abort[_-]on[_-]uncaught[_-]exception$/; + // Setup the callbacks that node::AsyncWrap will call when there are hooks to // process. They use the same functions as the JS embedder API. These callbacks // are setup immediately to prevent async_wrap.setupHooks() from being hijacked @@ -72,7 +97,7 @@ function fatalError(e) { Error.captureStackTrace(o, fatalError); process._rawDebug(o.stack); } - if (process.execArgv.some((e) => /^--abort[_-]on[_-]uncaught[_-]exception$/.test(e))) { + if (process.execArgv.some((e) => abort_regex.test(e))) { process.abort(); } process.exit(1); @@ -122,7 +147,7 @@ class AsyncHook { hooks_array.push(this); if (prev_kTotals === 0 && hook_fields[kTotals] > 0) - async_wrap.enablePromiseHook(); + enablePromiseHook(); return this; } @@ -144,7 +169,7 @@ class AsyncHook { hooks_array.splice(index, 1); if (prev_kTotals > 0 && hook_fields[kTotals] === 0) - async_wrap.disablePromiseHook(); + disablePromiseHook(); return this; } @@ -152,41 +177,41 @@ class AsyncHook { function getHookArrays() { - if (processing_hook === 0) - return [active_hooks_array, async_hook_fields]; + if (active_hooks.call_depth === 0) + return [active_hooks.array, async_hook_fields]; // If this hook is being enabled while in the middle of processing the array // of currently active hooks then duplicate the current set of active hooks // and store this there. This shouldn't fire until the next time hooks are // processed. - if (tmp_active_hooks_array === null) + if (active_hooks.tmp_array === null) storeActiveHooks(); - return [tmp_active_hooks_array, tmp_async_hook_fields]; + return [active_hooks.tmp_array, active_hooks.tmp_fields]; } function storeActiveHooks() { - tmp_active_hooks_array = active_hooks_array.slice(); + active_hooks.tmp_array = active_hooks.array.slice(); // Don't want to make the assumption that kInit to kDestroy are indexes 0 to // 4. So do this the long way. - tmp_async_hook_fields = []; - tmp_async_hook_fields[kInit] = async_hook_fields[kInit]; - tmp_async_hook_fields[kBefore] = async_hook_fields[kBefore]; - tmp_async_hook_fields[kAfter] = async_hook_fields[kAfter]; - tmp_async_hook_fields[kDestroy] = async_hook_fields[kDestroy]; + active_hooks.tmp_fields = []; + active_hooks.tmp_fields[kInit] = async_hook_fields[kInit]; + active_hooks.tmp_fields[kBefore] = async_hook_fields[kBefore]; + active_hooks.tmp_fields[kAfter] = async_hook_fields[kAfter]; + active_hooks.tmp_fields[kDestroy] = async_hook_fields[kDestroy]; } // Then restore the correct hooks array in case any hooks were added/removed // during hook callback execution. -function restoreTmpHooks() { - active_hooks_array = tmp_active_hooks_array; - async_hook_fields[kInit] = tmp_async_hook_fields[kInit]; - async_hook_fields[kBefore] = tmp_async_hook_fields[kBefore]; - async_hook_fields[kAfter] = tmp_async_hook_fields[kAfter]; - async_hook_fields[kDestroy] = tmp_async_hook_fields[kDestroy]; - - tmp_active_hooks_array = null; - tmp_async_hook_fields = null; +function restoreActiveHooks() { + active_hooks.array = active_hooks.tmp_array; + async_hook_fields[kInit] = active_hooks.tmp_fields[kInit]; + async_hook_fields[kBefore] = active_hooks.tmp_fields[kBefore]; + async_hook_fields[kAfter] = active_hooks.tmp_fields[kAfter]; + async_hook_fields[kDestroy] = active_hooks.tmp_fields[kDestroy]; + + active_hooks.tmp_array = null; + active_hooks.tmp_fields = null; } @@ -334,25 +359,30 @@ function emitHookFactory(symbol, name) { // before this is called. // eslint-disable-next-line func-style const fn = function(asyncId) { - processing_hook += 1; + active_hooks.call_depth += 1; // Use a single try/catch for all hook to avoid setting up one per // iteration. try { - for (var i = 0; i < active_hooks_array.length; i++) { - if (typeof active_hooks_array[i][symbol] === 'function') { - active_hooks_array[i][symbol](asyncId); + for (var i = 0; i < active_hooks.array.length; i++) { + if (typeof active_hooks.array[i][symbol] === 'function') { + active_hooks.array[i][symbol](asyncId); } } } catch (e) { fatalError(e); } finally { - processing_hook -= 1; + active_hooks.call_depth -= 1; } - if (processing_hook === 0 && tmp_active_hooks_array !== null) { - restoreTmpHooks(); + // Hooks can only be restored if there have been no recursive hook calls. + // Also the active hooks do not need to be restored if enable()/disable() + // weren't called during hook execution, in which case + // active_hooks.tmp_array will be null. + if (active_hooks.call_depth === 0 && active_hooks.tmp_array !== null) { + restoreActiveHooks(); } }; + // Set the name property of the anonymous function as it looks good in the // stack trace. Object.defineProperty(fn, 'name', { @@ -379,9 +409,6 @@ function emitBeforeScript(asyncId, triggerAsyncId) { } -// TODO(trevnorris): Calling emitBefore/emitAfter from native can't adjust the -// kIdStackIndex. But what happens if the user doesn't have both before and -// after callbacks. function emitAfterScript(asyncId) { if (async_hook_fields[kAfter] > 0) emitAfterNative(asyncId); @@ -399,26 +426,16 @@ function emitDestroyScript(asyncId) { } -// Emit callbacks for native calls. Since some state can be setup directly from -// C++ there's no need to perform all the work here. - -// This should only be called if hooks_array has kInit > 0. There are no global -// values to setup. Though hooks_array will be cloned if C++ needed to call -// init(). -// TODO(trevnorris): Perhaps have MakeCallback call a single JS function that -// does the before/callback/after calls to remove two additional calls to JS. - -// Force the application to shutdown if one of the callbacks throws. This may -// change in the future depending on whether it can be determined if there's a -// slim chance of the application remaining stable after handling one of these -// exceptions. +// Used by C++ to call all init() callbacks. Because some state can be setup +// from C++ there's no need to perform all the same operations as in +// emitInitScript. function emitInitNative(asyncId, type, triggerAsyncId, resource) { - processing_hook += 1; + active_hooks.call_depth += 1; // Use a single try/catch for all hook to avoid setting up one per iteration. try { - for (var i = 0; i < active_hooks_array.length; i++) { - if (typeof active_hooks_array[i][init_symbol] === 'function') { - active_hooks_array[i][init_symbol]( + for (var i = 0; i < active_hooks.array.length; i++) { + if (typeof active_hooks.array[i][init_symbol] === 'function') { + active_hooks.array[i][init_symbol]( asyncId, type, triggerAsyncId, resource ); @@ -427,18 +444,15 @@ function emitInitNative(asyncId, type, triggerAsyncId, resource) { } catch (e) { fatalError(e); } finally { - processing_hook -= 1; + active_hooks.call_depth -= 1; } - // * `tmp_active_hooks_array` is null if no hooks were added/removed while - // the hooks were running. In that case no restoration is needed. - // * In the case where another hook was added/removed while the hooks were - // running and a handle was created causing the `init` hooks to fire again, - // then `restoreTmpHooks` should not be called for the nested `hooks`. - // Otherwise `active_hooks_array` can change during execution of the - // `hooks`. - if (processing_hook === 0 && tmp_active_hooks_array !== null) { - restoreTmpHooks(); + // Hooks can only be restored if there have been no recursive hook calls. + // Also the active hooks do not need to be restored if enable()/disable() + // weren't called during hook execution, in which case active_hooks.tmp_array + // will be null. + if (active_hooks.call_depth === 0 && active_hooks.tmp_array !== null) { + restoreActiveHooks(); } } diff --git a/src/env-inl.h b/src/env-inl.h index b651d969a3d7e8..87fe7e36a2f001 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -180,13 +180,13 @@ inline void Environment::AsyncHooks::clear_id_stack() { inline Environment::AsyncHooks::InitScope::InitScope( Environment* env, double init_trigger_id) : env_(env), - uid_fields_(env->async_hooks()->uid_fields()) { - env->async_hooks()->push_ids(uid_fields_[AsyncHooks::kCurrentAsyncId], + uid_fields_ref_(env->async_hooks()->uid_fields()) { + env->async_hooks()->push_ids(uid_fields_ref_[AsyncHooks::kCurrentAsyncId], init_trigger_id); } inline Environment::AsyncHooks::InitScope::~InitScope() { - env_->async_hooks()->pop_ids(uid_fields_[AsyncHooks::kCurrentAsyncId]); + env_->async_hooks()->pop_ids(uid_fields_ref_[AsyncHooks::kCurrentAsyncId]); } inline Environment::AsyncHooks::ExecScope::ExecScope( diff --git a/src/env.h b/src/env.h index 47a6487b77dbe6..7ec6bfe08bb7e9 100644 --- a/src/env.h +++ b/src/env.h @@ -410,7 +410,7 @@ class Environment { private: Environment* env_; - double* uid_fields_; + double* uid_fields_ref_; DISALLOW_COPY_AND_ASSIGN(InitScope); }; @@ -443,12 +443,10 @@ class Environment { v8::Isolate* isolate_; // Stores the ids of the current execution context stack. std::stack ids_stack_; - // Used to communicate state between C++ and JS cheaply. Is placed in an - // Uint32Array() and attached to the async_wrap object. + // Attached to a Uint32Array that tracks the number of active hooks for + // each type. uint32_t fields_[kFieldsCount]; - // Used to communicate ids between C++ and JS cheaply. Placed in a - // Float64Array and attached to the async_wrap object. Using a double only - // gives us 2^53-1 unique ids, but that should be sufficient. + // Attached to a Float64Array that tracks the state of async resources. double uid_fields_[kUidFieldsCount]; DISALLOW_COPY_AND_ASSIGN(AsyncHooks); From 78a36e0dd143ce385848b73ca3740728d84d4dff Mon Sep 17 00:00:00 2001 From: Trevor Norris Date: Thu, 3 Aug 2017 17:03:41 -0600 Subject: [PATCH 194/231] async_wrap: unroll unnecessarily DRY code * Because Emit{Before,After}() will always exit the process if there's an exception there's no need to check a return value. This simplifies the conditions and makes {Pre,Post}CallbackExecution() unnecessary. They have been removed and relevant code has been moved to the respective call sites. Which are: * PromiseHook() never needs to run domains, and without a return value to check place the calls to Emit{Before,After}() on location. * The logic in MakeCallback() is simplified by moving the single calls to Emit{Before,After}() then doing a simpler conditional to check if the domain has been disposed. * Change Domain{Enter,Exit}() to return true if the instance has been disposed. Makes the conditional check in MakeCallback() simpler to reason about. * Add UNREACHABLE() after FatalException() to make sure all error handlers have been cleared and the process really does exit. PR-URL: https://github.com/nodejs/node/pull/14722 Reviewed-By: Anna Henningsen Reviewed-By: Refael Ackermann --- src/async-wrap.cc | 102 +++++++++++++++++++--------------------------- src/async-wrap.h | 5 ++- src/node.cc | 8 ++-- 3 files changed, 50 insertions(+), 65 deletions(-) diff --git a/src/async-wrap.cc b/src/async-wrap.cc index 8f513ba4524795..71b1e256592b08 100644 --- a/src/async-wrap.cc +++ b/src/async-wrap.cc @@ -165,6 +165,7 @@ static void DestroyIdsCb(uv_timer_t* handle) { if (ret.IsEmpty()) { ClearFatalExceptionHandlers(env); FatalException(env->isolate(), try_catch); + UNREACHABLE(); } } } while (!env->destroy_ids_list()->empty()); @@ -218,69 +219,43 @@ bool DomainExit(Environment* env, v8::Local object) { } -static bool PreCallbackExecution(AsyncWrap* wrap, bool run_domain_cbs) { - if (wrap->env()->using_domains() && run_domain_cbs) { - bool is_disposed = DomainEnter(wrap->env(), wrap->object()); - if (is_disposed) - return false; - } - - return AsyncWrap::EmitBefore(wrap->env(), wrap->get_id()); -} - - -bool AsyncWrap::EmitBefore(Environment* env, double async_id) { +void AsyncWrap::EmitBefore(Environment* env, double async_id) { AsyncHooks* async_hooks = env->async_hooks(); - if (async_hooks->fields()[AsyncHooks::kBefore] > 0) { - Local uid = Number::New(env->isolate(), async_id); - Local fn = env->async_hooks_before_function(); - TryCatch try_catch(env->isolate()); - MaybeLocal ar = fn->Call( - env->context(), Undefined(env->isolate()), 1, &uid); - if (ar.IsEmpty()) { - ClearFatalExceptionHandlers(env); - FatalException(env->isolate(), try_catch); - return false; - } - } - - return true; -} - - -static bool PostCallbackExecution(AsyncWrap* wrap, bool run_domain_cbs) { - if (!AsyncWrap::EmitAfter(wrap->env(), wrap->get_id())) - return false; + if (async_hooks->fields()[AsyncHooks::kBefore] == 0) + return; - if (wrap->env()->using_domains() && run_domain_cbs) { - bool is_disposed = DomainExit(wrap->env(), wrap->object()); - if (is_disposed) - return false; + Local uid = Number::New(env->isolate(), async_id); + Local fn = env->async_hooks_before_function(); + TryCatch try_catch(env->isolate()); + MaybeLocal ar = fn->Call( + env->context(), Undefined(env->isolate()), 1, &uid); + if (ar.IsEmpty()) { + ClearFatalExceptionHandlers(env); + FatalException(env->isolate(), try_catch); + UNREACHABLE(); } - - return true; } -bool AsyncWrap::EmitAfter(Environment* env, double async_id) { + +void AsyncWrap::EmitAfter(Environment* env, double async_id) { AsyncHooks* async_hooks = env->async_hooks(); - // If the callback failed then the after() hooks will be called at the end - // of _fatalException(). - if (async_hooks->fields()[AsyncHooks::kAfter] > 0) { - Local uid = Number::New(env->isolate(), async_id); - Local fn = env->async_hooks_after_function(); - TryCatch try_catch(env->isolate()); - MaybeLocal ar = fn->Call( - env->context(), Undefined(env->isolate()), 1, &uid); - if (ar.IsEmpty()) { - ClearFatalExceptionHandlers(env); - FatalException(env->isolate(), try_catch); - return false; - } - } + if (async_hooks->fields()[AsyncHooks::kAfter] == 0) + return; - return true; + // If the user's callback failed then the after() hooks will be called at the + // end of _fatalException(). + Local uid = Number::New(env->isolate(), async_id); + Local fn = env->async_hooks_after_function(); + TryCatch try_catch(env->isolate()); + MaybeLocal ar = fn->Call( + env->context(), Undefined(env->isolate()), 1, &uid); + if (ar.IsEmpty()) { + ClearFatalExceptionHandlers(env); + FatalException(env->isolate(), try_catch); + UNREACHABLE(); + } } class PromiseWrap : public AsyncWrap { @@ -373,9 +348,9 @@ static void PromiseHook(PromiseHookType type, Local promise, CHECK_NE(wrap, nullptr); if (type == PromiseHookType::kBefore) { env->async_hooks()->push_ids(wrap->get_id(), wrap->get_trigger_id()); - PreCallbackExecution(wrap, false); + AsyncWrap::EmitBefore(wrap->env(), wrap->get_id()); } else if (type == PromiseHookType::kAfter) { - PostCallbackExecution(wrap, false); + AsyncWrap::EmitAfter(wrap->env(), wrap->get_id()); if (env->current_async_id() == wrap->get_id()) { // This condition might not be true if async_hooks was enabled during // the promise callback execution. @@ -696,18 +671,27 @@ MaybeLocal AsyncWrap::MakeCallback(const Local cb, get_id(), get_trigger_id()); - if (!PreCallbackExecution(this, true)) { + // Return v8::Undefined() because returning an empty handle will cause + // ToLocalChecked() to abort. + if (env()->using_domains() && DomainEnter(env(), object())) { return Undefined(env()->isolate()); } - // Finally... Get to running the user's callback. + // No need to check a return value because the application will exit if an + // exception occurs. + AsyncWrap::EmitBefore(env(), get_id()); + MaybeLocal ret = cb->Call(env()->context(), object(), argc, argv); if (ret.IsEmpty()) { return ret; } - if (!PostCallbackExecution(this, true)) { + AsyncWrap::EmitAfter(env(), get_id()); + + // Return v8::Undefined() because returning an empty handle will cause + // ToLocalChecked() to abort. + if (env()->using_domains() && DomainExit(env(), object())) { return Undefined(env()->isolate()); } diff --git a/src/async-wrap.h b/src/async-wrap.h index 5bc48d7faf0c30..10d0150844a7ab 100644 --- a/src/async-wrap.h +++ b/src/async-wrap.h @@ -113,8 +113,8 @@ class AsyncWrap : public BaseObject { double id, double trigger_id); - static bool EmitBefore(Environment* env, double id); - static bool EmitAfter(Environment* env, double id); + static void EmitBefore(Environment* env, double id); + static void EmitAfter(Environment* env, double id); inline ProviderType provider_type() const; @@ -148,6 +148,7 @@ class AsyncWrap : public BaseObject { void LoadAsyncWrapperInfo(Environment* env); +// Return value is an indicator whether the domain was disposed. bool DomainEnter(Environment* env, v8::Local object); bool DomainExit(Environment* env, v8::Local object); diff --git a/src/node.cc b/src/node.cc index 3c9a6e6ce65743..34785693c8a7ad 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1334,8 +1334,9 @@ MaybeLocal MakeCallback(Environment* env, asyncContext.trigger_async_id); if (asyncContext.async_id != 0) { - if (!AsyncWrap::EmitBefore(env, asyncContext.async_id)) - return Local(); + // No need to check a return value because the application will exit if + // an exception occurs. + AsyncWrap::EmitBefore(env, asyncContext.async_id); } ret = callback->Call(env->context(), recv, argc, argv); @@ -1348,8 +1349,7 @@ MaybeLocal MakeCallback(Environment* env, } if (asyncContext.async_id != 0) { - if (!AsyncWrap::EmitAfter(env, asyncContext.async_id)) - return Local(); + AsyncWrap::EmitAfter(env, asyncContext.async_id); } } From b40105df3bd00544d56591a3029c86e9ccb99f66 Mon Sep 17 00:00:00 2001 From: Trevor Norris Date: Thu, 3 Aug 2017 14:43:41 -0600 Subject: [PATCH 195/231] async_hooks: don't abort unnecessarily * id values of -1 are allowed. They indicate that the id was never correctly assigned to the async resource. These will appear in any call graph, and will only be apparent to those using the async_hooks module, then reported in an issue. * ids < -1 are still not allowed and will cause the application to exit the process; because there is no scenario where this should ever happen. * Add asyncId range checks to emitAfterScript(). * Fix emitBeforeScript() range checks which should have been || not &&. * Replace errors with entries in internal/errors. * Fix async_hooks tests that check for exceptions to match new internal/errors entries. NOTE: emit{Before,After,Destroy}() must continue to exit the process because in the case of an exception during hook execution the state of the application is unknowable. For example, an exception could cause a memory leak: const id_map = new Map(); before(id) { id_map.set(id, /* data object or similar */); }, after(id) { throw new Error('id never dies!'); id_map.delete(id); } Allowing a recoverable exception may also cause an abort because of a stack check in Environment::AsyncHooks::pop_ids() that verifies the async id and pop'd ids match. This case would be more difficult to debug than if fatalError() (lib/async_hooks.js) was called immediately. try { async_hooks.emitBefore(null, NaN); } catch (e) { } // do something async_hooks.emitAfter(5); It also allows an edge case where emitBefore() could be called twice and not have the pop_ids() CHECK fail: try { async_hooks.emitBefore(5, NaN); } catch (e) { } async_hooks.emitBefore(5); // do something async_hooks.emitAfter(5); There is the option of allowing mismatches in the stack and ignoring the check if no async hooks are enabled, but I don't believe going this far is necessary. PR-URL: https://github.com/nodejs/node/pull/14722 Reviewed-By: Anna Henningsen Reviewed-By: Refael Ackermann --- lib/async_hooks.js | 68 ++++++++++++------- lib/internal/errors.js | 3 + src/env-inl.h | 7 +- .../test-embedder.api.async-resource.js | 16 +++-- test/async-hooks/test-emit-before-after.js | 16 ++--- test/async-hooks/test-emit-init.js | 24 +++++-- ...t-async-hooks-asyncresource-constructor.js | 22 ++++-- test/parallel/test-async-wrap-constructor.js | 7 +- 8 files changed, 112 insertions(+), 51 deletions(-) diff --git a/lib/async_hooks.js b/lib/async_hooks.js index 38bdbb0306b6be..67e81ecaecb28a 100644 --- a/lib/async_hooks.js +++ b/lib/async_hooks.js @@ -2,6 +2,7 @@ const internalUtil = require('internal/util'); const async_wrap = process.binding('async_wrap'); +const errors = require('internal/errors'); /* async_hook_fields is a Uint32Array wrapping the uint32_t array of * Environment::AsyncHooks::fields_[]. Each index tracks the number of active * hooks for each type. @@ -109,13 +110,13 @@ function fatalError(e) { class AsyncHook { constructor({ init, before, after, destroy }) { if (init !== undefined && typeof init !== 'function') - throw new TypeError('init must be a function'); + throw new errors.TypeError('ERR_ASYNC_CALLBACK', 'init'); if (before !== undefined && typeof before !== 'function') - throw new TypeError('before must be a function'); + throw new errors.TypeError('ERR_ASYNC_CALLBACK', 'before'); if (after !== undefined && typeof after !== 'function') - throw new TypeError('after must be a function'); + throw new errors.TypeError('ERR_ASYNC_CALLBACK', 'before'); if (destroy !== undefined && typeof destroy !== 'function') - throw new TypeError('destroy must be a function'); + throw new errors.TypeError('ERR_ASYNC_CALLBACK', 'before'); this[init_symbol] = init; this[before_symbol] = before; @@ -236,8 +237,11 @@ class AsyncResource { constructor(type, triggerAsyncId = initTriggerId()) { // Unlike emitInitScript, AsyncResource doesn't supports null as the // triggerAsyncId. - if (!Number.isSafeInteger(triggerAsyncId) || triggerAsyncId < 0) - throw new RangeError('triggerAsyncId must be an unsigned integer'); + if (!Number.isSafeInteger(triggerAsyncId) || triggerAsyncId < -1) { + throw new errors.RangeError('ERR_INVALID_ASYNC_ID', + 'triggerAsyncId', + triggerAsyncId); + } this[async_id_symbol] = ++async_uid_fields[kAsyncUidCntr]; this[trigger_id_symbol] = triggerAsyncId; @@ -342,14 +346,17 @@ function emitInitScript(asyncId, type, triggerAsyncId, resource) { async_uid_fields[kInitTriggerId] = 0; } - // TODO(trevnorris): I'd prefer allowing these checks to not exist, or only - // throw in a debug build, in order to improve performance. - if (!Number.isSafeInteger(asyncId) || asyncId < 0) - throw new RangeError('asyncId must be an unsigned integer'); - if (typeof type !== 'string' || type.length <= 0) - throw new TypeError('type must be a string with length > 0'); - if (!Number.isSafeInteger(triggerAsyncId) || triggerAsyncId < 0) - throw new RangeError('triggerAsyncId must be an unsigned integer'); + if (!Number.isSafeInteger(asyncId) || asyncId < -1) { + throw new errors.RangeError('ERR_INVALID_ASYNC_ID', 'asyncId', asyncId); + } + if (!Number.isSafeInteger(triggerAsyncId) || triggerAsyncId < -1) { + throw new errors.RangeError('ERR_INVALID_ASYNC_ID', + 'triggerAsyncId', + triggerAsyncId); + } + if (typeof type !== 'string' || type.length <= 0) { + throw new errors.TypeError('ERR_ASYNC_TYPE', type); + } emitInitNative(asyncId, type, triggerAsyncId, resource); } @@ -393,13 +400,17 @@ function emitHookFactory(symbol, name) { function emitBeforeScript(asyncId, triggerAsyncId) { - // CHECK(Number.isSafeInteger(asyncId) && asyncId > 0) - // CHECK(Number.isSafeInteger(triggerAsyncId) && triggerAsyncId > 0) - - // Validate the ids. - if (asyncId < 0 || triggerAsyncId < 0) { - fatalError('before(): asyncId or triggerAsyncId is less than zero ' + - `(asyncId: ${asyncId}, triggerAsyncId: ${triggerAsyncId})`); + // Validate the ids. An id of -1 means it was never set and is visible on the + // call graph. An id < -1 should never happen in any circumstance. Throw + // on user calls because async state should still be recoverable. + if (!Number.isSafeInteger(asyncId) || asyncId < -1) { + fatalError( + new errors.RangeError('ERR_INVALID_ASYNC_ID', 'asyncId', asyncId)); + } + if (!Number.isSafeInteger(triggerAsyncId) || triggerAsyncId < -1) { + fatalError(new errors.RangeError('ERR_INVALID_ASYNC_ID', + 'triggerAsyncId', + triggerAsyncId)); } pushAsyncIds(asyncId, triggerAsyncId); @@ -410,6 +421,11 @@ function emitBeforeScript(asyncId, triggerAsyncId) { function emitAfterScript(asyncId) { + if (!Number.isSafeInteger(asyncId) || asyncId < -1) { + fatalError( + new errors.RangeError('ERR_INVALID_ASYNC_ID', 'asyncId', asyncId)); + } + if (async_hook_fields[kAfter] > 0) emitAfterNative(asyncId); @@ -418,9 +434,13 @@ function emitAfterScript(asyncId) { function emitDestroyScript(asyncId) { - // Return early if there are no destroy callbacks, or on attempt to emit - // destroy on the void. - if (async_hook_fields[kDestroy] === 0 || asyncId === 0) + if (!Number.isSafeInteger(asyncId) || asyncId < -1) { + fatalError( + new errors.RangeError('ERR_INVALID_ASYNC_ID', 'asyncId', asyncId)); + } + + // Return early if there are no destroy callbacks, or invalid asyncId. + if (async_hook_fields[kDestroy] === 0 || asyncId <= 0) return; async_wrap.addIdToDestroyList(asyncId); } diff --git a/lib/internal/errors.js b/lib/internal/errors.js index b7dd509070731d..2b06583dd483e3 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -109,6 +109,8 @@ module.exports = exports = { // Note: Please try to keep these in alphabetical order E('ERR_ARG_NOT_ITERABLE', '%s must be iterable'); E('ERR_ASSERTION', (msg) => msg); +E('ERR_ASYNC_CALLBACK', (name) => `${name} must be a function`); +E('ERR_ASYNC_TYPE', (s) => `Invalid name for async "type": ${s}`); E('ERR_ENCODING_INVALID_ENCODED_DATA', (enc) => `The encoded data was not valid for encoding ${enc}`); E('ERR_ENCODING_NOT_SUPPORTED', @@ -184,6 +186,7 @@ E('ERR_HTTP2_UNSUPPORTED_PROTOCOL', (protocol) => `protocol "${protocol}" is unsupported.`); E('ERR_INDEX_OUT_OF_RANGE', 'Index out of range'); E('ERR_INVALID_ARG_TYPE', invalidArgType); +E('ERR_INVALID_ASYNC_ID', (type, id) => `Invalid ${type} value: ${id}`); E('ERR_INVALID_CALLBACK', 'callback must be a function'); E('ERR_INVALID_FD', (fd) => `"fd" must be a positive integer: ${fd}`); E('ERR_INVALID_FILE_URL_HOST', 'File URL host %s'); diff --git a/src/env-inl.h b/src/env-inl.h index 87fe7e36a2f001..69b5a5e858b458 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -127,8 +127,8 @@ inline v8::Local Environment::AsyncHooks::provider_string(int idx) { inline void Environment::AsyncHooks::push_ids(double async_id, double trigger_id) { - CHECK_GE(async_id, 0); - CHECK_GE(trigger_id, 0); + CHECK_GE(async_id, -1); + CHECK_GE(trigger_id, -1); ids_stack_.push({ uid_fields_[kCurrentAsyncId], uid_fields_[kCurrentTriggerId] }); @@ -181,6 +181,7 @@ inline Environment::AsyncHooks::InitScope::InitScope( Environment* env, double init_trigger_id) : env_(env), uid_fields_ref_(env->async_hooks()->uid_fields()) { + CHECK_GE(init_trigger_id, -1); env->async_hooks()->push_ids(uid_fields_ref_[AsyncHooks::kCurrentAsyncId], init_trigger_id); } @@ -194,6 +195,8 @@ inline Environment::AsyncHooks::ExecScope::ExecScope( : env_(env), async_id_(async_id), disposed_(false) { + CHECK_GE(async_id, -1); + CHECK_GE(trigger_id, -1); env->async_hooks()->push_ids(async_id, trigger_id); } diff --git a/test/async-hooks/test-embedder.api.async-resource.js b/test/async-hooks/test-embedder.api.async-resource.js index 78985b955ad683..533df6a5c84549 100644 --- a/test/async-hooks/test-embedder.api.async-resource.js +++ b/test/async-hooks/test-embedder.api.async-resource.js @@ -12,10 +12,18 @@ const { checkInvocations } = require('./hook-checks'); const hooks = initHooks(); hooks.enable(); -assert.throws(() => new AsyncResource(), - /^TypeError: type must be a string with length > 0$/); -assert.throws(() => new AsyncResource('invalid_trigger_id', null), - /^RangeError: triggerAsyncId must be an unsigned integer$/); +assert.throws(() => { + new AsyncResource(); +}, common.expectsError({ + code: 'ERR_ASYNC_TYPE', + type: TypeError, +})); +assert.throws(() => { + new AsyncResource('invalid_trigger_id', null); +}, common.expectsError({ + code: 'ERR_INVALID_ASYNC_ID', + type: RangeError, +})); assert.strictEqual( new AsyncResource('default_trigger_id').triggerAsyncId(), diff --git a/test/async-hooks/test-emit-before-after.js b/test/async-hooks/test-emit-before-after.js index 1334767c766a25..2b22739fa9478d 100644 --- a/test/async-hooks/test-emit-before-after.js +++ b/test/async-hooks/test-emit-before-after.js @@ -8,25 +8,25 @@ const initHooks = require('./init-hooks'); switch (process.argv[2]) { case 'test_invalid_async_id': - async_hooks.emitBefore(-1, 1); + async_hooks.emitBefore(-2, 1); return; case 'test_invalid_trigger_id': - async_hooks.emitBefore(1, -1); + async_hooks.emitBefore(1, -2); return; } assert.ok(!process.argv[2]); const c1 = spawnSync(process.execPath, [__filename, 'test_invalid_async_id']); -assert.strictEqual(c1.stderr.toString().split(/[\r\n]+/g)[0], - 'Error: before(): asyncId or triggerAsyncId is less than ' + - 'zero (asyncId: -1, triggerAsyncId: 1)'); +assert.strictEqual( + c1.stderr.toString().split(/[\r\n]+/g)[0], + 'RangeError [ERR_INVALID_ASYNC_ID]: Invalid asyncId value: -2'); assert.strictEqual(c1.status, 1); const c2 = spawnSync(process.execPath, [__filename, 'test_invalid_trigger_id']); -assert.strictEqual(c2.stderr.toString().split(/[\r\n]+/g)[0], - 'Error: before(): asyncId or triggerAsyncId is less than ' + - 'zero (asyncId: 1, triggerAsyncId: -1)'); +assert.strictEqual( + c2.stderr.toString().split(/[\r\n]+/g)[0], + 'RangeError [ERR_INVALID_ASYNC_ID]: Invalid triggerAsyncId value: -2'); assert.strictEqual(c2.status, 1); const expectedId = async_hooks.newUid(); diff --git a/test/async-hooks/test-emit-init.js b/test/async-hooks/test-emit-init.js index feee3d944b8afb..631dcd759968cb 100644 --- a/test/async-hooks/test-emit-init.js +++ b/test/async-hooks/test-emit-init.js @@ -25,12 +25,24 @@ const hooks1 = initHooks({ hooks1.enable(); -assert.throws(() => async_hooks.emitInit(), - /^RangeError: asyncId must be an unsigned integer$/); -assert.throws(() => async_hooks.emitInit(expectedId), - /^TypeError: type must be a string with length > 0$/); -assert.throws(() => async_hooks.emitInit(expectedId, expectedType, -1), - /^RangeError: triggerAsyncId must be an unsigned integer$/); +assert.throws(() => { + async_hooks.emitInit(); +}, common.expectsError({ + code: 'ERR_INVALID_ASYNC_ID', + type: RangeError, +})); +assert.throws(() => { + async_hooks.emitInit(expectedId); +}, common.expectsError({ + code: 'ERR_INVALID_ASYNC_ID', + type: RangeError, +})); +assert.throws(() => { + async_hooks.emitInit(expectedId, expectedType, -2); +}, common.expectsError({ + code: 'ERR_INVALID_ASYNC_ID', + type: RangeError, +})); async_hooks.emitInit(expectedId, expectedType, expectedTriggerId, expectedResource); diff --git a/test/parallel/test-async-hooks-asyncresource-constructor.js b/test/parallel/test-async-hooks-asyncresource-constructor.js index 2fb0bb14ccf64e..2ab5f067ca6c76 100644 --- a/test/parallel/test-async-hooks-asyncresource-constructor.js +++ b/test/parallel/test-async-hooks-asyncresource-constructor.js @@ -1,8 +1,8 @@ 'use strict'; -require('../common'); // This tests that AsyncResource throws an error if bad parameters are passed +const common = require('../common'); const assert = require('assert'); const async_hooks = require('async_hooks'); const { AsyncResource } = async_hooks; @@ -14,16 +14,28 @@ async_hooks.createHook({ assert.throws(() => { return new AsyncResource(); -}, /^TypeError: type must be a string with length > 0$/); +}, common.expectsError({ + code: 'ERR_ASYNC_TYPE', + type: TypeError, +})); assert.throws(() => { new AsyncResource(''); -}, /^TypeError: type must be a string with length > 0$/); +}, common.expectsError({ + code: 'ERR_ASYNC_TYPE', + type: TypeError, +})); assert.throws(() => { new AsyncResource('type', -4); -}, /^RangeError: triggerAsyncId must be an unsigned integer$/); +}, common.expectsError({ + code: 'ERR_INVALID_ASYNC_ID', + type: RangeError, +})); assert.throws(() => { new AsyncResource('type', Math.PI); -}, /^RangeError: triggerAsyncId must be an unsigned integer$/); +}, common.expectsError({ + code: 'ERR_INVALID_ASYNC_ID', + type: RangeError, +})); diff --git a/test/parallel/test-async-wrap-constructor.js b/test/parallel/test-async-wrap-constructor.js index 4f344fd99bcff4..86fce0e3f39e68 100644 --- a/test/parallel/test-async-wrap-constructor.js +++ b/test/parallel/test-async-wrap-constructor.js @@ -1,8 +1,8 @@ 'use strict'; -require('../common'); // This tests that using falsy values in createHook throws an error. +const common = require('../common'); const assert = require('assert'); const async_hooks = require('async_hooks'); @@ -10,6 +10,9 @@ for (const badArg of [0, 1, false, true, null, 'hello']) { for (const field of ['init', 'before', 'after', 'destroy']) { assert.throws(() => { async_hooks.createHook({ [field]: badArg }); - }, new RegExp(`^TypeError: ${field} must be a function$`)); + }, common.expectsError({ + code: 'ERR_ASYNC_CALLBACK', + type: TypeError, + })); } } From 7facfaab66ee1e15a173a0dd476a0e1ff0cc94b0 Mon Sep 17 00:00:00 2001 From: Beth Griggs Date: Mon, 14 Aug 2017 12:12:49 +0100 Subject: [PATCH 196/231] test: preserve env in test cases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allows env vars to be passed through to child processes. This is needed for things like NODE_TEST_DIR or LD_LIBRARY_PATH if testing the shared library. PR-URL: https://github.com/nodejs/node/pull/14822 Refs: https://github.com/nodejs/node/pull/13390 Reviewed-By: Colin Ihrig Reviewed-By: Refael Ackermann Reviewed-By: Michael Dawson Reviewed-By: Luigi Pinca Reviewed-By: Anna Henningsen Reviewed-By: Tobias Nießen Reviewed-By: James M Snell --- test/parallel/test-benchmark-crypto.js | 4 +++- test/parallel/test-benchmark-timers.js | 5 +++- test/parallel/test-env-var-no-warnings.js | 3 ++- test/parallel/test-tls-env-bad-extra-ca.js | 4 ++-- test/parallel/test-tls-env-extra-ca.js | 4 ++-- .../test-benchmark-child-process.js | 23 +++++++++++++------ test/sequential/test-benchmark-net.js | 4 +++- 7 files changed, 32 insertions(+), 15 deletions(-) diff --git a/test/parallel/test-benchmark-crypto.js b/test/parallel/test-benchmark-crypto.js index 25dbc95b4c27f4..db0e9f499117a5 100644 --- a/test/parallel/test-benchmark-crypto.js +++ b/test/parallel/test-benchmark-crypto.js @@ -27,7 +27,9 @@ const argv = ['--set', 'algo=sha256', '--set', 'writes=1', 'crypto']; -const child = fork(runjs, argv, {env: {NODEJS_BENCHMARK_ZERO_ALLOWED: 1}}); +const env = Object.assign({}, process.env, + { NODEJS_BENCHMARK_ZERO_ALLOWED: 1 }); +const child = fork(runjs, argv, { env }); child.on('exit', (code, signal) => { assert.strictEqual(code, 0); assert.strictEqual(signal, null); diff --git a/test/parallel/test-benchmark-timers.js b/test/parallel/test-benchmark-timers.js index 956ab657bc5295..991ffda7186e72 100644 --- a/test/parallel/test-benchmark-timers.js +++ b/test/parallel/test-benchmark-timers.js @@ -15,7 +15,10 @@ const argv = ['--set', 'type=depth', '--set', 'thousands=0.001', 'timers']; -const child = fork(runjs, argv, {env: {NODEJS_BENCHMARK_ZERO_ALLOWED: 1}}); +const env = Object.assign({}, process.env, + { NODEJS_BENCHMARK_ZERO_ALLOWED: 1 }); + +const child = fork(runjs, argv, { env }); child.on('exit', (code, signal) => { assert.strictEqual(code, 0); assert.strictEqual(signal, null); diff --git a/test/parallel/test-env-var-no-warnings.js b/test/parallel/test-env-var-no-warnings.js index 3a99cbc112d8b9..e2a11656f33df2 100644 --- a/test/parallel/test-env-var-no-warnings.js +++ b/test/parallel/test-env-var-no-warnings.js @@ -6,7 +6,8 @@ const cp = require('child_process'); if (process.argv[2] === 'child') { process.emitWarning('foo'); } else { - function test(env) { + function test(newEnv) { + const env = Object.assign({}, process.env, newEnv); const cmd = `"${process.execPath}" "${__filename}" child`; cp.exec(cmd, { env }, common.mustCall((err, stdout, stderr) => { diff --git a/test/parallel/test-tls-env-bad-extra-ca.js b/test/parallel/test-tls-env-bad-extra-ca.js index ece93f33539d71..5c6e47d52a654a 100644 --- a/test/parallel/test-tls-env-bad-extra-ca.js +++ b/test/parallel/test-tls-env-bad-extra-ca.js @@ -15,10 +15,10 @@ if (process.env.CHILD) { return tls.createServer({}); } -const env = { +const env = Object.assign({}, process.env, { CHILD: 'yes', NODE_EXTRA_CA_CERTS: `${common.fixturesDir}/no-such-file-exists`, -}; +}); const opts = { env: env, diff --git a/test/parallel/test-tls-env-extra-ca.js b/test/parallel/test-tls-env-extra-ca.js index 5f011f33382ac6..4dd12a78b6387c 100644 --- a/test/parallel/test-tls-env-extra-ca.js +++ b/test/parallel/test-tls-env-extra-ca.js @@ -32,11 +32,11 @@ const server = tls.createServer(options, common.mustCall(function(s) { s.end('bye'); server.close(); })).listen(0, common.mustCall(function() { - const env = { + const env = Object.assign({}, process.env, { CHILD: 'yes', PORT: this.address().port, NODE_EXTRA_CA_CERTS: fixtures.path('keys', 'ca1-cert.pem') - }; + }); fork(__filename, {env: env}).on('exit', common.mustCall(function(status) { assert.strictEqual(status, 0, 'client did not succeed in connecting'); diff --git a/test/sequential/test-benchmark-child-process.js b/test/sequential/test-benchmark-child-process.js index 2314c89948c701..f993238549fca4 100644 --- a/test/sequential/test-benchmark-child-process.js +++ b/test/sequential/test-benchmark-child-process.js @@ -8,13 +8,22 @@ const path = require('path'); const runjs = path.join(__dirname, '..', '..', 'benchmark', 'run.js'); -const child = fork(runjs, ['--set', 'dur=0', - '--set', 'n=1', - '--set', 'len=1', - '--set', 'params=1', - '--set', 'methodName=execSync', - 'child_process'], - {env: {NODEJS_BENCHMARK_ZERO_ALLOWED: 1}}); +const env = Object.assign({}, process.env, + { NODEJS_BENCHMARK_ZERO_ALLOWED: 1 }); + +const child = fork( + runjs, + [ + '--set', 'dur=0', + '--set', 'n=1', + '--set', 'len=1', + '--set', 'params=1', + '--set', 'methodName=execSync', + 'child_process' + ], + { env } +); + child.on('exit', (code, signal) => { assert.strictEqual(code, 0); assert.strictEqual(signal, null); diff --git a/test/sequential/test-benchmark-net.js b/test/sequential/test-benchmark-net.js index ef5f4fbb91c760..b2d360328504fb 100644 --- a/test/sequential/test-benchmark-net.js +++ b/test/sequential/test-benchmark-net.js @@ -15,12 +15,14 @@ const path = require('path'); const runjs = path.join(__dirname, '..', '..', 'benchmark', 'run.js'); +const env = Object.assign({}, process.env, + { NODEJS_BENCHMARK_ZERO_ALLOWED: 1 }); const child = fork(runjs, ['--set', 'dur=0', '--set', 'len=1024', '--set', 'type=buf', 'net'], - {env: {NODEJS_BENCHMARK_ZERO_ALLOWED: 1}}); + { env }); child.on('exit', (code, signal) => { assert.strictEqual(code, 0); assert.strictEqual(signal, null); From e2ae08b48dfeb0fba9c9b959afca47e346812f1d Mon Sep 17 00:00:00 2001 From: Eugene Ostroukhov Date: Mon, 24 Jul 2017 09:23:04 -0700 Subject: [PATCH 197/231] inspector: rewrite inspector test helper Helper was rewritten to rely on promises instead of manually written queue and callbacks. This simplifies the code and makes it easier to maintain and extend. PR-URL: https://github.com/nodejs/node/pull/14797 Reviewed-By: Timothy Gu Reviewed-By: Colin Ihrig --- test/common/README.md | 9 + test/common/index.js | 42 + test/inspector/inspector-helper.js | 771 +++++++----------- test/inspector/test-break-when-eval.js | 68 ++ test/inspector/test-debug-brk-flag.js | 41 + test/inspector/test-debug-end.js | 46 ++ test/inspector/test-exception.js | 45 + .../test-inspector-break-when-eval.js | 128 --- test/inspector/test-inspector-debug-brk.js | 59 -- test/inspector/test-inspector-exception.js | 64 -- test/inspector/test-inspector-ip-detection.js | 51 -- .../test-inspector-stop-profile-after-done.js | 21 - test/inspector/test-inspector.js | 501 +++++------- test/inspector/test-ip-detection.js | 48 ++ test/inspector/test-not-blocked-on-idle.js | 28 +- test/inspector/test-off-no-session.js | 11 - .../test-off-with-session-then-on.js | 24 - ...r-port-cluster.js => test-port-cluster.js} | 0 ...o-cluster.js => test-port-zero-cluster.js} | 0 ...spector-port-zero.js => test-port-zero.js} | 0 .../inspector/test-stop-profile-after-done.js | 30 + ...stops-no-file.js => test-stops-no-file.js} | 0 22 files changed, 868 insertions(+), 1119 deletions(-) create mode 100644 test/inspector/test-break-when-eval.js create mode 100644 test/inspector/test-debug-brk-flag.js create mode 100644 test/inspector/test-debug-end.js create mode 100644 test/inspector/test-exception.js delete mode 100644 test/inspector/test-inspector-break-when-eval.js delete mode 100644 test/inspector/test-inspector-debug-brk.js delete mode 100644 test/inspector/test-inspector-exception.js delete mode 100644 test/inspector/test-inspector-ip-detection.js delete mode 100644 test/inspector/test-inspector-stop-profile-after-done.js create mode 100644 test/inspector/test-ip-detection.js delete mode 100644 test/inspector/test-off-no-session.js delete mode 100644 test/inspector/test-off-with-session-then-on.js rename test/inspector/{test-inspector-port-cluster.js => test-port-cluster.js} (100%) rename test/inspector/{test-inspector-port-zero-cluster.js => test-port-zero-cluster.js} (100%) rename test/inspector/{test-inspector-port-zero.js => test-port-zero.js} (100%) create mode 100644 test/inspector/test-stop-profile-after-done.js rename test/inspector/{test-inspector-stops-no-file.js => test-stops-no-file.js} (100%) diff --git a/test/common/README.md b/test/common/README.md index aa3fcb3d4a41b5..59b02cf52a9a48 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -99,6 +99,15 @@ Tests whether `name` and `expected` are part of a raised warning. Checks if `pathname` exists +### fires(promise, [error], [timeoutMs]) +* promise [<Promise] +* error [<String] default = 'timeout' +* timeoutMs [<Number] default = 100 + +Returns a new promise that will propagate `promise` resolution or rejection if +that happens within the `timeoutMs` timespan, or rejects with `error` as +a reason otherwise. + ### fixturesDir * return [<String>] diff --git a/test/common/index.js b/test/common/index.js index fa5573b2e6f1e0..d274fce9cdbf93 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -819,6 +819,32 @@ function restoreWritable(name) { delete process[name].writeTimes; } +function onResolvedOrRejected(promise, callback) { + return promise.then((result) => { + callback(); + return result; + }, (error) => { + callback(); + throw error; + }); +} + +function timeoutPromise(error, timeoutMs) { + let clearCallback = null; + let done = false; + const promise = onResolvedOrRejected(new Promise((resolve, reject) => { + const timeout = setTimeout(() => reject(error), timeoutMs); + clearCallback = () => { + if (done) + return; + clearTimeout(timeout); + resolve(); + }; + }), () => done = true); + promise.clear = clearCallback; + return promise; +} + exports.hijackStdout = hijackStdWritable.bind(null, 'stdout'); exports.hijackStderr = hijackStdWritable.bind(null, 'stderr'); exports.restoreStdout = restoreWritable.bind(null, 'stdout'); @@ -832,3 +858,19 @@ exports.firstInvalidFD = function firstInvalidFD() { } catch (e) {} return fd; }; + +exports.fires = function fires(promise, error, timeoutMs) { + if (!timeoutMs && util.isNumber(error)) { + timeoutMs = error; + error = null; + } + if (!error) + error = 'timeout'; + if (!timeoutMs) + timeoutMs = 100; + const timeout = timeoutPromise(error, timeoutMs); + return Promise.race([ + onResolvedOrRejected(promise, () => timeout.clear()), + timeout + ]); +}; diff --git a/test/inspector/inspector-helper.js b/test/inspector/inspector-helper.js index dd58d2a67e5817..e9464c3679a00c 100644 --- a/test/inspector/inspector-helper.js +++ b/test/inspector/inspector-helper.js @@ -4,61 +4,61 @@ const assert = require('assert'); const fs = require('fs'); const http = require('http'); const path = require('path'); -const spawn = require('child_process').spawn; +const { spawn } = require('child_process'); const url = require('url'); +const _MAINSCRIPT = path.join(common.fixturesDir, 'loop.js'); const DEBUG = false; const TIMEOUT = 15 * 1000; -const EXPECT_ALIVE_SYMBOL = Symbol('isAlive'); -const DONT_EXPECT_RESPONSE_SYMBOL = Symbol('dontExpectResponse'); -const mainScript = path.join(common.fixturesDir, 'loop.js'); -function send(socket, message, id, callback) { - const msg = JSON.parse(JSON.stringify(message)); // Clone! - msg['id'] = id; - if (DEBUG) - console.log('[sent]', JSON.stringify(msg)); - const messageBuf = Buffer.from(JSON.stringify(msg)); - - const wsHeaderBuf = Buffer.allocUnsafe(16); - wsHeaderBuf.writeUInt8(0x81, 0); - let byte2 = 0x80; - const bodyLen = messageBuf.length; - - let maskOffset = 2; - if (bodyLen < 126) { - byte2 = 0x80 + bodyLen; - } else if (bodyLen < 65536) { - byte2 = 0xFE; - wsHeaderBuf.writeUInt16BE(bodyLen, 2); - maskOffset = 4; +function spawnChildProcess(inspectorFlags, scriptContents, scriptFile) { + const args = [].concat(inspectorFlags); + if (scriptContents) { + args.push('-e', scriptContents); } else { - byte2 = 0xFF; - wsHeaderBuf.writeUInt32BE(bodyLen, 2); - wsHeaderBuf.writeUInt32BE(0, 6); - maskOffset = 10; + args.push(scriptFile); } - wsHeaderBuf.writeUInt8(byte2, 1); - wsHeaderBuf.writeUInt32BE(0x01020408, maskOffset); + const child = spawn(process.execPath, args); - for (let i = 0; i < messageBuf.length; i++) - messageBuf[i] = messageBuf[i] ^ (1 << (i % 4)); - socket.write( - Buffer.concat([wsHeaderBuf.slice(0, maskOffset + 4), messageBuf]), - callback); + const handler = tearDown.bind(null, child); + process.on('exit', handler); + process.on('uncaughtException', handler); + process.on('unhandledRejection', handler); + process.on('SIGINT', handler); + + return child; } -function sendEnd(socket) { - socket.write(Buffer.from([0x88, 0x80, 0x2D, 0x0E, 0x1E, 0xFA])); +function makeBufferingDataCallback(dataCallback) { + let buffer = Buffer.alloc(0); + return (data) => { + const newData = Buffer.concat([buffer, data]); + const str = newData.toString('utf8'); + const lines = str.replace(/\r/g, '').split('\n'); + if (str.endsWith('\n')) + buffer = Buffer.alloc(0); + else + buffer = Buffer.from(lines.pop(), 'utf8'); + for (const line of lines) + dataCallback(line); + }; +} + +function tearDown(child, err) { + child.kill(); + if (err) { + console.error(err); + process.exit(1); + } } -function parseWSFrame(buffer, handler) { +function parseWSFrame(buffer) { // Protocol described in https://tools.ietf.org/html/rfc6455#section-5 + let message = null; if (buffer.length < 2) - return 0; + return { length: 0, message }; if (buffer[0] === 0x88 && buffer[1] === 0x00) { - handler(null); - return 2; + return { length: 2, message, closed: true }; } assert.strictEqual(0x81, buffer[0]); let dataLen = 0x7F & buffer[1]; @@ -74,10 +74,9 @@ function parseWSFrame(buffer, handler) { bodyOffset = 10; } if (buffer.length < bodyOffset + dataLen) - return 0; + return { length: 0, message }; const jsonPayload = buffer.slice(bodyOffset, bodyOffset + dataLen).toString('utf8'); - let message; try { message = JSON.parse(jsonPayload); } catch (e) { @@ -86,470 +85,326 @@ function parseWSFrame(buffer, handler) { } if (DEBUG) console.log('[received]', JSON.stringify(message)); - handler(message); - return bodyOffset + dataLen; + return { length: bodyOffset + dataLen, message }; } -function tearDown(child, err) { - child.kill(); - if (err instanceof Error) { - console.error(err.stack); - process.exit(1); - } -} +function formatWSFrame(message) { + const messageBuf = Buffer.from(JSON.stringify(message)); -function checkHttpResponse(host, port, path, callback, errorcb) { - const req = http.get({host, port, path}, function(res) { - let response = ''; - res.setEncoding('utf8'); - res - .on('data', (data) => response += data.toString()) - .on('end', () => { - let err = null; - let json = undefined; - try { - json = JSON.parse(response); - } catch (e) { - err = e; - err.response = response; - } - callback(err, json); - }); - }); - if (errorcb) - req.on('error', errorcb); -} - -function makeBufferingDataCallback(dataCallback) { - let buffer = Buffer.alloc(0); - return (data) => { - const newData = Buffer.concat([buffer, data]); - const str = newData.toString('utf8'); - const lines = str.split('\n'); - if (str.endsWith('\n')) - buffer = Buffer.alloc(0); - else - buffer = Buffer.from(lines.pop(), 'utf8'); - for (const line of lines) - dataCallback(line); - }; -} + const wsHeaderBuf = Buffer.allocUnsafe(16); + wsHeaderBuf.writeUInt8(0x81, 0); + let byte2 = 0x80; + const bodyLen = messageBuf.length; -function timeout(message, multiplicator) { - return setTimeout(common.mustNotCall(message), - TIMEOUT * (multiplicator || 1)); -} + let maskOffset = 2; + if (bodyLen < 126) { + byte2 = 0x80 + bodyLen; + } else if (bodyLen < 65536) { + byte2 = 0xFE; + wsHeaderBuf.writeUInt16BE(bodyLen, 2); + maskOffset = 4; + } else { + byte2 = 0xFF; + wsHeaderBuf.writeUInt32BE(bodyLen, 2); + wsHeaderBuf.writeUInt32BE(0, 6); + maskOffset = 10; + } + wsHeaderBuf.writeUInt8(byte2, 1); + wsHeaderBuf.writeUInt32BE(0x01020408, maskOffset); -function TestSession(socket, harness) { - this.mainScriptPath = harness.mainScriptPath; - this.mainScriptId = null; - - this.harness_ = harness; - this.socket_ = socket; - this.expectClose_ = false; - this.scripts_ = {}; - this.messagefilter_ = null; - this.responseCheckers_ = {}; - this.lastId_ = 0; - this.messages_ = {}; - this.expectedId_ = 1; - this.lastMessageResponseCallback_ = null; - this.closeCallback_ = null; + for (let i = 0; i < messageBuf.length; i++) + messageBuf[i] = messageBuf[i] ^ (1 << (i % 4)); - let buffer = Buffer.alloc(0); - socket.on('data', (data) => { - buffer = Buffer.concat([buffer, data]); - let consumed; - do { - consumed = parseWSFrame(buffer, this.processMessage_.bind(this)); - if (consumed) - buffer = buffer.slice(consumed); - } while (consumed); - }).on('close', () => { - assert(this.expectClose_, 'Socket closed prematurely'); - this.closeCallback_ && this.closeCallback_(); - }); + return Buffer.concat([wsHeaderBuf.slice(0, maskOffset + 4), messageBuf]); } -TestSession.prototype.scriptUrlForId = function(id) { - return this.scripts_[id]; -}; - -TestSession.prototype.processMessage_ = function(message) { - if (message === null) { - sendEnd(this.socket_); - return; +class InspectorSession { + constructor(socket, instance) { + this._instance = instance; + this._socket = socket; + this._nextId = 1; + this._commandResponsePromises = new Map(); + this._unprocessedNotifications = []; + this._notificationCallback = null; + this._scriptsIdsByUrl = new Map(); + + let buffer = Buffer.alloc(0); + socket.on('data', (data) => { + buffer = Buffer.concat([buffer, data]); + do { + const { length, message, closed } = parseWSFrame(buffer); + if (!length) + break; + + if (closed) { + socket.write(Buffer.from([0x88, 0x00])); // WS close frame + } + buffer = buffer.slice(length); + if (message) + this._onMessage(message); + } while (true); + }); + this._terminationPromise = new Promise((resolve) => { + socket.once('close', resolve); + }); } - const method = message['method']; - if (method === 'Debugger.scriptParsed') { - const script = message['params']; - const scriptId = script['scriptId']; - const url = script['url']; - this.scripts_[scriptId] = url; - if (url === mainScript) - this.mainScriptId = scriptId; + waitForServerDisconnect() { + return this._terminationPromise; } - this.messagefilter_ && this.messagefilter_(message); - const id = message['id']; - if (id) { - this.expectedId_++; - if (this.responseCheckers_[id]) { - const messageJSON = JSON.stringify(message); - const idJSON = JSON.stringify(this.messages_[id]); - assert(message['result'], `${messageJSON} (response to ${idJSON})`); - this.responseCheckers_[id](message['result']); - delete this.responseCheckers_[id]; - } - const messageJSON = JSON.stringify(message); - const idJSON = JSON.stringify(this.messages_[id]); - assert(!message['error'], `${messageJSON} (replying to ${idJSON})`); - delete this.messages_[id]; - if (id === this.lastId_) { - this.lastMessageResponseCallback_ && this.lastMessageResponseCallback_(); - this.lastMessageResponseCallback_ = null; - } + + disconnect() { + this._socket.destroy(); } -}; -TestSession.prototype.sendAll_ = function(commands, callback) { - if (!commands.length) { - callback(); - } else { - let id = ++this.lastId_; - let command = commands[0]; - if (command instanceof Array) { - this.responseCheckers_[id] = command[1]; - command = command[0]; - } - if (command instanceof Function) - command = command(); - if (!command[DONT_EXPECT_RESPONSE_SYMBOL]) { - this.messages_[id] = command; + _onMessage(message) { + if (message.id) { + const { resolve, reject } = this._commandResponsePromises.get(message.id); + this._commandResponsePromises.delete(message.id); + if (message.result) + resolve(message.result); + else + reject(message.error); } else { - id += 100000; - this.lastId_--; + if (message.method === 'Debugger.scriptParsed') { + const script = message['params']; + const scriptId = script['scriptId']; + const url = script['url']; + this._scriptsIdsByUrl.set(scriptId, url); + if (url === _MAINSCRIPT) + this.mainScriptId = scriptId; + } + + if (this._notificationCallback) { + // In case callback needs to install another + const callback = this._notificationCallback; + this._notificationCallback = null; + callback(message); + } else { + this._unprocessedNotifications.push(message); + } } - send(this.socket_, command, id, - () => this.sendAll_(commands.slice(1), callback)); } -}; -TestSession.prototype.sendInspectorCommands = function(commands) { - if (!(commands instanceof Array)) - commands = [commands]; - return this.enqueue((callback) => { - let timeoutId = null; - this.lastMessageResponseCallback_ = () => { - timeoutId && clearTimeout(timeoutId); - callback(); - }; - this.sendAll_(commands, () => { - timeoutId = setTimeout(() => { - assert.fail(`Messages without response: ${ - Object.keys(this.messages_).join(', ')}`); - }, TIMEOUT); - }); - }); -}; + _sendMessage(message) { + const msg = JSON.parse(JSON.stringify(message)); // Clone! + msg['id'] = this._nextId++; + if (DEBUG) + console.log('[sent]', JSON.stringify(msg)); -TestSession.prototype.sendCommandsAndExpectClose = function(commands) { - if (!(commands instanceof Array)) - commands = [commands]; - return this.enqueue((callback) => { - let timeoutId = null; - let done = false; - this.expectClose_ = true; - this.closeCallback_ = function() { - if (timeoutId) - clearTimeout(timeoutId); - done = true; - callback(); - }; - this.sendAll_(commands, () => { - if (!done) { - timeoutId = timeout('Session still open'); - } + const responsePromise = new Promise((resolve, reject) => { + this._commandResponsePromises.set(msg['id'], { resolve, reject }); }); - }); -}; -TestSession.prototype.createCallbackWithTimeout_ = function(message) { - const promise = new Promise((resolve) => { - this.enqueue((callback) => { - const timeoutId = timeout(message); - resolve(() => { - clearTimeout(timeoutId); - callback(); - }); - }); - }); - return () => promise.then((callback) => callback()); -}; + return new Promise( + (resolve) => this._socket.write(formatWSFrame(msg), resolve)) + .then(() => responsePromise); + } -TestSession.prototype.expectMessages = function(expects) { - if (!(expects instanceof Array)) expects = [ expects ]; - - const callback = this.createCallbackWithTimeout_( - `Matching response was not received:\n${expects[0]}`); - this.messagefilter_ = (message) => { - if (expects[0](message)) - expects.shift(); - if (!expects.length) { - this.messagefilter_ = null; - callback(); + send(commands) { + if (Array.isArray(commands)) { + // Multiple commands means the response does not matter. There might even + // never be a response. + return Promise + .all(commands.map((command) => this._sendMessage(command))) + .then(() => {}); + } else { + return this._sendMessage(commands); } - }; - return this; -}; - -TestSession.prototype.expectStderrOutput = function(regexp) { - this.harness_.addStderrFilter( - regexp, - this.createCallbackWithTimeout_(`Timed out waiting for ${regexp}`)); - return this; -}; - -TestSession.prototype.runNext_ = function() { - if (this.task_) { - setImmediate(() => { - this.task_(() => { - this.task_ = this.task_.next_; - this.runNext_(); - }); - }); } -}; -TestSession.prototype.enqueue = function(task) { - if (!this.task_) { - this.task_ = task; - this.runNext_(); - } else { - let t = this.task_; - while (t.next_) - t = t.next_; - t.next_ = task; + waitForNotification(methodOrPredicate, description) { + const desc = description || methodOrPredicate; + const message = `Timed out waiting for matching notification (${desc}))`; + return common.fires( + this._asyncWaitForNotification(methodOrPredicate), message, TIMEOUT); } - return this; -}; - -TestSession.prototype.disconnect = function(childDone) { - return this.enqueue((callback) => { - this.expectClose_ = true; - this.socket_.destroy(); - console.log('[test]', 'Connection terminated'); - callback(); - }, childDone); -}; - -TestSession.prototype.expectClose = function() { - return this.enqueue((callback) => { - this.expectClose_ = true; - callback(); - }); -}; - -TestSession.prototype.assertClosed = function() { - return this.enqueue((callback) => { - assert.strictEqual(this.closed_, true); - callback(); - }); -}; - -TestSession.prototype.testHttpResponse = function(path, check) { - return this.enqueue((callback) => - checkHttpResponse(null, this.harness_.port, path, (err, response) => { - check.call(this, err, response); - callback(); - })); -}; + async _asyncWaitForNotification(methodOrPredicate) { + function matchMethod(notification) { + return notification.method === methodOrPredicate; + } + const predicate = + typeof methodOrPredicate === 'string' ? matchMethod : methodOrPredicate; + let notification = null; + do { + if (this._unprocessedNotifications.length) { + notification = this._unprocessedNotifications.shift(); + } else { + notification = await new Promise( + (resolve) => this._notificationCallback = resolve); + } + } while (!predicate(notification)); + return notification; + } -function Harness(port, childProcess) { - this.port = port; - this.mainScriptPath = mainScript; - this.stderrFilters_ = []; - this.process_ = childProcess; - this.result_ = {}; - this.running_ = true; - - childProcess.stdout.on('data', makeBufferingDataCallback( - (line) => console.log('[out]', line))); - - - childProcess.stderr.on('data', makeBufferingDataCallback((message) => { - const pending = []; - console.log('[err]', message); - for (const filter of this.stderrFilters_) - if (!filter(message)) pending.push(filter); - this.stderrFilters_ = pending; - })); - childProcess.on('exit', (code, signal) => { - this.result_ = {code, signal}; - this.running_ = false; - }); -} - -Harness.prototype.addStderrFilter = function(regexp, callback) { - this.stderrFilters_.push((message) => { - if (message.match(regexp)) { - callback(); + _isBreakOnLineNotification(message, line, url) { + if ('Debugger.paused' === message['method']) { + const callFrame = message['params']['callFrames'][0]; + const location = callFrame['location']; + assert.strictEqual(url, this._scriptsIdsByUrl.get(location['scriptId'])); + assert.strictEqual(line, location['lineNumber']); return true; } - }); -}; - -Harness.prototype.assertStillAlive = function() { - assert.strictEqual(this.running_, true, - `Child died: ${JSON.stringify(this.result_)}`); -}; - -Harness.prototype.run_ = function() { - setImmediate(() => { - if (!this.task_[EXPECT_ALIVE_SYMBOL]) - this.assertStillAlive(); - this.task_(() => { - this.task_ = this.task_.next_; - if (this.task_) - this.run_(); - }); - }); -}; + } -Harness.prototype.enqueue_ = function(task, expectAlive) { - task[EXPECT_ALIVE_SYMBOL] = !!expectAlive; - if (!this.task_) { - this.task_ = task; - this.run_(); - } else { - let chain = this.task_; - while (chain.next_) - chain = chain.next_; - chain.next_ = task; + waitForBreakOnLine(line, url) { + return this + .waitForNotification( + (notification) => + this._isBreakOnLineNotification(notification, line, url), + `break on ${url}:${line}`) + .then((notification) => + notification.params.callFrames[0].scopeChain[0].object.objectId); } - return this; -}; -Harness.prototype.testHttpResponse = function(host, path, check, errorcb) { - return this.enqueue_((doneCallback) => { - function wrap(callback) { - if (callback) { - return function() { - callback(...arguments); - doneCallback(); - }; + _matchesConsoleOutputNotification(notification, type, values) { + if (!Array.isArray(values)) + values = [ values ]; + if ('Runtime.consoleAPICalled' === notification['method']) { + const params = notification['params']; + if (params['type'] === type) { + let i = 0; + for (const value of params['args']) { + if (value['value'] !== values[i++]) + return false; + } + return i === values.length; } } - checkHttpResponse(host, this.port, path, wrap(check), wrap(errorcb)); - }); -}; + } -Harness.prototype.wsHandshake = function(devtoolsUrl, tests, readyCallback) { - http.get({ - port: this.port, - path: url.parse(devtoolsUrl).path, - headers: { - 'Connection': 'Upgrade', - 'Upgrade': 'websocket', - 'Sec-WebSocket-Version': 13, - 'Sec-WebSocket-Key': 'key==' - } - }).on('upgrade', (message, socket) => { - const session = new TestSession(socket, this); - if (!(tests instanceof Array)) - tests = [tests]; - function enqueue(tests) { - session.enqueue((sessionCb) => { - if (tests.length) { - tests[0](session); - session.enqueue((cb2) => { - enqueue(tests.slice(1)); - cb2(); - }); - } else { - readyCallback(); - } - sessionCb(); - }); - } - enqueue(tests); - }).on('response', common.mustNotCall('Upgrade was not received')); -}; + waitForConsoleOutput(type, values) { + const desc = `Console output matching ${JSON.stringify(values)}`; + return this.waitForNotification( + (notification) => this._matchesConsoleOutputNotification(notification, + type, values), + desc); + } -Harness.prototype.runFrontendSession = function(tests) { - return this.enqueue_((callback) => { - checkHttpResponse(null, this.port, '/json/list', (err, response) => { - assert.ifError(err); - this.wsHandshake(response[0]['webSocketDebuggerUrl'], tests, callback); + async runToCompletion() { + console.log('[test]', 'Verify node waits for the frontend to disconnect'); + await this.send({ 'method': 'Debugger.resume' }); + await this.waitForNotification((notification) => { + return notification.method === 'Runtime.executionContextDestroyed' && + notification.params.executionContextId === 1; }); - }); -}; + while ((await this._instance.nextStderrString()) !== + 'Waiting for the debugger to disconnect...'); + await this.disconnect(); + } +} -Harness.prototype.expectShutDown = function(errorCode) { - this.enqueue_((callback) => { - if (this.running_) { - const timeoutId = timeout('Have not terminated'); - this.process_.on('exit', (code, signal) => { - clearTimeout(timeoutId); - assert.strictEqual(errorCode, code, JSON.stringify({code, signal})); - callback(); +class NodeInstance { + constructor(inspectorFlags = ['--inspect-brk=0'], + scriptContents = '', + scriptFile = _MAINSCRIPT) { + this._portCallback = null; + this.portPromise = new Promise((resolve) => this._portCallback = resolve); + this._process = spawnChildProcess(inspectorFlags, scriptContents, + scriptFile); + this._running = true; + this._stderrLineCallback = null; + this._unprocessedStderrLines = []; + + this._process.stdout.on('data', makeBufferingDataCallback( + (line) => console.log('[out]', line))); + + this._process.stderr.on('data', makeBufferingDataCallback( + (message) => this.onStderrLine(message))); + + this._shutdownPromise = new Promise((resolve) => { + this._process.once('exit', (exitCode, signal) => { + resolve({ exitCode, signal }); + this._running = false; }); + }); + } + + onStderrLine(line) { + console.log('[err]', line); + if (this._portCallback) { + const matches = line.match(/Debugger listening on ws:\/\/.+:(\d+)\/.+/); + if (matches) + this._portCallback(matches[1]); + this._portCallback = null; + } + if (this._stderrLineCallback) { + this._stderrLineCallback(line); + this._stderrLineCallback = null; } else { - assert.strictEqual(errorCode, this.result_.code); - callback(); + this._unprocessedStderrLines.push(line); } - }, true); -}; - -Harness.prototype.kill = function() { - return this.enqueue_((callback) => { - this.process_.kill(); - callback(); - }); -}; - -exports.startNodeForInspectorTest = function(callback, - inspectorFlags = ['--inspect-brk'], - scriptContents = '', - scriptFile = mainScript) { - const args = [].concat(inspectorFlags); - if (scriptContents) { - args.push('-e', scriptContents); - } else { - args.push(scriptFile); } - const child = spawn(process.execPath, args); - - const timeoutId = timeout('Child process did not start properly', 4); + httpGet(host, path) { + console.log('[test]', `Testing ${path}`); + return this.portPromise.then((port) => new Promise((resolve, reject) => { + const req = http.get({ host, port, path }, (res) => { + let response = ''; + res.setEncoding('utf8'); + res + .on('data', (data) => response += data.toString()) + .on('end', () => { + resolve(response); + }); + }); + req.on('error', reject); + })).then((response) => { + try { + return JSON.parse(response); + } catch (e) { + e.body = response; + throw e; + } + }); + } - let found = false; + wsHandshake(devtoolsUrl) { + return this.portPromise.then((port) => new Promise((resolve) => { + http.get({ + port, + path: url.parse(devtoolsUrl).path, + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Sec-WebSocket-Version': 13, + 'Sec-WebSocket-Key': 'key==' + } + }).on('upgrade', (message, socket) => { + resolve(new InspectorSession(socket, this)); + }).on('response', common.mustNotCall('Upgrade was not received')); + })); + } - const dataCallback = makeBufferingDataCallback((text) => { - clearTimeout(timeoutId); - console.log('[err]', text); - if (found) return; - const match = text.match(/Debugger listening on ws:\/\/.+:(\d+)\/.+/); - found = true; - child.stderr.removeListener('data', dataCallback); - assert.ok(match, text); - callback(new Harness(match[1], child)); - }); + async connectInspectorSession() { + console.log('[test]', 'Connecting to a child Node process'); + const response = await this.httpGet(null, '/json/list'); + const url = response[0]['webSocketDebuggerUrl']; + return await this.wsHandshake(url); + } - child.stderr.on('data', dataCallback); + expectShutdown() { + return this._shutdownPromise; + } - const handler = tearDown.bind(null, child); + nextStderrString() { + if (this._unprocessedStderrLines.length) + return Promise.resolve(this._unprocessedStderrLines.shift()); + return new Promise((resolve) => this._stderrLineCallback = resolve); + } - process.on('exit', handler); - process.on('uncaughtException', handler); - process.on('SIGINT', handler); -}; + kill() { + this._process.kill(); + } +} -exports.mainScriptSource = function() { - return fs.readFileSync(mainScript, 'utf8'); -}; +function readMainScriptSource() { + return fs.readFileSync(_MAINSCRIPT, 'utf8'); +} -exports.markMessageNoResponse = function(message) { - message[DONT_EXPECT_RESPONSE_SYMBOL] = true; +module.exports = { + mainScriptPath: _MAINSCRIPT, + readMainScriptSource, + NodeInstance }; diff --git a/test/inspector/test-break-when-eval.js b/test/inspector/test-break-when-eval.js new file mode 100644 index 00000000000000..ddd8220bb92e5f --- /dev/null +++ b/test/inspector/test-break-when-eval.js @@ -0,0 +1,68 @@ +'use strict'; +const common = require('../common'); +common.skipIfInspectorDisabled(); +const assert = require('assert'); +const { NodeInstance } = require('./inspector-helper.js'); +const path = require('path'); + +const script = path.join(path.dirname(module.filename), 'global-function.js'); + +async function setupDebugger(session) { + console.log('[test]', 'Setting up a debugger'); + const commands = [ + { 'method': 'Runtime.enable' }, + { 'method': 'Debugger.enable' }, + { 'method': 'Debugger.setAsyncCallStackDepth', + 'params': { 'maxDepth': 0 } }, + { 'method': 'Runtime.runIfWaitingForDebugger' }, + ]; + session.send(commands); + await session.waitForNotification('Runtime.consoleAPICalled'); +} + +async function breakOnLine(session) { + console.log('[test]', 'Breaking in the code'); + const commands = [ + { 'method': 'Debugger.setBreakpointByUrl', + 'params': { 'lineNumber': 9, + 'url': script, + 'columnNumber': 0, + 'condition': '' + } + }, + { 'method': 'Runtime.evaluate', + 'params': { 'expression': 'sum()', + 'objectGroup': 'console', + 'includeCommandLineAPI': true, + 'silent': false, + 'contextId': 1, + 'returnByValue': false, + 'generatePreview': true, + 'userGesture': true, + 'awaitPromise': false + } + } + ]; + session.send(commands); + await session.waitForBreakOnLine(9, script); +} + +async function stepOverConsoleStatement(session) { + console.log('[test]', 'Step over console statement and test output'); + session.send({ 'method': 'Debugger.stepOver' }); + await session.waitForConsoleOutput('log', [0, 3]); + await session.waitForNotification('Debugger.paused'); +} + +async function runTests() { + const child = new NodeInstance(['--inspect=0'], undefined, script); + const session = await child.connectInspectorSession(); + await setupDebugger(session); + await breakOnLine(session); + await stepOverConsoleStatement(session); + await session.runToCompletion(); + assert.strictEqual(0, (await child.expectShutdown()).exitCode); +} + +common.crashOnUnhandledRejection(); +runTests(); diff --git a/test/inspector/test-debug-brk-flag.js b/test/inspector/test-debug-brk-flag.js new file mode 100644 index 00000000000000..f0a4d976028f09 --- /dev/null +++ b/test/inspector/test-debug-brk-flag.js @@ -0,0 +1,41 @@ +'use strict'; +const common = require('../common'); + +common.skipIfInspectorDisabled(); + +const assert = require('assert'); +const { mainScriptPath, + NodeInstance } = require('./inspector-helper.js'); + +async function testBreakpointOnStart(session) { + const commands = [ + { 'method': 'Runtime.enable' }, + { 'method': 'Debugger.enable' }, + { 'method': 'Debugger.setPauseOnExceptions', + 'params': { 'state': 'none' } }, + { 'method': 'Debugger.setAsyncCallStackDepth', + 'params': { 'maxDepth': 0 } }, + { 'method': 'Profiler.enable' }, + { 'method': 'Profiler.setSamplingInterval', + 'params': { 'interval': 100 } }, + { 'method': 'Debugger.setBlackboxPatterns', + 'params': { 'patterns': [] } }, + { 'method': 'Runtime.runIfWaitingForDebugger' } + ]; + + session.send(commands); + await session.waitForBreakOnLine(0, mainScriptPath); +} + +async function runTests() { + const child = new NodeInstance(['--inspect', '--debug-brk']); + const session = await child.connectInspectorSession(); + + await testBreakpointOnStart(session); + await session.runToCompletion(); + + assert.strictEqual(55, (await child.expectShutdown()).exitCode); +} + +common.crashOnUnhandledRejection(); +runTests(); diff --git a/test/inspector/test-debug-end.js b/test/inspector/test-debug-end.js new file mode 100644 index 00000000000000..57ce0190838504 --- /dev/null +++ b/test/inspector/test-debug-end.js @@ -0,0 +1,46 @@ +'use strict'; +const common = require('../common'); +common.skipIfInspectorDisabled(); +const { strictEqual } = require('assert'); +const { NodeInstance } = require('./inspector-helper.js'); + +async function testNoServerNoCrash() { + console.log('Test there\'s no crash stopping server that was not started'); + const instance = new NodeInstance([], + `process._debugEnd(); + process.exit(42);`); + strictEqual(42, (await instance.expectShutdown()).exitCode); +} + +async function testNoSessionNoCrash() { + console.log('Test there\'s no crash stopping server without connecting'); + const instance = new NodeInstance('--inspect=0', + 'process._debugEnd();process.exit(42);'); + strictEqual(42, (await instance.expectShutdown()).exitCode); +} + +async function testSessionNoCrash() { + console.log('Test there\'s no crash stopping server after connecting'); + const script = `process._debugEnd(); + process._debugProcess(process.pid); + setTimeout(() => { + console.log("Done"); + process.exit(42); + });`; + + const instance = new NodeInstance('--inspect-brk=0', script); + const session = await instance.connectInspectorSession(); + await session.send({ 'method': 'Runtime.runIfWaitingForDebugger' }); + await session.waitForServerDisconnect(); + strictEqual(42, (await instance.expectShutdown()).exitCode); +} + +async function runTest() { + await testNoServerNoCrash(); + await testNoSessionNoCrash(); + await testSessionNoCrash(); +} + +common.crashOnUnhandledRejection(); + +runTest(); diff --git a/test/inspector/test-exception.js b/test/inspector/test-exception.js new file mode 100644 index 00000000000000..ca3994c0a0005f --- /dev/null +++ b/test/inspector/test-exception.js @@ -0,0 +1,45 @@ +'use strict'; +const common = require('../common'); + +common.skipIfInspectorDisabled(); + +const assert = require('assert'); +const { NodeInstance } = require('./inspector-helper.js'); +const path = require('path'); + +const script = path.join(common.fixturesDir, 'throws_error.js'); + +async function testBreakpointOnStart(session) { + console.log('[test]', + 'Verifying debugger stops on start (--inspect-brk option)'); + const commands = [ + { 'method': 'Runtime.enable' }, + { 'method': 'Debugger.enable' }, + { 'method': 'Debugger.setPauseOnExceptions', + 'params': { 'state': 'none' } }, + { 'method': 'Debugger.setAsyncCallStackDepth', + 'params': { 'maxDepth': 0 } }, + { 'method': 'Profiler.enable' }, + { 'method': 'Profiler.setSamplingInterval', + 'params': { 'interval': 100 } }, + { 'method': 'Debugger.setBlackboxPatterns', + 'params': { 'patterns': [] } }, + { 'method': 'Runtime.runIfWaitingForDebugger' } + ]; + + await session.send(commands); + await session.waitForBreakOnLine(0, script); +} + + +async function runTest() { + const child = new NodeInstance(undefined, undefined, script); + const session = await child.connectInspectorSession(); + await testBreakpointOnStart(session); + await session.runToCompletion(); + assert.strictEqual(1, (await child.expectShutdown()).exitCode); +} + +common.crashOnUnhandledRejection(); + +runTest(); diff --git a/test/inspector/test-inspector-break-when-eval.js b/test/inspector/test-inspector-break-when-eval.js deleted file mode 100644 index 388edf6f5c21a7..00000000000000 --- a/test/inspector/test-inspector-break-when-eval.js +++ /dev/null @@ -1,128 +0,0 @@ -'use strict'; -const common = require('../common'); -common.skipIfInspectorDisabled(); -const assert = require('assert'); -const helper = require('./inspector-helper.js'); -const path = require('path'); - -const script = path.join(path.dirname(module.filename), 'global-function.js'); - - -function setupExpectBreakOnLine(line, url, session) { - return function(message) { - if ('Debugger.paused' === message['method']) { - const callFrame = message['params']['callFrames'][0]; - const location = callFrame['location']; - assert.strictEqual(url, session.scriptUrlForId(location['scriptId'])); - assert.strictEqual(line, location['lineNumber']); - return true; - } - }; -} - -function setupExpectConsoleOutputAndBreak(type, values) { - if (!(values instanceof Array)) - values = [ values ]; - let consoleLog = false; - function matchConsoleLog(message) { - if ('Runtime.consoleAPICalled' === message['method']) { - const params = message['params']; - if (params['type'] === type) { - let i = 0; - for (const value of params['args']) { - if (value['value'] !== values[i++]) - return false; - } - return i === values.length; - } - } - } - - return function(message) { - if (consoleLog) - return message['method'] === 'Debugger.paused'; - consoleLog = matchConsoleLog(message); - return false; - }; -} - -function setupExpectContextDestroyed(id) { - return function(message) { - if ('Runtime.executionContextDestroyed' === message['method']) - return message['params']['executionContextId'] === id; - }; -} - -function setupDebugger(session) { - console.log('[test]', 'Setting up a debugger'); - const commands = [ - { 'method': 'Runtime.enable' }, - { 'method': 'Debugger.enable' }, - { 'method': 'Debugger.setAsyncCallStackDepth', - 'params': {'maxDepth': 0} }, - { 'method': 'Runtime.runIfWaitingForDebugger' }, - ]; - - session - .sendInspectorCommands(commands) - .expectMessages((message) => 'Runtime.consoleAPICalled' === message.method); -} - -function breakOnLine(session) { - console.log('[test]', 'Breaking in the code'); - const commands = [ - { 'method': 'Debugger.setBreakpointByUrl', - 'params': { 'lineNumber': 9, - 'url': script, - 'columnNumber': 0, - 'condition': '' - } - }, - { 'method': 'Runtime.evaluate', - 'params': { 'expression': 'sum()', - 'objectGroup': 'console', - 'includeCommandLineAPI': true, - 'silent': false, - 'contextId': 1, - 'returnByValue': false, - 'generatePreview': true, - 'userGesture': true, - 'awaitPromise': false - } - } - ]; - helper.markMessageNoResponse(commands[1]); - session - .sendInspectorCommands(commands) - .expectMessages(setupExpectBreakOnLine(9, script, session)); -} - -function stepOverConsoleStatement(session) { - console.log('[test]', 'Step over console statement and test output'); - session - .sendInspectorCommands({ 'method': 'Debugger.stepOver' }) - .expectMessages(setupExpectConsoleOutputAndBreak('log', [0, 3])); -} - -function testWaitsForFrontendDisconnect(session, harness) { - console.log('[test]', 'Verify node waits for the frontend to disconnect'); - session.sendInspectorCommands({ 'method': 'Debugger.resume'}) - .expectMessages(setupExpectContextDestroyed(1)) - .expectStderrOutput('Waiting for the debugger to disconnect...') - .disconnect(true); -} - -function runTests(harness) { - harness - .runFrontendSession([ - setupDebugger, - breakOnLine, - stepOverConsoleStatement, - testWaitsForFrontendDisconnect - ]).expectShutDown(0); -} - -helper.startNodeForInspectorTest(runTests, - ['--inspect'], - undefined, - script); diff --git a/test/inspector/test-inspector-debug-brk.js b/test/inspector/test-inspector-debug-brk.js deleted file mode 100644 index 27c9c5fdbc328d..00000000000000 --- a/test/inspector/test-inspector-debug-brk.js +++ /dev/null @@ -1,59 +0,0 @@ -'use strict'; -const common = require('../common'); - -common.skipIfInspectorDisabled(); - -const assert = require('assert'); -const helper = require('./inspector-helper.js'); - -function setupExpectBreakOnLine(line, url, session, scopeIdCallback) { - return function(message) { - if ('Debugger.paused' === message['method']) { - const callFrame = message['params']['callFrames'][0]; - const location = callFrame['location']; - assert.strictEqual(url, session.scriptUrlForId(location['scriptId'])); - assert.strictEqual(line, location['lineNumber']); - scopeIdCallback && - scopeIdCallback(callFrame['scopeChain'][0]['object']['objectId']); - return true; - } - }; -} - -function testBreakpointOnStart(session) { - const commands = [ - { 'method': 'Runtime.enable' }, - { 'method': 'Debugger.enable' }, - { 'method': 'Debugger.setPauseOnExceptions', - 'params': {'state': 'none'} }, - { 'method': 'Debugger.setAsyncCallStackDepth', - 'params': {'maxDepth': 0} }, - { 'method': 'Profiler.enable' }, - { 'method': 'Profiler.setSamplingInterval', - 'params': {'interval': 100} }, - { 'method': 'Debugger.setBlackboxPatterns', - 'params': {'patterns': []} }, - { 'method': 'Runtime.runIfWaitingForDebugger' } - ]; - - session - .sendInspectorCommands(commands) - .expectMessages(setupExpectBreakOnLine(0, session.mainScriptPath, session)); -} - -function testWaitsForFrontendDisconnect(session, harness) { - console.log('[test]', 'Verify node waits for the frontend to disconnect'); - session.sendInspectorCommands({ 'method': 'Debugger.resume'}) - .expectStderrOutput('Waiting for the debugger to disconnect...') - .disconnect(true); -} - -function runTests(harness) { - harness - .runFrontendSession([ - testBreakpointOnStart, - testWaitsForFrontendDisconnect - ]).expectShutDown(55); -} - -helper.startNodeForInspectorTest(runTests, ['--inspect', '--debug-brk']); diff --git a/test/inspector/test-inspector-exception.js b/test/inspector/test-inspector-exception.js deleted file mode 100644 index 3f470c94c18f1d..00000000000000 --- a/test/inspector/test-inspector-exception.js +++ /dev/null @@ -1,64 +0,0 @@ -'use strict'; -const common = require('../common'); - -common.skipIfInspectorDisabled(); - -const assert = require('assert'); -const helper = require('./inspector-helper.js'); -const path = require('path'); - -const script = path.join(common.fixturesDir, 'throws_error.js'); - - -function setupExpectBreakOnLine(line, url, session) { - return function(message) { - if ('Debugger.paused' === message['method']) { - const callFrame = message['params']['callFrames'][0]; - const location = callFrame['location']; - assert.strictEqual(url, session.scriptUrlForId(location['scriptId'])); - assert.strictEqual(line, location['lineNumber']); - return true; - } - }; -} - -function testBreakpointOnStart(session) { - const commands = [ - { 'method': 'Runtime.enable' }, - { 'method': 'Debugger.enable' }, - { 'method': 'Debugger.setPauseOnExceptions', - 'params': {'state': 'none'} }, - { 'method': 'Debugger.setAsyncCallStackDepth', - 'params': {'maxDepth': 0} }, - { 'method': 'Profiler.enable' }, - { 'method': 'Profiler.setSamplingInterval', - 'params': {'interval': 100} }, - { 'method': 'Debugger.setBlackboxPatterns', - 'params': {'patterns': []} }, - { 'method': 'Runtime.runIfWaitingForDebugger' } - ]; - - session - .sendInspectorCommands(commands) - .expectMessages(setupExpectBreakOnLine(0, script, session)); -} - -function testWaitsForFrontendDisconnect(session, harness) { - console.log('[test]', 'Verify node waits for the frontend to disconnect'); - session.sendInspectorCommands({ 'method': 'Debugger.resume'}) - .expectStderrOutput('Waiting for the debugger to disconnect...') - .disconnect(true); -} - -function runTests(harness) { - harness - .runFrontendSession([ - testBreakpointOnStart, - testWaitsForFrontendDisconnect - ]).expectShutDown(1); -} - -helper.startNodeForInspectorTest(runTests, - undefined, - undefined, - script); diff --git a/test/inspector/test-inspector-ip-detection.js b/test/inspector/test-inspector-ip-detection.js deleted file mode 100644 index be5e34a977eb84..00000000000000 --- a/test/inspector/test-inspector-ip-detection.js +++ /dev/null @@ -1,51 +0,0 @@ -'use strict'; -const common = require('../common'); - -common.skipIfInspectorDisabled(); - -const assert = require('assert'); -const helper = require('./inspector-helper.js'); -const os = require('os'); - -const ip = pickIPv4Address(); - -if (!ip) - common.skip('No IP address found'); - -function checkListResponse(instance, err, response) { - assert.ifError(err); - const res = response[0]; - const wsUrl = res['webSocketDebuggerUrl']; - assert.ok(wsUrl); - const match = wsUrl.match(/^ws:\/\/(.*):9229\/(.*)/); - assert.strictEqual(ip, match[1]); - assert.strictEqual(res['id'], match[2]); - assert.strictEqual(ip, res['devtoolsFrontendUrl'].match(/.*ws=(.*):9229/)[1]); - instance.childInstanceDone = true; -} - -function checkError(instance, error) { - // Some OSes will not allow us to connect - if (error.code === 'EHOSTUNREACH') { - common.printSkipMessage('Unable to connect to self'); - } else { - throw error; - } - instance.childInstanceDone = true; -} - -function runTests(instance) { - instance - .testHttpResponse(ip, '/json/list', checkListResponse.bind(null, instance), - checkError.bind(null, instance)) - .kill(); -} - -function pickIPv4Address() { - for (const i of [].concat(...Object.values(os.networkInterfaces()))) { - if (i.family === 'IPv4' && i.address !== '127.0.0.1') - return i.address; - } -} - -helper.startNodeForInspectorTest(runTests, '--inspect-brk=0.0.0.0'); diff --git a/test/inspector/test-inspector-stop-profile-after-done.js b/test/inspector/test-inspector-stop-profile-after-done.js deleted file mode 100644 index db43e4ae79c4bd..00000000000000 --- a/test/inspector/test-inspector-stop-profile-after-done.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; -const common = require('../common'); -common.skipIfInspectorDisabled(); -const helper = require('./inspector-helper.js'); - -function test(session) { - session.sendInspectorCommands([ - { 'method': 'Runtime.runIfWaitingForDebugger' }, - { 'method': 'Profiler.setSamplingInterval', 'params': { 'interval': 100 } }, - { 'method': 'Profiler.enable' }, - { 'method': 'Profiler.start' }]); - session.expectStderrOutput('Waiting for the debugger to disconnect...'); - session.sendInspectorCommands({ 'method': 'Profiler.stop' }); - session.disconnect(true); -} - -function runTests(harness) { - harness.runFrontendSession([test]).expectShutDown(0); -} - -helper.startNodeForInspectorTest(runTests, ['--inspect-brk'], 'let a = 2;'); diff --git a/test/inspector/test-inspector.js b/test/inspector/test-inspector.js index 4b87e0263672d1..3139940451a515 100644 --- a/test/inspector/test-inspector.js +++ b/test/inspector/test-inspector.js @@ -4,12 +4,11 @@ const common = require('../common'); common.skipIfInspectorDisabled(); const assert = require('assert'); -const helper = require('./inspector-helper.js'); +const { mainScriptPath, + readMainScriptSource, + NodeInstance } = require('./inspector-helper.js'); -let scopeId; - -function checkListResponse(err, response) { - assert.ifError(err); +function checkListResponse(response) { assert.strictEqual(1, response.length); assert.ok(response[0]['devtoolsFrontendUrl']); assert.ok( @@ -17,8 +16,7 @@ function checkListResponse(err, response) { .test(response[0]['webSocketDebuggerUrl'])); } -function checkVersion(err, response) { - assert.ifError(err); +function checkVersion(response) { assert.ok(response); const expected = { 'Browser': `node.js/${process.version}`, @@ -28,10 +26,10 @@ function checkVersion(err, response) { JSON.stringify(expected)); } -function checkBadPath(err, response) { +function checkBadPath(err) { assert(err instanceof SyntaxError); - assert(/Unexpected token/.test(err.message)); - assert(/WebSockets request was expected/.test(err.response)); + assert(/Unexpected token/.test(err.message), err.message); + assert(/WebSockets request was expected/.test(err.body), err.body); } function checkException(message) { @@ -39,342 +37,267 @@ function checkException(message) { 'An exception occurred during execution'); } -function expectMainScriptSource(result) { - const expected = helper.mainScriptSource(); - const source = result['scriptSource']; - assert(source && (source.includes(expected)), - `Script source is wrong: ${source}`); -} - -function setupExpectBreakOnLine(line, url, session, scopeIdCallback) { - return function(message) { - if ('Debugger.paused' === message['method']) { - const callFrame = message['params']['callFrames'][0]; - const location = callFrame['location']; - assert.strictEqual(url, session.scriptUrlForId(location['scriptId'])); - assert.strictEqual(line, location['lineNumber']); - scopeIdCallback && - scopeIdCallback(callFrame['scopeChain'][0]['object']['objectId']); - return true; - } - }; -} - -function setupExpectConsoleOutput(type, values) { - if (!(values instanceof Array)) - values = [ values ]; - return function(message) { - if ('Runtime.consoleAPICalled' === message['method']) { - const params = message['params']; - if (params['type'] === type) { - let i = 0; - for (const value of params['args']) { - if (value['value'] !== values[i++]) - return false; - } - return i === values.length; - } - } - }; +function assertNoUrlsWhileConnected(response) { + assert.strictEqual(1, response.length); + assert.ok(!response[0].hasOwnProperty('devtoolsFrontendUrl')); + assert.ok(!response[0].hasOwnProperty('webSocketDebuggerUrl')); } -function setupExpectScopeValues(expected) { - return function(result) { - for (const actual of result['result']) { - const value = expected[actual['name']]; - if (value) - assert.strictEqual(value, actual['value']['value']); +function assertScopeValues({ result }, expected) { + const unmatched = new Set(Object.keys(expected)); + for (const actual of result) { + const value = expected[actual['name']]; + if (value) { + assert.strictEqual(value, actual['value']['value']); + unmatched.delete(actual['name']); } - }; + } + if (unmatched.size) + assert.fail(Array.from(unmatched.values())); } -function setupExpectValue(value) { - return function(result) { - assert.strictEqual(value, result['result']['value']); - }; -} - -function setupExpectContextDestroyed(id) { - return function(message) { - if ('Runtime.executionContextDestroyed' === message['method']) - return message['params']['executionContextId'] === id; - }; -} - -function testBreakpointOnStart(session) { +async function testBreakpointOnStart(session) { console.log('[test]', 'Verifying debugger stops on start (--inspect-brk option)'); const commands = [ { 'method': 'Runtime.enable' }, { 'method': 'Debugger.enable' }, { 'method': 'Debugger.setPauseOnExceptions', - 'params': {'state': 'none'} }, + 'params': { 'state': 'none' } }, { 'method': 'Debugger.setAsyncCallStackDepth', - 'params': {'maxDepth': 0} }, + 'params': { 'maxDepth': 0 } }, { 'method': 'Profiler.enable' }, { 'method': 'Profiler.setSamplingInterval', - 'params': {'interval': 100} }, + 'params': { 'interval': 100 } }, { 'method': 'Debugger.setBlackboxPatterns', - 'params': {'patterns': []} }, + 'params': { 'patterns': [] } }, { 'method': 'Runtime.runIfWaitingForDebugger' } ]; - session - .sendInspectorCommands(commands) - .expectMessages(setupExpectBreakOnLine(0, session.mainScriptPath, session)); + await session.send(commands); + await session.waitForBreakOnLine(0, mainScriptPath); } -function testSetBreakpointAndResume(session) { +async function testBreakpoint(session) { console.log('[test]', 'Setting a breakpoint and verifying it is hit'); const commands = [ { 'method': 'Debugger.setBreakpointByUrl', 'params': { 'lineNumber': 5, - 'url': session.mainScriptPath, + 'url': mainScriptPath, 'columnNumber': 0, 'condition': '' } }, - { 'method': 'Debugger.resume'}, - [ { 'method': 'Debugger.getScriptSource', - 'params': { 'scriptId': session.mainScriptId } }, - expectMainScriptSource ], + { 'method': 'Debugger.resume' }, ]; - session - .sendInspectorCommands(commands) - .expectMessages([ - setupExpectConsoleOutput('log', ['A message', 5]), - setupExpectBreakOnLine(5, session.mainScriptPath, - session, (id) => scopeId = id), - ]); -} + await session.send(commands); + const { scriptSource } = await session.send({ + 'method': 'Debugger.getScriptSource', + 'params': { 'scriptId': session.mainScriptId } }); + assert(scriptSource && (scriptSource.includes(readMainScriptSource())), + `Script source is wrong: ${scriptSource}`); + + await session.waitForConsoleOutput('log', ['A message', 5]); + const scopeId = await session.waitForBreakOnLine(5, mainScriptPath); -function testInspectScope(session) { console.log('[test]', 'Verify we can read current application state'); - session.sendInspectorCommands([ - [ - { - 'method': 'Runtime.getProperties', - 'params': { - 'objectId': scopeId, - 'ownProperties': false, - 'accessorPropertiesOnly': false, - 'generatePreview': true - } - }, setupExpectScopeValues({t: 1001, k: 1}) - ], - [ - { - 'method': 'Debugger.evaluateOnCallFrame', 'params': { - 'callFrameId': '{"ordinal":0,"injectedScriptId":1}', - 'expression': 'k + t', - 'objectGroup': 'console', - 'includeCommandLineAPI': true, - 'silent': false, - 'returnByValue': false, - 'generatePreview': true - } - }, setupExpectValue(1002) - ], - [ - { - 'method': 'Runtime.evaluate', 'params': { - 'expression': '5 * 5' - } - }, (message) => assert.strictEqual(25, message['result']['value']) - ], - ]); -} + const response = await session.send({ + 'method': 'Runtime.getProperties', + 'params': { + 'objectId': scopeId, + 'ownProperties': false, + 'accessorPropertiesOnly': false, + 'generatePreview': true + } + }); + assertScopeValues(response, { t: 1001, k: 1 }); -function testNoUrlsWhenConnected(session) { - session.testHttpResponse('/json/list', (err, response) => { - assert.ifError(err); - assert.strictEqual(1, response.length); - assert.ok(!response[0].hasOwnProperty('devtoolsFrontendUrl')); - assert.ok(!response[0].hasOwnProperty('webSocketDebuggerUrl')); + let { result } = await session.send({ + 'method': 'Debugger.evaluateOnCallFrame', 'params': { + 'callFrameId': '{"ordinal":0,"injectedScriptId":1}', + 'expression': 'k + t', + 'objectGroup': 'console', + 'includeCommandLineAPI': true, + 'silent': false, + 'returnByValue': false, + 'generatePreview': true + } }); + + assert.strictEqual(1002, result['value']); + + result = (await session.send({ + 'method': 'Runtime.evaluate', 'params': { + 'expression': '5 * 5' + } + })).result; + assert.strictEqual(25, result['value']); } -function testI18NCharacters(session) { +async function testI18NCharacters(session) { console.log('[test]', 'Verify sending and receiving UTF8 characters'); const chars = 'טֶ字и'; - session.sendInspectorCommands([ - { - 'method': 'Debugger.evaluateOnCallFrame', 'params': { - 'callFrameId': '{"ordinal":0,"injectedScriptId":1}', - 'expression': `console.log("${chars}")`, - 'objectGroup': 'console', - 'includeCommandLineAPI': true, - 'silent': false, - 'returnByValue': false, - 'generatePreview': true - } + session.send({ + 'method': 'Debugger.evaluateOnCallFrame', 'params': { + 'callFrameId': '{"ordinal":0,"injectedScriptId":1}', + 'expression': `console.log("${chars}")`, + 'objectGroup': 'console', + 'includeCommandLineAPI': true, + 'silent': false, + 'returnByValue': false, + 'generatePreview': true } - ]).expectMessages([ - setupExpectConsoleOutput('log', [chars]), - ]); + }); + await session.waitForConsoleOutput('log', [chars]); } -function testCommandLineAPI(session) { +async function testCommandLineAPI(session) { const testModulePath = require.resolve('../fixtures/empty.js'); const testModuleStr = JSON.stringify(testModulePath); const printAModulePath = require.resolve('../fixtures/printA.js'); const printAModuleStr = JSON.stringify(printAModulePath); const printBModulePath = require.resolve('../fixtures/printB.js'); const printBModuleStr = JSON.stringify(printBModulePath); - session.sendInspectorCommands([ - [ // we can use `require` outside of a callframe with require in scope - { - 'method': 'Runtime.evaluate', 'params': { - 'expression': 'typeof require("fs").readFile === "function"', - 'includeCommandLineAPI': true - } - }, (message) => { - checkException(message); - assert.strictEqual(message['result']['value'], true); + + // we can use `require` outside of a callframe with require in scope + let result = await session.send( + { + 'method': 'Runtime.evaluate', 'params': { + 'expression': 'typeof require("fs").readFile === "function"', + 'includeCommandLineAPI': true } - ], - [ // the global require has the same properties as a normal `require` - { - 'method': 'Runtime.evaluate', 'params': { - 'expression': [ - 'typeof require.resolve === "function"', - 'typeof require.extensions === "object"', - 'typeof require.cache === "object"' - ].join(' && '), - 'includeCommandLineAPI': true - } - }, (message) => { - checkException(message); - assert.strictEqual(message['result']['value'], true); + }); + checkException(result); + assert.strictEqual(result['result']['value'], true); + + // the global require has the same properties as a normal `require` + result = await session.send( + { + 'method': 'Runtime.evaluate', 'params': { + 'expression': [ + 'typeof require.resolve === "function"', + 'typeof require.extensions === "object"', + 'typeof require.cache === "object"' + ].join(' && '), + 'includeCommandLineAPI': true } - ], - [ // `require` twice returns the same value - { - 'method': 'Runtime.evaluate', 'params': { - // 1. We require the same module twice - // 2. We mutate the exports so we can compare it later on - 'expression': ` - Object.assign( - require(${testModuleStr}), - { old: 'yes' } - ) === require(${testModuleStr})`, - 'includeCommandLineAPI': true - } - }, (message) => { - checkException(message); - assert.strictEqual(message['result']['value'], true); + }); + checkException(result); + assert.strictEqual(result['result']['value'], true); + // `require` twice returns the same value + result = await session.send( + { + 'method': 'Runtime.evaluate', 'params': { + // 1. We require the same module twice + // 2. We mutate the exports so we can compare it later on + 'expression': ` + Object.assign( + require(${testModuleStr}), + { old: 'yes' } + ) === require(${testModuleStr})`, + 'includeCommandLineAPI': true } - ], - [ // after require the module appears in require.cache - { - 'method': 'Runtime.evaluate', 'params': { - 'expression': `JSON.stringify( - require.cache[${testModuleStr}].exports - )`, - 'includeCommandLineAPI': true - } - }, (message) => { - checkException(message); - assert.deepStrictEqual(JSON.parse(message['result']['value']), - { old: 'yes' }); + }); + checkException(result); + assert.strictEqual(result['result']['value'], true); + // after require the module appears in require.cache + result = await session.send( + { + 'method': 'Runtime.evaluate', 'params': { + 'expression': `JSON.stringify( + require.cache[${testModuleStr}].exports + )`, + 'includeCommandLineAPI': true } - ], - [ // remove module from require.cache - { - 'method': 'Runtime.evaluate', 'params': { - 'expression': `delete require.cache[${testModuleStr}]`, - 'includeCommandLineAPI': true - } - }, (message) => { - checkException(message); - assert.strictEqual(message['result']['value'], true); + }); + checkException(result); + assert.deepStrictEqual(JSON.parse(result['result']['value']), + { old: 'yes' }); + // remove module from require.cache + result = await session.send( + { + 'method': 'Runtime.evaluate', 'params': { + 'expression': `delete require.cache[${testModuleStr}]`, + 'includeCommandLineAPI': true } - ], - [ // require again, should get fresh (empty) exports - { - 'method': 'Runtime.evaluate', 'params': { - 'expression': `JSON.stringify(require(${testModuleStr}))`, - 'includeCommandLineAPI': true - } - }, (message) => { - checkException(message); - assert.deepStrictEqual(JSON.parse(message['result']['value']), {}); + }); + checkException(result); + assert.strictEqual(result['result']['value'], true); + // require again, should get fresh (empty) exports + result = await session.send( + { + 'method': 'Runtime.evaluate', 'params': { + 'expression': `JSON.stringify(require(${testModuleStr}))`, + 'includeCommandLineAPI': true } - ], - [ // require 2nd module, exports an empty object - { - 'method': 'Runtime.evaluate', 'params': { - 'expression': `JSON.stringify(require(${printAModuleStr}))`, - 'includeCommandLineAPI': true - } - }, (message) => { - checkException(message); - assert.deepStrictEqual(JSON.parse(message['result']['value']), {}); + }); + checkException(result); + assert.deepStrictEqual(JSON.parse(result['result']['value']), {}); + // require 2nd module, exports an empty object + result = await session.send( + { + 'method': 'Runtime.evaluate', 'params': { + 'expression': `JSON.stringify(require(${printAModuleStr}))`, + 'includeCommandLineAPI': true } - ], - [ // both modules end up with the same module.parent - { - 'method': 'Runtime.evaluate', 'params': { - 'expression': `JSON.stringify({ - parentsEqual: - require.cache[${testModuleStr}].parent === - require.cache[${printAModuleStr}].parent, - parentId: require.cache[${testModuleStr}].parent.id, - })`, - 'includeCommandLineAPI': true - } - }, (message) => { - checkException(message); - assert.deepStrictEqual(JSON.parse(message['result']['value']), { - parentsEqual: true, - parentId: '' - }); + }); + checkException(result); + assert.deepStrictEqual(JSON.parse(result['result']['value']), {}); + // both modules end up with the same module.parent + result = await session.send( + { + 'method': 'Runtime.evaluate', 'params': { + 'expression': `JSON.stringify({ + parentsEqual: + require.cache[${testModuleStr}].parent === + require.cache[${printAModuleStr}].parent, + parentId: require.cache[${testModuleStr}].parent.id, + })`, + 'includeCommandLineAPI': true } - ], - [ // the `require` in the module shadows the command line API's `require` - { - 'method': 'Debugger.evaluateOnCallFrame', 'params': { - 'callFrameId': '{"ordinal":0,"injectedScriptId":1}', - 'expression': `( - require(${printBModuleStr}), - require.cache[${printBModuleStr}].parent.id - )`, - 'includeCommandLineAPI': true - } - }, (message) => { - checkException(message); - assert.notStrictEqual(message['result']['value'], - ''); + }); + checkException(result); + assert.deepStrictEqual(JSON.parse(result['result']['value']), { + parentsEqual: true, + parentId: '' + }); + // the `require` in the module shadows the command line API's `require` + result = await session.send( + { + 'method': 'Debugger.evaluateOnCallFrame', 'params': { + 'callFrameId': '{"ordinal":0,"injectedScriptId":1}', + 'expression': `( + require(${printBModuleStr}), + require.cache[${printBModuleStr}].parent.id + )`, + 'includeCommandLineAPI': true } - ], - ]); + }); + checkException(result); + assert.notStrictEqual(result['result']['value'], + ''); } -function testWaitsForFrontendDisconnect(session, harness) { - console.log('[test]', 'Verify node waits for the frontend to disconnect'); - session.sendInspectorCommands({ 'method': 'Debugger.resume'}) - .expectMessages(setupExpectContextDestroyed(1)) - .expectStderrOutput('Waiting for the debugger to disconnect...') - .disconnect(true); -} +async function runTest() { + const child = new NodeInstance(); + checkListResponse(await child.httpGet(null, '/json')); + checkListResponse(await child.httpGet(null, '/json/list')); + checkVersion(await child.httpGet(null, '/json/version')); + + await child.httpGet(null, '/json/activate').catch(checkBadPath); + await child.httpGet(null, '/json/activate/boom').catch(checkBadPath); + await child.httpGet(null, '/json/badpath').catch(checkBadPath); -function runTests(harness) { - harness - .testHttpResponse(null, '/json', checkListResponse) - .testHttpResponse(null, '/json/list', checkListResponse) - .testHttpResponse(null, '/json/version', checkVersion) - .testHttpResponse(null, '/json/activate', checkBadPath) - .testHttpResponse(null, '/json/activate/boom', checkBadPath) - .testHttpResponse(null, '/json/badpath', checkBadPath) - .runFrontendSession([ - testNoUrlsWhenConnected, - testBreakpointOnStart, - testSetBreakpointAndResume, - testInspectScope, - testI18NCharacters, - testCommandLineAPI, - testWaitsForFrontendDisconnect - ]).expectShutDown(55); + const session = await child.connectInspectorSession(); + assertNoUrlsWhileConnected(await child.httpGet(null, '/json/list')); + await testBreakpointOnStart(session); + await testBreakpoint(session); + await testI18NCharacters(session); + await testCommandLineAPI(session); + await session.runToCompletion(); + assert.strictEqual(55, (await child.expectShutdown()).exitCode); } -helper.startNodeForInspectorTest(runTests); +common.crashOnUnhandledRejection(); + +runTest(); diff --git a/test/inspector/test-ip-detection.js b/test/inspector/test-ip-detection.js new file mode 100644 index 00000000000000..5a6a116144e7d2 --- /dev/null +++ b/test/inspector/test-ip-detection.js @@ -0,0 +1,48 @@ +'use strict'; +const common = require('../common'); + +common.skipIfInspectorDisabled(); + +const assert = require('assert'); +const { NodeInstance } = require('./inspector-helper.js'); +const os = require('os'); + +const ip = pickIPv4Address(); + +if (!ip) + common.skip('No IP address found'); + +function checkIpAddress(ip, response) { + const res = response[0]; + const wsUrl = res['webSocketDebuggerUrl']; + assert.ok(wsUrl); + const match = wsUrl.match(/^ws:\/\/(.*):\d+\/(.*)/); + assert.strictEqual(ip, match[1]); + assert.strictEqual(res['id'], match[2]); + assert.strictEqual(ip, res['devtoolsFrontendUrl'].match(/.*ws=(.*):\d+/)[1]); +} + +function pickIPv4Address() { + for (const i of [].concat(...Object.values(os.networkInterfaces()))) { + if (i.family === 'IPv4' && i.address !== '127.0.0.1') + return i.address; + } +} + +async function test() { + const instance = new NodeInstance('--inspect-brk=0.0.0.0:0'); + try { + checkIpAddress(ip, await instance.httpGet(ip, '/json/list')); + } catch (error) { + if (error.code === 'EHOSTUNREACH') { + common.printSkipMessage('Unable to connect to self'); + } else { + throw error; + } + } + instance.kill(); +} + +common.crashOnUnhandledRejection(); + +test(); diff --git a/test/inspector/test-not-blocked-on-idle.js b/test/inspector/test-not-blocked-on-idle.js index 1573e875cd4f9c..8684d6f3143387 100644 --- a/test/inspector/test-not-blocked-on-idle.js +++ b/test/inspector/test-not-blocked-on-idle.js @@ -1,21 +1,21 @@ 'use strict'; const common = require('../common'); common.skipIfInspectorDisabled(); -const helper = require('./inspector-helper.js'); +const { NodeInstance } = require('./inspector-helper.js'); -function shouldShutDown(session) { - session - .sendInspectorCommands([ - { 'method': 'Debugger.enable' }, - { 'method': 'Debugger.pause' }, - ]) - .disconnect(true); -} - -function runTests(harness) { +async function runTests() { + const script = 'setInterval(() => {debugger;}, 60000);'; + const node = new NodeInstance('--inspect=0', script); // 1 second wait to make sure the inferior began running the script - setTimeout(() => harness.runFrontendSession([shouldShutDown]).kill(), 1000); + await new Promise((resolve) => setTimeout(() => resolve(), 1000)); + const session = await node.connectInspectorSession(); + await session.send([ + { 'method': 'Debugger.enable' }, + { 'method': 'Debugger.pause' } + ]); + session.disconnect(); + node.kill(); } -const script = 'setInterval(() => {debugger;}, 60000);'; -helper.startNodeForInspectorTest(runTests, '--inspect', script); +common.crashOnUnhandledRejection(); +runTests(); diff --git a/test/inspector/test-off-no-session.js b/test/inspector/test-off-no-session.js deleted file mode 100644 index 2ec54f3651e268..00000000000000 --- a/test/inspector/test-off-no-session.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict'; -const common = require('../common'); -common.skipIfInspectorDisabled(); -const helper = require('./inspector-helper.js'); - -function testStop(harness) { - harness.expectShutDown(42); -} - -helper.startNodeForInspectorTest(testStop, '--inspect', - 'process._debugEnd();process.exit(42);'); diff --git a/test/inspector/test-off-with-session-then-on.js b/test/inspector/test-off-with-session-then-on.js deleted file mode 100644 index bd6455699d8dc0..00000000000000 --- a/test/inspector/test-off-with-session-then-on.js +++ /dev/null @@ -1,24 +0,0 @@ -'use strict'; -const common = require('../common'); -common.skipIfInspectorDisabled(); -const helper = require('./inspector-helper.js'); - -function testResume(session) { - session.sendCommandsAndExpectClose([ - { 'method': 'Runtime.runIfWaitingForDebugger' } - ]); -} - -function testDisconnectSession(harness) { - harness - .runFrontendSession([ - testResume, - ]).expectShutDown(42); -} - -const script = 'process._debugEnd();' + - 'process._debugProcess(process.pid);' + - 'setTimeout(() => {console.log("Done");process.exit(42)});'; - -helper.startNodeForInspectorTest(testDisconnectSession, '--inspect-brk', - script); diff --git a/test/inspector/test-inspector-port-cluster.js b/test/inspector/test-port-cluster.js similarity index 100% rename from test/inspector/test-inspector-port-cluster.js rename to test/inspector/test-port-cluster.js diff --git a/test/inspector/test-inspector-port-zero-cluster.js b/test/inspector/test-port-zero-cluster.js similarity index 100% rename from test/inspector/test-inspector-port-zero-cluster.js rename to test/inspector/test-port-zero-cluster.js diff --git a/test/inspector/test-inspector-port-zero.js b/test/inspector/test-port-zero.js similarity index 100% rename from test/inspector/test-inspector-port-zero.js rename to test/inspector/test-port-zero.js diff --git a/test/inspector/test-stop-profile-after-done.js b/test/inspector/test-stop-profile-after-done.js new file mode 100644 index 00000000000000..314c429d461723 --- /dev/null +++ b/test/inspector/test-stop-profile-after-done.js @@ -0,0 +1,30 @@ +'use strict'; +const common = require('../common'); +common.skipIfInspectorDisabled(); +const assert = require('assert'); +const { NodeInstance } = require('./inspector-helper.js'); + +async function runTests() { + const child = new NodeInstance(['--inspect=0'], + `let c = 0; + const interval = setInterval(() => { + console.log(new Object()); + if (c++ === 10) + clearInterval(interval); + }, 10);`); + const session = await child.connectInspectorSession(); + + session.send([ + { 'method': 'Profiler.setSamplingInterval', 'params': { 'interval': 100 } }, + { 'method': 'Profiler.enable' }, + { 'method': 'Runtime.runIfWaitingForDebugger' }, + { 'method': 'Profiler.start' }]); + while (await child.nextStderrString() !== + 'Waiting for the debugger to disconnect...'); + await session.send({ 'method': 'Profiler.stop' }); + session.disconnect(); + assert.strictEqual(0, (await child.expectShutdown()).exitCode); +} + +common.crashOnUnhandledRejection(); +runTests(); diff --git a/test/inspector/test-inspector-stops-no-file.js b/test/inspector/test-stops-no-file.js similarity index 100% rename from test/inspector/test-inspector-stops-no-file.js rename to test/inspector/test-stops-no-file.js From 77bc72ad542afd2cf3a005586c3d6a39fdc3f8e6 Mon Sep 17 00:00:00 2001 From: Timothy Gu Date: Thu, 17 Aug 2017 14:53:25 +0800 Subject: [PATCH 198/231] test: fix inspector helper port sniffing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/13870 Reviewed-By: Miroslav Bajtoš --- test/inspector/inspector-helper.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/inspector/inspector-helper.js b/test/inspector/inspector-helper.js index e9464c3679a00c..11e86395407b5c 100644 --- a/test/inspector/inspector-helper.js +++ b/test/inspector/inspector-helper.js @@ -325,9 +325,10 @@ class NodeInstance { console.log('[err]', line); if (this._portCallback) { const matches = line.match(/Debugger listening on ws:\/\/.+:(\d+)\/.+/); - if (matches) + if (matches) { this._portCallback(matches[1]); - this._portCallback = null; + this._portCallback = null; + } } if (this._stderrLineCallback) { this._stderrLineCallback(line); From 8c61b72f90a71d5c9f05b0bcf4f84fad31b212f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Mon, 17 Jul 2017 16:51:26 +0200 Subject: [PATCH 199/231] inspector: enable async stack traces Implement a special async_hooks listener that forwards information about async tasks to V8Inspector asyncTask* API, thus enabling DevTools feature "async stack traces". The feature is enabled only on 64bit platforms due to a technical limitation of V8 Inspector: inspector uses a pointer as a task id, while async_hooks use 64bit numbers as ids. To avoid performance penalty of async_hooks when not debugging, the new listener is enabled only when the process enters a debug mode: - When the process is started with `--inspect` or `--inspect-brk`, the listener is enabled immediately and async stack traces lead all the way to the first tick of the event loop. - When the debug mode is enabled via SIGUSR1 or `_debugProcess()`, the listener is enabled together with the debugger. As a result, only async operations started after the signal was received will be correctly observed and reported to V8 Inspector. For example, a `setInterval()` called in the first tick of the event will not be shown in the async stack trace when the callback is invoked. This behaviour is consistent with Chrome DevTools. Last but not least, this commit fixes handling of InspectorAgent's internal property `enabled_` to ensure it's set back to `false` after the debugger is deactivated (typically via `process._debugEnd()`). Fixes: https://github.com/nodejs/node/issues/11370 PR-URL: https://github.com/nodejs/node/pull/13870 Reviewed-by: Timothy Gu Reviewed-by: Anna Henningsen --- lib/internal/bootstrap_node.js | 2 + lib/internal/inspector_async_hook.js | 64 +++++++ node.gyp | 1 + src/inspector_agent.cc | 165 +++++++++++++++++- src/inspector_agent.h | 27 +-- src/node_config.cc | 14 ++ test/common/README.md | 10 ++ test/common/index.js | 6 + test/inspector/inspector-helper.js | 16 +- .../test-async-hook-setup-at-inspect-brk.js | 45 +++++ .../test-async-hook-setup-at-inspect.js | 70 ++++++++ .../test-async-hook-setup-at-signal.js | 81 +++++++++ .../test-async-hook-teardown-at-debug-end.js | 33 ++++ .../test-async-stack-traces-promise-then.js | 69 ++++++++ .../test-async-stack-traces-set-interval.js | 41 +++++ test/inspector/test-inspector-enabled.js | 26 +++ test/inspector/test-inspector.js | 3 +- 17 files changed, 656 insertions(+), 17 deletions(-) create mode 100644 lib/internal/inspector_async_hook.js create mode 100644 test/inspector/test-async-hook-setup-at-inspect-brk.js create mode 100644 test/inspector/test-async-hook-setup-at-inspect.js create mode 100644 test/inspector/test-async-hook-setup-at-signal.js create mode 100644 test/inspector/test-async-hook-teardown-at-debug-end.js create mode 100644 test/inspector/test-async-stack-traces-promise-then.js create mode 100644 test/inspector/test-async-stack-traces-set-interval.js create mode 100644 test/inspector/test-inspector-enabled.js diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index ad579ab2b549fc..b9410eb071d298 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -62,6 +62,8 @@ if (global.__coverage__) NativeModule.require('internal/process/write-coverage').setup(); + NativeModule.require('internal/inspector_async_hook').setup(); + // Do not initialize channel in debugger agent, it deletes env variable // and the main thread won't see it. if (process.argv[1] !== '--debug-agent') diff --git a/lib/internal/inspector_async_hook.js b/lib/internal/inspector_async_hook.js new file mode 100644 index 00000000000000..e32a026cd69155 --- /dev/null +++ b/lib/internal/inspector_async_hook.js @@ -0,0 +1,64 @@ +'use strict'; + +const { createHook } = require('async_hooks'); +const inspector = process.binding('inspector'); +const config = process.binding('config'); + +if (!inspector || !inspector.asyncTaskScheduled) { + exports.setup = function() {}; + return; +} + +const hook = createHook({ + init(asyncId, type, triggerAsyncId, resource) { + // It's difficult to tell which tasks will be recurring and which won't, + // therefore we mark all tasks as recurring. Based on the discussion + // in https://github.com/nodejs/node/pull/13870#discussion_r124515293, + // this should be fine as long as we call asyncTaskCanceled() too. + const recurring = true; + inspector.asyncTaskScheduled(type, asyncId, recurring); + }, + + before(asyncId) { + inspector.asyncTaskStarted(asyncId); + }, + + after(asyncId) { + inspector.asyncTaskFinished(asyncId); + }, + + destroy(asyncId) { + inspector.asyncTaskCanceled(asyncId); + }, +}); + +function enable() { + if (config.bits < 64) { + // V8 Inspector stores task ids as (void*) pointers. + // async_hooks store ids as 64bit numbers. + // As a result, we cannot reliably translate async_hook ids to V8 async_task + // ids on 32bit platforms. + process.emitWarning( + 'Warning: Async stack traces in debugger are not available ' + + `on ${config.bits}bit platforms. The feature is disabled.`, + { + code: 'INSPECTOR_ASYNC_STACK_TRACES_NOT_AVAILABLE', + }); + } else { + hook.enable(); + } +} + +function disable() { + hook.disable(); +} + +exports.setup = function() { + inspector.registerAsyncHook(enable, disable); + + if (inspector.isEnabled()) { + // If the inspector was already enabled via --inspect or --inspect-brk, + // the we need to enable the async hook immediately at startup. + enable(); + } +}; diff --git a/node.gyp b/node.gyp index 5bec65caddc084..6ff0cb02ccaacf 100644 --- a/node.gyp +++ b/node.gyp @@ -88,6 +88,7 @@ 'lib/internal/freelist.js', 'lib/internal/fs.js', 'lib/internal/http.js', + 'lib/internal/inspector_async_hook.js', 'lib/internal/linkedlist.js', 'lib/internal/net.js', 'lib/internal/module.js', diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index 804afeaafcc8f7..828006ecf2fbb4 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -23,20 +23,27 @@ namespace node { namespace inspector { namespace { + +using node::FatalError; + using v8::Array; +using v8::Boolean; using v8::Context; using v8::External; using v8::Function; using v8::FunctionCallbackInfo; using v8::HandleScope; +using v8::Integer; using v8::Isolate; using v8::Local; using v8::Maybe; using v8::MaybeLocal; +using v8::Name; using v8::NewStringType; using v8::Object; using v8::Persistent; using v8::String; +using v8::Undefined; using v8::Value; using v8_inspector::StringBuffer; @@ -613,6 +620,28 @@ class NodeInspectorClient : public V8InspectorClient { timers_.erase(data); } + // Async stack traces instrumentation. + void AsyncTaskScheduled(const StringView& task_name, void* task, + bool recurring) { + client_->asyncTaskScheduled(task_name, task, recurring); + } + + void AsyncTaskCanceled(void* task) { + client_->asyncTaskCanceled(task); + } + + void AsyncTaskStarted(void* task) { + client_->asyncTaskStarted(task); + } + + void AsyncTaskFinished(void* task) { + client_->asyncTaskFinished(task); + } + + void AllAsyncTasksCanceled() { + client_->allAsyncTasksCanceled(); + } + private: node::Environment* env_; node::NodePlatform* platform_; @@ -673,9 +702,21 @@ bool Agent::StartIoThread(bool wait_for_connect) { } v8::Isolate* isolate = parent_env_->isolate(); + HandleScope handle_scope(isolate); + + // Enable tracking of async stack traces + if (!enable_async_hook_function_.IsEmpty()) { + Local enable_fn = enable_async_hook_function_.Get(isolate); + auto context = parent_env_->context(); + auto result = enable_fn->Call(context, Undefined(isolate), 0, nullptr); + if (result.IsEmpty()) { + FatalError( + "node::InspectorAgent::StartIoThread", + "Cannot enable Inspector's AsyncHook, please report this."); + } + } // Send message to enable debug in workers - HandleScope handle_scope(isolate); Local process_object = parent_env_->process_object(); Local emit_fn = process_object->Get(FIXED_ONE_BYTE_STRING(isolate, "emit")); @@ -714,10 +755,40 @@ void Agent::Stop() { if (io_ != nullptr) { io_->Stop(); io_.reset(); + enabled_ = false; + } + + v8::Isolate* isolate = parent_env_->isolate(); + HandleScope handle_scope(isolate); + + // Disable tracking of async stack traces + if (!disable_async_hook_function_.IsEmpty()) { + Local disable_fn = disable_async_hook_function_.Get(isolate); + auto result = disable_fn->Call(parent_env_->context(), + Undefined(parent_env_->isolate()), 0, nullptr); + if (result.IsEmpty()) { + FatalError( + "node::InspectorAgent::Stop", + "Cannot disable Inspector's AsyncHook, please report this."); + } } } void Agent::Connect(InspectorSessionDelegate* delegate) { + if (!enabled_) { + // Enable tracking of async stack traces + v8::Isolate* isolate = parent_env_->isolate(); + HandleScope handle_scope(isolate); + auto context = parent_env_->context(); + Local enable_fn = enable_async_hook_function_.Get(isolate); + auto result = enable_fn->Call(context, Undefined(isolate), 0, nullptr); + if (result.IsEmpty()) { + FatalError( + "node::InspectorAgent::Connect", + "Cannot enable Inspector's AsyncHook, please report this."); + } + } + enabled_ = true; client_->connectFrontend(delegate); } @@ -770,6 +841,34 @@ void Agent::PauseOnNextJavascriptStatement(const std::string& reason) { channel->schedulePauseOnNextStatement(reason); } +void Agent::RegisterAsyncHook(Isolate* isolate, + v8::Local enable_function, + v8::Local disable_function) { + enable_async_hook_function_.Reset(isolate, enable_function); + disable_async_hook_function_.Reset(isolate, disable_function); +} + +void Agent::AsyncTaskScheduled(const StringView& task_name, void* task, + bool recurring) { + client_->AsyncTaskScheduled(task_name, task, recurring); +} + +void Agent::AsyncTaskCanceled(void* task) { + client_->AsyncTaskCanceled(task); +} + +void Agent::AsyncTaskStarted(void* task) { + client_->AsyncTaskStarted(task); +} + +void Agent::AsyncTaskFinished(void* task) { + client_->AsyncTaskFinished(task); +} + +void Agent::AllAsyncTasksCanceled() { + client_->AllAsyncTasksCanceled(); +} + void Open(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); inspector::Agent* agent = env->inspector_agent(); @@ -807,6 +906,59 @@ void Url(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(OneByteString(env->isolate(), url.c_str())); } +static void* GetAsyncTask(int64_t asyncId) { + // The inspector assumes that when other clients use its asyncTask* API, + // they use real pointers, or at least something aligned like real pointer. + // In general it means that our task_id should always be even. + // + // On 32bit platforms, the 64bit asyncId would get truncated when converted + // to a 32bit pointer. However, the javascript part will never enable + // the async_hook on 32bit platforms, therefore the truncation will never + // happen in practice. + return reinterpret_cast(asyncId << 1); +} + +template +static void InvokeAsyncTaskFnWithId(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK(args[0]->IsNumber()); + int64_t task_id = args[0]->IntegerValue(env->context()).FromJust(); + (env->inspector_agent()->*asyncTaskFn)(GetAsyncTask(task_id)); +} + +static void AsyncTaskScheduledWrapper(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + CHECK(args[0]->IsString()); + Local task_name = args[0].As(); + String::Value task_name_value(task_name); + StringView task_name_view(*task_name_value, task_name_value.length()); + + CHECK(args[1]->IsNumber()); + int64_t task_id = args[1]->IntegerValue(env->context()).FromJust(); + void* task = GetAsyncTask(task_id); + + CHECK(args[2]->IsBoolean()); + bool recurring = args[2]->BooleanValue(env->context()).FromJust(); + + env->inspector_agent()->AsyncTaskScheduled(task_name_view, task, recurring); +} + +static void RegisterAsyncHookWrapper(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + CHECK(args[0]->IsFunction()); + v8::Local enable_function = args[0].As(); + CHECK(args[1]->IsFunction()); + v8::Local disable_function = args[1].As(); + env->inspector_agent()->RegisterAsyncHook(env->isolate(), + enable_function, disable_function); +} + +static void IsEnabled(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + args.GetReturnValue().Set(env->inspector_agent()->enabled()); +} // static void Agent::InitInspector(Local target, Local unused, @@ -827,6 +979,17 @@ void Agent::InitInspector(Local target, Local unused, env->SetMethod(target, "connect", ConnectJSBindingsSession); env->SetMethod(target, "open", Open); env->SetMethod(target, "url", Url); + + env->SetMethod(target, "asyncTaskScheduled", AsyncTaskScheduledWrapper); + env->SetMethod(target, "asyncTaskCanceled", + InvokeAsyncTaskFnWithId<&Agent::AsyncTaskCanceled>); + env->SetMethod(target, "asyncTaskStarted", + InvokeAsyncTaskFnWithId<&Agent::AsyncTaskStarted>); + env->SetMethod(target, "asyncTaskFinished", + InvokeAsyncTaskFnWithId<&Agent::AsyncTaskFinished>); + + env->SetMethod(target, "registerAsyncHook", RegisterAsyncHookWrapper); + env->SetMethod(target, "isEnabled", IsEnabled); } void Agent::RequestIoThreadStart() { diff --git a/src/inspector_agent.h b/src/inspector_agent.h index 5d21047c4a2c64..8195e001c2eb3c 100644 --- a/src/inspector_agent.h +++ b/src/inspector_agent.h @@ -17,17 +17,7 @@ class Environment; class NodePlatform; } // namespace node -namespace v8 { -class Context; -template -class FunctionCallbackInfo; -template -class Local; -class Message; -class Object; -class Platform; -class Value; -} // namespace v8 +#include "v8.h" namespace v8_inspector { class StringView; @@ -68,6 +58,18 @@ class Agent { void FatalException(v8::Local error, v8::Local message); + // Async stack traces instrumentation. + void AsyncTaskScheduled(const v8_inspector::StringView& taskName, void* task, + bool recurring); + void AsyncTaskCanceled(void* task); + void AsyncTaskStarted(void* task); + void AsyncTaskFinished(void* task); + void AllAsyncTasksCanceled(); + + void RegisterAsyncHook(v8::Isolate* isolate, + v8::Local enable_function, + v8::Local disable_function); + // These methods are called by the WS protocol and JS binding to create // inspector sessions. The inspector responds by using the delegate to send // messages back. @@ -108,6 +110,9 @@ class Agent { std::string path_; DebugOptions debug_options_; int next_context_number_; + + v8::Persistent enable_async_hook_function_; + v8::Persistent disable_async_hook_function_; }; } // namespace inspector diff --git a/src/node_config.cc b/src/node_config.cc index 02701259c77ba9..64263fb2d69af4 100644 --- a/src/node_config.cc +++ b/src/node_config.cc @@ -12,6 +12,7 @@ using v8::Boolean; using v8::Context; using v8::Integer; using v8::Local; +using v8::Number; using v8::Object; using v8::ReadOnly; using v8::String; @@ -29,6 +30,15 @@ using v8::Value; True(env->isolate()), ReadOnly).FromJust(); \ } while (0) +#define READONLY_PROPERTY(obj, name, value) \ + do { \ + obj->DefineOwnProperty(env->context(), \ + OneByteString(env->isolate(), name), \ + value, \ + ReadOnly).FromJust(); \ + } while (0) + + static void InitConfig(Local target, Local unused, Local context) { @@ -90,6 +100,10 @@ static void InitConfig(Local target, if (config_expose_http2) READONLY_BOOLEAN_PROPERTY("exposeHTTP2"); + + READONLY_PROPERTY(target, + "bits", + Number::New(env->isolate(), 8 * sizeof(intptr_t))); } // InitConfig } // namespace node diff --git a/test/common/README.md b/test/common/README.md index 59b02cf52a9a48..b8d9af2fcf70f5 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -325,6 +325,16 @@ Path to the 'root' directory. either `/` or `c:\\` (windows) Logs '1..0 # Skipped: ' + `msg` and exits with exit code `0`. +### skipIfInspectorDisabled() + +Skip the rest of the tests in the current file when the Inspector +was disabled at compile time. + +### skipIf32Bits() + +Skip the rest of the tests in the current file when the Node.js executable +was compiled with a pointer size smaller than 64 bits. + ### spawnPwd(options) * `options` [<Object>] * return [<Object>] diff --git a/test/common/index.js b/test/common/index.js index d274fce9cdbf93..f2dfa61d16b27a 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -746,6 +746,12 @@ exports.skipIfInspectorDisabled = function skipIfInspectorDisabled() { } }; +exports.skipIf32Bits = function skipIf32Bits() { + if (process.binding('config').bits < 64) { + exports.skip('The tested feature is not available in 32bit builds'); + } +}; + const arrayBufferViews = [ Int8Array, Uint8Array, diff --git a/test/inspector/inspector-helper.js b/test/inspector/inspector-helper.js index 11e86395407b5c..9c1cca3a771293 100644 --- a/test/inspector/inspector-helper.js +++ b/test/inspector/inspector-helper.js @@ -9,7 +9,7 @@ const url = require('url'); const _MAINSCRIPT = path.join(common.fixturesDir, 'loop.js'); const DEBUG = false; -const TIMEOUT = 15 * 1000; +const TIMEOUT = common.platformTimeout(15 * 1000); function spawnChildProcess(inspectorFlags, scriptContents, scriptFile) { const args = [].concat(inspectorFlags); @@ -253,9 +253,7 @@ class InspectorSession { .waitForNotification( (notification) => this._isBreakOnLineNotification(notification, line, url), - `break on ${url}:${line}`) - .then((notification) => - notification.params.callFrames[0].scopeChain[0].object.objectId); + `break on ${url}:${line}`); } _matchesConsoleOutputNotification(notification, type, values) { @@ -321,6 +319,16 @@ class NodeInstance { }); } + static async startViaSignal(scriptContents) { + const instance = new NodeInstance( + [], `${scriptContents}\nprocess._rawDebug('started');`, undefined); + const msg = 'Timed out waiting for process to start'; + while (await common.fires(instance.nextStderrString(), msg, TIMEOUT) !== + 'started') {} + process._debugProcess(instance._process.pid); + return instance; + } + onStderrLine(line) { console.log('[err]', line); if (this._portCallback) { diff --git a/test/inspector/test-async-hook-setup-at-inspect-brk.js b/test/inspector/test-async-hook-setup-at-inspect-brk.js new file mode 100644 index 00000000000000..70887ff63d9d4e --- /dev/null +++ b/test/inspector/test-async-hook-setup-at-inspect-brk.js @@ -0,0 +1,45 @@ +'use strict'; +const common = require('../common'); +common.skipIfInspectorDisabled(); +common.skipIf32Bits(); +common.crashOnUnhandledRejection(); +const { NodeInstance } = require('./inspector-helper.js'); +const assert = require('assert'); + +const script = ` +setTimeout(() => { + debugger; + process.exitCode = 55; +}, 50); +`; + +async function checkAsyncStackTrace(session) { + console.error('[test]', 'Verify basic properties of asyncStackTrace'); + const paused = await session.waitForBreakOnLine(2, '[eval]'); + assert(paused.params.asyncStackTrace, + `${Object.keys(paused.params)} contains "asyncStackTrace" property`); + assert(paused.params.asyncStackTrace.description, 'Timeout'); + assert(paused.params.asyncStackTrace.callFrames + .some((frame) => frame.functionName === 'Module._compile')); +} + +async function runTests() { + const instance = new NodeInstance(undefined, script); + const session = await instance.connectInspectorSession(); + await session.send([ + { 'method': 'Runtime.enable' }, + { 'method': 'Debugger.enable' }, + { 'method': 'Debugger.setAsyncCallStackDepth', + 'params': { 'maxDepth': 10 } }, + { 'method': 'Debugger.setBlackboxPatterns', + 'params': { 'patterns': [] } }, + { 'method': 'Runtime.runIfWaitingForDebugger' } + ]); + + await checkAsyncStackTrace(session); + + await session.runToCompletion(); + assert.strictEqual(55, (await instance.expectShutdown()).exitCode); +} + +runTests(); diff --git a/test/inspector/test-async-hook-setup-at-inspect.js b/test/inspector/test-async-hook-setup-at-inspect.js new file mode 100644 index 00000000000000..bbf418a858838c --- /dev/null +++ b/test/inspector/test-async-hook-setup-at-inspect.js @@ -0,0 +1,70 @@ +'use strict'; +const common = require('../common'); +common.skipIfInspectorDisabled(); +common.skipIf32Bits(); +common.crashOnUnhandledRejection(); +const { NodeInstance } = require('../inspector/inspector-helper.js'); +const assert = require('assert'); + +// Even with --inspect, the default async call stack depth is 0. We need a +// chance to call Debugger.setAsyncCallStackDepth *before* activating the timer +// for async stack traces to work. +const script = ` +process._rawDebug('Waiting until the inspector is activated...'); +const waiting = setInterval(() => { debugger; }, 50); + +// This function is called by the inspector client (session) +function setupTimeoutWithBreak() { + clearInterval(waiting); + process._rawDebug('Debugger ready, setting up timeout with a break'); + setTimeout(() => { debugger; }, 50); +} +`; + +async function waitForInitialSetup(session) { + console.error('[test]', 'Waiting for initial setup'); + await session.waitForBreakOnLine(2, '[eval]'); +} + +async function setupTimeoutForStackTrace(session) { + console.error('[test]', 'Setting up timeout for async stack trace'); + await session.send([ + { 'method': 'Runtime.evaluate', + 'params': { expression: 'setupTimeoutWithBreak()' } }, + { 'method': 'Debugger.resume' } + ]); +} + +async function checkAsyncStackTrace(session) { + console.error('[test]', 'Verify basic properties of asyncStackTrace'); + const paused = await session.waitForBreakOnLine(8, '[eval]'); + assert(paused.params.asyncStackTrace, + `${Object.keys(paused.params)} contains "asyncStackTrace" property`); + assert(paused.params.asyncStackTrace.description, 'Timeout'); + assert(paused.params.asyncStackTrace.callFrames + .some((frame) => frame.functionName === 'setupTimeoutWithBreak')); +} + +async function runTests() { + const instance = new NodeInstance(['--inspect=0'], script); + const session = await instance.connectInspectorSession(); + await session.send([ + { 'method': 'Runtime.enable' }, + { 'method': 'Debugger.enable' }, + { 'method': 'Debugger.setAsyncCallStackDepth', + 'params': { 'maxDepth': 10 } }, + { 'method': 'Debugger.setBlackboxPatterns', + 'params': { 'patterns': [] } }, + { 'method': 'Runtime.runIfWaitingForDebugger' } + ]); + + await waitForInitialSetup(session); + await setupTimeoutForStackTrace(session); + await checkAsyncStackTrace(session); + + console.error('[test]', 'Stopping child instance'); + session.disconnect(); + instance.kill(); +} + +runTests(); diff --git a/test/inspector/test-async-hook-setup-at-signal.js b/test/inspector/test-async-hook-setup-at-signal.js new file mode 100644 index 00000000000000..f0150bc7acc0a9 --- /dev/null +++ b/test/inspector/test-async-hook-setup-at-signal.js @@ -0,0 +1,81 @@ +'use strict'; +const common = require('../common'); +common.skipIfInspectorDisabled(); +common.skipIf32Bits(); +common.crashOnUnhandledRejection(); +const { NodeInstance } = require('../inspector/inspector-helper.js'); +const assert = require('assert'); + +const script = ` +process._rawDebug('Waiting until a signal enables the inspector...'); +let waiting = setInterval(waitUntilDebugged, 50); + +function waitUntilDebugged() { + if (!process.binding('inspector').isEnabled()) return; + clearInterval(waiting); + // At this point, even though the Inspector is enabled, the default async + // call stack depth is 0. We need a chance to call + // Debugger.setAsyncCallStackDepth *before* activating the actual timer for + // async stack traces to work. Directly using a debugger statement would be + // too brittle, and using a longer timeout would unnecesarily slow down the + // test on most machines. Triggering a debugger break through an interval is + // a faster and more reliable way. + process._rawDebug('Signal received, waiting for debugger setup'); + waiting = setInterval(() => { debugger; }, 50); +} + +// This function is called by the inspector client (session) +function setupTimeoutWithBreak() { + clearInterval(waiting); + process._rawDebug('Debugger ready, setting up timeout with a break'); + setTimeout(() => { debugger; }, 50); +} +`; + +async function waitForInitialSetup(session) { + console.error('[test]', 'Waiting for initial setup'); + await session.waitForBreakOnLine(15, '[eval]'); +} + +async function setupTimeoutForStackTrace(session) { + console.error('[test]', 'Setting up timeout for async stack trace'); + await session.send([ + { 'method': 'Runtime.evaluate', + 'params': { expression: 'setupTimeoutWithBreak()' } }, + { 'method': 'Debugger.resume' } + ]); +} + +async function checkAsyncStackTrace(session) { + console.error('[test]', 'Verify basic properties of asyncStackTrace'); + const paused = await session.waitForBreakOnLine(22, '[eval]'); + assert(paused.params.asyncStackTrace, + `${Object.keys(paused.params)} contains "asyncStackTrace" property`); + assert(paused.params.asyncStackTrace.description, 'Timeout'); + assert(paused.params.asyncStackTrace.callFrames + .some((frame) => frame.functionName === 'setupTimeoutWithBreak')); +} + +async function runTests() { + const instance = await NodeInstance.startViaSignal(script); + const session = await instance.connectInspectorSession(); + await session.send([ + { 'method': 'Runtime.enable' }, + { 'method': 'Debugger.enable' }, + { 'method': 'Debugger.setAsyncCallStackDepth', + 'params': { 'maxDepth': 10 } }, + { 'method': 'Debugger.setBlackboxPatterns', + 'params': { 'patterns': [] } }, + { 'method': 'Runtime.runIfWaitingForDebugger' } + ]); + + await waitForInitialSetup(session); + await setupTimeoutForStackTrace(session); + await checkAsyncStackTrace(session); + + console.error('[test]', 'Stopping child instance'); + session.disconnect(); + instance.kill(); +} + +runTests(); diff --git a/test/inspector/test-async-hook-teardown-at-debug-end.js b/test/inspector/test-async-hook-teardown-at-debug-end.js new file mode 100644 index 00000000000000..9084efdd412bcf --- /dev/null +++ b/test/inspector/test-async-hook-teardown-at-debug-end.js @@ -0,0 +1,33 @@ +'use strict'; +const common = require('../common'); +common.skipIfInspectorDisabled(); +common.skipIf32Bits(); + +const spawn = require('child_process').spawn; + +const script = ` +const assert = require('assert'); + +// Verify that inspector-async-hook is registered +// by checking that emitInit with invalid arguments +// throw an error. +// See test/async-hooks/test-emit-init.js +assert.throws( + () => async_hooks.emitInit(), + 'inspector async hook should have been enabled initially'); + +process._debugEnd(); + +// Verify that inspector-async-hook is no longer registered, +// thus emitInit() ignores invalid arguments +// See test/async-hooks/test-emit-init.js +assert.doesNotThrow( + () => async_hooks.emitInit(), + 'inspector async hook should have beend disabled by _debugEnd()'); +`; + +const args = ['--inspect', '-e', script]; +const child = spawn(process.execPath, args, { stdio: 'inherit' }); +child.on('exit', (code, signal) => { + process.exit(code || signal); +}); diff --git a/test/inspector/test-async-stack-traces-promise-then.js b/test/inspector/test-async-stack-traces-promise-then.js new file mode 100644 index 00000000000000..68584b0a3c5dad --- /dev/null +++ b/test/inspector/test-async-stack-traces-promise-then.js @@ -0,0 +1,69 @@ +'use strict'; +const common = require('../common'); +common.skipIfInspectorDisabled(); +common.skipIf32Bits(); +common.crashOnUnhandledRejection(); +const { NodeInstance } = require('./inspector-helper'); +const assert = require('assert'); + +const script = `runTest(); +function runTest() { + const p = Promise.resolve(); + p.then(function break1() { // lineNumber 3 + debugger; + }); + p.then(function break2() { // lineNumber 6 + debugger; + }); +} +`; + +async function runTests() { + const instance = new NodeInstance(undefined, script); + const session = await instance.connectInspectorSession(); + await session.send([ + { 'method': 'Runtime.enable' }, + { 'method': 'Debugger.enable' }, + { 'method': 'Debugger.setAsyncCallStackDepth', + 'params': { 'maxDepth': 10 } }, + { 'method': 'Debugger.setBlackboxPatterns', + 'params': { 'patterns': [] } }, + { 'method': 'Runtime.runIfWaitingForDebugger' } + ]); + + console.error('[test] Waiting for break1'); + debuggerPausedAt(await session.waitForBreakOnLine(4, '[eval]'), + 'break1', 'runTest:3'); + + await session.send({ 'method': 'Debugger.resume' }); + + console.error('[test] Waiting for break2'); + debuggerPausedAt(await session.waitForBreakOnLine(7, '[eval]'), + 'break2', 'runTest:6'); + + await session.runToCompletion(); + assert.strictEqual(0, (await instance.expectShutdown()).exitCode); +} + +function debuggerPausedAt(msg, functionName, previousTickLocation) { + assert( + !!msg.params.asyncStackTrace, + `${Object.keys(msg.params)} contains "asyncStackTrace" property`); + + assert.strictEqual(msg.params.callFrames[0].functionName, functionName); + assert.strictEqual(msg.params.asyncStackTrace.description, 'PROMISE'); + + const frameLocations = msg.params.asyncStackTrace.callFrames.map( + (frame) => `${frame.functionName}:${frame.lineNumber}`); + assertArrayIncludes(frameLocations, previousTickLocation); +} + +function assertArrayIncludes(actual, expected) { + const expectedString = JSON.stringify(expected); + const actualString = JSON.stringify(actual); + assert( + actual.includes(expected), + `Expected ${actualString} to contain ${expectedString}.`); +} + +runTests(); diff --git a/test/inspector/test-async-stack-traces-set-interval.js b/test/inspector/test-async-stack-traces-set-interval.js new file mode 100644 index 00000000000000..bc96df9588fc6a --- /dev/null +++ b/test/inspector/test-async-stack-traces-set-interval.js @@ -0,0 +1,41 @@ +'use strict'; +const common = require('../common'); +common.skipIfInspectorDisabled(); +common.skipIf32Bits(); +common.crashOnUnhandledRejection(); +const { NodeInstance } = require('./inspector-helper'); +const assert = require('assert'); + +const script = 'setInterval(() => { debugger; }, 50);'; + +async function checkAsyncStackTrace(session) { + console.error('[test]', 'Verify basic properties of asyncStackTrace'); + const paused = await session.waitForBreakOnLine(0, '[eval]'); + assert(paused.params.asyncStackTrace, + `${Object.keys(paused.params)} contains "asyncStackTrace" property`); + assert(paused.params.asyncStackTrace.description, 'Timeout'); + assert(paused.params.asyncStackTrace.callFrames + .some((frame) => frame.functionName === 'Module._compile')); +} + +async function runTests() { + const instance = new NodeInstance(undefined, script); + const session = await instance.connectInspectorSession(); + await session.send([ + { 'method': 'Runtime.enable' }, + { 'method': 'Debugger.enable' }, + { 'method': 'Debugger.setAsyncCallStackDepth', + 'params': { 'maxDepth': 10 } }, + { 'method': 'Debugger.setBlackboxPatterns', + 'params': { 'patterns': [] } }, + { 'method': 'Runtime.runIfWaitingForDebugger' } + ]); + + await checkAsyncStackTrace(session); + + console.error('[test]', 'Stopping child instance'); + session.disconnect(); + instance.kill(); +} + +runTests(); diff --git a/test/inspector/test-inspector-enabled.js b/test/inspector/test-inspector-enabled.js new file mode 100644 index 00000000000000..a7a0832793283c --- /dev/null +++ b/test/inspector/test-inspector-enabled.js @@ -0,0 +1,26 @@ +'use strict'; +const common = require('../common'); +common.skipIfInspectorDisabled(); + +const spawn = require('child_process').spawn; + +const script = ` +const assert = require('assert'); +const inspector = process.binding('inspector'); + +assert( + !!inspector.isEnabled(), + 'inspector.isEnabled() should be true when run with --inspect'); + +process._debugEnd(); + +assert( + !inspector.isEnabled(), + 'inspector.isEnabled() should be false after _debugEnd()'); +`; + +const args = ['--inspect', '-e', script]; +const child = spawn(process.execPath, args, { stdio: 'inherit' }); +child.on('exit', (code, signal) => { + process.exit(code || signal); +}); diff --git a/test/inspector/test-inspector.js b/test/inspector/test-inspector.js index 3139940451a515..6a5dbe60030a64 100644 --- a/test/inspector/test-inspector.js +++ b/test/inspector/test-inspector.js @@ -98,7 +98,8 @@ async function testBreakpoint(session) { `Script source is wrong: ${scriptSource}`); await session.waitForConsoleOutput('log', ['A message', 5]); - const scopeId = await session.waitForBreakOnLine(5, mainScriptPath); + const paused = await session.waitForBreakOnLine(5, mainScriptPath); + const scopeId = paused.params.callFrames[0].scopeChain[0].object.objectId; console.log('[test]', 'Verify we can read current application state'); const response = await session.send({ From bda558501250c18214e87757f02655d0fe9d79bb Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Mon, 14 Aug 2017 12:54:05 +0200 Subject: [PATCH 200/231] buffer: fix MAX_LENGTH constant export MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was a typo and accidentally returned the wrong value. Fixes: https://github.com/nodejs/node/issues/14819 Ref: https://github.com/nodejs/node/pull/13467 PR-URL: https://github.com/nodejs/node/pull/14821 Reviewed-By: Evan Lucas Reviewed-By: Yuta Hiroto Reviewed-By: Tobias Nießen Reviewed-By: Colin Ihrig Reviewed-By: Luigi Pinca Reviewed-By: Michaël Zasso --- lib/buffer.js | 2 +- test/parallel/test-buffer-constants.js | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/buffer.js b/lib/buffer.js index cae73edde5ad9e..ac4f61bf5d0849 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -47,7 +47,7 @@ exports.kMaxLength = binding.kMaxLength; const constants = Object.defineProperties({}, { MAX_LENGTH: { - value: binding.kStringMaxLength, + value: binding.kMaxLength, writable: false, enumerable: true }, diff --git a/test/parallel/test-buffer-constants.js b/test/parallel/test-buffer-constants.js index 59f5b6d0deada4..820c8dc993ed8e 100644 --- a/test/parallel/test-buffer-constants.js +++ b/test/parallel/test-buffer-constants.js @@ -2,6 +2,7 @@ require('../common'); const assert = require('assert'); +const { kMaxLength, kStringMaxLength } = require('buffer'); const { MAX_LENGTH, MAX_STRING_LENGTH } = require('buffer').constants; assert.strictEqual(typeof MAX_LENGTH, 'number'); @@ -11,3 +12,7 @@ assert.throws(() => ' '.repeat(MAX_STRING_LENGTH + 1), /^RangeError: Invalid string length$/); assert.doesNotThrow(() => ' '.repeat(MAX_STRING_LENGTH)); + +// Legacy values match: +assert.strictEqual(kMaxLength, MAX_LENGTH); +assert.strictEqual(kStringMaxLength, MAX_STRING_LENGTH); From 1c3cb49f004c44955e2a5c219e5a7ad5ee3bf9fc Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 16 Aug 2017 10:46:26 -0700 Subject: [PATCH 201/231] src: miscellaneous cleanups for node_config Includes a fix for setting the `icuDataDir` as a UTF8 string rather than one byte. Previously, if the dir contained any non-ascii characters they would be mangled. This won't cover cases that involve paths with other character encodings (thank you Linux).. but it will cover the most likely. Other miscellaneous cleanups are included PR-URL: https://github.com/nodejs/node/pull/14868 Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig --- src/node_config.cc | 94 +++++++++++++++++++++++++--------------------- 1 file changed, 52 insertions(+), 42 deletions(-) diff --git a/src/node_config.cc b/src/node_config.cc index 64263fb2d69af4..a2d980d793fc2e 100644 --- a/src/node_config.cc +++ b/src/node_config.cc @@ -11,6 +11,7 @@ namespace node { using v8::Boolean; using v8::Context; using v8::Integer; +using v8::Isolate; using v8::Local; using v8::Number; using v8::Object; @@ -25,24 +26,24 @@ using v8::Value; #define READONLY_BOOLEAN_PROPERTY(str) \ do { \ - target->DefineOwnProperty(env->context(), \ - OneByteString(env->isolate(), str), \ - True(env->isolate()), ReadOnly).FromJust(); \ + target->DefineOwnProperty(context, \ + FIXED_ONE_BYTE_STRING(isolate, str), \ + True(isolate), ReadOnly).FromJust(); \ } while (0) #define READONLY_PROPERTY(obj, name, value) \ do { \ obj->DefineOwnProperty(env->context(), \ - OneByteString(env->isolate(), name), \ - value, \ - ReadOnly).FromJust(); \ + FIXED_ONE_BYTE_STRING(isolate, name), \ + value, ReadOnly).FromJust(); \ } while (0) - static void InitConfig(Local target, Local unused, Local context) { Environment* env = Environment::GetCurrent(context); + Isolate* isolate = env->isolate(); + #ifdef NODE_HAVE_I18N_SUPPORT READONLY_BOOLEAN_PROPERTY("hasIntl"); @@ -51,10 +52,13 @@ static void InitConfig(Local target, READONLY_BOOLEAN_PROPERTY("hasSmallICU"); #endif // NODE_HAVE_SMALL_ICU - target->DefineOwnProperty(env->context(), - OneByteString(env->isolate(), "icuDataDir"), - OneByteString(env->isolate(), icu_data_dir.data())) - .FromJust(); + target->DefineOwnProperty( + context, + FIXED_ONE_BYTE_STRING(isolate, "icuDataDir"), + String::NewFromUtf8(isolate, + icu_data_dir.data(), + v8::NewStringType::kNormal).ToLocalChecked(), + ReadOnly).FromJust(); #endif // NODE_HAVE_I18N_SUPPORT @@ -64,37 +68,6 @@ static void InitConfig(Local target, if (config_pending_deprecation) READONLY_BOOLEAN_PROPERTY("pendingDeprecation"); - if (!config_warning_file.empty()) { - Local name = OneByteString(env->isolate(), "warningFile"); - Local value = String::NewFromUtf8(env->isolate(), - config_warning_file.data(), - v8::NewStringType::kNormal, - config_warning_file.size()) - .ToLocalChecked(); - target->DefineOwnProperty(env->context(), name, value).FromJust(); - } - - Local debugOptions = Object::New(env->isolate()); - - target->DefineOwnProperty(env->context(), - OneByteString(env->isolate(), "debugOptions"), - debugOptions).FromJust(); - - debugOptions->DefineOwnProperty(env->context(), - OneByteString(env->isolate(), "host"), - String::NewFromUtf8(env->isolate(), - debug_options.host_name().c_str())).FromJust(); - - debugOptions->DefineOwnProperty(env->context(), - OneByteString(env->isolate(), "port"), - Integer::New(env->isolate(), - debug_options.port())).FromJust(); - - debugOptions->DefineOwnProperty(env->context(), - OneByteString(env->isolate(), "inspectorEnabled"), - Boolean::New(env->isolate(), - debug_options.inspector_enabled())).FromJust(); - if (config_expose_internals) READONLY_BOOLEAN_PROPERTY("exposeInternals"); @@ -104,6 +77,43 @@ static void InitConfig(Local target, READONLY_PROPERTY(target, "bits", Number::New(env->isolate(), 8 * sizeof(intptr_t))); + + if (!config_warning_file.empty()) { + target->DefineOwnProperty( + context, + FIXED_ONE_BYTE_STRING(isolate, "warningFile"), + String::NewFromUtf8(isolate, + config_warning_file.data(), + v8::NewStringType::kNormal).ToLocalChecked(), + ReadOnly).FromJust(); + } + + Local debugOptions = Object::New(isolate); + + target->DefineOwnProperty( + context, + FIXED_ONE_BYTE_STRING(isolate, "debugOptions"), + debugOptions, ReadOnly).FromJust(); + + debugOptions->DefineOwnProperty( + context, + FIXED_ONE_BYTE_STRING(isolate, "host"), + String::NewFromUtf8(isolate, + debug_options.host_name().c_str(), + v8::NewStringType::kNormal).ToLocalChecked(), + ReadOnly).FromJust(); + + debugOptions->DefineOwnProperty( + context, + FIXED_ONE_BYTE_STRING(isolate, "port"), + Integer::New(isolate, debug_options.port()), + ReadOnly).FromJust(); + + debugOptions->DefineOwnProperty( + context, + FIXED_ONE_BYTE_STRING(isolate, "inspectorEnabled"), + Boolean::New(isolate, debug_options.inspector_enabled()), ReadOnly) + .FromJust(); } // InitConfig } // namespace node From 5a05dfe0a757766343b088e93e334823acf0adad Mon Sep 17 00:00:00 2001 From: James M Snell Date: Fri, 18 Aug 2017 21:58:25 -0700 Subject: [PATCH 202/231] src: fixup strings, reduce duplication PR-URL: https://github.com/nodejs/node/pull/14937 Reviewed-By: Anna Henningsen --- src/cares_wrap.cc | 32 ++++++++++++++++---------------- src/js_stream.cc | 8 +++++--- src/node_config.cc | 2 +- src/node_crypto.cc | 7 ++++--- src/node_file.cc | 7 ++++--- src/node_http2.cc | 3 +-- src/node_serdes.cc | 12 ++++++++---- src/node_stat_watcher.cc | 8 +++++--- src/node_zlib.cc | 6 ++++-- src/pipe_wrap.cc | 13 ++++++++----- src/process_wrap.cc | 7 ++++--- src/signal_wrap.cc | 8 +++++--- src/stream_wrap.cc | 14 ++++++++------ src/tcp_wrap.cc | 14 ++++++++------ src/timer_wrap.cc | 7 ++++--- src/tls_wrap.cc | 9 ++++++--- src/tty_wrap.cc | 7 +++++-- src/udp_wrap.cc | 13 ++++++++----- 18 files changed, 104 insertions(+), 73 deletions(-) diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc index 5417a9ee55def3..976cbfc759ff53 100644 --- a/src/cares_wrap.cc +++ b/src/cares_wrap.cc @@ -2167,28 +2167,28 @@ void Initialize(Local target, FunctionTemplate::New(env->isolate(), is_construct_call_callback); aiw->InstanceTemplate()->SetInternalFieldCount(1); env->SetProtoMethod(aiw, "getAsyncId", AsyncWrap::GetAsyncId); - aiw->SetClassName( - FIXED_ONE_BYTE_STRING(env->isolate(), "GetAddrInfoReqWrap")); - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "GetAddrInfoReqWrap"), - aiw->GetFunction()); + Local addrInfoWrapString = + FIXED_ONE_BYTE_STRING(env->isolate(), "GetAddrInfoReqWrap"); + aiw->SetClassName(addrInfoWrapString); + target->Set(addrInfoWrapString, aiw->GetFunction()); Local niw = FunctionTemplate::New(env->isolate(), is_construct_call_callback); niw->InstanceTemplate()->SetInternalFieldCount(1); env->SetProtoMethod(niw, "getAsyncId", AsyncWrap::GetAsyncId); - niw->SetClassName( - FIXED_ONE_BYTE_STRING(env->isolate(), "GetNameInfoReqWrap")); - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "GetNameInfoReqWrap"), - niw->GetFunction()); + Local nameInfoWrapString = + FIXED_ONE_BYTE_STRING(env->isolate(), "GetNameInfoReqWrap"); + niw->SetClassName(nameInfoWrapString); + target->Set(nameInfoWrapString, niw->GetFunction()); Local qrw = FunctionTemplate::New(env->isolate(), is_construct_call_callback); qrw->InstanceTemplate()->SetInternalFieldCount(1); env->SetProtoMethod(qrw, "getAsyncId", AsyncWrap::GetAsyncId); - qrw->SetClassName( - FIXED_ONE_BYTE_STRING(env->isolate(), "QueryReqWrap")); - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "QueryReqWrap"), - qrw->GetFunction()); + Local queryWrapString = + FIXED_ONE_BYTE_STRING(env->isolate(), "QueryReqWrap"); + qrw->SetClassName(queryWrapString); + target->Set(queryWrapString, qrw->GetFunction()); Local channel_wrap = env->NewFunctionTemplate(ChannelWrap::New); @@ -2212,10 +2212,10 @@ void Initialize(Local target, env->SetProtoMethod(channel_wrap, "setServers", SetServers); env->SetProtoMethod(channel_wrap, "cancel", Cancel); - channel_wrap->SetClassName( - FIXED_ONE_BYTE_STRING(env->isolate(), "ChannelWrap")); - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "ChannelWrap"), - channel_wrap->GetFunction()); + Local channelWrapString = + FIXED_ONE_BYTE_STRING(env->isolate(), "ChannelWrap"); + channel_wrap->SetClassName(channelWrapString); + target->Set(channelWrapString, channel_wrap->GetFunction()); } } // anonymous namespace diff --git a/src/js_stream.cc b/src/js_stream.cc index d88cc853c800cc..caef92f1cadb98 100644 --- a/src/js_stream.cc +++ b/src/js_stream.cc @@ -18,6 +18,7 @@ using v8::HandleScope; using v8::Local; using v8::MaybeLocal; using v8::Object; +using v8::String; using v8::Value; @@ -212,7 +213,9 @@ void JSStream::Initialize(Local target, Environment* env = Environment::GetCurrent(context); Local t = env->NewFunctionTemplate(New); - t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "JSStream")); + Local jsStreamString = + FIXED_ONE_BYTE_STRING(env->isolate(), "JSStream"); + t->SetClassName(jsStreamString); t->InstanceTemplate()->SetInternalFieldCount(1); env->SetProtoMethod(t, "getAsyncId", AsyncWrap::GetAsyncId); @@ -226,8 +229,7 @@ void JSStream::Initialize(Local target, env->SetProtoMethod(t, "emitEOF", EmitEOF); StreamBase::AddMethods(env, t, StreamBase::kFlagHasWritev); - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "JSStream"), - t->GetFunction()); + target->Set(jsStreamString, t->GetFunction()); env->set_jsstream_constructor_template(t); } diff --git a/src/node_config.cc b/src/node_config.cc index a2d980d793fc2e..87110dd8c644f7 100644 --- a/src/node_config.cc +++ b/src/node_config.cc @@ -105,7 +105,7 @@ static void InitConfig(Local target, debugOptions->DefineOwnProperty( context, - FIXED_ONE_BYTE_STRING(isolate, "port"), + env->port_string(), Integer::New(isolate, debug_options.port()), ReadOnly).FromJust(); diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 664bf1a72c7e8c..f279cb89f6f2c9 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -312,7 +312,9 @@ bool EntropySource(unsigned char* buffer, size_t length) { void SecureContext::Initialize(Environment* env, Local target) { Local t = env->NewFunctionTemplate(SecureContext::New); t->InstanceTemplate()->SetInternalFieldCount(1); - t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "SecureContext")); + Local secureContextString = + FIXED_ONE_BYTE_STRING(env->isolate(), "SecureContext"); + t->SetClassName(secureContextString); env->SetProtoMethod(t, "init", SecureContext::Init); env->SetProtoMethod(t, "setKey", SecureContext::SetKey); @@ -359,8 +361,7 @@ void SecureContext::Initialize(Environment* env, Local target) { static_cast(ReadOnly | DontDelete), AccessorSignature::New(env->isolate(), t)); - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "SecureContext"), - t->GetFunction()); + target->Set(secureContextString, t->GetFunction()); env->set_secure_context_constructor_template(t); } diff --git a/src/node_file.cc b/src/node_file.cc index 9e0553a7a579a0..407e692f99bb23 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -1484,9 +1484,10 @@ void InitFs(Local target, FunctionTemplate::New(env->isolate(), NewFSReqWrap); fst->InstanceTemplate()->SetInternalFieldCount(1); env->SetProtoMethod(fst, "getAsyncId", AsyncWrap::GetAsyncId); - fst->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "FSReqWrap")); - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "FSReqWrap"), - fst->GetFunction()); + Local wrapString = + FIXED_ONE_BYTE_STRING(env->isolate(), "FSReqWrap"); + fst->SetClassName(wrapString); + target->Set(wrapString, fst->GetFunction()); } } // end namespace node diff --git a/src/node_http2.cc b/src/node_http2.cc index 9308e9e68e930e..70dcc5e00d1746 100755 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -1206,8 +1206,7 @@ void Initialize(Local target, env->SetMethod(target, "nghttp2ErrorString", HttpErrorString); Local http2SessionClassName = - String::NewFromUtf8(isolate, "Http2Session", - v8::NewStringType::kInternalized).ToLocalChecked(); + FIXED_ONE_BYTE_STRING(isolate, "Http2Session"); Local session = env->NewFunctionTemplate(Http2Session::New); diff --git a/src/node_serdes.cc b/src/node_serdes.cc index a077598dfe4cbf..ced9e4dd982f79 100644 --- a/src/node_serdes.cc +++ b/src/node_serdes.cc @@ -451,9 +451,11 @@ void InitializeSerdesBindings(Local target, "_setTreatArrayBufferViewsAsHostObjects", SerializerContext::SetTreatArrayBufferViewsAsHostObjects); - ser->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "Serializer")); + Local serializerString = + FIXED_ONE_BYTE_STRING(env->isolate(), "Serializer"); + ser->SetClassName(serializerString); target->Set(env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "Serializer"), + serializerString, ser->GetFunction(env->context()).ToLocalChecked()).FromJust(); Local des = @@ -474,9 +476,11 @@ void InitializeSerdesBindings(Local target, env->SetProtoMethod(des, "readDouble", DeserializerContext::ReadDouble); env->SetProtoMethod(des, "_readRawBytes", DeserializerContext::ReadRawBytes); - des->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "Deserializer")); + Local deserializerString = + FIXED_ONE_BYTE_STRING(env->isolate(), "Deserializer"); + des->SetClassName(deserializerString); target->Set(env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "Deserializer"), + deserializerString, des->GetFunction(env->context()).ToLocalChecked()).FromJust(); } diff --git a/src/node_stat_watcher.cc b/src/node_stat_watcher.cc index 18bf2c54193d7b..3f9354efcef3ed 100644 --- a/src/node_stat_watcher.cc +++ b/src/node_stat_watcher.cc @@ -39,6 +39,7 @@ using v8::HandleScope; using v8::Integer; using v8::Local; using v8::Object; +using v8::String; using v8::Value; @@ -47,14 +48,15 @@ void StatWatcher::Initialize(Environment* env, Local target) { Local t = env->NewFunctionTemplate(StatWatcher::New); t->InstanceTemplate()->SetInternalFieldCount(1); - t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "StatWatcher")); + Local statWatcherString = + FIXED_ONE_BYTE_STRING(env->isolate(), "StatWatcher"); + t->SetClassName(statWatcherString); env->SetProtoMethod(t, "getAsyncId", AsyncWrap::GetAsyncId); env->SetProtoMethod(t, "start", StatWatcher::Start); env->SetProtoMethod(t, "stop", StatWatcher::Stop); - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "StatWatcher"), - t->GetFunction()); + target->Set(statWatcherString, t->GetFunction()); } diff --git a/src/node_zlib.cc b/src/node_zlib.cc index 85fc4927a03ca9..2e83291c9f3375 100644 --- a/src/node_zlib.cc +++ b/src/node_zlib.cc @@ -48,6 +48,7 @@ using v8::Integer; using v8::Local; using v8::Number; using v8::Object; +using v8::String; using v8::Value; namespace { @@ -691,8 +692,9 @@ void InitZlib(Local target, env->SetProtoMethod(z, "params", ZCtx::Params); env->SetProtoMethod(z, "reset", ZCtx::Reset); - z->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "Zlib")); - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "Zlib"), z->GetFunction()); + Local zlibString = FIXED_ONE_BYTE_STRING(env->isolate(), "Zlib"); + z->SetClassName(zlibString); + target->Set(zlibString, z->GetFunction()); target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "ZLIB_VERSION"), FIXED_ONE_BYTE_STRING(env->isolate(), ZLIB_VERSION)); diff --git a/src/pipe_wrap.cc b/src/pipe_wrap.cc index 2185580b0662e8..0daec0b1449d00 100644 --- a/src/pipe_wrap.cc +++ b/src/pipe_wrap.cc @@ -44,6 +44,7 @@ using v8::FunctionTemplate; using v8::HandleScope; using v8::Local; using v8::Object; +using v8::String; using v8::Value; using AsyncHooks = Environment::AsyncHooks; @@ -67,7 +68,8 @@ void PipeWrap::Initialize(Local target, Environment* env = Environment::GetCurrent(context); Local t = env->NewFunctionTemplate(New); - t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "Pipe")); + Local pipeString = FIXED_ONE_BYTE_STRING(env->isolate(), "Pipe"); + t->SetClassName(pipeString); t->InstanceTemplate()->SetInternalFieldCount(1); env->SetProtoMethod(t, "getAsyncId", AsyncWrap::GetAsyncId); @@ -92,7 +94,7 @@ void PipeWrap::Initialize(Local target, env->SetProtoMethod(t, "setPendingInstances", SetPendingInstances); #endif - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "Pipe"), t->GetFunction()); + target->Set(pipeString, t->GetFunction()); env->set_pipe_constructor_template(t); // Create FunctionTemplate for PipeConnectWrap. @@ -103,9 +105,10 @@ void PipeWrap::Initialize(Local target, auto cwt = FunctionTemplate::New(env->isolate(), constructor); cwt->InstanceTemplate()->SetInternalFieldCount(1); env->SetProtoMethod(cwt, "getAsyncId", AsyncWrap::GetAsyncId); - cwt->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "PipeConnectWrap")); - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "PipeConnectWrap"), - cwt->GetFunction()); + Local wrapString = + FIXED_ONE_BYTE_STRING(env->isolate(), "PipeConnectWrap"); + cwt->SetClassName(wrapString); + target->Set(wrapString, cwt->GetFunction()); } diff --git a/src/process_wrap.cc b/src/process_wrap.cc index cae0788927bc89..26e233b2bd0ee2 100644 --- a/src/process_wrap.cc +++ b/src/process_wrap.cc @@ -53,7 +53,9 @@ class ProcessWrap : public HandleWrap { Environment* env = Environment::GetCurrent(context); Local constructor = env->NewFunctionTemplate(New); constructor->InstanceTemplate()->SetInternalFieldCount(1); - constructor->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "Process")); + Local processString = + FIXED_ONE_BYTE_STRING(env->isolate(), "Process"); + constructor->SetClassName(processString); env->SetProtoMethod(constructor, "getAsyncId", AsyncWrap::GetAsyncId); @@ -66,8 +68,7 @@ class ProcessWrap : public HandleWrap { env->SetProtoMethod(constructor, "unref", HandleWrap::Unref); env->SetProtoMethod(constructor, "hasRef", HandleWrap::HasRef); - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "Process"), - constructor->GetFunction()); + target->Set(processString, constructor->GetFunction()); } size_t self_size() const override { return sizeof(*this); } diff --git a/src/signal_wrap.cc b/src/signal_wrap.cc index 1af98a9311f102..eed010b388d82f 100644 --- a/src/signal_wrap.cc +++ b/src/signal_wrap.cc @@ -37,6 +37,7 @@ using v8::HandleScope; using v8::Integer; using v8::Local; using v8::Object; +using v8::String; using v8::Value; namespace { @@ -49,7 +50,9 @@ class SignalWrap : public HandleWrap { Environment* env = Environment::GetCurrent(context); Local constructor = env->NewFunctionTemplate(New); constructor->InstanceTemplate()->SetInternalFieldCount(1); - constructor->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "Signal")); + Local signalString = + FIXED_ONE_BYTE_STRING(env->isolate(), "Signal"); + constructor->SetClassName(signalString); env->SetProtoMethod(constructor, "getAsyncId", AsyncWrap::GetAsyncId); env->SetProtoMethod(constructor, "close", HandleWrap::Close); @@ -59,8 +62,7 @@ class SignalWrap : public HandleWrap { env->SetProtoMethod(constructor, "start", Start); env->SetProtoMethod(constructor, "stop", Stop); - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "Signal"), - constructor->GetFunction()); + target->Set(signalString, constructor->GetFunction()); } size_t self_size() const override { return sizeof(*this); } diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index eb156a91a2bdc0..57c93ac489ba2f 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -67,18 +67,20 @@ void StreamWrap::Initialize(Local target, Local sw = FunctionTemplate::New(env->isolate(), is_construct_call_callback); sw->InstanceTemplate()->SetInternalFieldCount(1); - sw->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "ShutdownWrap")); + Local wrapString = + FIXED_ONE_BYTE_STRING(env->isolate(), "ShutdownWrap"); + sw->SetClassName(wrapString); env->SetProtoMethod(sw, "getAsyncId", AsyncWrap::GetAsyncId); - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "ShutdownWrap"), - sw->GetFunction()); + target->Set(wrapString, sw->GetFunction()); Local ww = FunctionTemplate::New(env->isolate(), is_construct_call_callback); ww->InstanceTemplate()->SetInternalFieldCount(1); - ww->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "WriteWrap")); + Local writeWrapString = + FIXED_ONE_BYTE_STRING(env->isolate(), "WriteWrap"); + ww->SetClassName(writeWrapString); env->SetProtoMethod(ww, "getAsyncId", AsyncWrap::GetAsyncId); - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "WriteWrap"), - ww->GetFunction()); + target->Set(writeWrapString, ww->GetFunction()); env->set_write_wrap_constructor_function(ww->GetFunction()); } diff --git a/src/tcp_wrap.cc b/src/tcp_wrap.cc index 4bee4b97097f40..e721108caecb83 100644 --- a/src/tcp_wrap.cc +++ b/src/tcp_wrap.cc @@ -71,11 +71,12 @@ void TCPWrap::Initialize(Local target, Environment* env = Environment::GetCurrent(context); Local t = env->NewFunctionTemplate(New); - t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "TCP")); + Local tcpString = FIXED_ONE_BYTE_STRING(env->isolate(), "TCP"); + t->SetClassName(tcpString); t->InstanceTemplate()->SetInternalFieldCount(1); // Init properties - t->InstanceTemplate()->Set(String::NewFromUtf8(env->isolate(), "reading"), + t->InstanceTemplate()->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "reading"), Boolean::New(env->isolate(), false)); t->InstanceTemplate()->Set(env->owner_string(), Null(env->isolate())); t->InstanceTemplate()->Set(env->onread_string(), Null(env->isolate())); @@ -109,7 +110,7 @@ void TCPWrap::Initialize(Local target, env->SetProtoMethod(t, "setSimultaneousAccepts", SetSimultaneousAccepts); #endif - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "TCP"), t->GetFunction()); + target->Set(tcpString, t->GetFunction()); env->set_tcp_constructor_template(t); // Create FunctionTemplate for TCPConnectWrap. @@ -120,9 +121,10 @@ void TCPWrap::Initialize(Local target, auto cwt = FunctionTemplate::New(env->isolate(), constructor); cwt->InstanceTemplate()->SetInternalFieldCount(1); env->SetProtoMethod(cwt, "getAsyncId", AsyncWrap::GetAsyncId); - cwt->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "TCPConnectWrap")); - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "TCPConnectWrap"), - cwt->GetFunction()); + Local wrapString = + FIXED_ONE_BYTE_STRING(env->isolate(), "TCPConnectWrap"); + cwt->SetClassName(wrapString); + target->Set(wrapString, cwt->GetFunction()); } diff --git a/src/timer_wrap.cc b/src/timer_wrap.cc index 609d087e334f72..03e7ad32626c61 100644 --- a/src/timer_wrap.cc +++ b/src/timer_wrap.cc @@ -39,6 +39,7 @@ using v8::HandleScope; using v8::Integer; using v8::Local; using v8::Object; +using v8::String; using v8::Value; const uint32_t kOnTimeout = 0; @@ -50,8 +51,9 @@ class TimerWrap : public HandleWrap { Local context) { Environment* env = Environment::GetCurrent(context); Local constructor = env->NewFunctionTemplate(New); + Local timerString = FIXED_ONE_BYTE_STRING(env->isolate(), "Timer"); constructor->InstanceTemplate()->SetInternalFieldCount(1); - constructor->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "Timer")); + constructor->SetClassName(timerString); constructor->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnTimeout"), Integer::New(env->isolate(), kOnTimeout)); @@ -67,8 +69,7 @@ class TimerWrap : public HandleWrap { env->SetProtoMethod(constructor, "start", Start); env->SetProtoMethod(constructor, "stop", Stop); - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "Timer"), - constructor->GetFunction()); + target->Set(timerString, constructor->GetFunction()); } size_t self_size() const override { return sizeof(*this); } diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index f03dbd7fd650d8..4e9eb1993037fe 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -938,9 +938,13 @@ void TLSWrap::Initialize(Local target, CHECK(args.IsConstructCall()); args.This()->SetAlignedPointerInInternalField(0, nullptr); }; + + Local tlsWrapString = + FIXED_ONE_BYTE_STRING(env->isolate(), "TLSWrap"); + auto t = env->NewFunctionTemplate(constructor); t->InstanceTemplate()->SetInternalFieldCount(1); - t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "TLSWrap")); + t->SetClassName(tlsWrapString); env->SetProtoMethod(t, "getAsyncId", AsyncWrap::GetAsyncId); env->SetProtoMethod(t, "asyncReset", AsyncWrap::AsyncReset); @@ -962,8 +966,7 @@ void TLSWrap::Initialize(Local target, env->set_tls_wrap_constructor_template(t); env->set_tls_wrap_constructor_function(t->GetFunction()); - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "TLSWrap"), - t->GetFunction()); + target->Set(tlsWrapString, t->GetFunction()); } } // namespace node diff --git a/src/tty_wrap.cc b/src/tty_wrap.cc index f3f1edfe5d3248..88e9a9826499ab 100644 --- a/src/tty_wrap.cc +++ b/src/tty_wrap.cc @@ -41,6 +41,7 @@ using v8::FunctionTemplate; using v8::Integer; using v8::Local; using v8::Object; +using v8::String; using v8::Value; @@ -49,8 +50,10 @@ void TTYWrap::Initialize(Local target, Local context) { Environment* env = Environment::GetCurrent(context); + Local ttyString = FIXED_ONE_BYTE_STRING(env->isolate(), "TTY"); + Local t = env->NewFunctionTemplate(New); - t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "TTY")); + t->SetClassName(ttyString); t->InstanceTemplate()->SetInternalFieldCount(1); env->SetProtoMethod(t, "getAsyncId", AsyncWrap::GetAsyncId); @@ -68,7 +71,7 @@ void TTYWrap::Initialize(Local target, env->SetMethod(target, "isTTY", IsTTY); env->SetMethod(target, "guessHandleType", GuessHandleType); - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "TTY"), t->GetFunction()); + target->Set(ttyString, t->GetFunction()); env->set_tty_constructor_template(t); } diff --git a/src/udp_wrap.cc b/src/udp_wrap.cc index c192de6d628cec..702a5263c89956 100644 --- a/src/udp_wrap.cc +++ b/src/udp_wrap.cc @@ -106,7 +106,9 @@ void UDPWrap::Initialize(Local target, Local t = env->NewFunctionTemplate(New); t->InstanceTemplate()->SetInternalFieldCount(1); - t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "UDP")); + Local udpString = + FIXED_ONE_BYTE_STRING(env->isolate(), "UDP"); + t->SetClassName(udpString); enum PropertyAttribute attributes = static_cast(v8::ReadOnly | v8::DontDelete); @@ -139,7 +141,7 @@ void UDPWrap::Initialize(Local target, env->SetProtoMethod(t, "getAsyncId", AsyncWrap::GetAsyncId); - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "UDP"), t->GetFunction()); + target->Set(udpString, t->GetFunction()); env->set_udp_constructor_function(t->GetFunction()); // Create FunctionTemplate for SendWrap @@ -147,9 +149,10 @@ void UDPWrap::Initialize(Local target, FunctionTemplate::New(env->isolate(), NewSendWrap); swt->InstanceTemplate()->SetInternalFieldCount(1); env->SetProtoMethod(swt, "getAsyncId", AsyncWrap::GetAsyncId); - swt->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "SendWrap")); - target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "SendWrap"), - swt->GetFunction()); + Local sendWrapString = + FIXED_ONE_BYTE_STRING(env->isolate(), "SendWrap"); + swt->SetClassName(sendWrapString); + target->Set(sendWrapString, swt->GetFunction()); } From 8cee5d66bdadffd0a51cab70956da011937a9868 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Fri, 18 Aug 2017 22:37:37 -0700 Subject: [PATCH 203/231] src: reduce code duplication Adds `AsyncWrap::AddWrapMethods()` to add common methods to a `Local`. Follows same pattern as stream base. PR-URL: https://github.com/nodejs/node/pull/14937 Reviewed-By: Anna Henningsen --- src/async-wrap.cc | 7 +++++++ src/async-wrap.h | 9 +++++++++ src/cares_wrap.cc | 8 ++++---- src/fs_event_wrap.cc | 2 +- src/js_stream.cc | 2 +- src/node_crypto.cc | 6 +++--- src/node_file.cc | 2 +- src/node_http2.cc | 2 +- src/node_http_parser.cc | 2 +- src/node_stat_watcher.cc | 2 +- src/node_zlib.cc | 2 +- src/pipe_wrap.cc | 4 ++-- src/process_wrap.cc | 2 +- src/signal_wrap.cc | 2 +- src/stream_wrap.cc | 4 ++-- src/tcp_wrap.cc | 5 ++--- src/timer_wrap.cc | 2 +- src/tls_wrap.cc | 3 +-- src/tty_wrap.cc | 2 +- src/udp_wrap.cc | 4 ++-- 20 files changed, 43 insertions(+), 29 deletions(-) diff --git a/src/async-wrap.cc b/src/async-wrap.cc index 71b1e256592b08..468ac975247829 100644 --- a/src/async-wrap.cc +++ b/src/async-wrap.cc @@ -475,6 +475,13 @@ void AsyncWrap::QueueDestroyId(const FunctionCallbackInfo& args) { PushBackDestroyId(Environment::GetCurrent(args), args[0]->NumberValue()); } +void AsyncWrap::AddWrapMethods(Environment* env, + Local constructor, + int flag) { + env->SetProtoMethod(constructor, "getAsyncId", AsyncWrap::GetAsyncId); + if (flag & kFlagHasReset) + env->SetProtoMethod(constructor, "asyncReset", AsyncWrap::AsyncReset); +} void AsyncWrap::Initialize(Local target, Local unused, diff --git a/src/async-wrap.h b/src/async-wrap.h index 10d0150844a7ab..a4c42d01b73d36 100644 --- a/src/async-wrap.h +++ b/src/async-wrap.h @@ -88,6 +88,11 @@ class AsyncWrap : public BaseObject { PROVIDERS_LENGTH, }; + enum Flags { + kFlagNone = 0x0, + kFlagHasReset = 0x1 + }; + AsyncWrap(Environment* env, v8::Local object, ProviderType provider, @@ -95,6 +100,10 @@ class AsyncWrap : public BaseObject { virtual ~AsyncWrap(); + static void AddWrapMethods(Environment* env, + v8::Local constructor, + int flags = kFlagNone); + static void Initialize(v8::Local target, v8::Local unused, v8::Local context); diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc index 976cbfc759ff53..e800e0f2fee260 100644 --- a/src/cares_wrap.cc +++ b/src/cares_wrap.cc @@ -2166,7 +2166,7 @@ void Initialize(Local target, Local aiw = FunctionTemplate::New(env->isolate(), is_construct_call_callback); aiw->InstanceTemplate()->SetInternalFieldCount(1); - env->SetProtoMethod(aiw, "getAsyncId", AsyncWrap::GetAsyncId); + AsyncWrap::AddWrapMethods(env, aiw); Local addrInfoWrapString = FIXED_ONE_BYTE_STRING(env->isolate(), "GetAddrInfoReqWrap"); aiw->SetClassName(addrInfoWrapString); @@ -2175,7 +2175,7 @@ void Initialize(Local target, Local niw = FunctionTemplate::New(env->isolate(), is_construct_call_callback); niw->InstanceTemplate()->SetInternalFieldCount(1); - env->SetProtoMethod(niw, "getAsyncId", AsyncWrap::GetAsyncId); + AsyncWrap::AddWrapMethods(env, niw); Local nameInfoWrapString = FIXED_ONE_BYTE_STRING(env->isolate(), "GetNameInfoReqWrap"); niw->SetClassName(nameInfoWrapString); @@ -2184,7 +2184,7 @@ void Initialize(Local target, Local qrw = FunctionTemplate::New(env->isolate(), is_construct_call_callback); qrw->InstanceTemplate()->SetInternalFieldCount(1); - env->SetProtoMethod(qrw, "getAsyncId", AsyncWrap::GetAsyncId); + AsyncWrap::AddWrapMethods(env, qrw); Local queryWrapString = FIXED_ONE_BYTE_STRING(env->isolate(), "QueryReqWrap"); qrw->SetClassName(queryWrapString); @@ -2193,7 +2193,7 @@ void Initialize(Local target, Local channel_wrap = env->NewFunctionTemplate(ChannelWrap::New); channel_wrap->InstanceTemplate()->SetInternalFieldCount(1); - env->SetProtoMethod(channel_wrap, "getAsyncId", AsyncWrap::GetAsyncId); + AsyncWrap::AddWrapMethods(env, channel_wrap); env->SetProtoMethod(channel_wrap, "queryAny", Query); env->SetProtoMethod(channel_wrap, "queryA", Query); diff --git a/src/fs_event_wrap.cc b/src/fs_event_wrap.cc index 228c3a344edf3c..8ec8dd6dcfbd76 100644 --- a/src/fs_event_wrap.cc +++ b/src/fs_event_wrap.cc @@ -94,7 +94,7 @@ void FSEventWrap::Initialize(Local target, t->InstanceTemplate()->SetInternalFieldCount(1); t->SetClassName(fsevent_string); - env->SetProtoMethod(t, "getAsyncId", AsyncWrap::GetAsyncId); + AsyncWrap::AddWrapMethods(env, t); env->SetProtoMethod(t, "start", Start); env->SetProtoMethod(t, "close", Close); diff --git a/src/js_stream.cc b/src/js_stream.cc index caef92f1cadb98..2e7f082e289918 100644 --- a/src/js_stream.cc +++ b/src/js_stream.cc @@ -218,7 +218,7 @@ void JSStream::Initialize(Local target, t->SetClassName(jsStreamString); t->InstanceTemplate()->SetInternalFieldCount(1); - env->SetProtoMethod(t, "getAsyncId", AsyncWrap::GetAsyncId); + AsyncWrap::AddWrapMethods(env, t); env->SetProtoMethod(t, "doAlloc", DoAlloc); env->SetProtoMethod(t, "doRead", DoRead); diff --git a/src/node_crypto.cc b/src/node_crypto.cc index f279cb89f6f2c9..e935490971c5b2 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -2729,7 +2729,7 @@ void Connection::Initialize(Environment* env, Local target) { t->InstanceTemplate()->SetInternalFieldCount(1); t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "Connection")); - env->SetProtoMethod(t, "getAsyncId", AsyncWrap::GetAsyncId); + AsyncWrap::AddWrapMethods(env, t); env->SetProtoMethod(t, "encIn", Connection::EncIn); env->SetProtoMethod(t, "clearOut", Connection::ClearOut); env->SetProtoMethod(t, "clearIn", Connection::ClearIn); @@ -6129,14 +6129,14 @@ void InitCrypto(Local target, Local pb = FunctionTemplate::New(env->isolate()); pb->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "PBKDF2")); - env->SetProtoMethod(pb, "getAsyncId", AsyncWrap::GetAsyncId); + AsyncWrap::AddWrapMethods(env, pb); Local pbt = pb->InstanceTemplate(); pbt->SetInternalFieldCount(1); env->set_pbkdf2_constructor_template(pbt); Local rb = FunctionTemplate::New(env->isolate()); rb->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "RandomBytes")); - env->SetProtoMethod(rb, "getAsyncId", AsyncWrap::GetAsyncId); + AsyncWrap::AddWrapMethods(env, rb); Local rbt = rb->InstanceTemplate(); rbt->SetInternalFieldCount(1); env->set_randombytes_constructor_template(rbt); diff --git a/src/node_file.cc b/src/node_file.cc index 407e692f99bb23..ee69915ddcaf70 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -1483,7 +1483,7 @@ void InitFs(Local target, Local fst = FunctionTemplate::New(env->isolate(), NewFSReqWrap); fst->InstanceTemplate()->SetInternalFieldCount(1); - env->SetProtoMethod(fst, "getAsyncId", AsyncWrap::GetAsyncId); + AsyncWrap::AddWrapMethods(env, fst); Local wrapString = FIXED_ONE_BYTE_STRING(env->isolate(), "FSReqWrap"); fst->SetClassName(wrapString); diff --git a/src/node_http2.cc b/src/node_http2.cc index 70dcc5e00d1746..a8eb0a37bf8062 100755 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -1212,7 +1212,7 @@ void Initialize(Local target, env->NewFunctionTemplate(Http2Session::New); session->SetClassName(http2SessionClassName); session->InstanceTemplate()->SetInternalFieldCount(1); - env->SetProtoMethod(session, "getAsyncId", AsyncWrap::GetAsyncId); + AsyncWrap::AddWrapMethods(env, session); env->SetProtoMethod(session, "consume", Http2Session::Consume); env->SetProtoMethod(session, "destroy", diff --git a/src/node_http_parser.cc b/src/node_http_parser.cc index c897a771a5225b..dfe002814773c8 100644 --- a/src/node_http_parser.cc +++ b/src/node_http_parser.cc @@ -794,7 +794,7 @@ void InitHttpParser(Local target, #undef V target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "methods"), methods); - env->SetProtoMethod(t, "getAsyncId", AsyncWrap::GetAsyncId); + AsyncWrap::AddWrapMethods(env, t); env->SetProtoMethod(t, "close", Parser::Close); env->SetProtoMethod(t, "execute", Parser::Execute); env->SetProtoMethod(t, "finish", Parser::Finish); diff --git a/src/node_stat_watcher.cc b/src/node_stat_watcher.cc index 3f9354efcef3ed..4b2e4db7bc418c 100644 --- a/src/node_stat_watcher.cc +++ b/src/node_stat_watcher.cc @@ -52,7 +52,7 @@ void StatWatcher::Initialize(Environment* env, Local target) { FIXED_ONE_BYTE_STRING(env->isolate(), "StatWatcher"); t->SetClassName(statWatcherString); - env->SetProtoMethod(t, "getAsyncId", AsyncWrap::GetAsyncId); + AsyncWrap::AddWrapMethods(env, t); env->SetProtoMethod(t, "start", StatWatcher::Start); env->SetProtoMethod(t, "stop", StatWatcher::Stop); diff --git a/src/node_zlib.cc b/src/node_zlib.cc index 2e83291c9f3375..e48482cdc752f3 100644 --- a/src/node_zlib.cc +++ b/src/node_zlib.cc @@ -684,7 +684,7 @@ void InitZlib(Local target, z->InstanceTemplate()->SetInternalFieldCount(1); - env->SetProtoMethod(z, "getAsyncId", AsyncWrap::GetAsyncId); + AsyncWrap::AddWrapMethods(env, z); env->SetProtoMethod(z, "write", ZCtx::Write); env->SetProtoMethod(z, "writeSync", ZCtx::Write); env->SetProtoMethod(z, "init", ZCtx::Init); diff --git a/src/pipe_wrap.cc b/src/pipe_wrap.cc index 0daec0b1449d00..e29cf6d70f1a74 100644 --- a/src/pipe_wrap.cc +++ b/src/pipe_wrap.cc @@ -72,7 +72,7 @@ void PipeWrap::Initialize(Local target, t->SetClassName(pipeString); t->InstanceTemplate()->SetInternalFieldCount(1); - env->SetProtoMethod(t, "getAsyncId", AsyncWrap::GetAsyncId); + AsyncWrap::AddWrapMethods(env, t); env->SetProtoMethod(t, "close", HandleWrap::Close); env->SetProtoMethod(t, "unref", HandleWrap::Unref); @@ -104,7 +104,7 @@ void PipeWrap::Initialize(Local target, }; auto cwt = FunctionTemplate::New(env->isolate(), constructor); cwt->InstanceTemplate()->SetInternalFieldCount(1); - env->SetProtoMethod(cwt, "getAsyncId", AsyncWrap::GetAsyncId); + AsyncWrap::AddWrapMethods(env, cwt); Local wrapString = FIXED_ONE_BYTE_STRING(env->isolate(), "PipeConnectWrap"); cwt->SetClassName(wrapString); diff --git a/src/process_wrap.cc b/src/process_wrap.cc index 26e233b2bd0ee2..7accc8c129ecbc 100644 --- a/src/process_wrap.cc +++ b/src/process_wrap.cc @@ -57,7 +57,7 @@ class ProcessWrap : public HandleWrap { FIXED_ONE_BYTE_STRING(env->isolate(), "Process"); constructor->SetClassName(processString); - env->SetProtoMethod(constructor, "getAsyncId", AsyncWrap::GetAsyncId); + AsyncWrap::AddWrapMethods(env, constructor); env->SetProtoMethod(constructor, "close", HandleWrap::Close); diff --git a/src/signal_wrap.cc b/src/signal_wrap.cc index eed010b388d82f..048a3de4beaece 100644 --- a/src/signal_wrap.cc +++ b/src/signal_wrap.cc @@ -54,7 +54,7 @@ class SignalWrap : public HandleWrap { FIXED_ONE_BYTE_STRING(env->isolate(), "Signal"); constructor->SetClassName(signalString); - env->SetProtoMethod(constructor, "getAsyncId", AsyncWrap::GetAsyncId); + AsyncWrap::AddWrapMethods(env, constructor); env->SetProtoMethod(constructor, "close", HandleWrap::Close); env->SetProtoMethod(constructor, "ref", HandleWrap::Ref); env->SetProtoMethod(constructor, "unref", HandleWrap::Unref); diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index 57c93ac489ba2f..1e3863cad24f29 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -70,7 +70,7 @@ void StreamWrap::Initialize(Local target, Local wrapString = FIXED_ONE_BYTE_STRING(env->isolate(), "ShutdownWrap"); sw->SetClassName(wrapString); - env->SetProtoMethod(sw, "getAsyncId", AsyncWrap::GetAsyncId); + AsyncWrap::AddWrapMethods(env, sw); target->Set(wrapString, sw->GetFunction()); Local ww = @@ -79,7 +79,7 @@ void StreamWrap::Initialize(Local target, Local writeWrapString = FIXED_ONE_BYTE_STRING(env->isolate(), "WriteWrap"); ww->SetClassName(writeWrapString); - env->SetProtoMethod(ww, "getAsyncId", AsyncWrap::GetAsyncId); + AsyncWrap::AddWrapMethods(env, ww); target->Set(writeWrapString, ww->GetFunction()); env->set_write_wrap_constructor_function(ww->GetFunction()); } diff --git a/src/tcp_wrap.cc b/src/tcp_wrap.cc index e721108caecb83..2c2aeab6dd9356 100644 --- a/src/tcp_wrap.cc +++ b/src/tcp_wrap.cc @@ -82,8 +82,7 @@ void TCPWrap::Initialize(Local target, t->InstanceTemplate()->Set(env->onread_string(), Null(env->isolate())); t->InstanceTemplate()->Set(env->onconnection_string(), Null(env->isolate())); - env->SetProtoMethod(t, "getAsyncId", AsyncWrap::GetAsyncId); - env->SetProtoMethod(t, "asyncReset", AsyncWrap::AsyncReset); + AsyncWrap::AddWrapMethods(env, t, AsyncWrap::kFlagHasReset); env->SetProtoMethod(t, "close", HandleWrap::Close); @@ -120,7 +119,7 @@ void TCPWrap::Initialize(Local target, }; auto cwt = FunctionTemplate::New(env->isolate(), constructor); cwt->InstanceTemplate()->SetInternalFieldCount(1); - env->SetProtoMethod(cwt, "getAsyncId", AsyncWrap::GetAsyncId); + AsyncWrap::AddWrapMethods(env, cwt); Local wrapString = FIXED_ONE_BYTE_STRING(env->isolate(), "TCPConnectWrap"); cwt->SetClassName(wrapString); diff --git a/src/timer_wrap.cc b/src/timer_wrap.cc index 03e7ad32626c61..e398a471e74242 100644 --- a/src/timer_wrap.cc +++ b/src/timer_wrap.cc @@ -59,7 +59,7 @@ class TimerWrap : public HandleWrap { env->SetTemplateMethod(constructor, "now", Now); - env->SetProtoMethod(constructor, "getAsyncId", AsyncWrap::GetAsyncId); + AsyncWrap::AddWrapMethods(env, constructor); env->SetProtoMethod(constructor, "close", HandleWrap::Close); env->SetProtoMethod(constructor, "ref", HandleWrap::Ref); diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index 4e9eb1993037fe..77d8ce7b5ea447 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -946,8 +946,7 @@ void TLSWrap::Initialize(Local target, t->InstanceTemplate()->SetInternalFieldCount(1); t->SetClassName(tlsWrapString); - env->SetProtoMethod(t, "getAsyncId", AsyncWrap::GetAsyncId); - env->SetProtoMethod(t, "asyncReset", AsyncWrap::AsyncReset); + AsyncWrap::AddWrapMethods(env, t, AsyncWrap::kFlagHasReset); env->SetProtoMethod(t, "receive", Receive); env->SetProtoMethod(t, "start", Start); env->SetProtoMethod(t, "setVerifyMode", SetVerifyMode); diff --git a/src/tty_wrap.cc b/src/tty_wrap.cc index 88e9a9826499ab..ee793056c845d8 100644 --- a/src/tty_wrap.cc +++ b/src/tty_wrap.cc @@ -56,7 +56,7 @@ void TTYWrap::Initialize(Local target, t->SetClassName(ttyString); t->InstanceTemplate()->SetInternalFieldCount(1); - env->SetProtoMethod(t, "getAsyncId", AsyncWrap::GetAsyncId); + AsyncWrap::AddWrapMethods(env, t); env->SetProtoMethod(t, "close", HandleWrap::Close); env->SetProtoMethod(t, "unref", HandleWrap::Unref); diff --git a/src/udp_wrap.cc b/src/udp_wrap.cc index 702a5263c89956..37396602baac5b 100644 --- a/src/udp_wrap.cc +++ b/src/udp_wrap.cc @@ -139,7 +139,7 @@ void UDPWrap::Initialize(Local target, env->SetProtoMethod(t, "unref", HandleWrap::Unref); env->SetProtoMethod(t, "hasRef", HandleWrap::HasRef); - env->SetProtoMethod(t, "getAsyncId", AsyncWrap::GetAsyncId); + AsyncWrap::AddWrapMethods(env, t); target->Set(udpString, t->GetFunction()); env->set_udp_constructor_function(t->GetFunction()); @@ -148,7 +148,7 @@ void UDPWrap::Initialize(Local target, Local swt = FunctionTemplate::New(env->isolate(), NewSendWrap); swt->InstanceTemplate()->SetInternalFieldCount(1); - env->SetProtoMethod(swt, "getAsyncId", AsyncWrap::GetAsyncId); + AsyncWrap::AddWrapMethods(env, swt); Local sendWrapString = FIXED_ONE_BYTE_STRING(env->isolate(), "SendWrap"); swt->SetClassName(sendWrapString); From f75faddb1f7ffb6b713f8fced7dc499eb6632df8 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 7 Aug 2017 15:53:24 -0700 Subject: [PATCH 204/231] perf_hooks: implementation of the perf timing API An initial implementation of the Performance Timing API for Node.js. This is the same Performance Timing API implemented by modern browsers with a number of Node.js specific properties. The User Timing mark() and measure() APIs are implemented, garbage collection timing, and node startup milestone timing. ```js const { performance } = require('perf_hooks'); performance.mark('A'); setTimeout(() => { performance.mark('B'); performance.measure('A to B', 'A', 'B'); const entry = performance.getEntriesByName('A to B', 'measure')[0]; console.log(entry.duration); }, 10000); ``` The implementation is at the native layer and makes use of uv_hrtime(). This should enable *eventual* integration with things like Tracing and Inspection. The implementation is extensible and should allow us to add new performance entry types as we go (e.g. for measuring i/o perf, etc). Documentation and a test are provided. PR-URL: https://github.com/nodejs/node/pull/14680 Reviewed-By: Matteo Collina --- doc/api/_toc.md | 1 + doc/api/all.md | 1 + doc/api/perf_hooks.md | 656 +++++++++++++++++++++ doc/api/process.md | 6 +- lib/internal/bootstrap_node.js | 38 +- lib/internal/errors.js | 3 + lib/internal/module.js | 4 +- lib/internal/process.js | 5 + lib/perf_hooks.js | 553 +++++++++++++++++ node.gyp | 5 + src/env-inl.h | 46 ++ src/env.h | 23 + src/node.cc | 5 + src/node_internals.h | 1 + src/node_perf.cc | 389 ++++++++++++ src/node_perf.h | 174 ++++++ src/node_perf_common.h | 71 +++ test/parallel/test-performance-function.js | 93 +++ test/parallel/test-performance-gc.js | 51 ++ test/parallel/test-performance.js | 124 ++++ test/parallel/test-performanceobserver.js | 139 +++++ 21 files changed, 2381 insertions(+), 7 deletions(-) create mode 100755 doc/api/perf_hooks.md create mode 100644 lib/perf_hooks.js create mode 100644 src/node_perf.cc create mode 100644 src/node_perf.h create mode 100644 src/node_perf_common.h create mode 100644 test/parallel/test-performance-function.js create mode 100755 test/parallel/test-performance-gc.js create mode 100644 test/parallel/test-performance.js create mode 100644 test/parallel/test-performanceobserver.js diff --git a/doc/api/_toc.md b/doc/api/_toc.md index 6791e63f0c601a..67e0c62256fa14 100644 --- a/doc/api/_toc.md +++ b/doc/api/_toc.md @@ -32,6 +32,7 @@ * [Net](net.html) * [OS](os.html) * [Path](path.html) +* [Performance Hooks](perf_hooks.html) * [Process](process.html) * [Punycode](punycode.html) * [Query Strings](querystring.html) diff --git a/doc/api/all.md b/doc/api/all.md index 425513e2568d03..849a39a4bd7142 100644 --- a/doc/api/all.md +++ b/doc/api/all.md @@ -27,6 +27,7 @@ @include net @include os @include path +@include perf_hooks @include process @include punycode @include querystring diff --git a/doc/api/perf_hooks.md b/doc/api/perf_hooks.md new file mode 100755 index 00000000000000..7972e1edb24f43 --- /dev/null +++ b/doc/api/perf_hooks.md @@ -0,0 +1,656 @@ +# Performance Timing API + + +The Performance Timing API provides an implementation of the +[W3C Performance Timeline][] specification. The purpose of the API +is to support collection of high resolution performance metrics. +This is the same Performance API as implemented in modern Web browsers. + +```js +const { performance } = require('perf_hooks'); +performance.mark('A'); +doSomeLongRunningProcess(() => { + performance.mark('B'); + performance.measure('A to B', 'A', 'B'); + const measure = performance.getEntriesByName('A to B')[0]; + console.log(measure.duration); + // Prints the number of milliseconds between Mark 'A' and Mark 'B' +}); +``` + +## Class: Performance + + +The `Performance` provides access to performance metric data. A single +instance of this class is provided via the `performance` property. + +### performance.clearFunctions([name]) + + +* `name` {string} + +If `name` is not provided, removes all `PerformanceFunction` objects from the +Performance Timeline. If `name` is provided, removes entries with `name`. + +### performance.clearMarks([name]) + + +* `name` {string} + +If `name` is not provided, removes all `PerformanceMark` objects from the +Performance Timeline. If `name` is provided, removes only the named mark. + +### performance.clearMeasures([name]) + + +* `name` {string} + +If `name` is not provided, removes all `PerformanceMeasure` objects from the +Performance Timeline. If `name` is provided, removes only objects whose +`performanceEntry.name` matches `name`. + +### performance.getEntries() + + +* Returns: {Array} + +Returns a list of all `PerformanceEntry` objects in chronological order +with respect to `performanceEntry.startTime`. + +### performance.getEntriesByName(name[, type]) + + +* `name` {string} +* `type` {string} +* Returns: {Array} + +Returns a list of all `PerformanceEntry` objects in chronological order +with respect to `performanceEntry.startTime` whose `performanceEntry.name` is +equal to `name`, and optionally, whose `performanceEntry.entryType` is equal to +`type`. + +### performance.getEntriesByType(type) + + +* `type` {string} +* Returns: {Array} + +Returns a list of all `PerformanceEntry` objects in chronological order +with respect to `performanceEntry.startTime` whose `performanceEntry.entryType` +is equal to `type`. + +### performance.mark([name]) + + +* `name` {string} + +Creates a new `PerformanceMark` entry in the Performance Timeline. A +`PerformanceMark` is a subclass of `PerformanceEntry` whose +`performanceEntry.entryType` is always `'mark'`, and whose +`performanceEntry.duration` is always `0`. Performance marks are used +to mark specific significant moments in the Performance Timeline. + +### performance.measure(name, startMark, endMark) + + +* `name` {string} +* `startMark` {string} +* `endMark` {string} + +Creates a new `PerformanceMeasure` entry in the Performance Timeline. A +`PerformanceMeasure` is a subclass of `PerformanceEntry` whose +`performanceEntry.entryType` is always `'measure'`, and whose +`performanceEntry.duration` measures the number of milliseconds elapsed since +`startMark` and `endMark`. + +The `startMark` argument may identify any *existing* `PerformanceMark` in the +the Performance Timeline, or *may* identify any of the timestamp properties +provided by the `PerformanceNodeTiming` class. If the named `startMark` does +not exist, then `startMark` is set to [`timeOrigin`][] by default. + +The `endMark` argument must identify any *existing* `PerformanceMark` in the +the Performance Timeline or any of the timestamp properties provided by the +`PerformanceNodeTiming` class. If the named `endMark` does not exist, an +error will be thrown. + +### performance.nodeFrame + + +* {PerformanceFrame} + +An instance of the `PerformanceFrame` class that provides performance metrics +for the event loop. + +### performance.nodeTiming + + +* {PerformanceNodeTiming} + +An instance of the `PerformanceNodeTiming` class that provides performance +metrics for specific Node.js operational milestones. + +### performance.now() + + +* Returns: {number} + +Returns the current high resolution millisecond timestamp. + +### performance.timeOrigin + + +* {number} + +The [`timeOrigin`][] specifies the high resolution millisecond timestamp from +which all performance metric durations are measured. + +### performance.timerify(fn) + + +* `fn` {Function} + +Wraps a function within a new function that measures the running time of the +wrapped function. A `PerformanceObserver` must be subscribed to the `'function'` +event type in order for the timing details to be accessed. + +```js +const { + performance, + PerformanceObserver +} = require('perf_hooks'); + +function someFunction() { + console.log('hello world'); +} + +const wrapped = performance.timerify(someFunction); + +const obs = new PerformanceObserver((list) => { + console.log(list.getEntries()[0].duration); + obs.disconnect(); + performance.clearFunctions(); +}); +obs.observe({ entryTypes: 'function' }); + +// A performance timeline entry will be created +wrapped(); +``` + +## Class: PerformanceEntry + + +### performanceEntry.duration + + +* {number} + +The total number of milliseconds elapsed for this entry. This value will not +be meaningful for all Performance Entry types. + +### performanceEntry.name + + +* {string} + +The name of the performance entry. + +### performanceEntry.startTime + + +* {number} + +The high resolution millisecond timestamp marking the starting time of the +Performance Entry. + +### performanceEntry.entryType + + +* {string} + +The type of the performance entry. Current it may be one of: `'node'`, `'mark'`, +`'measure'`, `'gc'`, or `'function'`. + +### performanceEntry.kind + + +* {number} + +When `performanceEntry.entryType` is equal to `'gc'`, the `performance.kind` +property identifies the type of garbage collection operation that occurred. +The value may be one of: + +* `perf_hooks.constants.NODE_PERFORMANCE_GC_MAJOR` +* `perf_hooks.constants.NODE_PERFORMANCE_GC_MINOR` +* `perf_hooks.constants.NODE_PERFORMANCE_GC_INCREMENTAL` +* `perf_hooks.constants.NODE_PERFORMANCE_GC_WEAKCB` + +## Class: PerformanceNodeFrame extends PerformanceEntry + + +Provides timing details for the Node.js event loop. + +### performanceNodeFrame.frameCheck + +The high resolution timestamp when `uv_check_t` processing occurred on the +current loop. + +### performanceNodeFrame.frameCount + +The total number of event loop iterations (iterated when `uv_idle_t` +processing occurrs). + +### performanceNodeFrame.frameIdle + +The high resolution timestamp when `uv_idle_t` processing occurred on the +current loop. + +### performanceNodeFrame.framesPerSecond + +The number of event loop iterations per second. + +### performanceNodeFrame.framePrepare + +The high resolution timestamp when `uv_prepare_t` processing occurred on the +current loop. + +## Class: PerformanceNodeTiming extends PerformanceEntry + + +Provides timing details for Node.js itself. + +### performanceNodeTiming.bootstrapComplete + + +* {number} + +The high resolution millisecond timestamp at which the Node.js process +completed bootstrap. + +### performanceNodeTiming.clusterSetupEnd + + +* {number} + +The high resolution millisecond timestamp at which cluster processing ended. + +### performanceNodeTiming.clusterSetupStart + + +* {number} + +The high resolution millisecond timestamp at which cluster processing started. + +### performanceNodeTiming.loopExit + + +* {number} + +The high resolution millisecond timestamp at which the Node.js event loop +exited. + +### performanceNodeTiming.loopStart + + +* {number} + +The high resolution millisecond timestamp at which the Node.js event loop +started. + +### performanceNodeTiming.moduleLoadEnd + + +* {number} + +The high resolution millisecond timestamp at which main module load ended. + +### performanceNodeTiming.moduleLoadStart + + +* {number} + +The high resolution millisecond timestamp at which main module load started. + +### performanceNodeTiming.nodeStart + + +* {number} + +The high resolution millisecond timestamp at which the Node.js process was +initialized. + +### performanceNodeTiming.preloadModuleLoadEnd + + +* {number} + +The high resolution millisecond timestamp at which preload module load ended. + +### performanceNodeTiming.preloadModuleLoadStart + + +* {number} + +The high resolution millisecond timestamp at which preload module load started. + +### performanceNodeTiming.thirdPartyMainEnd + + +* {number} + +The high resolution millisecond timestamp at which third_party_main processing +ended. + +### performanceNodeTiming.thirdPartyMainStart + + +* {number} + +The high resolution millisecond timestamp at which third_party_main processing +started. + +### performanceNodeTiming.v8Start + + +* {number} + +The high resolution millisecond timestamp at which the V8 platform was +initialized. + + +## Class: PerformanceObserver(callback) + + +* `callback` {Function} A `PerformanceObserverCallback` callback function. + +`PerformanceObserver` objects provide notifications when new +`PerformanceEntry` instances have been added to the Performance Timeline. + +```js +const { + performance, + PerformanceObserver +} = require('perf_hooks'); + +const obs = new PerformanceObserver((list, observer) => { + console.log(list.getEntries()); + observer.disconnect(); +}); +obs.observe({ entryTypes: ['mark'], buffered: true }); + +performance.mark('test'); +``` + +Because `PerformanceObserver` instances introduce their own additional +performance overhead, instances should not be left subscribed to notifications +indefinitely. Users should disconnect observers as soon as they are no +longer needed. + +### Callback: PerformanceObserverCallback(list, observer) + + +* `list` {PerformanceObserverEntryList} +* `observer` {PerformanceObserver} + +The `PerformanceObserverCallback` is invoked when a `PerformanceObserver` is +notified about new `PerformanceEntry` instances. The callback receives a +`PerformanceObserverEntryList` instance and a reference to the +`PerformanceObserver`. + +### Class: PerformanceObserverEntryList + + +The `PerformanceObserverEntryList` class is used to provide access to the +`PerformanceEntry` instances passed to a `PerformanceObserver`. + +#### performanceObserverEntryList.getEntries() + + +* Returns: {Array} + +Returns a list of `PerformanceEntry` objects in chronological order +with respect to `performanceEntry.startTime`. + +#### performanceObserverEntryList.getEntriesByName(name[, type]) + + +* `name` {string} +* `type` {string} +* Returns: {Array} + +Returns a list of `PerformanceEntry` objects in chronological order +with respect to `performanceEntry.startTime` whose `performanceEntry.name` is +equal to `name`, and optionally, whose `performanceEntry.entryType` is equal to +`type`. + +#### performanceObserverEntryList.getEntriesByType(type) + + +* `type` {string} +* Returns: {Array} + +Returns a list of `PerformanceEntry` objects in chronological order +with respect to `performanceEntry.startTime` whose `performanceEntry.entryType` +is equal to `type`. + +### performanceObserver.disconnect() + +Disconnects the `PerformanceObserver` instance from all notifications. + +### performanceObserver.observe(options) + +* `options` {Object} + * `entryTypes` {Array} An array of strings identifying the types of + `PerformanceEntry` instances the observer is interested in. If not + provided an error will be thrown. + * `buffered` {boolean} If true, the notification callback will be + called using `setImmediate()` and multiple `PerformanceEntry` instance + notifications will be buffered internally. If `false`, notifications will + be immediate and synchronous. Defaults to `false`. + +Subscribes the `PerformanceObserver` instance to notifications of new +`PerformanceEntry` instances identified by `options.entryTypes`. + +When `options.buffered` is `false`, the `callback` will be invoked once for +every `PerformanceEntry` instance: + +```js +const { + performance, + PerformanceObserver +} = require('perf_hooks'); + +const obs = new PerformanceObserver((list, observer) => { + // called three times synchronously. list contains one item +}); +obs.observe({ entryTypes: ['mark'] }); + +for (let n = 0; n < 3; n++) + performance.mark(`test${n}`); +``` + +```js +const { + performance, + PerformanceObserver +} = require('perf_hooks'); + +const obs = new PerformanceObserver((list, observer) => { + // called once. list contains three items +}); +obs.observe({ entryTypes: ['mark'], buffered: true }); + +for (let n = 0; n < 3; n++) + performance.mark(`test${n}`); +``` + +## Examples + +### Measuring the duration of async operations + +The following example uses the [Async Hooks][] and Performance APIs to measure +the actual duration of a Timeout operation (including the amount of time it +to execute the callback). + +```js +'use strict'; +const async_hooks = require('async_hooks'); +const { + performance, + PerformanceObserver +} = require('perf_hooks'); + +const set = new Set(); +const hook = async_hooks.createHook({ + init(id, type) { + if (type === 'Timeout') { + performance.mark(`Timeout-${id}-Init`); + set.add(id); + } + }, + destroy(id) { + if (set.has(id)) { + set.delete(id); + performance.mark(`Timeout-${id}-Destroy`); + performance.measure(`Timeout-${id}`, + `Timeout-${id}-Init`, + `Timeout-${id}-Destroy`); + } + } +}); +hook.enable(); + +const obs = new PerformanceObserver((list, observer) => { + console.log(list.getEntries()[0]); + performance.clearMarks(); + performance.clearMeasures(); + observer.disconnect(); +}); +obs.observe({ entryTypes: ['measure'], buffered: true }); + +setTimeout(() => {}, 1000); +``` + +### Measuring how long it takes to load dependencies + +The following example measures the duration of `require()` operations to load +dependencies: + + +```js +'use strict'; +const { + performance, + PerformanceObserver +} = require('perf_hooks'); +const mod = require('module'); + +// Monkey patch the require function +mod.Module.prototype.require = + performance.timerify(mod.Module.prototype.require); +require = performance.timerify(require); + +// Activate the observer +const obs = new PerformanceObserver((list) => { + const entries = list.getEntries(); + entries.forEach((entry) => { + console.log(`require('${entry[0]}')`, entry.duration); + }); + obs.disconnect(); + // Free memory + performance.clearFunctions(); +}); +obs.observe({ entryTypes: ['function'], buffered: true }); + +require('some-module'); +``` + +[`timeOrigin`]: https://w3c.github.io/hr-time/#dom-performance-timeorigin +[W3C Performance Timeline]: https://w3c.github.io/performance-timeline/ diff --git a/doc/api/process.md b/doc/api/process.md index 9dae7693313826..80498aac0766af 100644 --- a/doc/api/process.md +++ b/doc/api/process.md @@ -1851,11 +1851,11 @@ cases: [Cluster]: cluster.html [Duplex]: stream.html#stream_duplex_and_transform_streams [LTS]: https://github.com/nodejs/LTS/ +[note on process I/O]: process.html#process_a_note_on_process_i_o +[process_emit_warning]: #process_process_emitwarning_warning_type_code_ctor +[process_warning]: #process_event_warning [Readable]: stream.html#stream_readable_streams [Signal Events]: #process_signal_events [Stream compatibility]: stream.html#stream_compatibility_with_older_node_js_versions [TTY]: tty.html#tty_tty [Writable]: stream.html#stream_writable_streams -[note on process I/O]: process.html#process_a_note_on_process_i_o -[process_emit_warning]: #process_process_emitwarning_warning_type_code_ctor -[process_warning]: #process_event_warning diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index b9410eb071d298..20e0ae3f7ff617 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -33,8 +33,21 @@ } const _process = NativeModule.require('internal/process'); + const perf = process.binding('performance'); + const { + NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE, + NODE_PERFORMANCE_MILESTONE_THIRD_PARTY_MAIN_START, + NODE_PERFORMANCE_MILESTONE_THIRD_PARTY_MAIN_END, + NODE_PERFORMANCE_MILESTONE_CLUSTER_SETUP_START, + NODE_PERFORMANCE_MILESTONE_CLUSTER_SETUP_END, + NODE_PERFORMANCE_MILESTONE_MODULE_LOAD_START, + NODE_PERFORMANCE_MILESTONE_MODULE_LOAD_END, + NODE_PERFORMANCE_MILESTONE_PRELOAD_MODULE_LOAD_START, + NODE_PERFORMANCE_MILESTONE_PRELOAD_MODULE_LOAD_END + } = perf.constants; _process.setup_hrtime(); + _process.setup_performance(); _process.setup_cpuUsage(); _process.setupMemoryUsage(); _process.setupConfig(NativeModule._source); @@ -106,7 +119,9 @@ // one to drop a file lib/_third_party_main.js into the build // directory which will be executed instead of Node's normal loading. process.nextTick(function() { + perf.markMilestone(NODE_PERFORMANCE_MILESTONE_THIRD_PARTY_MAIN_START); NativeModule.require('_third_party_main'); + perf.markMilestone(NODE_PERFORMANCE_MILESTONE_THIRD_PARTY_MAIN_END); }); } else if (process.argv[1] === 'inspect' || process.argv[1] === 'debug') { @@ -131,22 +146,30 @@ // channel. This needs to be done before any user code gets executed // (including preload modules). if (process.argv[1] && process.env.NODE_UNIQUE_ID) { + perf.markMilestone(NODE_PERFORMANCE_MILESTONE_CLUSTER_SETUP_START); const cluster = NativeModule.require('cluster'); cluster._setupWorker(); - + perf.markMilestone(NODE_PERFORMANCE_MILESTONE_CLUSTER_SETUP_END); // Make sure it's not accidentally inherited by child processes. delete process.env.NODE_UNIQUE_ID; } if (process._eval != null && !process._forceRepl) { + perf.markMilestone(NODE_PERFORMANCE_MILESTONE_MODULE_LOAD_START); + perf.markMilestone(NODE_PERFORMANCE_MILESTONE_MODULE_LOAD_END); // User passed '-e' or '--eval' arguments to Node without '-i' or // '--interactive' + + perf.markMilestone( + NODE_PERFORMANCE_MILESTONE_PRELOAD_MODULE_LOAD_START); preloadModules(); + perf.markMilestone(NODE_PERFORMANCE_MILESTONE_PRELOAD_MODULE_LOAD_END); const internalModule = NativeModule.require('internal/module'); internalModule.addBuiltinLibsToObject(global); evalScript('[eval]'); } else if (process.argv[1] && process.argv[1] !== '-') { + perf.markMilestone(NODE_PERFORMANCE_MILESTONE_MODULE_LOAD_START); // make process.argv[1] into a full path const path = NativeModule.require('path'); process.argv[1] = path.resolve(process.argv[1]); @@ -162,11 +185,21 @@ checkScriptSyntax(source, filename); process.exit(0); } - + perf.markMilestone(NODE_PERFORMANCE_MILESTONE_MODULE_LOAD_END); + perf.markMilestone( + NODE_PERFORMANCE_MILESTONE_PRELOAD_MODULE_LOAD_START); preloadModules(); + perf.markMilestone( + NODE_PERFORMANCE_MILESTONE_PRELOAD_MODULE_LOAD_END); Module.runMain(); } else { + perf.markMilestone(NODE_PERFORMANCE_MILESTONE_MODULE_LOAD_START); + perf.markMilestone(NODE_PERFORMANCE_MILESTONE_MODULE_LOAD_END); + perf.markMilestone( + NODE_PERFORMANCE_MILESTONE_PRELOAD_MODULE_LOAD_START); preloadModules(); + perf.markMilestone( + NODE_PERFORMANCE_MILESTONE_PRELOAD_MODULE_LOAD_END); // If -i or --interactive were passed, or stdin is a TTY. if (process._forceRepl || NativeModule.require('tty').isatty(0)) { // REPL @@ -210,6 +243,7 @@ } } } + perf.markMilestone(NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE); } function setupProcessObject() { diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 2b06583dd483e3..d81360b10cca44 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -196,6 +196,7 @@ E('ERR_INVALID_OPT_VALUE', (name, value) => { return `The value "${String(value)}" is invalid for option "${name}"`; }); +E('ERR_INVALID_PERFORMANCE_MARK', 'The "%s" performance mark has not been set'); E('ERR_INVALID_SYNC_FORK_INPUT', (value) => { return 'Asynchronous forks do not support Buffer, Uint8Array or string' + @@ -229,6 +230,8 @@ E('ERR_UNKNOWN_BUILTIN_MODULE', (id) => `No such built-in module: ${id}`); E('ERR_UNKNOWN_SIGNAL', (signal) => `Unknown signal: ${signal}`); E('ERR_UNKNOWN_STDIN_TYPE', 'Unknown stdin file type'); E('ERR_UNKNOWN_STREAM_TYPE', 'Unknown stream file type'); +E('ERR_VALID_PERFORMANCE_ENTRY_TYPE', + 'At least one valid performance entry type is required'); // Add new errors from here... function invalidArgType(name, expected, actual) { diff --git a/lib/internal/module.js b/lib/internal/module.js index cf994b51c0675f..a6da58a8d73663 100644 --- a/lib/internal/module.js +++ b/lib/internal/module.js @@ -79,8 +79,8 @@ function stripShebang(content) { const builtinLibs = [ 'assert', 'async_hooks', 'buffer', 'child_process', 'cluster', 'crypto', 'dgram', 'dns', 'domain', 'events', 'fs', 'http', 'https', 'net', - 'os', 'path', 'punycode', 'querystring', 'readline', 'repl', 'stream', - 'string_decoder', 'tls', 'tty', 'url', 'util', 'v8', 'vm', 'zlib' + 'os', 'path', 'perf_hooks', 'punycode', 'querystring', 'readline', 'repl', + 'stream', 'string_decoder', 'tls', 'tty', 'url', 'util', 'v8', 'vm', 'zlib' ]; const { exposeHTTP2 } = process.binding('config'); diff --git a/lib/internal/process.js b/lib/internal/process.js index 2e11249cb2dd30..92be0b674056fd 100644 --- a/lib/internal/process.js +++ b/lib/internal/process.js @@ -16,6 +16,10 @@ const assert = process.assert = function(x, msg) { }; +function setup_performance() { + require('perf_hooks'); +} + // Set up the process.cpuUsage() function. function setup_cpuUsage() { // Get the native function, which will be replaced with a JS version. @@ -259,6 +263,7 @@ function setupRawDebug() { } module.exports = { + setup_performance, setup_cpuUsage, setup_hrtime, setupMemoryUsage, diff --git a/lib/perf_hooks.js b/lib/perf_hooks.js new file mode 100644 index 00000000000000..4e7a0de7eb37be --- /dev/null +++ b/lib/perf_hooks.js @@ -0,0 +1,553 @@ +'use strict'; + +const { + PerformanceEntry, + mark: _mark, + measure: _measure, + milestones, + observerCounts, + setupObservers, + timeOrigin, + timerify, + constants +} = process.binding('performance'); + +const { + NODE_PERFORMANCE_ENTRY_TYPE_NODE, + NODE_PERFORMANCE_ENTRY_TYPE_MARK, + NODE_PERFORMANCE_ENTRY_TYPE_MEASURE, + NODE_PERFORMANCE_ENTRY_TYPE_GC, + NODE_PERFORMANCE_ENTRY_TYPE_FUNCTION, + + NODE_PERFORMANCE_MILESTONE_NODE_START, + NODE_PERFORMANCE_MILESTONE_V8_START, + NODE_PERFORMANCE_MILESTONE_LOOP_START, + NODE_PERFORMANCE_MILESTONE_LOOP_EXIT, + NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE, + NODE_PERFORMANCE_MILESTONE_ENVIRONMENT, + NODE_PERFORMANCE_MILESTONE_THIRD_PARTY_MAIN_START, + NODE_PERFORMANCE_MILESTONE_THIRD_PARTY_MAIN_END, + NODE_PERFORMANCE_MILESTONE_CLUSTER_SETUP_START, + NODE_PERFORMANCE_MILESTONE_CLUSTER_SETUP_END, + NODE_PERFORMANCE_MILESTONE_MODULE_LOAD_START, + NODE_PERFORMANCE_MILESTONE_MODULE_LOAD_END, + NODE_PERFORMANCE_MILESTONE_PRELOAD_MODULE_LOAD_START, + NODE_PERFORMANCE_MILESTONE_PRELOAD_MODULE_LOAD_END +} = constants; + +const L = require('internal/linkedlist'); +const kInspect = require('internal/util').customInspectSymbol; +const { inherits } = require('util'); + +const kCallback = Symbol('callback'); +const kTypes = Symbol('types'); +const kEntries = Symbol('entries'); +const kBuffer = Symbol('buffer'); +const kBuffering = Symbol('buffering'); +const kQueued = Symbol('queued'); +const kTimerified = Symbol('timerified'); +const kInsertEntry = Symbol('insert-entry'); +const kIndexEntry = Symbol('index-entry'); +const kClearEntry = Symbol('clear-entry'); +const kGetEntries = Symbol('get-entries'); +const kIndex = Symbol('index'); +const kMarks = Symbol('marks'); + +observerCounts[NODE_PERFORMANCE_ENTRY_TYPE_MARK] = 1; +observerCounts[NODE_PERFORMANCE_ENTRY_TYPE_MEASURE] = 1; +const observers = {}; +const observerableTypes = [ + 'node', + 'mark', + 'measure', + 'gc', + 'function' +]; + +let errors; +function lazyErrors() { + if (errors === undefined) + errors = require('internal/errors'); + return errors; +} + +function now() { + const hr = process.hrtime(); + return hr[0] * 1000 + hr[1] / 1e6; +} + +class PerformanceNodeTiming { + constructor() {} + + get name() { + return 'node'; + } + + get entryType() { + return 'node'; + } + + get startTime() { + return timeOrigin; + } + + get duration() { + return now() - timeOrigin; + } + + get nodeStart() { + return milestones[NODE_PERFORMANCE_MILESTONE_NODE_START]; + } + + get v8Start() { + return milestones[NODE_PERFORMANCE_MILESTONE_V8_START]; + } + + get environment() { + return milestones[NODE_PERFORMANCE_MILESTONE_ENVIRONMENT]; + } + + get loopStart() { + return milestones[NODE_PERFORMANCE_MILESTONE_LOOP_START]; + } + + get loopExit() { + return milestones[NODE_PERFORMANCE_MILESTONE_LOOP_EXIT]; + } + + get bootstrapComplete() { + return milestones[NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE]; + } + + get thirdPartyMainStart() { + return milestones[NODE_PERFORMANCE_MILESTONE_THIRD_PARTY_MAIN_START]; + } + + get thirdPartyMainEnd() { + return milestones[NODE_PERFORMANCE_MILESTONE_THIRD_PARTY_MAIN_END]; + } + + get clusterSetupStart() { + return milestones[NODE_PERFORMANCE_MILESTONE_CLUSTER_SETUP_START]; + } + + get clusterSetupEnd() { + return milestones[NODE_PERFORMANCE_MILESTONE_CLUSTER_SETUP_END]; + } + + get moduleLoadStart() { + return milestones[NODE_PERFORMANCE_MILESTONE_MODULE_LOAD_START]; + } + + get moduleLoadEnd() { + return milestones[NODE_PERFORMANCE_MILESTONE_MODULE_LOAD_END]; + } + + get preloadModuleLoadStart() { + return milestones[NODE_PERFORMANCE_MILESTONE_PRELOAD_MODULE_LOAD_START]; + } + + get preloadModuleLoadEnd() { + return milestones[NODE_PERFORMANCE_MILESTONE_PRELOAD_MODULE_LOAD_END]; + } + + [kInspect]() { + return { + name: 'node', + entryType: 'node', + startTime: this.startTime, + duration: this.duration, + nodeStart: this.nodeStart, + v8Start: this.v8Start, + bootstrapComplete: this.bootstrapComplete, + environment: this.environment, + loopStart: this.loopStart, + loopExit: this.loopExit, + thirdPartyMainStart: this.thirdPartyMainStart, + thirdPartyMainEnd: this.thirdPartyMainEnd, + clusterSetupStart: this.clusterSetupStart, + clusterSetupEnd: this.clusterSetupEnd, + moduleLoadStart: this.moduleLoadStart, + moduleLoadEnd: this.moduleLoadEnd, + preloadModuleLoadStart: this.preloadModuleLoadStart, + preloadModuleLoadEnd: this.preloadModuleLoadEnd + }; + } +} +// Use this instead of Extends because we want PerformanceEntry in the +// prototype chain but we do not want to use the PerformanceEntry +// constructor for this. +inherits(PerformanceNodeTiming, PerformanceEntry); + +const nodeTiming = new PerformanceNodeTiming(); + +// Maintains a list of entries as a linked list stored in insertion order. +class PerformanceObserverEntryList { + constructor() { + Object.defineProperty(this, kEntries, { + writable: true, + enumerable: false, + value: {} + }); + L.init(this[kEntries]); + } + + [kInsertEntry](entry) { + const item = { entry }; + L.append(this[kEntries], item); + this[kIndexEntry](item); + } + + [kIndexEntry](entry) { + // Default implementation does nothing + } + + [kGetEntries](name, type) { + const ret = []; + const list = this[kEntries]; + if (!L.isEmpty(list)) { + let item = L.peek(list); + while (item && item !== list) { + const entry = item.entry; + if ((name && entry.name !== name) || + (type && entry.entryType !== type)) { + item = item._idlePrev; + continue; + } + sortedInsert(ret, entry); + item = item._idlePrev; + } + } + return ret; + } + + // While the items are stored in insertion order, getEntries() is + // required to return items sorted by startTime. + getEntries() { + return this[kGetEntries](); + } + + getEntriesByType(type) { + return this[kGetEntries](undefined, `${type}`); + } + + getEntriesByName(name, type) { + return this[kGetEntries](`${name}`, type !== undefined ? `${type}` : type); + } +} + +class PerformanceObserver { + constructor(callback) { + if (typeof callback !== 'function') { + const errors = lazyErrors(); + throw new errors.TypeError('ERR_INVALID_CALLBACK'); + } + Object.defineProperties(this, { + [kTypes]: { + enumerable: false, + writable: true, + value: {} + }, + [kCallback]: { + enumerable: false, + writable: true, + value: callback + }, + [kBuffer]: { + enumerable: false, + writable: true, + value: new PerformanceObserverEntryList() + }, + [kBuffering]: { + enumerable: false, + writable: true, + value: false + }, + [kQueued]: { + enumerable: false, + writable: true, + value: false + } + }); + } + + disconnect() { + const types = this[kTypes]; + const keys = Object.keys(types); + for (var n = 0; n < keys.length; n++) { + const item = types[keys[n]]; + if (item) { + L.remove(item); + observerCounts[keys[n]]--; + } + } + this[kTypes] = {}; + } + + observe(options) { + const errors = lazyErrors(); + if (typeof options !== 'object' || options == null) { + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'options', 'Object'); + } + if (!Array.isArray(options.entryTypes)) { + throw new errors.TypeError('ERR_INVALID_OPT_VALUE', + 'entryTypes', options); + } + const entryTypes = options.entryTypes.filter(filterTypes).map(mapTypes); + if (entryTypes.length === 0) { + throw new errors.Error('ERR_VALID_PERFORMANCE_ENTRY_TYPE'); + } + this.disconnect(); + this[kBuffer][kEntries] = []; + L.init(this[kBuffer][kEntries]); + this[kBuffering] = Boolean(options.buffered); + for (var n = 0; n < entryTypes.length; n++) { + const entryType = entryTypes[n]; + const list = getObserversList(entryType); + const item = { obs: this }; + this[kTypes][entryType] = item; + L.append(list, item); + observerCounts[entryType]++; + } + } +} + +class Performance extends PerformanceObserverEntryList { + constructor() { + super(); + this[kIndex] = { + [kMarks]: new Set() + }; + this[kInsertEntry](nodeTiming); + } + + [kIndexEntry](item) { + const index = this[kIndex]; + const type = item.entry.entryType; + let items = index[type]; + if (!items) { + items = index[type] = {}; + L.init(items); + } + const entry = item.entry; + L.append(items, { entry, item }); + } + + [kClearEntry](type, name) { + const index = this[kIndex]; + const items = index[type]; + if (!items) return; + let item = L.peek(items); + while (item && item !== items) { + const entry = item.entry; + const next = item._idlePrev; + if (name !== undefined) { + if (entry.name === `${name}`) { + L.remove(item); // remove from the index + L.remove(item.item); // remove from the master + } + } else { + L.remove(item); // remove from the index + L.remove(item.item); // remove from the master + } + item = next; + } + } + + get nodeTiming() { + return nodeTiming; + } + + get timeOrigin() { + return timeOrigin; + } + + now() { + return now(); + } + + mark(name) { + name = `${name}`; + _mark(name); + this[kIndex][kMarks].add(name); + } + + measure(name, startMark, endMark) { + name = `${name}`; + endMark = `${endMark}`; + startMark = startMark !== undefined ? `${startMark}` : ''; + const marks = this[kIndex][kMarks]; + if (!marks.has(endMark) && !(endMark in nodeTiming)) { + const errors = lazyErrors(); + throw new errors.Error('ERR_INVALID_PERFORMANCE_MARK', endMark); + } + _measure(name, startMark, endMark); + } + + clearMarks(name) { + name = name !== undefined ? `${name}` : name; + this[kClearEntry]('mark', name); + if (name !== undefined) + this[kIndex][kMarks].delete(name); + else + this[kIndex][kMarks].clear(); + } + + clearMeasures(name) { + this[kClearEntry]('measure', name); + } + + clearGC() { + this[kClearEntry]('gc'); + } + + clearFunctions(name) { + this[kClearEntry]('function', name); + } + + timerify(fn) { + if (typeof fn !== 'function') { + const errors = lazyErrors(); + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'fn', 'Function'); + } + if (fn[kTimerified]) + return fn[kTimerified]; + const ret = timerify(fn, fn.length); + Object.defineProperty(fn, kTimerified, { + enumerable: false, + configurable: true, + writable: false, + value: ret + }); + Object.defineProperties(ret, { + [kTimerified]: { + enumerable: false, + configurable: true, + writable: false, + value: ret + }, + name: { + enumerable: false, + configurable: true, + writable: false, + value: `timerified ${fn.name}` + } + }); + return ret; + } + + [kInspect]() { + return { + timeOrigin, + nodeTiming + }; + } +} + +const performance = new Performance(); + +function getObserversList(type) { + let list = observers[type]; + if (list === undefined) { + list = observers[type] = {}; + L.init(list); + } + return list; +} + +function doNotify() { + this[kQueued] = false; + this[kCallback](this[kBuffer], this); + this[kBuffer][kEntries] = []; + L.init(this[kBuffer][kEntries]); +} + +// Set up the callback used to receive PerformanceObserver notifications +function observersCallback(entry) { + const type = mapTypes(entry.entryType); + performance[kInsertEntry](entry); + const list = getObserversList(type); + + let current = L.peek(list); + + while (current && current.obs) { + const observer = current.obs; + // First, add the item to the observers buffer + const buffer = observer[kBuffer]; + buffer[kInsertEntry](entry); + // Second, check to see if we're buffering + if (observer[kBuffering]) { + // If we are, schedule a setImmediate call if one hasn't already + if (!observer[kQueued]) { + observer[kQueued] = true; + // Use setImmediate instead of nextTick to give more time + // for multiple entries to collect. + setImmediate(doNotify.bind(observer)); + } + } else { + // If not buffering, notify immediately + doNotify.call(observer); + } + current = current._idlePrev; + } +} +setupObservers(observersCallback); + +function filterTypes(i) { + return observerableTypes.indexOf(`${i}`) >= 0; +} + +function mapTypes(i) { + switch (i) { + case 'node': return NODE_PERFORMANCE_ENTRY_TYPE_NODE; + case 'mark': return NODE_PERFORMANCE_ENTRY_TYPE_MARK; + case 'measure': return NODE_PERFORMANCE_ENTRY_TYPE_MEASURE; + case 'gc': return NODE_PERFORMANCE_ENTRY_TYPE_GC; + case 'function': return NODE_PERFORMANCE_ENTRY_TYPE_FUNCTION; + } +} + +// The specification requires that PerformanceEntry instances are sorted +// according to startTime. Unfortunately, they are not necessarily created +// in that same order, and can be reported to the JS layer in any order, +// which means we need to keep the list sorted as we insert. +function getInsertLocation(list, entryStartTime) { + let start = 0; + let end = list.length; + while (start < end) { + const pivot = (end + start) >>> 1; + if (list[pivot].startTime === entryStartTime) + return pivot; + if (list[pivot].startTime < entryStartTime) + start = pivot + 1; + else + end = pivot; + } + return start; +} + +function sortedInsert(list, entry) { + const entryStartTime = entry.startTime; + if (list.length === 0 || + (list[list.length - 1].startTime < entryStartTime)) { + list.push(entry); + return; + } + if (list[0] && (list[0].startTime > entryStartTime)) { + list.unshift(entry); + return; + } + const location = getInsertLocation(list, entryStartTime); + list.splice(location, 0, entry); +} + +module.exports = { + performance, + PerformanceObserver +}; + +Object.defineProperty(module.exports, 'constants', { + configurable: false, + enumerable: true, + value: constants +}); diff --git a/node.gyp b/node.gyp index 6ff0cb02ccaacf..14acf375e128a4 100644 --- a/node.gyp +++ b/node.gyp @@ -50,6 +50,7 @@ 'lib/net.js', 'lib/os.js', 'lib/path.js', + 'lib/perf_hooks.js', 'lib/process.js', 'lib/punycode.js', 'lib/querystring.js', @@ -191,6 +192,7 @@ 'src/node_main.cc', 'src/node_os.cc', 'src/node_platform.cc', + 'src/node_perf.cc', 'src/node_serdes.cc', 'src/node_url.cc', 'src/node_util.cc', @@ -239,6 +241,8 @@ 'src/node_javascript.h', 'src/node_mutex.h', 'src/node_platform.h', + 'src/node_perf.h', + 'src/node_perf_common.h', 'src/node_root_certs.h', 'src/node_version.h', 'src/node_watchdog.h', @@ -657,6 +661,7 @@ '<(OBJ_PATH)<(OBJ_SEPARATOR)node.<(OBJ_SUFFIX)', '<(OBJ_PATH)<(OBJ_SEPARATOR)node_buffer.<(OBJ_SUFFIX)', '<(OBJ_PATH)<(OBJ_SEPARATOR)node_i18n.<(OBJ_SUFFIX)', + '<(OBJ_PATH)<(OBJ_SEPARATOR)node_perf.<(OBJ_SUFFIX)', '<(OBJ_PATH)<(OBJ_SEPARATOR)node_url.<(OBJ_SUFFIX)', '<(OBJ_PATH)<(OBJ_SEPARATOR)util.<(OBJ_SUFFIX)', '<(OBJ_PATH)<(OBJ_SEPARATOR)string_bytes.<(OBJ_SUFFIX)', diff --git a/src/env-inl.h b/src/env-inl.h index 69b5a5e858b458..d31b3602e97e79 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -322,6 +322,16 @@ inline Environment::Environment(IsolateData* isolate_data, AssignToContext(context); destroy_ids_list_.reserve(512); + performance_state_ = Calloc(1); + performance_state_->milestones[ + performance::NODE_PERFORMANCE_MILESTONE_ENVIRONMENT] = + PERFORMANCE_NOW(); + performance_state_->milestones[ + performance::NODE_PERFORMANCE_MILESTONE_NODE_START] = + performance::performance_node_start; + performance_state_->milestones[ + performance::NODE_PERFORMANCE_MILESTONE_V8_START] = + performance::performance_v8_start; } inline Environment::~Environment() { @@ -337,6 +347,7 @@ inline Environment::~Environment() { delete[] heap_space_statistics_buffer_; delete[] http_parser_buffer_; free(http2_state_buffer_); + free(performance_state_); } inline v8::Isolate* Environment::isolate() const { @@ -504,6 +515,41 @@ inline void Environment::set_fs_stats_field_array(double* fields) { fs_stats_field_array_ = fields; } +inline performance::performance_state* Environment::performance_state() { + return performance_state_; +} + +inline std::map* Environment::performance_marks() { + return &performance_marks_; +} + +inline Environment* Environment::from_performance_check_handle( + uv_check_t* handle) { + return ContainerOf(&Environment::performance_check_handle_, handle); +} + +inline Environment* Environment::from_performance_idle_handle( + uv_idle_t* handle) { + return ContainerOf(&Environment::performance_idle_handle_, handle); +} + +inline Environment* Environment::from_performance_prepare_handle( + uv_prepare_t* handle) { + return ContainerOf(&Environment::performance_prepare_handle_, handle); +} + +inline uv_check_t* Environment::performance_check_handle() { + return &performance_check_handle_; +} + +inline uv_idle_t* Environment::performance_idle_handle() { + return &performance_idle_handle_; +} + +inline uv_prepare_t* Environment::performance_prepare_handle() { + return &performance_prepare_handle_; +} + inline IsolateData* Environment::isolate_data() const { return isolate_data_; } diff --git a/src/env.h b/src/env.h index 7ec6bfe08bb7e9..02f740cfa3e9e8 100644 --- a/src/env.h +++ b/src/env.h @@ -36,6 +36,7 @@ #include "node.h" #include +#include #include #include #include @@ -104,6 +105,7 @@ struct http2_state; V(callback_string, "callback") \ V(change_string, "change") \ V(channel_string, "channel") \ + V(constants_string, "constants") \ V(oncertcb_string, "oncertcb") \ V(onclose_string, "_onclose") \ V(code_string, "code") \ @@ -303,6 +305,8 @@ struct http2_state; V(module_load_list_array, v8::Array) \ V(pbkdf2_constructor_template, v8::ObjectTemplate) \ V(pipe_constructor_template, v8::FunctionTemplate) \ + V(performance_entry_callback, v8::Function) \ + V(performance_entry_template, v8::Function) \ V(process_object, v8::Object) \ V(promise_reject_function, v8::Function) \ V(promise_wrap_template, v8::ObjectTemplate) \ @@ -612,6 +616,17 @@ class Environment { inline double* fs_stats_field_array() const; inline void set_fs_stats_field_array(double* fields); + inline performance::performance_state* performance_state(); + inline std::map* performance_marks(); + + static inline Environment* from_performance_check_handle(uv_check_t* handle); + static inline Environment* from_performance_idle_handle(uv_idle_t* handle); + static inline Environment* from_performance_prepare_handle( + uv_prepare_t* handle); + inline uv_check_t* performance_check_handle(); + inline uv_idle_t* performance_idle_handle(); + inline uv_prepare_t* performance_prepare_handle(); + inline void ThrowError(const char* errmsg); inline void ThrowTypeError(const char* errmsg); inline void ThrowRangeError(const char* errmsg); @@ -691,6 +706,10 @@ class Environment { uv_timer_t destroy_ids_timer_handle_; uv_prepare_t idle_prepare_handle_; uv_check_t idle_check_handle_; + uv_prepare_t performance_prepare_handle_; + uv_check_t performance_check_handle_; + uv_idle_t performance_idle_handle_; + AsyncHooks async_hooks_; DomainFlag domain_flag_; TickInfo tick_info_; @@ -701,6 +720,10 @@ class Environment { bool abort_on_uncaught_exception_; size_t makecallback_cntr_; std::vector destroy_ids_list_; + + performance::performance_state* performance_state_ = nullptr; + std::map performance_marks_; + #if HAVE_INSPECTOR inspector::Agent inspector_agent_; #endif diff --git a/src/node.cc b/src/node.cc index 34785693c8a7ad..bdff4527c67857 100644 --- a/src/node.cc +++ b/src/node.cc @@ -28,6 +28,7 @@ #include "node_internals.h" #include "node_revert.h" #include "node_debug_options.h" +#include "node_perf.h" #if defined HAVE_PERFCTR #include "node_counters.h" @@ -4559,6 +4560,7 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data, { SealHandleScope seal(isolate); bool more; + PERFORMANCE_MARK(&env, LOOP_START); do { uv_run(env.event_loop(), UV_RUN_DEFAULT); @@ -4569,6 +4571,7 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data, // event, or after running some callbacks. more = uv_loop_alive(env.event_loop()); } while (more == true); + PERFORMANCE_MARK(&env, LOOP_EXIT); } env.set_trace_sync_io(false); @@ -4638,6 +4641,7 @@ inline int Start(uv_loop_t* event_loop, int Start(int argc, char** argv) { atexit([] () { uv_tty_reset_mode(); }); PlatformInit(); + node::performance::performance_node_start = PERFORMANCE_NOW(); CHECK_GT(argc, 0); @@ -4674,6 +4678,7 @@ int Start(int argc, char** argv) { v8_platform.StartTracingAgent(); } V8::Initialize(); + node::performance::performance_v8_start = PERFORMANCE_NOW(); v8_initialized = true; const int exit_code = Start(uv_default_loop(), argc, argv, exec_argc, exec_argv); diff --git a/src/node_internals.h b/src/node_internals.h index c3d34cb3ca824d..05cfdbe9256a9e 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -30,6 +30,7 @@ #include "uv.h" #include "v8.h" #include "tracing/trace_event.h" +#include "node_perf_common.h" #include "node_debug_options.h" #include diff --git a/src/node_perf.cc b/src/node_perf.cc new file mode 100644 index 00000000000000..2398bb9b8fd6bb --- /dev/null +++ b/src/node_perf.cc @@ -0,0 +1,389 @@ +#include "node.h" +#include "v8.h" +#include "env.h" +#include "env-inl.h" +#include "node_perf.h" +#include "uv.h" + +#include + +namespace node { +namespace performance { + +using v8::Array; +using v8::ArrayBuffer; +using v8::Context; +using v8::Function; +using v8::FunctionCallbackInfo; +using v8::FunctionTemplate; +using v8::HandleScope; +using v8::Integer; +using v8::Isolate; +using v8::Local; +using v8::Name; +using v8::Object; +using v8::ObjectTemplate; +using v8::PropertyCallbackInfo; +using v8::String; +using v8::Value; + +const uint64_t timeOrigin = PERFORMANCE_NOW(); +uint64_t performance_node_start; +uint64_t performance_v8_start; + +uint64_t performance_last_gc_start_mark_ = 0; +v8::GCType performance_last_gc_type_ = v8::GCType::kGCTypeAll; + +void PerformanceEntry::New(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + Isolate* isolate = env->isolate(); + Utf8Value name(isolate, args[0]); + Utf8Value type(isolate, args[1]); + uint64_t now = PERFORMANCE_NOW(); + new PerformanceEntry(env, args.This(), *name, *type, now, now); +} + +void PerformanceEntry::NotifyObservers(Environment* env, + PerformanceEntry* entry) { + uint32_t* observers = env->performance_state()->observers; + PerformanceEntryType type = ToPerformanceEntryTypeEnum(entry->type().c_str()); + if (observers == nullptr || + type == NODE_PERFORMANCE_ENTRY_TYPE_INVALID || + !observers[type]) { + return; + } + Local context = env->context(); + Isolate* isolate = env->isolate(); + Local argv = entry->object(); + env->performance_entry_callback()->Call(context, + v8::Undefined(isolate), + 1, &argv); +} + +void Mark(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + Local context = env->context(); + Isolate* isolate = env->isolate(); + Utf8Value name(isolate, args[0]); + uint64_t now = PERFORMANCE_NOW(); + auto marks = env->performance_marks(); + (*marks)[*name] = now; + + // TODO(jasnell): Once Tracing API is fully implemented, this should + // record a trace event also. + + Local fn = env->performance_entry_template(); + Local obj = fn->NewInstance(context).ToLocalChecked(); + new PerformanceEntry(env, obj, *name, "mark", now, now); + args.GetReturnValue().Set(obj); +} + +inline uint64_t GetPerformanceMark(Environment* env, std::string name) { + auto marks = env->performance_marks(); + auto res = marks->find(name); + return res != marks->end() ? res->second : 0; +} + +void Measure(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + Local context = env->context(); + Isolate* isolate = env->isolate(); + Utf8Value name(isolate, args[0]); + Utf8Value startMark(isolate, args[1]); + Utf8Value endMark(isolate, args[2]); + + double* milestones = env->performance_state()->milestones; + + uint64_t startTimestamp = timeOrigin; + uint64_t start = GetPerformanceMark(env, *startMark); + if (start != 0) { + startTimestamp = start; + } else { + PerformanceMilestone milestone = ToPerformanceMilestoneEnum(*startMark); + if (milestone != NODE_PERFORMANCE_MILESTONE_INVALID) + startTimestamp = milestones[milestone]; + } + + uint64_t endTimestamp = GetPerformanceMark(env, *endMark); + if (endTimestamp == 0) { + PerformanceMilestone milestone = ToPerformanceMilestoneEnum(*endMark); + if (milestone != NODE_PERFORMANCE_MILESTONE_INVALID) + endTimestamp = milestones[milestone]; + } + + if (endTimestamp < startTimestamp) + endTimestamp = startTimestamp; + + // TODO(jasnell): Once Tracing API is fully implemented, this should + // record a trace event also. + + Local fn = env->performance_entry_template(); + Local obj = fn->NewInstance(context).ToLocalChecked(); + new PerformanceEntry(env, obj, *name, "measure", + startTimestamp, endTimestamp); + args.GetReturnValue().Set(obj); +} + +void GetPerformanceEntryName(const Local prop, + const PropertyCallbackInfo& info) { + Isolate* isolate = info.GetIsolate(); + PerformanceEntry* entry; + ASSIGN_OR_RETURN_UNWRAP(&entry, info.Holder()); + info.GetReturnValue().Set( + String::NewFromUtf8(isolate, entry->name().c_str(), String::kNormalString)); +} + +void GetPerformanceEntryType(const Local prop, + const PropertyCallbackInfo& info) { + Isolate* isolate = info.GetIsolate(); + PerformanceEntry* entry; + ASSIGN_OR_RETURN_UNWRAP(&entry, info.Holder()); + info.GetReturnValue().Set( + String::NewFromUtf8(isolate, entry->type().c_str(), String::kNormalString)); +} + +void GetPerformanceEntryStartTime(const Local prop, + const PropertyCallbackInfo& info) { + PerformanceEntry* entry; + ASSIGN_OR_RETURN_UNWRAP(&entry, info.Holder()); + info.GetReturnValue().Set(entry->startTime()); +} + +void GetPerformanceEntryDuration(const Local prop, + const PropertyCallbackInfo& info) { + PerformanceEntry* entry; + ASSIGN_OR_RETURN_UNWRAP(&entry, info.Holder()); + info.GetReturnValue().Set(entry->duration()); +} + +void MarkMilestone(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + Local context = env->context(); + double* milestones = env->performance_state()->milestones; + PerformanceMilestone milestone = + static_cast( + args[0]->Int32Value(context).ToChecked()); + if (milestone != NODE_PERFORMANCE_MILESTONE_INVALID) { + milestones[milestone] = PERFORMANCE_NOW(); + } +} + +void SetupPerformanceObservers(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK(args[0]->IsFunction()); + env->set_performance_entry_callback(args[0].As()); +} + +inline void PerformanceGCCallback(uv_async_t* handle) { + PerformanceEntry::Data* data = + static_cast(handle->data); + Isolate* isolate = Isolate::GetCurrent(); + HandleScope scope(isolate); + Environment* env = Environment::GetCurrent(isolate); + Local context = env->context(); + Local fn; + Local obj; + PerformanceGCKind kind = static_cast(data->data()); + + uint32_t* observers = env->performance_state()->observers; + if (!observers[NODE_PERFORMANCE_ENTRY_TYPE_GC]) { + goto cleanup; + } + + fn = env->performance_entry_template(); + obj = fn->NewInstance(context).ToLocalChecked(); + obj->Set(context, + FIXED_ONE_BYTE_STRING(isolate, "kind"), + Integer::New(isolate, kind)).FromJust(); + new PerformanceEntry(env, obj, data); + + cleanup: + delete data; + auto closeCB = [](uv_handle_t* handle) { delete handle; }; + uv_close(reinterpret_cast(handle), closeCB); +} + +inline void MarkGarbageCollectionStart(Isolate* isolate, + v8::GCType type, + v8::GCCallbackFlags flags) { + performance_last_gc_start_mark_ = PERFORMANCE_NOW(); + performance_last_gc_type_ = type; +} + +inline void MarkGarbageCollectionEnd(Isolate* isolate, + v8::GCType type, + v8::GCCallbackFlags flags) { + uv_async_t *async = new uv_async_t; + async->data = + new PerformanceEntry::Data("gc", "gc", + performance_last_gc_start_mark_, + PERFORMANCE_NOW(), type); + uv_async_init(uv_default_loop(), async, PerformanceGCCallback); + uv_async_send(async); +} + +inline void SetupGarbageCollectionTracking(Isolate* isolate) { + isolate->AddGCPrologueCallback(MarkGarbageCollectionStart); + isolate->AddGCPrologueCallback(MarkGarbageCollectionEnd); +} + +inline Local GetName(Local fn) { + Local val = fn->GetDebugName(); + if (val.IsEmpty() || val->IsUndefined()) { + Local boundFunction = fn->GetBoundFunction(); + if (!boundFunction.IsEmpty() && !boundFunction->IsUndefined()) { + val = GetName(boundFunction.As()); + } + } + return val; +} + +void TimerFunctionCall(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + Environment* env = Environment::GetCurrent(isolate); + Local context = env->context(); + Local fn = args.Data().As(); + size_t count = args.Length(); + size_t idx; + std::vector> call_args; + for (size_t i = 0; i < count; ++i) { + call_args.push_back(args[i]); + } + + Utf8Value name(isolate, GetName(fn)); + + uint64_t start; + uint64_t end; + v8::TryCatch try_catch(isolate); + if (args.IsConstructCall()) { + start = PERFORMANCE_NOW(); + v8::MaybeLocal ret = fn->NewInstance(context, + call_args.size(), + call_args.data()); + end = PERFORMANCE_NOW(); + if (ret.IsEmpty()) { + try_catch.ReThrow(); + return; + } + args.GetReturnValue().Set(ret.ToLocalChecked()); + } else { + start = PERFORMANCE_NOW(); + v8::MaybeLocal ret = fn->Call(context, + args.This(), + call_args.size(), + call_args.data()); + end = PERFORMANCE_NOW(); + if (ret.IsEmpty()) { + try_catch.ReThrow(); + return; + } + args.GetReturnValue().Set(ret.ToLocalChecked()); + } + + + uint32_t* observers = env->performance_state()->observers; + if (!observers[NODE_PERFORMANCE_ENTRY_TYPE_FUNCTION]) + return; + + Local ctor = env->performance_entry_template(); + v8::MaybeLocal instance = ctor->NewInstance(context); + Local obj = instance.ToLocalChecked(); + for (idx = 0; idx < count; idx++) { + obj->Set(context, idx, args[idx]); + } + new PerformanceEntry(env, obj, *name, "function", start, end); +} + +void Timerify(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + Local context = env->context(); + CHECK(args[0]->IsFunction()); + CHECK(args[1]->IsNumber()); + Local fn = args[0].As(); + int length = args[1]->IntegerValue(context).ToChecked(); + Local wrap = + Function::New(context, TimerFunctionCall, fn, length).ToLocalChecked(); + args.GetReturnValue().Set(wrap); +} + +void Init(Local target, + Local unused, + Local context) { + Environment* env = Environment::GetCurrent(context); + Isolate* isolate = env->isolate(); + performance_state* state = env->performance_state(); + auto state_ab = ArrayBuffer::New(isolate, state, sizeof(*state)); + + #define SET_STATE_TYPEDARRAY(name, type, field) \ + target->Set(context, \ + FIXED_ONE_BYTE_STRING(isolate, (name)), \ + type::New(state_ab, \ + offsetof(performance_state, field), \ + arraysize(state->field))) \ + .FromJust() + SET_STATE_TYPEDARRAY("observerCounts", v8::Uint32Array, observers); + SET_STATE_TYPEDARRAY("milestones", v8::Float64Array, milestones); + #undef SET_STATE_TYPEDARRAY + + Local performanceEntryString = + FIXED_ONE_BYTE_STRING(isolate, "PerformanceEntry"); + + Local pe = env->NewFunctionTemplate(PerformanceEntry::New); + pe->InstanceTemplate()->SetInternalFieldCount(1); + pe->SetClassName(performanceEntryString); + Local ot = pe->InstanceTemplate(); + ot->SetAccessor(env->name_string(), GetPerformanceEntryName); + ot->SetAccessor(FIXED_ONE_BYTE_STRING(isolate, "entryType"), + GetPerformanceEntryType); + ot->SetAccessor(FIXED_ONE_BYTE_STRING(isolate, "startTime"), + GetPerformanceEntryStartTime); + ot->SetAccessor(FIXED_ONE_BYTE_STRING(isolate, "duration"), + GetPerformanceEntryDuration); + Local fn = pe->GetFunction(); + target->Set(performanceEntryString, fn); + env->set_performance_entry_template(fn); + + env->SetMethod(target, "mark", Mark); + env->SetMethod(target, "measure", Measure); + env->SetMethod(target, "markMilestone", MarkMilestone); + env->SetMethod(target, "setupObservers", SetupPerformanceObservers); + env->SetMethod(target, "timerify", Timerify); + + Local constants = Object::New(isolate); + + NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_MAJOR); + NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_MINOR); + NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_INCREMENTAL); + NODE_DEFINE_CONSTANT(constants, NODE_PERFORMANCE_GC_WEAKCB); + +#define V(name, _) \ + NODE_DEFINE_HIDDEN_CONSTANT(constants, NODE_PERFORMANCE_ENTRY_TYPE_##name); + NODE_PERFORMANCE_ENTRY_TYPES(V) +#undef V + +#define V(name, _) \ + NODE_DEFINE_HIDDEN_CONSTANT(constants, NODE_PERFORMANCE_MILESTONE_##name); + NODE_PERFORMANCE_MILESTONES(V) +#undef V + + v8::PropertyAttribute attr = + static_cast(v8::ReadOnly | v8::DontDelete); + + target->DefineOwnProperty(context, + FIXED_ONE_BYTE_STRING(isolate, "timeOrigin"), + v8::Number::New(isolate, timeOrigin / 1e6), + attr); + + target->DefineOwnProperty(context, + env->constants_string(), + constants, + attr); + + SetupGarbageCollectionTracking(isolate); +} + +} // namespace performance +} // namespace node + +NODE_MODULE_CONTEXT_AWARE_BUILTIN(performance, node::performance::Init) diff --git a/src/node_perf.h b/src/node_perf.h new file mode 100644 index 00000000000000..412479c90236d1 --- /dev/null +++ b/src/node_perf.h @@ -0,0 +1,174 @@ +#ifndef SRC_NODE_PERF_H_ +#define SRC_NODE_PERF_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "node.h" +#include "node_perf_common.h" +#include "env.h" +#include "base-object.h" +#include "base-object-inl.h" + +#include "v8.h" +#include "uv.h" + +#include +#include + +namespace node { +namespace performance { + +using v8::FunctionCallbackInfo; +using v8::GCType; +using v8::Local; +using v8::Object; +using v8::Value; + +static inline PerformanceMilestone ToPerformanceMilestoneEnum(const char* str) { +#define V(name, label) \ + if (strcmp(str, label) == 0) return NODE_PERFORMANCE_MILESTONE_##name; + NODE_PERFORMANCE_MILESTONES(V) +#undef V + return NODE_PERFORMANCE_MILESTONE_INVALID; +} + +static inline PerformanceEntryType ToPerformanceEntryTypeEnum( + const char* type) { +#define V(name, label) \ + if (strcmp(type, label) == 0) return NODE_PERFORMANCE_ENTRY_TYPE_##name; + NODE_PERFORMANCE_ENTRY_TYPES(V) +#undef V + return NODE_PERFORMANCE_ENTRY_TYPE_INVALID; +} + +const double MAX_DOUBLE = std::numeric_limits::max(); + +NODE_EXTERN inline void MarkPerformanceMilestone( + Environment* env, + PerformanceMilestone milestone) { + env->performance_state()->milestones[milestone] = PERFORMANCE_NOW(); + } + +class PerformanceEntry : public BaseObject { + public: + // Used for temporary storage of performance entry details when the + // object cannot be created immediately. + class Data { + public: + Data( + const char* name, + const char* type, + uint64_t startTime, + uint64_t endTime, + int data = 0) : + name_(name), + type_(type), + startTime_(startTime), + endTime_(endTime), + data_(data) {} + + std::string name() const { + return name_; + } + + std::string type() const { + return type_; + } + + uint64_t startTime() const { + return startTime_; + } + + uint64_t endTime() const { + return endTime_; + } + + int data() const { + return data_; + } + + private: + std::string name_; + std::string type_; + uint64_t startTime_; + uint64_t endTime_; + int data_; + }; + + static void NotifyObservers(Environment* env, PerformanceEntry* entry); + + static void New(const FunctionCallbackInfo& args); + + PerformanceEntry(Environment* env, + Local wrap, + const char* name, + const char* type, + uint64_t startTime, + uint64_t endTime) : + BaseObject(env, wrap), + name_(name), + type_(type), + startTime_(startTime), + endTime_(endTime) { + MakeWeak(this); + NotifyObservers(env, this); + } + + PerformanceEntry(Environment* env, + Local wrap, + Data* data) : + BaseObject(env, wrap), + name_(data->name()), + type_(data->type()), + startTime_(data->startTime()), + endTime_(data->endTime()) { + MakeWeak(this); + NotifyObservers(env, this); + } + + ~PerformanceEntry() {} + + std::string name() const { + return name_; + } + + std::string type() const { + return type_; + } + + double startTime() const { + return startTime_ / 1e6; + } + + double duration() const { + return durationNano() / 1e6; + } + + uint64_t startTimeNano() const { + return startTime_; + } + + uint64_t durationNano() const { + return endTime_ - startTime_; + } + + private: + std::string name_; + std::string type_; + uint64_t startTime_; + uint64_t endTime_; +}; + +enum PerformanceGCKind { + NODE_PERFORMANCE_GC_MAJOR = GCType::kGCTypeMarkSweepCompact, + NODE_PERFORMANCE_GC_MINOR = GCType::kGCTypeScavenge, + NODE_PERFORMANCE_GC_INCREMENTAL = GCType::kGCTypeIncrementalMarking, + NODE_PERFORMANCE_GC_WEAKCB = GCType::kGCTypeProcessWeakCallbacks +}; + +} // namespace performance +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_NODE_PERF_H_ diff --git a/src/node_perf_common.h b/src/node_perf_common.h new file mode 100644 index 00000000000000..30efcb6134b741 --- /dev/null +++ b/src/node_perf_common.h @@ -0,0 +1,71 @@ +#ifndef SRC_NODE_PERF_COMMON_H_ +#define SRC_NODE_PERF_COMMON_H_ + +#include "node.h" +#include "v8.h" + +#include +#include + +namespace node { +namespace performance { + +#define PERFORMANCE_NOW() uv_hrtime() + +// These occur before the environment is created. Cache them +// here and add them to the milestones when the env is init'd. +extern uint64_t performance_node_start; +extern uint64_t performance_v8_start; + +#define NODE_PERFORMANCE_MILESTONES(V) \ + V(ENVIRONMENT, "environment") \ + V(NODE_START, "nodeStart") \ + V(V8_START, "v8Start") \ + V(LOOP_START, "loopStart") \ + V(LOOP_EXIT, "loopExit") \ + V(BOOTSTRAP_COMPLETE, "bootstrapComplete") \ + V(THIRD_PARTY_MAIN_START, "thirdPartyMainStart") \ + V(THIRD_PARTY_MAIN_END, "thirdPartyMainEnd") \ + V(CLUSTER_SETUP_START, "clusterSetupStart") \ + V(CLUSTER_SETUP_END, "clusterSetupEnd") \ + V(MODULE_LOAD_START, "moduleLoadStart") \ + V(MODULE_LOAD_END, "moduleLoadEnd") \ + V(PRELOAD_MODULE_LOAD_START, "preloadModulesLoadStart") \ + V(PRELOAD_MODULE_LOAD_END, "preloadModulesLoadEnd") + +#define NODE_PERFORMANCE_ENTRY_TYPES(V) \ + V(NODE, "node") \ + V(MARK, "mark") \ + V(MEASURE, "measure") \ + V(GC, "gc") \ + V(FUNCTION, "function") + +enum PerformanceMilestone { +#define V(name, _) NODE_PERFORMANCE_MILESTONE_##name, + NODE_PERFORMANCE_MILESTONES(V) +#undef V + NODE_PERFORMANCE_MILESTONE_INVALID +}; + +enum PerformanceEntryType { +#define V(name, _) NODE_PERFORMANCE_ENTRY_TYPE_##name, + NODE_PERFORMANCE_ENTRY_TYPES(V) +#undef V + NODE_PERFORMANCE_ENTRY_TYPE_INVALID +}; + +#define PERFORMANCE_MARK(env, n) \ + do { \ + node::performance::MarkPerformanceMilestone(env, \ + node::performance::NODE_PERFORMANCE_MILESTONE_##n); \ + } while (0); + +struct performance_state { + uint32_t observers[NODE_PERFORMANCE_ENTRY_TYPE_INVALID]; + double milestones[NODE_PERFORMANCE_MILESTONE_INVALID]; +}; + +} // namespace performance +} // namespace node + +#endif // SRC_NODE_PERF_COMMON_H_ diff --git a/test/parallel/test-performance-function.js b/test/parallel/test-performance-function.js new file mode 100644 index 00000000000000..8dd4d3004baed1 --- /dev/null +++ b/test/parallel/test-performance-function.js @@ -0,0 +1,93 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +const { + performance, + PerformanceObserver +} = require('perf_hooks'); + +{ + // Intentional non-op. Do not wrap in common.mustCall(); + const n = performance.timerify(() => {}); + n(); + const entries = performance.getEntriesByType('function'); + assert.strictEqual(entries.length, 0); + + const obs = new PerformanceObserver(common.mustCall((list) => { + const entries = list.getEntries(); + const entry = entries[0]; + assert(entry); + assert.strictEqual(entry.name, 'performance.timerify'); + assert.strictEqual(entry.entryType, 'function'); + assert.strictEqual(typeof entry.duration, 'number'); + assert.strictEqual(typeof entry.startTime, 'number'); + obs.disconnect(); + performance.clearFunctions(); + })); + obs.observe({ entryTypes: ['function'] }); + n(); +} + +{ + // If the error throws, the error should just be bubbled up and the + // performance timeline entry will not be reported. + const obs = new PerformanceObserver(common.mustNotCall()); + obs.observe({ entryTypes: ['function'] }); + const n = performance.timerify(() => { + throw new Error('test'); + }); + assert.throws(() => n(), /^Error: test$/); + const entries = performance.getEntriesByType('function'); + assert.strictEqual(entries.length, 0); + obs.disconnect(); +} + +{ + class N {} + const n = performance.timerify(N); + new n(); + const entries = performance.getEntriesByType('function'); + assert.strictEqual(entries.length, 0); + + const obs = new PerformanceObserver(common.mustCall((list) => { + const entries = list.getEntries(); + const entry = entries[0]; + assert.strictEqual(entry[0], 1); + assert.strictEqual(entry[1], 'abc'); + assert(entry); + assert.strictEqual(entry.name, 'N'); + assert.strictEqual(entry.entryType, 'function'); + assert.strictEqual(typeof entry.duration, 'number'); + assert.strictEqual(typeof entry.startTime, 'number'); + obs.disconnect(); + performance.clearFunctions(); + })); + obs.observe({ entryTypes: ['function'] }); + + new n(1, 'abc'); +} + +{ + [1, {}, [], null, undefined, Infinity].forEach((i) => { + assert.throws(() => performance.timerify(i), + common.expectsError({ + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'The "fn" argument must be of type Function' + })); + }); +} + +// Function can only be wrapped once, also check length and name +{ + const m = (a, b = 1) => {}; + const n = performance.timerify(m); + const o = performance.timerify(m); + const p = performance.timerify(n); + assert.strictEqual(n, o); + assert.strictEqual(n, p); + assert.strictEqual(n.length, m.length); + assert.strictEqual(n.name, 'timerified m'); +} diff --git a/test/parallel/test-performance-gc.js b/test/parallel/test-performance-gc.js new file mode 100755 index 00000000000000..1ff4c9ae629942 --- /dev/null +++ b/test/parallel/test-performance-gc.js @@ -0,0 +1,51 @@ +// Flags: --expose-gc +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { + performance, + PerformanceObserver +} = require('perf_hooks'); + +const { + NODE_PERFORMANCE_GC_MAJOR, + NODE_PERFORMANCE_GC_MINOR, + NODE_PERFORMANCE_GC_INCREMENTAL, + NODE_PERFORMANCE_GC_WEAKCB +} = process.binding('performance').constants; + +const kinds = [ + NODE_PERFORMANCE_GC_MAJOR, + NODE_PERFORMANCE_GC_MINOR, + NODE_PERFORMANCE_GC_INCREMENTAL, + NODE_PERFORMANCE_GC_WEAKCB +]; + +// No observers for GC events, no entries should appear +{ + global.gc(); + const entries = performance.getEntriesByType('gc'); + assert.strictEqual(entries.length, 0); +} + +// Adding an observer should force at least one gc to appear +{ + const obs = new PerformanceObserver(common.mustCallAtLeast((list) => { + const entry = list.getEntries()[0]; + assert(entry); + assert.strictEqual(entry.name, 'gc'); + assert.strictEqual(entry.entryType, 'gc'); + assert(kinds.includes(entry.kind)); + assert.strictEqual(typeof entry.startTime, 'number'); + assert.strictEqual(typeof entry.duration, 'number'); + + performance.clearGC(); + + const entries = performance.getEntriesByType('gc'); + assert.strictEqual(entries.length, 0); + obs.disconnect(); + })); + obs.observe({ entryTypes: ['gc'] }); + global.gc(); +} diff --git a/test/parallel/test-performance.js b/test/parallel/test-performance.js new file mode 100644 index 00000000000000..62fb13ed289631 --- /dev/null +++ b/test/parallel/test-performance.js @@ -0,0 +1,124 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { performance } = require('perf_hooks'); + +assert(performance); +assert(performance.nodeTiming); +assert.strictEqual(typeof performance.timeOrigin, 'number'); + +{ + const entries = performance.getEntries(); + assert(Array.isArray(entries)); + assert.strictEqual(entries.length, 1); + assert.strictEqual(entries[0], performance.nodeTiming); +} + +{ + const entries = performance.getEntriesByName('node'); + assert(Array.isArray(entries)); + assert.strictEqual(entries.length, 1); + assert.strictEqual(entries[0], performance.nodeTiming); +} + +{ + let n; + let entries = performance.getEntries(); + for (n = 0; n < entries.length; n++) { + const entry = entries[n]; + assert.notStrictEqual(entry.name, 'A'); + assert.notStrictEqual(entry.entryType, 'mark'); + } + performance.mark('A'); + entries = performance.getEntries(); + const markA = entries[1]; + assert.strictEqual(markA.name, 'A'); + assert.strictEqual(markA.entryType, 'mark'); + performance.clearMarks('A'); + entries = performance.getEntries(); + for (n = 0; n < entries.length; n++) { + const entry = entries[n]; + assert.notStrictEqual(entry.name, 'A'); + assert.notStrictEqual(entry.entryType, 'mark'); + } +} + +{ + let entries = performance.getEntries(); + for (let n = 0; n < entries.length; n++) { + const entry = entries[n]; + assert.notStrictEqual(entry.name, 'B'); + assert.notStrictEqual(entry.entryType, 'mark'); + } + performance.mark('B'); + entries = performance.getEntries(); + const markB = entries[1]; + assert.strictEqual(markB.name, 'B'); + assert.strictEqual(markB.entryType, 'mark'); + performance.clearMarks(); + entries = performance.getEntries(); + assert.strictEqual(entries.length, 1); +} + +{ + performance.mark('A'); + [undefined, null, 'foo', 'initialize', 1].forEach((i) => { + assert.doesNotThrow(() => performance.measure('test', i, 'A')); + }); + + [undefined, null, 'foo', 1].forEach((i) => { + assert.throws(() => performance.measure('test', 'A', i), + common.expectsError({ + code: 'ERR_INVALID_PERFORMANCE_MARK', + type: Error, + message: `The "${i}" performance mark has not been set` + })); + }); + + performance.clearMeasures(); + performance.clearMarks(); + + const entries = performance.getEntries(); + assert.strictEqual(entries.length, 1); +} + +{ + performance.mark('A'); + setImmediate(() => { + performance.mark('B'); + performance.measure('foo', 'A', 'B'); + const entry = performance.getEntriesByName('foo')[0]; + const markA = performance.getEntriesByName('A', 'mark')[0]; + const markB = performance.getEntriesByName('B', 'mark')[0]; + assert.strictEqual(entry.name, 'foo'); + assert.strictEqual(entry.entryType, 'measure'); + assert.strictEqual(entry.startTime, markA.startTime); + assert.strictEqual(entry.duration.toPrecision(3), + (markB.startTime - markA.startTime).toPrecision(3)); + }); +} + +assert.strictEqual(performance.nodeTiming.name, 'node'); +assert.strictEqual(performance.nodeTiming.entryType, 'node'); + +[ + 'startTime', + 'duration', + 'nodeStart', + 'v8Start', + 'bootstrapComplete', + 'environment', + 'loopStart', + 'loopExit', + 'thirdPartyMainStart', + 'thirdPartyMainEnd', + 'clusterSetupStart', + 'clusterSetupEnd', + 'moduleLoadStart', + 'moduleLoadEnd', + 'preloadModuleLoadStart', + 'preloadModuleLoadEnd' +].forEach((i) => { + assert.strictEqual(typeof performance.nodeTiming[i], 'number'); +}); diff --git a/test/parallel/test-performanceobserver.js b/test/parallel/test-performanceobserver.js new file mode 100644 index 00000000000000..081362939ce2b5 --- /dev/null +++ b/test/parallel/test-performanceobserver.js @@ -0,0 +1,139 @@ +'use strict'; + +const common = require('../common'); +const Countdown = require('../common/countdown'); +const assert = require('assert'); +const { + observerCounts: counts +} = process.binding('performance'); +const { + performance, + PerformanceObserver, + constants +} = require('perf_hooks'); + +const { + NODE_PERFORMANCE_ENTRY_TYPE_NODE, + NODE_PERFORMANCE_ENTRY_TYPE_MARK, + NODE_PERFORMANCE_ENTRY_TYPE_MEASURE, + NODE_PERFORMANCE_ENTRY_TYPE_GC, + NODE_PERFORMANCE_ENTRY_TYPE_FUNCTION, +} = constants; + +assert.strictEqual(counts[NODE_PERFORMANCE_ENTRY_TYPE_NODE], 0); +assert.strictEqual(counts[NODE_PERFORMANCE_ENTRY_TYPE_MARK], 1); +assert.strictEqual(counts[NODE_PERFORMANCE_ENTRY_TYPE_MEASURE], 1); +assert.strictEqual(counts[NODE_PERFORMANCE_ENTRY_TYPE_GC], 0); +assert.strictEqual(counts[NODE_PERFORMANCE_ENTRY_TYPE_FUNCTION], 0); + +{ + [1, null, undefined, {}, [], Infinity].forEach((i) => { + assert.throws(() => new PerformanceObserver(i), + common.expectsError({ + code: 'ERR_INVALID_CALLBACK', + type: TypeError, + message: 'callback must be a function' + })); + }); + const observer = new PerformanceObserver(common.mustNotCall()); + + [1, null, undefined].forEach((i) => { + //observer.observe(i); + assert.throws(() => observer.observe(i), + common.expectsError({ + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'The "options" argument must be of type Object' + })); + }); + + [1, undefined, null, {}, Infinity].forEach((i) => { + assert.throws(() => observer.observe({ entryTypes: i }), + common.expectsError({ + code: 'ERR_INVALID_OPT_VALUE', + type: TypeError, + message: 'The value "[object Object]" is invalid for ' + + 'option "entryTypes"' + })); + }); +} + +// Test Non-Buffered +{ + const observer = + new PerformanceObserver(common.mustCall(callback, 3)); + + const countdown = + new Countdown(3, common.mustCall(() => { + observer.disconnect(); + assert.strictEqual(counts[NODE_PERFORMANCE_ENTRY_TYPE_MARK], 1); + })); + + function callback(list, obs) { + assert.strictEqual(obs, observer); + const entries = list.getEntries(); + assert.strictEqual(entries.length, 1); + countdown.dec(); + } + assert.strictEqual(counts[NODE_PERFORMANCE_ENTRY_TYPE_MARK], 1); + assert.doesNotThrow(() => observer.observe({ entryTypes: ['mark'] })); + assert.strictEqual(counts[NODE_PERFORMANCE_ENTRY_TYPE_MARK], 2); + performance.mark('test1'); + performance.mark('test2'); + performance.mark('test3'); +} + + +// Test Buffered +{ + const observer = + new PerformanceObserver(common.mustCall(callback, 1)); + assert.strictEqual(counts[NODE_PERFORMANCE_ENTRY_TYPE_MARK], 1); + + function callback(list, obs) { + assert.strictEqual(obs, observer); + const entries = list.getEntries(); + assert.strictEqual(entries.length, 3); + observer.disconnect(); + assert.strictEqual(counts[NODE_PERFORMANCE_ENTRY_TYPE_MARK], 1); + + { + const entriesByName = list.getEntriesByName('test1'); + assert.strictEqual(entriesByName.length, 1); + assert.strictEqual(entriesByName[0].name, 'test1'); + assert.strictEqual(entriesByName[0].entryType, 'mark'); + } + + { + const entriesByName = list.getEntriesByName('test1', 'mark'); + assert.strictEqual(entriesByName.length, 1); + assert.strictEqual(entriesByName[0].name, 'test1'); + assert.strictEqual(entriesByName[0].entryType, 'mark'); + } + + { + const entriesByName = list.getEntriesByName('test1', 'measure'); + assert.strictEqual(entriesByName.length, 0); + } + + { + const entriesByType = list.getEntriesByType('measure'); + assert.strictEqual(entriesByType.length, 1); + assert.strictEqual(entriesByType[0].name, 'test3'); + assert.strictEqual(entriesByType[0].entryType, 'measure'); + } + } + + assert.doesNotThrow(() => { + observer.observe({ entryTypes: ['mark', 'measure'], buffered: true }); + }); + // Do this twice to make sure it doesn't throw + assert.doesNotThrow(() => { + observer.observe({ entryTypes: ['mark', 'measure'], buffered: true }); + }); + // Even tho we called twice, count should be 1 + assert.strictEqual(counts[NODE_PERFORMANCE_ENTRY_TYPE_MARK], 2); + performance.mark('test1'); + performance.mark('test2'); + performance.measure('test3', 'test1', 'test2'); +} From 391855c2529c227930eac928fc42a3ab0cbdcde4 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Thu, 24 Aug 2017 02:06:49 +0200 Subject: [PATCH 205/231] src: fix build on certain platforms The `double` fields in `performance_state` could previously have been aligned at 4-byte instead of 8-byte boundaries, which would have made creating an Float64Array them as a array buffer view for an ArrayBuffer extending over the entire struct an invalid operation. Ref: 67269fd7f33279699b1ae71225f3d738518c844c Comments out related flaky failure PR-URL: https://github.com/nodejs/node/pull/14996 Reviewed-By: James M Snell Reviewed-By: Refael Ackermann --- src/node_http2.cc | 5 +++-- src/node_perf_common.h | 3 ++- test/parallel/test-performance.js | 7 ++++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/node_http2.cc b/src/node_http2.cc index a8eb0a37bf8062..610c8e8f2351ba 100755 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -67,11 +67,12 @@ enum Http2PaddingBufferFields { }; struct http2_state { + // doubles first so that they are always sizeof(double)-aligned + double session_state_buffer[IDX_SESSION_STATE_COUNT]; + double stream_state_buffer[IDX_STREAM_STATE_COUNT]; uint32_t padding_buffer[PADDING_BUF_FIELD_COUNT]; uint32_t options_buffer[IDX_OPTIONS_FLAGS + 1]; uint32_t settings_buffer[IDX_SETTINGS_COUNT + 1]; - double session_state_buffer[IDX_SESSION_STATE_COUNT]; - double stream_state_buffer[IDX_STREAM_STATE_COUNT]; }; Freelist diff --git a/src/node_perf_common.h b/src/node_perf_common.h index 30efcb6134b741..dc884cac97ce88 100644 --- a/src/node_perf_common.h +++ b/src/node_perf_common.h @@ -61,8 +61,9 @@ enum PerformanceEntryType { } while (0); struct performance_state { - uint32_t observers[NODE_PERFORMANCE_ENTRY_TYPE_INVALID]; + // doubles first so that they are always sizeof(double)-aligned double milestones[NODE_PERFORMANCE_MILESTONE_INVALID]; + uint32_t observers[NODE_PERFORMANCE_ENTRY_TYPE_INVALID]; }; } // namespace performance diff --git a/test/parallel/test-performance.js b/test/parallel/test-performance.js index 62fb13ed289631..23ae4370c3cd7a 100644 --- a/test/parallel/test-performance.js +++ b/test/parallel/test-performance.js @@ -90,12 +90,13 @@ assert.strictEqual(typeof performance.timeOrigin, 'number'); performance.measure('foo', 'A', 'B'); const entry = performance.getEntriesByName('foo')[0]; const markA = performance.getEntriesByName('A', 'mark')[0]; - const markB = performance.getEntriesByName('B', 'mark')[0]; + performance.getEntriesByName('B', 'mark')[0]; assert.strictEqual(entry.name, 'foo'); assert.strictEqual(entry.entryType, 'measure'); assert.strictEqual(entry.startTime, markA.startTime); - assert.strictEqual(entry.duration.toPrecision(3), - (markB.startTime - markA.startTime).toPrecision(3)); + // TODO(jasnell): This comparison is too imprecise on some systems + //assert.strictEqual(entry.duration.toPrecision(3), + // (markB.startTime - markA.startTime).toPrecision(3)); }); } From d5a376ab7a03ab2dcf623db0d806fe1786b46486 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 17 Aug 2017 02:10:43 -0300 Subject: [PATCH 206/231] lib: remove circular reference PR-URL: https://github.com/nodejs/node/pull/14885 Reviewed-By: Luigi Pinca Reviewed-By: Refael Ackermann Reviewed-By: James M Snell --- lib/assert.js | 24 +++++++++--------------- lib/util.js | 17 ++--------------- 2 files changed, 11 insertions(+), 30 deletions(-) diff --git a/lib/assert.js b/lib/assert.js index 06b4815ea6cfe6..6f340d8ef4e46b 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -21,10 +21,8 @@ 'use strict'; const { compare } = process.binding('buffer'); -const util = require('util'); -const { isSet, isMap } = process.binding('util'); +const { isSet, isMap, isDate, isRegExp } = process.binding('util'); const { objectToString } = require('internal/util'); -const { Buffer } = require('buffer'); const errors = require('internal/errors'); // The assert module provides functions that throw @@ -108,8 +106,8 @@ function areSimilarRegExps(a, b) { } // For small buffers it's faster to compare the buffer in a loop. The c++ -// barrier including the Buffer.from operation takes the advantage of the faster -// compare otherwise. 300 was the number after which compare became faster. +// barrier including the Uint8Array operation takes the advantage of the faster +// binary compare otherwise. The break even point was at about 300 characters. function areSimilarTypedArrays(a, b) { const len = a.byteLength; if (len !== b.byteLength) { @@ -123,12 +121,8 @@ function areSimilarTypedArrays(a, b) { } return true; } - return compare(Buffer.from(a.buffer, - a.byteOffset, - len), - Buffer.from(b.buffer, - b.byteOffset, - b.byteLength)) === 0; + return compare(new Uint8Array(a.buffer, a.byteOffset, len), + new Uint8Array(b.buffer, b.byteOffset, b.byteLength)) === 0; } function isFloatTypedArrayTag(tag) { @@ -189,11 +183,11 @@ function strictDeepEqual(actual, expected) { // Skip testing the part below and continue in the callee function. return; } - if (util.isDate(actual)) { + if (isDate(actual)) { if (actual.getTime() !== expected.getTime()) { return false; } - } else if (util.isRegExp(actual)) { + } else if (isRegExp(actual)) { if (!areSimilarRegExps(actual, expected)) { return false; } @@ -229,10 +223,10 @@ function looseDeepEqual(actual, expected) { if (expected === null || typeof expected !== 'object') { return false; } - if (util.isDate(actual) && util.isDate(expected)) { + if (isDate(actual) && isDate(expected)) { return actual.getTime() === expected.getTime(); } - if (util.isRegExp(actual) && util.isRegExp(expected)) { + if (isRegExp(actual) && isRegExp(expected)) { return areSimilarRegExps(actual, expected); } if (actual instanceof Error && expected instanceof Error) { diff --git a/lib/util.js b/lib/util.js index 03561bcb0914f6..a2c8a6436f837a 100644 --- a/lib/util.js +++ b/lib/util.js @@ -23,6 +23,7 @@ const errors = require('internal/errors'); const { TextDecoder, TextEncoder } = require('internal/encoding'); +const { isBuffer } = require('buffer').Buffer; const { errname } = process.binding('uv'); @@ -1118,6 +1119,7 @@ module.exports = exports = { inspect, isArray: Array.isArray, isBoolean, + isBuffer, isNull, isNullOrUndefined, isNumber, @@ -1149,18 +1151,3 @@ module.exports = exports = { 'util.puts is deprecated. Use console.log instead.', 'DEP0027') }; - -// Avoid a circular dependency -var isBuffer; -Object.defineProperty(exports, 'isBuffer', { - configurable: true, - enumerable: true, - get() { - if (!isBuffer) - isBuffer = require('buffer').Buffer.isBuffer; - return isBuffer; - }, - set(val) { - isBuffer = val; - } -}); From 69e3bc64cc871f656321e26b87ede62de365f707 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 23 Aug 2017 17:46:02 -0700 Subject: [PATCH 207/231] perf_hooks: mark as experimental PR-URL: https://github.com/nodejs/node/pull/14997 Reviewed-By: Refael Ackermann Reviewed-By: Timothy Gu Reviewed-By: Ruben Bridgewater Reviewed-By: Daniel Bevenius Reviewed-By: Matteo Collina Reviewed-By: Colin Ihrig Reviewed-By: Jeremiah Senkpiel --- doc/api/perf_hooks.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/api/perf_hooks.md b/doc/api/perf_hooks.md index 7972e1edb24f43..f41caf6f53112f 100755 --- a/doc/api/perf_hooks.md +++ b/doc/api/perf_hooks.md @@ -3,6 +3,8 @@ added: REPLACEME --> +> Stability: 1 - Experimental + The Performance Timing API provides an implementation of the [W3C Performance Timeline][] specification. The purpose of the API is to support collection of high resolution performance metrics. From ea2b5760d5c88aba7184ce20a3bf02db460c1e28 Mon Sep 17 00:00:00 2001 From: Jon Moss Date: Wed, 23 Aug 2017 23:30:39 -0700 Subject: [PATCH 208/231] errors: remove duplicated ERR_HTTP_INVALID_STATUS_CODE error PR-URL: https://github.com/nodejs/node/pull/15003 Reviewed-By: Colin Ihrig Reviewed-By: James M Snell Reviewed-By: Rich Trott Reviewed-By: Yuta Hiroto Reviewed-By: Daniel Bevenius Reviewed-By: Luigi Pinca --- lib/internal/errors.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/internal/errors.js b/lib/internal/errors.js index d81360b10cca44..f42bf13b5b287c 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -118,7 +118,6 @@ E('ERR_ENCODING_NOT_SUPPORTED', E('ERR_FALSY_VALUE_REJECTION', 'Promise was rejected with falsy value'); E('ERR_HTTP_HEADERS_SENT', 'Cannot render headers after they are sent to the client'); -E('ERR_HTTP_INVALID_STATUS_CODE', 'Invalid status code: %s'); E('ERR_HTTP_TRAILER_INVALID', 'Trailers are invalid with this transfer encoding'); E('ERR_HTTP_INVALID_CHAR', 'Invalid character in statusMessage.'); From ae91b1efc0fdbecf7dca0ac0bd2723cd232b384c Mon Sep 17 00:00:00 2001 From: Miguel Angel Asencio Hurtado Date: Wed, 9 Aug 2017 19:29:40 +0200 Subject: [PATCH 209/231] test: continue normalizing fixtures use MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/14716 Refs: https://github.com/nodejs/node/pull/14332 Reviewed-By: James M Snell Reviewed-By: Ruben Bridgewater Reviewed-By: Tobias Nießen Reviewed-By: Yuta Hiroto --- test/async-hooks/test-graph.tls-write.js | 7 +++---- test/async-hooks/test-tlswrap.js | 7 +++---- test/async-hooks/test-writewrap.js | 6 +++--- test/doctool/test-doctool-html.js | 11 ++++++----- test/doctool/test-doctool-json.js | 8 ++++---- test/fixtures/module-require-symlink/symlinked.js | 6 ++---- test/fixtures/tls-connect.js | 13 ++++--------- test/inspector/inspector-helper.js | 4 ++-- test/internet/test-tls-add-ca-cert.js | 4 ++-- test/known_issues/test-repl-require-context.js | 4 ++-- test/known_issues/test-url-parse-conformance.js | 8 +++----- test/pummel/test-https-ci-reneg-attack.js | 6 +++--- test/pummel/test-https-large-response.js | 6 +++--- test/pummel/test-https-no-reader.js | 7 +++---- test/pummel/test-regress-GH-892.js | 9 ++++----- test/pummel/test-tls-ci-reneg-attack.js | 6 +++--- test/pummel/test-tls-connect-memleak.js | 6 +++--- test/pummel/test-tls-securepair-client.js | 10 +++------- test/pummel/test-tls-server-large-request.js | 6 +++--- test/pummel/test-tls-session-timeout.js | 10 ++++------ test/pummel/test-tls-throttle.js | 6 +++--- test/pummel/test-watch-file.js | 8 ++++---- test/sequential/test-debugger-repeat-last.js | 7 ++----- test/sequential/test-init.js | 4 ++-- 24 files changed, 74 insertions(+), 95 deletions(-) diff --git a/test/async-hooks/test-graph.tls-write.js b/test/async-hooks/test-graph.tls-write.js index 1408ff40efda8f..4ba264c808392e 100644 --- a/test/async-hooks/test-graph.tls-write.js +++ b/test/async-hooks/test-graph.tls-write.js @@ -9,9 +9,8 @@ if (!common.hasIPv6) const initHooks = require('./init-hooks'); const verifyGraph = require('./verify-graph'); -const fs = require('fs'); const tls = require('tls'); -const path = require('path'); +const fixtures = require('../common/fixtures'); const hooks = initHooks(); hooks.enable(); @@ -21,8 +20,8 @@ hooks.enable(); // const server = tls .createServer({ - cert: fs.readFileSync(path.join(common.fixturesDir, 'test_cert.pem')), - key: fs.readFileSync(path.join(common.fixturesDir, 'test_key.pem')) + cert: fixtures.readSync('test_cert.pem'), + key: fixtures.readSync('test_key.pem') }) .on('listening', common.mustCall(onlistening)) .on('secureConnection', common.mustCall(onsecureConnection)) diff --git a/test/async-hooks/test-tlswrap.js b/test/async-hooks/test-tlswrap.js index 4aa2493765f37b..3876b860ca876a 100644 --- a/test/async-hooks/test-tlswrap.js +++ b/test/async-hooks/test-tlswrap.js @@ -5,8 +5,7 @@ if (!common.hasCrypto) common.skip('missing crypto'); const assert = require('assert'); -const fs = require('fs'); -const path = require('path'); +const fixtures = require('../common/fixtures'); const tls = require('tls'); const tick = require('./tick'); @@ -21,8 +20,8 @@ hooks.enable(); // const server = tls .createServer({ - cert: fs.readFileSync(path.join(common.fixturesDir, 'test_cert.pem')), - key: fs.readFileSync(path.join(common.fixturesDir, 'test_key.pem')) + cert: fixtures.readSync('test_cert.pem'), + key: fixtures.readSync('test_key.pem') }) .on('listening', common.mustCall(onlistening)) .on('secureConnection', common.mustCall(onsecureConnection)) diff --git a/test/async-hooks/test-writewrap.js b/test/async-hooks/test-writewrap.js index 274972e3ee043c..65f7b6175fb63a 100644 --- a/test/async-hooks/test-writewrap.js +++ b/test/async-hooks/test-writewrap.js @@ -6,7 +6,7 @@ if (!common.hasCrypto) const assert = require('assert'); const initHooks = require('./init-hooks'); -const fs = require('fs'); +const fixtures = require('../common/fixtures'); const { checkInvocations } = require('./hook-checks'); const tls = require('tls'); @@ -18,8 +18,8 @@ hooks.enable(); // const server = tls .createServer({ - cert: fs.readFileSync(`${common.fixturesDir}/test_cert.pem`), - key: fs.readFileSync(`${common.fixturesDir}/test_key.pem`) + cert: fixtures.readSync('test_cert.pem'), + key: fixtures.readSync('test_key.pem') }) .on('listening', common.mustCall(onlistening)) .on('secureConnection', common.mustCall(onsecureConnection)) diff --git a/test/doctool/test-doctool-html.js b/test/doctool/test-doctool-html.js index 64f1d7f8b142f5..9a4300a5d06e04 100644 --- a/test/doctool/test-doctool-html.js +++ b/test/doctool/test-doctool-html.js @@ -11,6 +11,7 @@ try { const assert = require('assert'); const fs = require('fs'); const path = require('path'); +const fixtures = require('../common/fixtures'); const processIncludes = require('../../tools/doc/preprocess.js'); const html = require('../../tools/doc/html.js'); @@ -21,12 +22,12 @@ const html = require('../../tools/doc/html.js'); // have an html parser. const testData = [ { - file: path.join(common.fixturesDir, 'sample_document.md'), + file: fixtures.path('sample_document.md'), html: '
    1. fish
    2. fish

    3. Redfish

    4. ' + '
    5. Bluefish
    ' }, { - file: path.join(common.fixturesDir, 'order_of_end_tags_5873.md'), + file: fixtures.path('order_of_end_tags_5873.md'), html: '

    ClassMethod: Buffer.from(array) ' + '#

    ' }, { - file: path.join(common.fixturesDir, 'doc_with_yaml.md'), + file: fixtures.path('doc_with_yaml.md'), html: '

    Sample Markdown with YAML info' + '#

    ' + @@ -72,7 +73,7 @@ const testData = [ '

    ' }, { - file: path.join(common.fixturesDir, 'doc_with_includes.md'), + file: fixtures.path('doc_with_includes.md'), html: '' + '

    Look here!

    ' + '' + @@ -83,7 +84,7 @@ const testData = [ '' }, { - file: path.join(common.fixturesDir, 'sample_document.md'), + file: fixtures.path('sample_document.md'), html: '
    1. fish
    2. fish

    3. Redfish

    4. ' + '
    5. Bluefish
    ', analyticsId: 'UA-67020396-1' diff --git a/test/doctool/test-doctool-json.js b/test/doctool/test-doctool-json.js index 4a4d3a895c3f20..1be086c3a6a7ea 100644 --- a/test/doctool/test-doctool-json.js +++ b/test/doctool/test-doctool-json.js @@ -10,7 +10,7 @@ try { const assert = require('assert'); const fs = require('fs'); -const path = require('path'); +const fixtures = require('../common/fixtures'); const json = require('../../tools/doc/json.js'); // Outputs valid json with the expected fields when given simple markdown @@ -19,7 +19,7 @@ const json = require('../../tools/doc/json.js'); // The json property is some json which will be generated by the doctool. const testData = [ { - file: path.join(common.fixturesDir, 'sample_document.md'), + file: fixtures.path('sample_document.md'), json: { source: 'foo', modules: [{ @@ -39,7 +39,7 @@ const testData = [ } }, { - file: path.join(common.fixturesDir, 'order_of_end_tags_5873.md'), + file: fixtures.path('order_of_end_tags_5873.md'), json: { source: 'foo', modules: [{ @@ -76,7 +76,7 @@ const testData = [ } }, { - file: path.join(common.fixturesDir, 'doc_with_yaml.md'), + file: fixtures.path('doc_with_yaml.md'), json: { source: 'foo', modules: [ diff --git a/test/fixtures/module-require-symlink/symlinked.js b/test/fixtures/module-require-symlink/symlinked.js index 657ef2662644c0..ced901b2466ac5 100644 --- a/test/fixtures/module-require-symlink/symlinked.js +++ b/test/fixtures/module-require-symlink/symlinked.js @@ -1,11 +1,9 @@ 'use strict'; -const common = require('../../common'); const assert = require('assert'); const foo = require('./foo'); -const path = require('path'); +const fixtures = require('../../common/fixtures'); -const linkScriptTarget = path.join(common.fixturesDir, - 'module-require-symlink', 'symlinked.js'); +const linkScriptTarget = fixtures.path('module-require-symlink', 'symlinked.js'); assert.strictEqual(foo.dep1.bar.version, 'CORRECT_VERSION'); assert.strictEqual(foo.dep2.bar.version, 'CORRECT_VERSION'); diff --git a/test/fixtures/tls-connect.js b/test/fixtures/tls-connect.js index 2ce75a53767724..cff424008663e2 100644 --- a/test/fixtures/tls-connect.js +++ b/test/fixtures/tls-connect.js @@ -9,8 +9,7 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); -const fs = require('fs'); -const join = require('path').join; +const fixtures = require('../common/fixtures'); const tls = require('tls'); const util = require('util'); @@ -33,17 +32,13 @@ const keys = exports.keys = { function load(cert, issuer) { issuer = issuer || cert; // Assume self-signed if no issuer const id = { - key: read(cert + '-key.pem'), - cert: read(cert + '-cert.pem'), - ca: read(issuer + '-cert.pem'), + key: fixtures.readKey(cert + '-key.pem', 'binary'), + cert: fixtures.readKey(cert + '-cert.pem', 'binary'), + ca: fixtures.readKey(issuer + '-cert.pem', 'binary'), }; return id; } -function read(file) { - return fs.readFileSync(join(common.fixturesDir, 'keys', file), 'binary'); -} - exports.connect = function connect(options, callback) { callback = common.mustCall(callback); diff --git a/test/inspector/inspector-helper.js b/test/inspector/inspector-helper.js index 9c1cca3a771293..81aa046bf3d94d 100644 --- a/test/inspector/inspector-helper.js +++ b/test/inspector/inspector-helper.js @@ -3,11 +3,11 @@ const common = require('../common'); const assert = require('assert'); const fs = require('fs'); const http = require('http'); -const path = require('path'); +const fixtures = require('../common/fixtures'); const { spawn } = require('child_process'); const url = require('url'); -const _MAINSCRIPT = path.join(common.fixturesDir, 'loop.js'); +const _MAINSCRIPT = fixtures.path('loop.js'); const DEBUG = false; const TIMEOUT = common.platformTimeout(15 * 1000); diff --git a/test/internet/test-tls-add-ca-cert.js b/test/internet/test-tls-add-ca-cert.js index 299e01405d7dbd..c3780acd50671a 100644 --- a/test/internet/test-tls-add-ca-cert.js +++ b/test/internet/test-tls-add-ca-cert.js @@ -8,11 +8,11 @@ if (!common.hasCrypto) const assert = require('assert'); const fs = require('fs'); -const path = require('path'); +const fixtures = require('../common/fixtures'); const tls = require('tls'); function filenamePEM(n) { - return path.join(common.fixturesDir, 'keys', `${n}.pem`); + return fixtures.path('keys', `${n}.pem`); } function loadPEM(n) { diff --git a/test/known_issues/test-repl-require-context.js b/test/known_issues/test-repl-require-context.js index e7a27f2ca68f6b..2b8737b8671669 100644 --- a/test/known_issues/test-repl-require-context.js +++ b/test/known_issues/test-repl-require-context.js @@ -2,12 +2,12 @@ // Refs: https://github.com/nodejs/node/issues/7788 const common = require('../common'); const assert = require('assert'); -const path = require('path'); +const path = require('../common/fixtures').path; const repl = require('repl'); const stream = require('stream'); const inputStream = new stream.PassThrough(); const outputStream = new stream.PassThrough(); -const fixture = path.join(common.fixturesDir, 'is-object.js'); +const fixture = path('is-object.js'); const r = repl.start({ input: inputStream, output: outputStream, diff --git a/test/known_issues/test-url-parse-conformance.js b/test/known_issues/test-url-parse-conformance.js index 62c36da87e6678..022a613a226549 100644 --- a/test/known_issues/test-url-parse-conformance.js +++ b/test/known_issues/test-url-parse-conformance.js @@ -1,13 +1,11 @@ 'use strict'; // Refs: https://github.com/nodejs/node/issues/5832 - -const common = require('../common'); +require('../common'); const url = require('url'); const assert = require('assert'); -const path = require('path'); - -const tests = require(path.join(common.fixturesDir, 'url-tests')); +const fixtures = require('../common/fixtures'); +const tests = require(fixtures.path('url-tests')); let failed = 0; let attempted = 0; diff --git a/test/pummel/test-https-ci-reneg-attack.js b/test/pummel/test-https-ci-reneg-attack.js index fbe0e37873c79a..9e132b7df9a488 100644 --- a/test/pummel/test-https-ci-reneg-attack.js +++ b/test/pummel/test-https-ci-reneg-attack.js @@ -31,7 +31,7 @@ const assert = require('assert'); const spawn = require('child_process').spawn; const tls = require('tls'); const https = require('https'); -const fs = require('fs'); +const fixtures = require('../common/fixtures'); // renegotiation limits to test const LIMITS = [0, 1, 2, 3, 5, 10, 16]; @@ -48,8 +48,8 @@ const LIMITS = [0, 1, 2, 3, 5, 10, 16]; function test(next) { const options = { - cert: fs.readFileSync(`${common.fixturesDir}/test_cert.pem`), - key: fs.readFileSync(`${common.fixturesDir}/test_key.pem`) + cert: fixtures.readSync('test_cert.pem'), + key: fixtures.readSync('test_key.pem') }; let seenError = false; diff --git a/test/pummel/test-https-large-response.js b/test/pummel/test-https-large-response.js index 7775ccca63e1c3..d72fd2a65ba877 100644 --- a/test/pummel/test-https-large-response.js +++ b/test/pummel/test-https-large-response.js @@ -25,12 +25,12 @@ if (!common.hasCrypto) common.skip('missing crypto'); const assert = require('assert'); -const fs = require('fs'); +const fixtures = require('../common/fixtures'); const https = require('https'); const options = { - key: fs.readFileSync(`${common.fixturesDir}/keys/agent1-key.pem`), - cert: fs.readFileSync(`${common.fixturesDir}/keys/agent1-cert.pem`) + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') }; process.stdout.write('build body...'); diff --git a/test/pummel/test-https-no-reader.js b/test/pummel/test-https-no-reader.js index b8071b9ba97819..985d888e6d065a 100644 --- a/test/pummel/test-https-no-reader.js +++ b/test/pummel/test-https-no-reader.js @@ -26,12 +26,11 @@ if (!common.hasCrypto) const assert = require('assert'); const https = require('https'); -const fs = require('fs'); -const path = require('path'); +const fixtures = require('../common/fixtures'); const options = { - key: fs.readFileSync(path.join(common.fixturesDir, 'test_key.pem')), - cert: fs.readFileSync(path.join(common.fixturesDir, 'test_cert.pem')) + key: fixtures.readSync('test_key.pem'), + cert: fixtures.readSync('test_cert.pem') }; const buf = Buffer.allocUnsafe(1024 * 1024); diff --git a/test/pummel/test-regress-GH-892.js b/test/pummel/test-regress-GH-892.js index b1d46adbabb362..6ac438287112cd 100644 --- a/test/pummel/test-regress-GH-892.js +++ b/test/pummel/test-regress-GH-892.js @@ -33,14 +33,13 @@ if (!common.hasCrypto) const assert = require('assert'); const spawn = require('child_process').spawn; const https = require('https'); -const fs = require('fs'); +const fixtures = require('../common/fixtures'); const bytesExpected = 1024 * 1024 * 32; let started = false; -const childScript = require('path').join(common.fixturesDir, - 'GH-892-request.js'); +const childScript = fixtures.path('GH-892-request.js'); function makeRequest() { if (started) return; @@ -78,8 +77,8 @@ function makeRequest() { const serverOptions = { - key: fs.readFileSync(`${common.fixturesDir}/keys/agent1-key.pem`), - cert: fs.readFileSync(`${common.fixturesDir}/keys/agent1-cert.pem`) + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') }; let uploadCount = 0; diff --git a/test/pummel/test-tls-ci-reneg-attack.js b/test/pummel/test-tls-ci-reneg-attack.js index 905d922db3a7b5..dede8ec9db7056 100644 --- a/test/pummel/test-tls-ci-reneg-attack.js +++ b/test/pummel/test-tls-ci-reneg-attack.js @@ -30,7 +30,7 @@ if (!common.opensslCli) const assert = require('assert'); const spawn = require('child_process').spawn; const tls = require('tls'); -const fs = require('fs'); +const fixtures = require('../common/fixtures'); // renegotiation limits to test const LIMITS = [0, 1, 2, 3, 5, 10, 16]; @@ -47,8 +47,8 @@ const LIMITS = [0, 1, 2, 3, 5, 10, 16]; function test(next) { const options = { - cert: fs.readFileSync(`${common.fixturesDir}/test_cert.pem`), - key: fs.readFileSync(`${common.fixturesDir}/test_key.pem`) + cert: fixtures.readSync('test_cert.pem'), + key: fixtures.readSync('test_key.pem') }; let seenError = false; diff --git a/test/pummel/test-tls-connect-memleak.js b/test/pummel/test-tls-connect-memleak.js index c086933a3e0474..4425e8d04eb42c 100644 --- a/test/pummel/test-tls-connect-memleak.js +++ b/test/pummel/test-tls-connect-memleak.js @@ -28,7 +28,7 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); -const fs = require('fs'); +const fixtures = require('../common/fixtures'); assert.strictEqual( typeof global.gc, @@ -37,8 +37,8 @@ assert.strictEqual( ); tls.createServer({ - cert: fs.readFileSync(`${common.fixturesDir}/test_cert.pem`), - key: fs.readFileSync(`${common.fixturesDir}/test_key.pem`) + cert: fixtures.readSync('test_cert.pem'), + key: fixtures.readSync('test_key.pem') }).listen(common.PORT); { diff --git a/test/pummel/test-tls-securepair-client.js b/test/pummel/test-tls-securepair-client.js index e9bae682d7290a..b397197c0cb484 100644 --- a/test/pummel/test-tls-securepair-client.js +++ b/test/pummel/test-tls-securepair-client.js @@ -29,10 +29,9 @@ if (!common.opensslCli) if (!common.hasCrypto) common.skip('missing crypto'); -const join = require('path').join; const net = require('net'); const assert = require('assert'); -const fs = require('fs'); +const fixtures = require('../common/fixtures'); const tls = require('tls'); const spawn = require('child_process').spawn; @@ -56,11 +55,8 @@ function test2() { } function test(keyfn, certfn, check, next) { - keyfn = join(common.fixturesDir, keyfn); - const key = fs.readFileSync(keyfn).toString(); - - certfn = join(common.fixturesDir, certfn); - const cert = fs.readFileSync(certfn).toString(); + const key = fixtures.readSync(keyfn).toString(); + const cert = fixtures.readSync(certfn).toString(); const server = spawn(common.opensslCli, ['s_server', '-accept', common.PORT, diff --git a/test/pummel/test-tls-server-large-request.js b/test/pummel/test-tls-server-large-request.js index 3255633ec7c41f..a99c142d53c818 100644 --- a/test/pummel/test-tls-server-large-request.js +++ b/test/pummel/test-tls-server-large-request.js @@ -26,15 +26,15 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); -const fs = require('fs'); +const fixtures = require('../common/fixtures'); const stream = require('stream'); const util = require('util'); const request = Buffer.from('ABCD'.repeat(1024 * 256 - 1)); // 1mb const options = { - key: fs.readFileSync(`${common.fixturesDir}/keys/agent1-key.pem`), - cert: fs.readFileSync(`${common.fixturesDir}/keys/agent1-cert.pem`) + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') }; function Mediator() { diff --git a/test/pummel/test-tls-session-timeout.js b/test/pummel/test-tls-session-timeout.js index 9b175da77e62a7..56fdfa16ea7eb5 100644 --- a/test/pummel/test-tls-session-timeout.js +++ b/test/pummel/test-tls-session-timeout.js @@ -43,14 +43,13 @@ function doTest() { const tls = require('tls'); const fs = require('fs'); const join = require('path').join; + const fixtures = require('../common/fixtures'); const spawn = require('child_process').spawn; const SESSION_TIMEOUT = 1; - const keyFile = join(common.fixturesDir, 'agent.key'); - const certFile = join(common.fixturesDir, 'agent.crt'); - const key = fs.readFileSync(keyFile); - const cert = fs.readFileSync(certFile); + const key = fixtures.path('agent.key'); + const cert = fixtures.path('agent.crt'); const options = { key: key, cert: cert, @@ -66,9 +65,8 @@ function doTest() { const sessionFileName = (function() { const ticketFileName = 'tls-session-ticket.txt'; - const fixturesPath = join(common.fixturesDir, ticketFileName); const tmpPath = join(common.tmpDir, ticketFileName); - fs.writeFileSync(tmpPath, fs.readFileSync(fixturesPath)); + fs.writeFileSync(tmpPath, fixtures.readSync(ticketFileName)); return tmpPath; }()); diff --git a/test/pummel/test-tls-throttle.js b/test/pummel/test-tls-throttle.js index 2d0ea1c673a70c..3e18c4cff43a75 100644 --- a/test/pummel/test-tls-throttle.js +++ b/test/pummel/test-tls-throttle.js @@ -29,15 +29,15 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); -const fs = require('fs'); +const fixtures = require('../common/fixtures'); process.stdout.write('build body...'); const body = 'hello world\n'.repeat(1024 * 1024); process.stdout.write('done\n'); const options = { - key: fs.readFileSync(`${common.fixturesDir}/keys/agent2-key.pem`), - cert: fs.readFileSync(`${common.fixturesDir}/keys/agent2-cert.pem`) + key: fixtures.readKey('agent2-key.pem'), + cert: fixtures.readKey('agent2-cert.pem') }; const server = tls.Server(options, common.mustCall(function(socket) { diff --git a/test/pummel/test-watch-file.js b/test/pummel/test-watch-file.js index 0ca8154ee2d254..4a7282052065d6 100644 --- a/test/pummel/test-watch-file.js +++ b/test/pummel/test-watch-file.js @@ -20,13 +20,13 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; -const common = require('../common'); -const assert = require('assert'); +require('../common'); +const assert = require('assert'); const fs = require('fs'); -const path = require('path'); +const fixtures = require('../common/fixtures'); -const f = path.join(common.fixturesDir, 'x.txt'); +const f = fixtures.path('x.txt'); let changes = 0; function watchFile() { diff --git a/test/sequential/test-debugger-repeat-last.js b/test/sequential/test-debugger-repeat-last.js index 39572ab99b3159..42638e5d2ebf12 100644 --- a/test/sequential/test-debugger-repeat-last.js +++ b/test/sequential/test-debugger-repeat-last.js @@ -1,13 +1,10 @@ 'use strict'; const common = require('../common'); common.skipIfInspectorDisabled(); -const path = require('path'); +const path = require('../common/fixtures').path; const spawn = require('child_process').spawn; const assert = require('assert'); -const fixture = path.join( - common.fixturesDir, - 'debugger-repeat-last.js' -); +const fixture = path('debugger-repeat-last.js'); const args = [ 'inspect', diff --git a/test/sequential/test-init.js b/test/sequential/test-init.js index 2e6a23cd346d58..1829905b42d24c 100644 --- a/test/sequential/test-init.js +++ b/test/sequential/test-init.js @@ -23,7 +23,7 @@ const common = require('../common'); const assert = require('assert'); const child = require('child_process'); -const path = require('path'); +const fixtures = require('../common/fixtures'); if (process.env['TEST_INIT']) { return process.stdout.write('Loaded successfully!'); @@ -57,6 +57,6 @@ function test(file, expected) { // ensures that `node fs` does not mistakenly load the native 'fs' module // instead of the desired file and that the fs module loads as // expected in node - process.chdir(path.join(common.fixturesDir, 'test-init-native')); + process.chdir(fixtures.path('test-init-native')); test('fs', 'fs loaded successfully'); } From e3f5c58423818e8335a845196b4e5ae401fc1cfe Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Thu, 24 Aug 2017 20:47:00 +0200 Subject: [PATCH 210/231] perf_hooks: fix presumed typo in node_perf.cc PR-URL: https://github.com/nodejs/node/pull/15019 Reviewed-By: James M Snell Reviewed-By: Richard Lau Reviewed-By: Colin Ihrig --- src/node_perf.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node_perf.cc b/src/node_perf.cc index 2398bb9b8fd6bb..a708877d5de46b 100644 --- a/src/node_perf.cc +++ b/src/node_perf.cc @@ -224,7 +224,7 @@ inline void MarkGarbageCollectionEnd(Isolate* isolate, inline void SetupGarbageCollectionTracking(Isolate* isolate) { isolate->AddGCPrologueCallback(MarkGarbageCollectionStart); - isolate->AddGCPrologueCallback(MarkGarbageCollectionEnd); + isolate->AddGCEpilogueCallback(MarkGarbageCollectionEnd); } inline Local GetName(Local fn) { From a83d427091a53dee05567f9514eabbcee523afb3 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Wed, 30 Aug 2017 18:04:47 +0200 Subject: [PATCH 211/231] src: remove unused persistent properties from env PR-URL: https://github.com/nodejs/node/pull/15096 Reviewed-By: James M Snell Reviewed-By: Daniel Bevenius Reviewed-By: Benjamin Gruenbaum Reviewed-By: Ruben Bridgewater Reviewed-By: Colin Ihrig Reviewed-By: Brian White --- src/env.h | 3 --- src/js_stream.cc | 1 - src/tls_wrap.cc | 1 - 3 files changed, 5 deletions(-) diff --git a/src/env.h b/src/env.h index 02f740cfa3e9e8..9f9a3a23e314d2 100644 --- a/src/env.h +++ b/src/env.h @@ -295,13 +295,11 @@ struct http2_state; V(async_hooks_before_function, v8::Function) \ V(async_hooks_after_function, v8::Function) \ V(binding_cache_object, v8::Object) \ - V(buffer_constructor_function, v8::Function) \ V(buffer_prototype_object, v8::Object) \ V(context, v8::Context) \ V(domain_array, v8::Array) \ V(domains_stack_array, v8::Array) \ V(inspector_console_api_object, v8::Object) \ - V(jsstream_constructor_template, v8::FunctionTemplate) \ V(module_load_list_array, v8::Array) \ V(pbkdf2_constructor_template, v8::ObjectTemplate) \ V(pipe_constructor_template, v8::FunctionTemplate) \ @@ -318,7 +316,6 @@ struct http2_state; V(tcp_constructor_template, v8::FunctionTemplate) \ V(tick_callback_function, v8::Function) \ V(tls_wrap_constructor_function, v8::Function) \ - V(tls_wrap_constructor_template, v8::FunctionTemplate) \ V(tty_constructor_template, v8::FunctionTemplate) \ V(udp_constructor_function, v8::Function) \ V(url_constructor_function, v8::Function) \ diff --git a/src/js_stream.cc b/src/js_stream.cc index 2e7f082e289918..b62dcf3ef5b407 100644 --- a/src/js_stream.cc +++ b/src/js_stream.cc @@ -230,7 +230,6 @@ void JSStream::Initialize(Local target, StreamBase::AddMethods(env, t, StreamBase::kFlagHasWritev); target->Set(jsStreamString, t->GetFunction()); - env->set_jsstream_constructor_template(t); } } // namespace node diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index 77d8ce7b5ea447..b5829cf5b82f6c 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -962,7 +962,6 @@ void TLSWrap::Initialize(Local target, env->SetProtoMethod(t, "setServername", SetServername); #endif // SSL_CRT_SET_TLSEXT_SERVERNAME_CB - env->set_tls_wrap_constructor_template(t); env->set_tls_wrap_constructor_function(t->GetFunction()); target->Set(tlsWrapString, t->GetFunction()); From aaf55db95b78bcb5fdfbdfa4b9b7766a1af6816e Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Fri, 1 Sep 2017 16:31:16 +0200 Subject: [PATCH 212/231] doc,lib,src,test: strip executable bits off files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the executable bits of the file modes of some files that accidentally received it. PR-URL: https://github.com/nodejs/node/pull/15132 Reviewed-By: Timothy Gu Reviewed-By: Michaël Zasso Reviewed-By: Colin Ihrig Reviewed-By: Benjamin Gruenbaum Reviewed-By: James M Snell --- doc/api/child_process.md | 0 doc/api/http2.md | 0 doc/api/perf_hooks.md | 0 lib/internal/http2/core.js | 0 src/node_http2.cc | 0 src/node_http2.h | 0 src/node_http2_core-inl.h | 0 src/node_http2_core.h | 0 test/parallel/test-http2-client-unescaped-path.js | 0 test/parallel/test-http2-client-upload.js | 0 test/parallel/test-http2-info-headers.js | 0 test/parallel/test-http2-options-max-reserved-streams.js | 0 test/parallel/test-http2-response-splitting.js | 0 test/parallel/test-http2-server-socket-destroy.js | 0 test/parallel/test-http2-server-socketerror.js | 0 test/parallel/test-performance-gc.js | 0 16 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 doc/api/child_process.md mode change 100755 => 100644 doc/api/http2.md mode change 100755 => 100644 doc/api/perf_hooks.md mode change 100755 => 100644 lib/internal/http2/core.js mode change 100755 => 100644 src/node_http2.cc mode change 100755 => 100644 src/node_http2.h mode change 100755 => 100644 src/node_http2_core-inl.h mode change 100755 => 100644 src/node_http2_core.h mode change 100755 => 100644 test/parallel/test-http2-client-unescaped-path.js mode change 100755 => 100644 test/parallel/test-http2-client-upload.js mode change 100755 => 100644 test/parallel/test-http2-info-headers.js mode change 100755 => 100644 test/parallel/test-http2-options-max-reserved-streams.js mode change 100755 => 100644 test/parallel/test-http2-response-splitting.js mode change 100755 => 100644 test/parallel/test-http2-server-socket-destroy.js mode change 100755 => 100644 test/parallel/test-http2-server-socketerror.js mode change 100755 => 100644 test/parallel/test-performance-gc.js diff --git a/doc/api/child_process.md b/doc/api/child_process.md old mode 100755 new mode 100644 diff --git a/doc/api/http2.md b/doc/api/http2.md old mode 100755 new mode 100644 diff --git a/doc/api/perf_hooks.md b/doc/api/perf_hooks.md old mode 100755 new mode 100644 diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js old mode 100755 new mode 100644 diff --git a/src/node_http2.cc b/src/node_http2.cc old mode 100755 new mode 100644 diff --git a/src/node_http2.h b/src/node_http2.h old mode 100755 new mode 100644 diff --git a/src/node_http2_core-inl.h b/src/node_http2_core-inl.h old mode 100755 new mode 100644 diff --git a/src/node_http2_core.h b/src/node_http2_core.h old mode 100755 new mode 100644 diff --git a/test/parallel/test-http2-client-unescaped-path.js b/test/parallel/test-http2-client-unescaped-path.js old mode 100755 new mode 100644 diff --git a/test/parallel/test-http2-client-upload.js b/test/parallel/test-http2-client-upload.js old mode 100755 new mode 100644 diff --git a/test/parallel/test-http2-info-headers.js b/test/parallel/test-http2-info-headers.js old mode 100755 new mode 100644 diff --git a/test/parallel/test-http2-options-max-reserved-streams.js b/test/parallel/test-http2-options-max-reserved-streams.js old mode 100755 new mode 100644 diff --git a/test/parallel/test-http2-response-splitting.js b/test/parallel/test-http2-response-splitting.js old mode 100755 new mode 100644 diff --git a/test/parallel/test-http2-server-socket-destroy.js b/test/parallel/test-http2-server-socket-destroy.js old mode 100755 new mode 100644 diff --git a/test/parallel/test-http2-server-socketerror.js b/test/parallel/test-http2-server-socketerror.js old mode 100755 new mode 100644 diff --git a/test/parallel/test-performance-gc.js b/test/parallel/test-performance-gc.js old mode 100755 new mode 100644 From f113d7332f1b7293ab64eea89721e61e2789f88e Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Thu, 31 Aug 2017 10:00:24 +0200 Subject: [PATCH 213/231] src: fix compiler warnings in node_perf.cc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, there are a few compiler warnings generated from node_perf.cc regarding unused return values: ../src/node_perf.cc:58:3: warning: ignoring return value of function declared with warn_unused_result attribute [-Wunused-result] env->performance_entry_callback()->Call(context, ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~ ../src/node_perf.cc:293:5: warning: ignoring return value of function declared with warn_unused_result attribute [-Wunused-result] obj->Set(context, idx, args[idx]); ^~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~ ../src/node_perf.cc:373:3: warning: ignoring return value of function declared with warn_unused_result attribute [-Wunused-result] target->DefineOwnProperty(context, ^~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~ ../src/node_perf.cc:378:3: warning: ignoring return value of function declared with warn_unused_result attribute [-Wunused-result] target->DefineOwnProperty(context, ^~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~ This commit add checks/casts to avoid the warnings. PR-URL: https://github.com/nodejs/node/pull/15112 Reviewed-By: Benjamin Gruenbaum Reviewed-By: Tobias Nießen Reviewed-By: Michaël Zasso Reviewed-By: Colin Ihrig Reviewed-By: Anna Henningsen Reviewed-By: James M Snell --- src/node_perf.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/node_perf.cc b/src/node_perf.cc index a708877d5de46b..48917d5d4ea971 100644 --- a/src/node_perf.cc +++ b/src/node_perf.cc @@ -57,7 +57,7 @@ void PerformanceEntry::NotifyObservers(Environment* env, Local argv = entry->object(); env->performance_entry_callback()->Call(context, v8::Undefined(isolate), - 1, &argv); + 1, &argv).ToLocalChecked(); } void Mark(const FunctionCallbackInfo& args) { @@ -290,7 +290,7 @@ void TimerFunctionCall(const FunctionCallbackInfo& args) { v8::MaybeLocal instance = ctor->NewInstance(context); Local obj = instance.ToLocalChecked(); for (idx = 0; idx < count; idx++) { - obj->Set(context, idx, args[idx]); + obj->Set(context, idx, args[idx]).ToChecked(); } new PerformanceEntry(env, obj, *name, "function", start, end); } @@ -373,12 +373,12 @@ void Init(Local target, target->DefineOwnProperty(context, FIXED_ONE_BYTE_STRING(isolate, "timeOrigin"), v8::Number::New(isolate, timeOrigin / 1e6), - attr); + attr).ToChecked(); target->DefineOwnProperty(context, env->constants_string(), constants, - attr); + attr).ToChecked(); SetupGarbageCollectionTracking(isolate); } From dea959e8412d6924d1efe67fbd107ee41471ed21 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Mon, 28 Aug 2017 13:15:24 -0700 Subject: [PATCH 214/231] test: fix flaky test-readline-interface Move test reliant on timer triggering in a timely fahion from parallel to sequential. The test can fail under high load when the timer is triggered too late and the `\r` and `\n` are treated as separate lines. PR-URL: https://github.com/nodejs/node/pull/15066 Reviewed-By: Refael Ackermann Reviewed-By: James M Snell Reviewed-By: Yuta Hiroto Reviewed-By: Luigi Pinca Reviewed-By: Minwoo Jung Reviewed-By: Anna Henningsen Reviewed-By: Ruben Bridgewater --- test/parallel/test-readline-interface.js | 24 -------------------- test/sequential/test-readline-interface.js | 26 +++++++++++++++++++++- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/test/parallel/test-readline-interface.js b/test/parallel/test-readline-interface.js index bddbc2053ac3cb..e1be69af0c90f4 100644 --- a/test/parallel/test-readline-interface.js +++ b/test/parallel/test-readline-interface.js @@ -291,30 +291,6 @@ function isWarned(emitter) { }), delay * 2); } - // Emit one line events when the delay between \r and \n is - // over the default crlfDelay but within the setting value - { - const fi = new FakeInput(); - const delay = 125; - const crlfDelay = common.platformTimeout(1000); - const rli = new readline.Interface({ - input: fi, - output: fi, - terminal: terminal, - crlfDelay - }); - let callCount = 0; - rli.on('line', function(line) { - callCount++; - }); - fi.emit('data', '\r'); - setTimeout(common.mustCall(() => { - fi.emit('data', '\n'); - assert.strictEqual(callCount, 1); - rli.close(); - }), delay); - } - // set crlfDelay to `Infinity` is allowed { const fi = new FakeInput(); diff --git a/test/sequential/test-readline-interface.js b/test/sequential/test-readline-interface.js index 915bcdd0c0750c..1e64d0aa934974 100644 --- a/test/sequential/test-readline-interface.js +++ b/test/sequential/test-readline-interface.js @@ -21,7 +21,7 @@ // Flags: --expose_internals 'use strict'; -require('../common'); +const common = require('../common'); // These test cases are in `sequential` rather than the analogous test file in // `parallel` because they become unrelaible under load. The unreliability under @@ -83,4 +83,28 @@ FakeInput.prototype.end = () => {}; assert.strictEqual(callCount, expectedLines.length); rli.close(); } + + // Emit one line event when the delay between \r and \n is + // over the default crlfDelay but within the setting value. + { + const fi = new FakeInput(); + const delay = 125; + const crlfDelay = common.platformTimeout(1000); + const rli = new readline.Interface({ + input: fi, + output: fi, + terminal: terminal, + crlfDelay + }); + let callCount = 0; + rli.on('line', function(line) { + callCount++; + }); + fi.emit('data', '\r'); + setTimeout(common.mustCall(() => { + fi.emit('data', '\n'); + assert.strictEqual(callCount, 1); + rli.close(); + }), delay); + } }); From e87cb32db2e7e18395fe70527726ea24c7c72aed Mon Sep 17 00:00:00 2001 From: Gibson Fahnestock Date: Tue, 15 Aug 2017 19:14:54 +0100 Subject: [PATCH 215/231] test: remove envPlus, use Object.assign everywhere MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/14845 Fixes: https://github.com/nodejs/node/issues/14823 Refs: https://github.com/nodejs/node/pull/14822 Reviewed-By: Refael Ackermann Reviewed-By: James M Snell Reviewed-By: Yuta Hiroto Reviewed-By: Ruben Bridgewater Reviewed-By: Tobias Nießen Reviewed-By: Rich Trott --- test/parallel/test-benchmark-crypto.js | 6 +++--- test/parallel/test-child-process-env.js | 6 ++++-- test/parallel/test-child-process-exec-env.js | 4 +++- test/parallel/test-cli-node-options.js | 5 +++-- test/parallel/test-crypto-fips.js | 21 ++++++------------- test/parallel/test-fs-readfile-error.js | 2 +- test/parallel/test-http-server-stale-close.js | 5 +++-- test/parallel/test-icu-data-dir.js | 2 +- test/parallel/test-inspector-open.js | 3 ++- test/parallel/test-npm-install.js | 11 +++++----- test/parallel/test-pending-deprecation.js | 2 +- .../test-process-redirect-warnings-env.js | 3 ++- test/parallel/test-repl-envvars.js | 2 +- test/parallel/test-require-symlink.js | 3 +-- test/parallel/test-stdin-script-child.js | 2 +- 15 files changed, 38 insertions(+), 39 deletions(-) diff --git a/test/parallel/test-benchmark-crypto.js b/test/parallel/test-benchmark-crypto.js index db0e9f499117a5..3675c38b9ea280 100644 --- a/test/parallel/test-benchmark-crypto.js +++ b/test/parallel/test-benchmark-crypto.js @@ -27,9 +27,9 @@ const argv = ['--set', 'algo=sha256', '--set', 'writes=1', 'crypto']; -const env = Object.assign({}, process.env, - { NODEJS_BENCHMARK_ZERO_ALLOWED: 1 }); -const child = fork(runjs, argv, { env }); +const child = fork(runjs, argv, { env: Object.assign({}, process.env, { + NODEJS_BENCHMARK_ZERO_ALLOWED: 1 }) }); + child.on('exit', (code, signal) => { assert.strictEqual(code, 0); assert.strictEqual(signal, null); diff --git a/test/parallel/test-child-process-env.js b/test/parallel/test-child-process-env.js index 015ab4e0c01975..4582c48fda5db1 100644 --- a/test/parallel/test-child-process-env.js +++ b/test/parallel/test-child-process-env.js @@ -34,9 +34,11 @@ Object.setPrototypeOf(env, { let child; if (common.isWindows) { - child = spawn('cmd.exe', ['/c', 'set'], {env: env}); + child = spawn('cmd.exe', ['/c', 'set'], + Object.assign({}, process.env, { env: env })); } else { - child = spawn('/usr/bin/env', [], {env: env}); + child = spawn('/usr/bin/env', [], + Object.assign({}, process.env, { env: env })); } diff --git a/test/parallel/test-child-process-exec-env.js b/test/parallel/test-child-process-exec-env.js index 3b1ef741e6c9b2..c34fdb7b49a161 100644 --- a/test/parallel/test-child-process-exec-env.js +++ b/test/parallel/test-child-process-exec-env.js @@ -44,7 +44,9 @@ function after(err, stdout, stderr) { if (!common.isWindows) { child = exec('/usr/bin/env', { env: { 'HELLO': 'WORLD' } }, after); } else { - child = exec('set', { env: { 'HELLO': 'WORLD' } }, after); + child = exec('set', + { env: Object.assign({}, process.env, { 'HELLO': 'WORLD' }) }, + after); } child.stdout.setEncoding('utf8'); diff --git a/test/parallel/test-cli-node-options.js b/test/parallel/test-cli-node-options.js index 83b387fa04a8f4..f52497e80839cb 100644 --- a/test/parallel/test-cli-node-options.js +++ b/test/parallel/test-cli-node-options.js @@ -29,7 +29,8 @@ disallow('--'); disallow('--no_warnings'); // Node options don't allow '_' instead of '-'. function disallow(opt) { - const options = {env: {NODE_OPTIONS: opt}}; + const options = { env: Object.assign({}, process.env, + { NODE_OPTIONS: opt }) }; exec(process.execPath, options, common.mustCall(function(err) { const message = err.message.split(/\r?\n/)[1]; const expect = `${process.execPath}: ${opt} is not allowed in NODE_OPTIONS`; @@ -71,7 +72,7 @@ function expect(opt, want) { const printB = require.resolve('../fixtures/printB.js'); const argv = [printB]; const opts = { - env: {NODE_OPTIONS: opt}, + env: Object.assign({}, process.env, { NODE_OPTIONS: opt }), maxBuffer: 1000000000, }; exec(process.execPath, argv, opts, common.mustCall(function(err, stdout) { diff --git a/test/parallel/test-crypto-fips.js b/test/parallel/test-crypto-fips.js index d72918756a200b..da2dd7b0ba8765 100644 --- a/test/parallel/test-crypto-fips.js +++ b/test/parallel/test-crypto-fips.js @@ -26,15 +26,6 @@ function sharedOpenSSL() { return process.config.variables.node_shared_openssl; } -function addToEnv(newVar, value) { - const envCopy = {}; - for (const e in process.env) { - envCopy[e] = process.env[e]; - } - envCopy[newVar] = value; - return envCopy; -} - function testHelper(stream, args, expectedOutput, cmd, env) { const fullArgs = args.concat(['-e', `console.log(${cmd})`]); const child = spawnSync(process.execPath, fullArgs, { @@ -72,7 +63,7 @@ testHelper( [], FIPS_DISABLED, 'require("crypto").fips', - addToEnv('OPENSSL_CONF', '')); + Object.assign({}, process.env, { 'OPENSSL_CONF': '' })); // --enable-fips should turn FIPS mode on testHelper( @@ -117,7 +108,7 @@ if (!sharedOpenSSL()) { [], compiledWithFips() ? FIPS_ENABLED : FIPS_DISABLED, 'require("crypto").fips', - addToEnv('OPENSSL_CONF', CNF_FIPS_ON)); + Object.assign({}, process.env, { 'OPENSSL_CONF': CNF_FIPS_ON })); // --openssl-config option should override OPENSSL_CONF testHelper( @@ -125,7 +116,7 @@ if (!sharedOpenSSL()) { [`--openssl-config=${CNF_FIPS_ON}`], compiledWithFips() ? FIPS_ENABLED : FIPS_DISABLED, 'require("crypto").fips', - addToEnv('OPENSSL_CONF', CNF_FIPS_OFF)); + Object.assign({}, process.env, { 'OPENSSL_CONF': CNF_FIPS_OFF })); } testHelper( @@ -133,7 +124,7 @@ testHelper( [`--openssl-config=${CNF_FIPS_OFF}`], FIPS_DISABLED, 'require("crypto").fips', - addToEnv('OPENSSL_CONF', CNF_FIPS_ON)); + Object.assign({}, process.env, { 'OPENSSL_CONF': CNF_FIPS_ON })); // --enable-fips should take precedence over OpenSSL config file testHelper( @@ -149,7 +140,7 @@ testHelper( ['--enable-fips'], compiledWithFips() ? FIPS_ENABLED : OPTION_ERROR_STRING, 'require("crypto").fips', - addToEnv('OPENSSL_CONF', CNF_FIPS_OFF)); + Object.assign({}, process.env, { 'OPENSSL_CONF': CNF_FIPS_OFF })); // --force-fips should take precedence over OpenSSL config file testHelper( @@ -165,7 +156,7 @@ testHelper( ['--force-fips'], compiledWithFips() ? FIPS_ENABLED : OPTION_ERROR_STRING, 'require("crypto").fips', - addToEnv('OPENSSL_CONF', CNF_FIPS_OFF)); + Object.assign({}, process.env, { 'OPENSSL_CONF': CNF_FIPS_OFF })); // setFipsCrypto should be able to turn FIPS mode on testHelper( diff --git a/test/parallel/test-fs-readfile-error.js b/test/parallel/test-fs-readfile-error.js index aa6e6bdecb0b08..e7c52f19a83d1c 100644 --- a/test/parallel/test-fs-readfile-error.js +++ b/test/parallel/test-fs-readfile-error.js @@ -33,7 +33,7 @@ const fixtures = require('../common/fixtures'); function test(env, cb) { const filename = fixtures.path('test-fs-readfile-error.js'); const execPath = `"${process.execPath}" "${filename}"`; - const options = { env: Object.assign(process.env, env) }; + const options = { env: Object.assign({}, process.env, env) }; exec(execPath, options, common.mustCall((err, stdout, stderr) => { assert(err); assert.strictEqual(stdout, ''); diff --git a/test/parallel/test-http-server-stale-close.js b/test/parallel/test-http-server-stale-close.js index 5d903f0c42f74e..77c0e4aa7e2849 100644 --- a/test/parallel/test-http-server-stale-close.js +++ b/test/parallel/test-http-server-stale-close.js @@ -22,7 +22,6 @@ 'use strict'; require('../common'); const http = require('http'); -const util = require('util'); const fork = require('child_process').fork; if (process.env.NODE_TEST_FORK_PORT) { @@ -45,7 +44,9 @@ if (process.env.NODE_TEST_FORK_PORT) { }); server.listen(0, function() { fork(__filename, { - env: util._extend(process.env, {NODE_TEST_FORK_PORT: this.address().port}) + env: Object.assign({}, process.env, { + NODE_TEST_FORK_PORT: this.address().port + }) }); }); } diff --git a/test/parallel/test-icu-data-dir.js b/test/parallel/test-icu-data-dir.js index 1a97699ca51691..800e20c48bab03 100644 --- a/test/parallel/test-icu-data-dir.js +++ b/test/parallel/test-icu-data-dir.js @@ -17,7 +17,7 @@ const expected = } { - const env = { NODE_ICU_DATA: '/' }; + const env = Object.assign({}, process.env, { NODE_ICU_DATA: '/' }); const child = spawnSync(process.execPath, ['-e', '0'], { env }); assert(child.stderr.toString().includes(expected)); } diff --git a/test/parallel/test-inspector-open.js b/test/parallel/test-inspector-open.js index 26647c7e276716..0bbf168471a72e 100644 --- a/test/parallel/test-inspector-open.js +++ b/test/parallel/test-inspector-open.js @@ -13,7 +13,8 @@ const url = require('url'); if (process.env.BE_CHILD) return beChild(); -const child = fork(__filename, {env: {BE_CHILD: 1}}); +const child = fork(__filename, + { env: Object.assign({}, process.env, { BE_CHILD: 1 }) }); child.once('message', common.mustCall((msg) => { assert.strictEqual(msg.cmd, 'started'); diff --git a/test/parallel/test-npm-install.js b/test/parallel/test-npm-install.js index 3340bc62a8d9c4..186b2e2b16099c 100644 --- a/test/parallel/test-npm-install.js +++ b/test/parallel/test-npm-install.js @@ -34,11 +34,12 @@ const pkgPath = path.join(installDir, 'package.json'); fs.writeFileSync(pkgPath, pkgContent); -const env = Object.create(process.env); -env['PATH'] = path.dirname(process.execPath); -env['NPM_CONFIG_PREFIX'] = path.join(npmSandbox, 'npm-prefix'); -env['NPM_CONFIG_TMP'] = path.join(npmSandbox, 'npm-tmp'); -env['HOME'] = path.join(npmSandbox, 'home'); +const env = Object.assign({}, process.env, { + PATH: path.dirname(process.execPath), + NPM_CONFIG_PREFIX: path.join(npmSandbox, 'npm-prefix'), + NPM_CONFIG_TMP: path.join(npmSandbox, 'npm-tmp'), + HOME: path.join(npmSandbox, 'home'), +}); exec(`${process.execPath} ${npmPath} install`, { cwd: installDir, diff --git a/test/parallel/test-pending-deprecation.js b/test/parallel/test-pending-deprecation.js index 402ec79b1bc04c..9b1fd5addfa0c6 100644 --- a/test/parallel/test-pending-deprecation.js +++ b/test/parallel/test-pending-deprecation.js @@ -37,7 +37,7 @@ switch (process.argv[2]) { // Test the NODE_PENDING_DEPRECATION environment var. fork(__filename, ['env'], { - env: {NODE_PENDING_DEPRECATION: 1}, + env: Object.assign({}, process.env, { NODE_PENDING_DEPRECATION: 1 }), silent: true }).on('exit', common.mustCall((code) => { assert.strictEqual(code, 0, message('NODE_PENDING_DEPRECATION')); diff --git a/test/parallel/test-process-redirect-warnings-env.js b/test/parallel/test-process-redirect-warnings-env.js index ae21cff37806cc..6748723cfe0bf3 100644 --- a/test/parallel/test-process-redirect-warnings-env.js +++ b/test/parallel/test-process-redirect-warnings-env.js @@ -16,7 +16,8 @@ common.refreshTmpDir(); const warnmod = require.resolve(`${common.fixturesDir}/warnings.js`); const warnpath = path.join(common.tmpDir, 'warnings.txt'); -fork(warnmod, {env: {NODE_REDIRECT_WARNINGS: warnpath}}) +fork(warnmod, { env: Object.assign({}, process.env, + { NODE_REDIRECT_WARNINGS: warnpath }) }) .on('exit', common.mustCall(() => { fs.readFile(warnpath, 'utf8', common.mustCall((err, data) => { assert.ifError(err); diff --git a/test/parallel/test-repl-envvars.js b/test/parallel/test-repl-envvars.js index c4efd184c4c91c..d29e7b3574c1f2 100644 --- a/test/parallel/test-repl-envvars.js +++ b/test/parallel/test-repl-envvars.js @@ -36,7 +36,7 @@ const tests = [ ]; function run(test) { - const env = test.env; + const env = Object.assign({}, process.env, test.env); const expected = test.expected; const opts = { terminal: true, diff --git a/test/parallel/test-require-symlink.js b/test/parallel/test-require-symlink.js index 927afa1a870071..4dd9bf51ed9213 100644 --- a/test/parallel/test-require-symlink.js +++ b/test/parallel/test-require-symlink.js @@ -5,7 +5,6 @@ const assert = require('assert'); const path = require('path'); const fs = require('fs'); const { exec, spawn } = require('child_process'); -const util = require('util'); const fixtures = require('../common/fixtures'); common.refreshTmpDir(); @@ -61,7 +60,7 @@ function test() { // Also verify that symlinks works for setting preserve via env variables const childEnv = spawn(node, [linkScript], { - env: util._extend(process.env, {NODE_PRESERVE_SYMLINKS: '1'}) + env: Object.assign({}, process.env, { NODE_PRESERVE_SYMLINKS: '1' }) }); childEnv.on('close', function(code, signal) { assert.strictEqual(code, 0); diff --git a/test/parallel/test-stdin-script-child.js b/test/parallel/test-stdin-script-child.js index 6e37fad9414de4..06adc9e113eda7 100644 --- a/test/parallel/test-stdin-script-child.js +++ b/test/parallel/test-stdin-script-child.js @@ -5,7 +5,7 @@ const assert = require('assert'); const { spawn } = require('child_process'); for (const args of [[], ['-']]) { const child = spawn(process.execPath, args, { - env: Object.assign(process.env, { + env: Object.assign({}, process.env, { NODE_DEBUG: process.argv[2] }) }); From e3d0ff901bbd0a4dfd0bf2d6d33cd214fcab7f14 Mon Sep 17 00:00:00 2001 From: Refael Ackermann Date: Wed, 12 Jul 2017 12:27:28 -0400 Subject: [PATCH 216/231] test: split test-cli-node-options * partitioning the subprocess groups * use `-e` instead of module * reduce maxBuffer PR-URL: https://github.com/nodejs/node/pull/14195 Fixes: https://github.com/nodejs/node/issues/14191 Reviewed-By: Rich Trott --- .../test-cli-node-options-disallowed.js | 40 ++++++++++++++ test/parallel/test-cli-node-options.js | 54 ++++--------------- 2 files changed, 49 insertions(+), 45 deletions(-) create mode 100644 test/parallel/test-cli-node-options-disallowed.js diff --git a/test/parallel/test-cli-node-options-disallowed.js b/test/parallel/test-cli-node-options-disallowed.js new file mode 100644 index 00000000000000..b55543bfa243bc --- /dev/null +++ b/test/parallel/test-cli-node-options-disallowed.js @@ -0,0 +1,40 @@ +'use strict'; +const common = require('../common'); +if (process.config.variables.node_without_node_options) + common.skip('missing NODE_OPTIONS support'); + +// Test options specified by env variable. + +const assert = require('assert'); +const exec = require('child_process').execFile; + +common.refreshTmpDir(); +process.chdir(common.tmpDir); + +disallow('--version'); +disallow('-v'); +disallow('--help'); +disallow('-h'); +disallow('--eval'); +disallow('-e'); +disallow('--print'); +disallow('-p'); +disallow('-pe'); +disallow('--check'); +disallow('-c'); +disallow('--interactive'); +disallow('-i'); +disallow('--v8-options'); +disallow('--'); +disallow('--no_warnings'); // Node options don't allow '_' instead of '-'. + +function disallow(opt) { + const env = Object.assign({}, process.env, { NODE_OPTIONS: opt }); + exec(process.execPath, { env }, common.mustCall(function(err) { + const message = err.message.split(/\r?\n/)[1]; + const expect = `${process.execPath}: ${opt} is not allowed in NODE_OPTIONS`; + + assert.strictEqual(err.code, 9); + assert.strictEqual(message, expect); + })); +} diff --git a/test/parallel/test-cli-node-options.js b/test/parallel/test-cli-node-options.js index f52497e80839cb..94cf6ca5fbcdab 100644 --- a/test/parallel/test-cli-node-options.js +++ b/test/parallel/test-cli-node-options.js @@ -11,38 +11,7 @@ const exec = require('child_process').execFile; common.refreshTmpDir(); process.chdir(common.tmpDir); -disallow('--version'); -disallow('-v'); -disallow('--help'); -disallow('-h'); -disallow('--eval'); -disallow('-e'); -disallow('--print'); -disallow('-p'); -disallow('-pe'); -disallow('--check'); -disallow('-c'); -disallow('--interactive'); -disallow('-i'); -disallow('--v8-options'); -disallow('--'); -disallow('--no_warnings'); // Node options don't allow '_' instead of '-'. - -function disallow(opt) { - const options = { env: Object.assign({}, process.env, - { NODE_OPTIONS: opt }) }; - exec(process.execPath, options, common.mustCall(function(err) { - const message = err.message.split(/\r?\n/)[1]; - const expect = `${process.execPath}: ${opt} is not allowed in NODE_OPTIONS`; - - assert.strictEqual(err.code, 9); - assert.strictEqual(message, expect); - })); -} - -const printA = require.resolve('../fixtures/printA.js'); - -expect(`-r ${printA}`, 'A\nB\n'); +expect(`-r ${require.resolve('../fixtures/printA.js')}`, 'A\nB\n'); expect('--no-deprecation', 'B\n'); expect('--no-warnings', 'B\n'); expect('--trace-warnings', 'B\n'); @@ -54,6 +23,7 @@ expect('--track-heap-objects', 'B\n'); expect('--throw-deprecation', 'B\n'); expect('--zero-fill-buffers', 'B\n'); expect('--v8-pool-size=10', 'B\n'); + if (common.hasCrypto) { expect('--use-openssl-ca', 'B\n'); expect('--use-bundled-ca', 'B\n'); @@ -61,26 +31,20 @@ if (common.hasCrypto) { } // V8 options -expect('--abort-on-uncaught-exception', 'B\n'); -expect('--abort_on_uncaught_exception', 'B\n'); expect('--abort_on-uncaught_exception', 'B\n'); -expect('--max_old_space_size=0', 'B\n'); -expect('--max-old_space-size=0', 'B\n'); expect('--max-old-space-size=0', 'B\n'); function expect(opt, want) { - const printB = require.resolve('../fixtures/printB.js'); - const argv = [printB]; + const argv = ['-e', 'console.log("B")']; const opts = { env: Object.assign({}, process.env, { NODE_OPTIONS: opt }), - maxBuffer: 1000000000, + maxBuffer: 1e6, }; - exec(process.execPath, argv, opts, common.mustCall(function(err, stdout) { + exec(process.execPath, argv, opts, common.mustCall((err, stdout) => { assert.ifError(err); - if (!RegExp(want).test(stdout)) { - console.error('For %j, failed to find %j in: <\n%s\n>', - opt, want, stdout); - assert.fail(`Expected ${want}`); - } + if (stdout.includes(want)) return; + + const o = JSON.stringify(opt); + assert.fail(`For ${o}, failed to find ${want} in: <\n${stdout}\n>`); })); } From 865a3c3daf415695c9f7e1c0e7feff024b6ff211 Mon Sep 17 00:00:00 2001 From: Bradley Farias Date: Mon, 5 Jun 2017 19:44:56 -0500 Subject: [PATCH 217/231] module: Allow runMain to be ESM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This follows the EPS an allows the node CLI to have ESM as an entry point. `node ./example.mjs`. A newer V8 is needed for `import()` so that is not included. `import.meta` is still in specification stage so that also is not included. PR-URL: https://github.com/nodejs/node/pull/14369 Author: Bradley Farias Author: Guy Bedford Author: Jan Krems Author: Timothy Gu Author: Michaël Zasso Author: Anna Henningsen Reviewed-By: James M Snell Reviewed-By: Jeremiah Senkpiel --- .eslintrc.yaml | 5 + Makefile | 4 +- doc/api/esm.md | 88 +++ lib/internal/bootstrap_node.js | 7 + lib/internal/errors.js | 4 + lib/internal/loader/Loader.js | 75 +++ lib/internal/loader/ModuleJob.js | 116 ++++ lib/internal/loader/ModuleMap.js | 33 ++ lib/internal/loader/ModuleWrap.js | 61 ++ lib/internal/loader/resolveRequestUrl.js | 104 ++++ lib/internal/loader/search.js | 33 ++ lib/internal/safe_globals.js | 26 + lib/internal/url.js | 7 + lib/module.js | 58 +- node.gyp | 9 + src/module_wrap.cc | 531 ++++++++++++++++++ src/module_wrap.h | 58 ++ src/node.cc | 8 + src/node_config.cc | 3 + src/node_internals.h | 4 + src/node_url.cc | 63 +++ src/node_url.h | 4 + test/cctest/test_url.cc | 25 + test/es-module/es-module.status | 7 + test/es-module/esm-snapshot-mutator.js | 5 + test/es-module/esm-snapshot.js | 3 + test/es-module/test-esm-basic-imports.mjs | 8 + .../es-module/test-esm-encoded-path-native.js | 10 + test/es-module/test-esm-encoded-path.mjs | 7 + test/es-module/test-esm-forbidden-globals.mjs | 24 + test/es-module/test-esm-namespace.mjs | 7 + test/es-module/test-esm-ok.mjs | 5 + test/es-module/test-esm-pkg-over-ext.mjs | 8 + test/es-module/test-esm-preserve-symlinks.js | 38 ++ test/es-module/test-esm-require-cache.mjs | 7 + test/es-module/test-esm-shebang.mjs | 6 + test/es-module/test-esm-snapshot.mjs | 7 + test/es-module/test-esm-symlink.js | 48 ++ test/es-module/testcfg.py | 6 + .../es-module-require-cache/counter.js | 2 + .../es-module-require-cache/preload.js | 1 + test/fixtures/es-module-url/empty.js | 0 test/fixtures/es-module-url/native.mjs | 2 + test/testpy/__init__.py | 13 +- tools/eslint-rules/required-modules.js | 60 +- tools/test.py | 18 +- 46 files changed, 1578 insertions(+), 40 deletions(-) create mode 100644 doc/api/esm.md create mode 100644 lib/internal/loader/Loader.js create mode 100644 lib/internal/loader/ModuleJob.js create mode 100644 lib/internal/loader/ModuleMap.js create mode 100644 lib/internal/loader/ModuleWrap.js create mode 100644 lib/internal/loader/resolveRequestUrl.js create mode 100644 lib/internal/loader/search.js create mode 100644 lib/internal/safe_globals.js create mode 100644 src/module_wrap.cc create mode 100644 src/module_wrap.h create mode 100644 test/es-module/es-module.status create mode 100644 test/es-module/esm-snapshot-mutator.js create mode 100644 test/es-module/esm-snapshot.js create mode 100644 test/es-module/test-esm-basic-imports.mjs create mode 100644 test/es-module/test-esm-encoded-path-native.js create mode 100644 test/es-module/test-esm-encoded-path.mjs create mode 100644 test/es-module/test-esm-forbidden-globals.mjs create mode 100644 test/es-module/test-esm-namespace.mjs create mode 100644 test/es-module/test-esm-ok.mjs create mode 100644 test/es-module/test-esm-pkg-over-ext.mjs create mode 100644 test/es-module/test-esm-preserve-symlinks.js create mode 100644 test/es-module/test-esm-require-cache.mjs create mode 100644 test/es-module/test-esm-shebang.mjs create mode 100644 test/es-module/test-esm-snapshot.mjs create mode 100644 test/es-module/test-esm-symlink.js create mode 100644 test/es-module/testcfg.py create mode 100644 test/fixtures/es-module-require-cache/counter.js create mode 100644 test/fixtures/es-module-require-cache/preload.js create mode 100644 test/fixtures/es-module-url/empty.js create mode 100644 test/fixtures/es-module-url/native.mjs diff --git a/.eslintrc.yaml b/.eslintrc.yaml index 3bded1a7ce0ec7..6643b4649d0da5 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -10,6 +10,11 @@ env: parserOptions: ecmaVersion: 2017 +overrides: + - files: ["doc/api/esm.md", "*.mjs"] + parserOptions: + sourceType: module + rules: # Possible Errors # http://eslint.org/docs/rules/#possible-errors diff --git a/Makefile b/Makefile index ab3f901a9863b6..274df0745871cf 100644 --- a/Makefile +++ b/Makefile @@ -151,7 +151,7 @@ coverage-build: all "$(CURDIR)/testing/coverage/gcovr-patches.diff"); fi if [ -d lib_ ]; then $(RM) -r lib; mv lib_ lib; fi mv lib lib_ - $(NODE) ./node_modules/.bin/nyc instrument lib_/ lib/ + $(NODE) ./node_modules/.bin/nyc instrument --extension .js --extension .mjs lib_/ lib/ $(MAKE) coverage-test: coverage-build @@ -887,7 +887,7 @@ JSLINT_TARGETS = benchmark doc lib test tools jslint: @echo "Running JS linter..." - $(NODE) tools/eslint/bin/eslint.js --cache --rulesdir=tools/eslint-rules --ext=.js,.md \ + $(NODE) tools/eslint/bin/eslint.js --cache --rulesdir=tools/eslint-rules --ext=.js,.mjs,.md \ $(JSLINT_TARGETS) jslint-ci: diff --git a/doc/api/esm.md b/doc/api/esm.md new file mode 100644 index 00000000000000..108fd76336495d --- /dev/null +++ b/doc/api/esm.md @@ -0,0 +1,88 @@ +# ECMAScript Modules + + + +> Stability: 1 - Experimental + + + +Node contains support for ES Modules based upon the [the Node EP for ES Modules][]. + +Not all features of the EP are complete and will be landing as both VM support and implementation is ready. Error messages are still being polished. + +## Enabling + + + +The `--experimental-modules` flag can be used to enable features for loading ESM modules. + +Once this has been set, files ending with `.mjs` will be able to be loaded as ES Modules. + +```sh +node --experimental-modules my-app.mjs +``` + +## Features + + + +### Supported + +Only the CLI argument for the main entry point to the program can be an entry point into an ESM graph. In the future `import()` can be used to create entry points into ESM graphs at run time. + +### Unsupported + +| Feature | Reason | +| --- | --- | +| `require('./foo.mjs')` | ES Modules have differing resolution and timing, use language standard `import()` | +| `import()` | pending newer V8 release used in Node.js | +| `import.meta` | pending V8 implementation | +| Loader Hooks | pending Node.js EP creation/consensus | + +## Notable differences between `import` and `require` + +### No NODE_PATH + +`NODE_PATH` is not part of resolving `import` specifiers. Please use symlinks if this behavior is desired. + +### No `require.extensions` + +`require.extensions` is not used by `import`. The expectation is that loader hooks can provide this workflow in the future. + +### No `require.cache` + +`require.cache` is not used by `import`. It has a separate cache. + +### URL based paths + +ESM are resolved and cached based upon [URL](url.spec.whatwg.org) semantics. This means that files containing special characters such as `#` and `?` need to be escaped. + +Modules will be loaded multiple times if the `import` specifier used to resolve them have a different query or fragment. + +```js +import './foo?query=1'; // loads ./foo with query of "?query=1" +import './foo?query=2'; // loads ./foo with query of "?query=2" +``` + +For now, only modules using the `file:` protocol can be loaded. + +## Interop with existing modules + +All CommonJS, JSON, and C++ modules can be used with `import`. + +Modules loaded this way will only be loaded once, even if their query or fragment string differs between `import` statements. + +When loaded via `import` these modules will provide a single `default` export representing the value of `module.exports` at the time they finished evaluating. + +```js +import fs from 'fs'; +fs.readFile('./foo.txt', (err, body) => { + if (err) { + console.error(err); + } else { + console.log(body); + } +}); +``` + +[the Node EP for ES Modules]: https://github.com/nodejs/node-eps/blob/master/002-es-modules.md diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index 20e0ae3f7ff617..8fdf94a7cbfd86 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -109,6 +109,13 @@ 'DeprecationWarning', 'DEP0062', startup, true); } + if (process.binding('config').experimentalModules) { + process.emitWarning( + 'The ESM module loader is experimental.', + 'ExperimentalWarning', undefined); + } + + // There are various modes that Node can run in. The most common two // are running from a script and running the REPL - but there are a few // others like the debugger or running --eval arguments. Here we decide diff --git a/lib/internal/errors.js b/lib/internal/errors.js index f42bf13b5b287c..3afbc68d963841 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -211,11 +211,15 @@ E('ERR_IPC_DISCONNECTED', 'IPC channel is already disconnected'); E('ERR_IPC_ONE_PIPE', 'Child process can have only one IPC pipe'); E('ERR_IPC_SYNC_FORK', 'IPC cannot be used with synchronous forks'); E('ERR_MISSING_ARGS', missingArgs); +E('ERR_MISSING_MODULE', 'Cannot find module %s'); +E('ERR_MODULE_RESOLUTION_LEGACY', '%s not found by import in %s.' + + 'Legacy behavior in require would have found it at %s'); E('ERR_NAPI_CONS_FUNCTION', 'Constructor must be a function'); E('ERR_NAPI_CONS_PROTOTYPE_OBJECT', 'Constructor.prototype must be an object'); E('ERR_NO_CRYPTO', 'Node.js is not compiled with OpenSSL crypto support'); E('ERR_NO_ICU', '%s is not supported on Node.js compiled without ICU'); E('ERR_PARSE_HISTORY_DATA', 'Could not parse history data in %s'); +E('ERR_REQUIRE_ESM', 'Must use import to load ES Module: %s'); E('ERR_SOCKET_ALREADY_BOUND', 'Socket is already bound'); E('ERR_SOCKET_BAD_TYPE', 'Bad socket type specified. Valid types are: udp4, udp6'); diff --git a/lib/internal/loader/Loader.js b/lib/internal/loader/Loader.js new file mode 100644 index 00000000000000..a409d397f85dd6 --- /dev/null +++ b/lib/internal/loader/Loader.js @@ -0,0 +1,75 @@ +'use strict'; + +const { URL } = require('url'); +const { getURLFromFilePath } = require('internal/url'); + +const { + getNamespaceOfModuleWrap +} = require('internal/loader/ModuleWrap'); + +const ModuleMap = require('internal/loader/ModuleMap'); +const ModuleJob = require('internal/loader/ModuleJob'); +const resolveRequestUrl = require('internal/loader/resolveRequestUrl'); +const errors = require('internal/errors'); + +function getBase() { + try { + return getURLFromFilePath(`${process.cwd()}/`); + } catch (e) { + e.stack; + // If the current working directory no longer exists. + if (e.code === 'ENOENT') { + return undefined; + } + throw e; + } +} + +class Loader { + constructor(base = getBase()) { + this.moduleMap = new ModuleMap(); + if (typeof base !== 'undefined' && base instanceof URL !== true) { + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'base', 'URL'); + } + this.base = base; + } + + async resolve(specifier) { + const request = resolveRequestUrl(this.base, specifier); + if (request.url.protocol !== 'file:') { + throw new errors.Error('ERR_INVALID_PROTOCOL', + request.url.protocol, 'file:'); + } + return request.url; + } + + async getModuleJob(dependentJob, specifier) { + if (!this.moduleMap.has(dependentJob.url)) { + throw new errors.Error('ERR_MISSING_MODULE', dependentJob.url); + } + const request = await resolveRequestUrl(dependentJob.url, specifier); + const url = `${request.url}`; + if (this.moduleMap.has(url)) { + return this.moduleMap.get(url); + } + const dependencyJob = new ModuleJob(this, request); + this.moduleMap.set(url, dependencyJob); + return dependencyJob; + } + + async import(specifier) { + const request = await resolveRequestUrl(this.base, specifier); + const url = `${request.url}`; + let job; + if (this.moduleMap.has(url)) { + job = this.moduleMap.get(url); + } else { + job = new ModuleJob(this, request); + this.moduleMap.set(url, job); + } + const module = await job.run(); + return getNamespaceOfModuleWrap(module); + } +} +Object.setPrototypeOf(Loader.prototype, null); +module.exports = Loader; diff --git a/lib/internal/loader/ModuleJob.js b/lib/internal/loader/ModuleJob.js new file mode 100644 index 00000000000000..db4cb6ae5c5031 --- /dev/null +++ b/lib/internal/loader/ModuleJob.js @@ -0,0 +1,116 @@ +'use strict'; + +const { SafeSet, SafePromise } = require('internal/safe_globals'); +const resolvedPromise = SafePromise.resolve(); +const resolvedArrayPromise = SafePromise.resolve([]); +const { ModuleWrap } = require('internal/loader/ModuleWrap'); + +const NOOP = () => { /* No-op */ }; +class ModuleJob { + /** + * @param {module: ModuleWrap?, compiled: Promise} moduleProvider + */ + constructor(loader, moduleProvider, url) { + this.url = `${moduleProvider.url}`; + this.moduleProvider = moduleProvider; + this.loader = loader; + this.error = null; + this.hadError = false; + + if (moduleProvider instanceof ModuleWrap !== true) { + // linked == promise for dependency jobs, with module populated, + // module wrapper linked + this.modulePromise = this.moduleProvider.createModule(); + this.module = undefined; + const linked = async () => { + const dependencyJobs = []; + this.module = await this.modulePromise; + this.module.link(async (dependencySpecifier) => { + const dependencyJobPromise = + this.loader.getModuleJob(this, dependencySpecifier); + dependencyJobs.push(dependencyJobPromise); + const dependencyJob = await dependencyJobPromise; + return dependencyJob.modulePromise; + }); + return SafePromise.all(dependencyJobs); + }; + this.linked = linked(); + + // instantiated == deep dependency jobs wrappers instantiated, + //module wrapper instantiated + this.instantiated = undefined; + } else { + const getModuleProvider = async () => moduleProvider; + this.modulePromise = getModuleProvider(); + this.moduleProvider = { finish: NOOP }; + this.module = moduleProvider; + this.linked = resolvedArrayPromise; + this.instantiated = this.modulePromise; + } + } + + instantiate() { + if (this.instantiated) { + return this.instantiated; + } + return this.instantiated = new Promise(async (resolve, reject) => { + const jobsInGraph = new SafeSet(); + let jobsReadyToInstantiate = 0; + // (this must be sync for counter to work) + const queueJob = (moduleJob) => { + if (jobsInGraph.has(moduleJob)) { + return; + } + jobsInGraph.add(moduleJob); + moduleJob.linked.then((dependencyJobs) => { + for (const dependencyJob of dependencyJobs) { + queueJob(dependencyJob); + } + checkComplete(); + }, (e) => { + if (!this.hadError) { + this.error = e; + this.hadError = true; + } + checkComplete(); + }); + }; + const checkComplete = () => { + if (++jobsReadyToInstantiate === jobsInGraph.size) { + // I believe we only throw once the whole tree is finished loading? + // or should the error bail early, leaving entire tree to still load? + if (this.hadError) { + reject(this.error); + } else { + try { + this.module.instantiate(); + for (const dependencyJob of jobsInGraph) { + dependencyJob.instantiated = resolvedPromise; + } + resolve(this.module); + } catch (e) { + e.stack; + reject(e); + } + } + } + }; + queueJob(this); + }); + } + + async run() { + const module = await this.instantiate(); + try { + module.evaluate(); + } catch (e) { + e.stack; + this.hadError = true; + this.error = e; + throw e; + } + return module; + } +} +Object.setPrototypeOf(ModuleJob.prototype, null); +module.exports = ModuleJob; diff --git a/lib/internal/loader/ModuleMap.js b/lib/internal/loader/ModuleMap.js new file mode 100644 index 00000000000000..aa238afbaedc05 --- /dev/null +++ b/lib/internal/loader/ModuleMap.js @@ -0,0 +1,33 @@ +'use strict'; + +const ModuleJob = require('internal/loader/ModuleJob'); +const { SafeMap } = require('internal/safe_globals'); +const debug = require('util').debuglog('esm'); +const errors = require('internal/errors'); + +// Tracks the state of the loader-level module cache +class ModuleMap extends SafeMap { + get(url) { + if (typeof url !== 'string') { + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'url', 'string'); + } + return super.get(url); + } + set(url, job) { + if (typeof url !== 'string') { + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'url', 'string'); + } + if (job instanceof ModuleJob !== true) { + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'job', 'ModuleJob'); + } + debug(`Storing ${url} in ModuleMap`); + return super.set(url, job); + } + has(url) { + if (typeof url !== 'string') { + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'url', 'string'); + } + return super.has(url); + } +} +module.exports = ModuleMap; diff --git a/lib/internal/loader/ModuleWrap.js b/lib/internal/loader/ModuleWrap.js new file mode 100644 index 00000000000000..4d35356ec2433e --- /dev/null +++ b/lib/internal/loader/ModuleWrap.js @@ -0,0 +1,61 @@ +'use strict'; + +const { ModuleWrap } = process.binding('module_wrap'); +const debug = require('util').debuglog('esm'); +const ArrayJoin = Function.call.bind(Array.prototype.join); +const ArrayMap = Function.call.bind(Array.prototype.map); + +const getNamespaceOfModuleWrap = (m) => { + const tmp = new ModuleWrap('import * as _ from "";_;', ''); + tmp.link(async () => m); + tmp.instantiate(); + return tmp.evaluate(); +}; + +const createDynamicModule = (exports, url = '', evaluate) => { + debug( + `creating ESM facade for ${url} with exports: ${ArrayJoin(exports, ', ')}` + ); + const names = ArrayMap(exports, (name) => `${name}`); + // sanitized ESM for reflection purposes + const src = `export let executor; + ${ArrayJoin(ArrayMap(names, (name) => `export let $${name}`), ';\n')} + ;(() => [ + fn => executor = fn, + { exports: { ${ + ArrayJoin(ArrayMap(names, (name) => `${name}: { + get: () => $${name}, + set: v => $${name} = v + }`), ',\n') +} } } + ]); + `; + const reflectiveModule = new ModuleWrap(src, `cjs-facade:${url}`); + reflectiveModule.instantiate(); + const [setExecutor, reflect] = reflectiveModule.evaluate()(); + // public exposed ESM + const reexports = `import { executor, + ${ArrayMap(names, (name) => `$${name}`)} + } from ""; + export { + ${ArrayJoin(ArrayMap(names, (name) => `$${name} as ${name}`), ', ')} + } + // add await to this later if top level await comes along + typeof executor === "function" ? executor() : void 0;`; + if (typeof evaluate === 'function') { + setExecutor(() => evaluate(reflect)); + } + const runner = new ModuleWrap(reexports, `${url}`); + runner.link(async () => reflectiveModule); + runner.instantiate(); + return { + module: runner, + reflect + }; +}; + +module.exports = { + createDynamicModule, + getNamespaceOfModuleWrap, + ModuleWrap +}; diff --git a/lib/internal/loader/resolveRequestUrl.js b/lib/internal/loader/resolveRequestUrl.js new file mode 100644 index 00000000000000..2245064bfe4ba8 --- /dev/null +++ b/lib/internal/loader/resolveRequestUrl.js @@ -0,0 +1,104 @@ +'use strict'; + +const { URL } = require('url'); +const internalCJSModule = require('internal/module'); +const internalURLModule = require('internal/url'); +const internalFS = require('internal/fs'); +const NativeModule = require('native_module'); +const { extname } = require('path'); +const { realpathSync } = require('fs'); +const preserveSymlinks = !!process.binding('config').preserveSymlinks; +const { + ModuleWrap, + createDynamicModule +} = require('internal/loader/ModuleWrap'); +const errors = require('internal/errors'); + +const search = require('internal/loader/search'); +const asyncReadFile = require('util').promisify(require('fs').readFile); +const debug = require('util').debuglog('esm'); + +const realpathCache = new Map(); + +class ModuleRequest { + constructor(url) { + this.url = url; + } +} +Object.setPrototypeOf(ModuleRequest.prototype, null); + +// Strategy for loading a standard JavaScript module +class StandardModuleRequest extends ModuleRequest { + async createModule() { + const source = `${await asyncReadFile(this.url)}`; + debug(`Loading StandardModule ${this.url}`); + return new ModuleWrap(internalCJSModule.stripShebang(source), + `${this.url}`); + } +} + +// Strategy for loading a node-style CommonJS module +class CJSModuleRequest extends ModuleRequest { + async createModule() { + const ctx = createDynamicModule(['default'], this.url, (reflect) => { + debug(`Loading CJSModule ${this.url.pathname}`); + const CJSModule = require('module'); + const pathname = internalURLModule.getPathFromURL(this.url); + CJSModule._load(pathname); + }); + this.finish = (module) => { + ctx.reflect.exports.default.set(module.exports); + }; + return ctx.module; + } +} + +// Strategy for loading a node builtin CommonJS module that isn't +// through normal resolution +class NativeModuleRequest extends CJSModuleRequest { + async createModule() { + const ctx = createDynamicModule(['default'], this.url, (reflect) => { + debug(`Loading NativeModule ${this.url.pathname}`); + const exports = require(this.url.pathname); + reflect.exports.default.set(exports); + }); + return ctx.module; + } +} + +const normalizeBaseURL = (baseURLOrString) => { + if (baseURLOrString instanceof URL) return baseURLOrString; + if (typeof baseURLOrString === 'string') return new URL(baseURLOrString); + return undefined; +}; + +const resolveRequestUrl = (baseURLOrString, specifier) => { + if (NativeModule.nonInternalExists(specifier)) { + return new NativeModuleRequest(new URL(`node:${specifier}`)); + } + + const baseURL = normalizeBaseURL(baseURLOrString); + let url = search(specifier, baseURL); + + if (url.protocol !== 'file:') { + throw new errors.Error('ERR_INVALID_PROTOCOL', url.protocol, 'file:'); + } + + if (!preserveSymlinks) { + const real = realpathSync(internalURLModule.getPathFromURL(url), { + [internalFS.realpathCacheKey]: realpathCache + }); + const old = url; + url = internalURLModule.getURLFromFilePath(real); + url.search = old.search; + url.hash = old.hash; + } + + const ext = extname(url.pathname); + if (ext === '.mjs') { + return new StandardModuleRequest(url); + } + + return new CJSModuleRequest(url); +}; +module.exports = resolveRequestUrl; diff --git a/lib/internal/loader/search.js b/lib/internal/loader/search.js new file mode 100644 index 00000000000000..f0ec34ae4e77c2 --- /dev/null +++ b/lib/internal/loader/search.js @@ -0,0 +1,33 @@ +'use strict'; + +const { URL } = require('url'); +const CJSmodule = require('module'); +const errors = require('internal/errors'); +const { resolve } = process.binding('module_wrap'); + +module.exports = (target, base) => { + target = `${target}`; + if (base === undefined) { + // We cannot search without a base. + throw new errors.Error('ERR_MISSING_MODULE', target); + } + base = `${base}`; + try { + return resolve(target, base); + } catch (e) { + e.stack; // cause V8 to generate stack before rethrow + let error = e; + try { + const questionedBase = new URL(base); + const tmpMod = new CJSmodule(questionedBase.pathname, null); + tmpMod.paths = CJSmodule._nodeModulePaths( + new URL('./', questionedBase).pathname); + const found = CJSmodule._resolveFilename(target, tmpMod); + error = new errors.Error('ERR_MODULE_RESOLUTION_LEGACY', target, + base, found); + } catch (problemChecking) { + // ignore + } + throw error; + } +}; diff --git a/lib/internal/safe_globals.js b/lib/internal/safe_globals.js new file mode 100644 index 00000000000000..ad58fa662b53ef --- /dev/null +++ b/lib/internal/safe_globals.js @@ -0,0 +1,26 @@ +'use strict'; + +const copyProps = (unsafe, safe) => { + for (const key of [...Object.getOwnPropertyNames(unsafe), + ...Object.getOwnPropertySymbols(unsafe) + ]) { + if (!Object.getOwnPropertyDescriptor(safe, key)) { + Object.defineProperty( + safe, + key, + Object.getOwnPropertyDescriptor(unsafe, key)); + } + } +}; +const makeSafe = (unsafe, safe) => { + copyProps(unsafe.prototype, safe.prototype); + copyProps(unsafe, safe); + Object.setPrototypeOf(safe.prototype, null); + Object.freeze(safe.prototype); + Object.freeze(safe); + return safe; +}; + +exports.SafeMap = makeSafe(Map, class SafeMap extends Map {}); +exports.SafeSet = makeSafe(Set, class SafeSet extends Set {}); +exports.SafePromise = makeSafe(Promise, class SafePromise extends Promise {}); diff --git a/lib/internal/url.js b/lib/internal/url.js index 10df37d8c25d7b..54a23e02515b8f 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -1378,6 +1378,12 @@ function getPathFromURL(path) { return isWindows ? getPathFromURLWin32(path) : getPathFromURLPosix(path); } +function getURLFromFilePath(filepath) { + const tmp = new URL('file://'); + tmp.pathname = filepath; + return tmp; +} + function NativeURL(ctx) { this[context] = ctx; } @@ -1406,6 +1412,7 @@ setURLConstructor(constructUrl); module.exports = { toUSVString, getPathFromURL, + getURLFromFilePath, URL, URLSearchParams, domainToASCII, diff --git a/lib/module.js b/lib/module.js index 339a228da91bdf..d02a676c90270f 100644 --- a/lib/module.js +++ b/lib/module.js @@ -24,6 +24,7 @@ const NativeModule = require('native_module'); const util = require('util'); const internalModule = require('internal/module'); +const { getURLFromFilePath } = require('internal/url'); const vm = require('vm'); const assert = require('assert').ok; const fs = require('fs'); @@ -32,6 +33,14 @@ const path = require('path'); const internalModuleReadFile = process.binding('fs').internalModuleReadFile; const internalModuleStat = process.binding('fs').internalModuleStat; const preserveSymlinks = !!process.binding('config').preserveSymlinks; +const experimentalModules = !!process.binding('config').experimentalModules; + +const errors = require('internal/errors'); + +const Loader = require('internal/loader/Loader'); +const ModuleJob = require('internal/loader/ModuleJob'); +const { createDynamicModule } = require('internal/loader/ModuleWrap'); +const ESMLoader = new Loader(); function stat(filename) { filename = path._makeLong(filename); @@ -436,7 +445,36 @@ Module._load = function(request, parent, isMain) { debug('Module._load REQUEST %s parent: %s', request, parent.id); } - var filename = Module._resolveFilename(request, parent, isMain); + var filename = null; + + if (isMain) { + let err; + try { + filename = Module._resolveFilename(request, parent, isMain); + } catch (e) { + // try to keep stack + e.stack; + err = e; + } + if (experimentalModules) { + if (filename === null || /\.mjs$/.test(filename)) { + try { + ESMLoader.import(request).catch((e) => { + console.error(e); + process.exit(1); + }); + return; + } catch (e) { + // well, it isn't ESM + } + } + } + if (err) { + throw err; + } + } else { + filename = Module._resolveFilename(request, parent, isMain); + } var cachedModule = Module._cache[filename]; if (cachedModule) { @@ -506,6 +544,19 @@ Module.prototype.load = function(filename) { if (!Module._extensions[extension]) extension = '.js'; Module._extensions[extension](this, filename); this.loaded = true; + + if (experimentalModules) { + const url = getURLFromFilePath(filename); + if (ESMLoader.moduleMap.has(`${url}`) !== true) { + const ctx = createDynamicModule(['default'], url); + ctx.reflect.exports.default.set(this.exports); + ESMLoader.moduleMap.set(`${url}`, + new ModuleJob(ESMLoader, ctx.module)); + } else { + ESMLoader.moduleMap.get(`${url}`).moduleProvider.finish( + Module._cache[filename]); + } + } }; @@ -602,6 +653,11 @@ Module._extensions['.node'] = function(module, filename) { return process.dlopen(module, path._makeLong(filename)); }; +if (experimentalModules) { + Module._extensions['.mjs'] = function(module, filename) { + throw new errors.Error('ERR_REQUIRE_ESM', filename); + }; +} // bootstrap main module. Module.runMain = function() { diff --git a/node.gyp b/node.gyp index 14acf375e128a4..79d9e0a68dcedf 100644 --- a/node.gyp +++ b/node.gyp @@ -91,6 +91,13 @@ 'lib/internal/http.js', 'lib/internal/inspector_async_hook.js', 'lib/internal/linkedlist.js', + 'lib/internal/loader/Loader.js', + 'lib/internal/loader/ModuleMap.js', + 'lib/internal/loader/ModuleJob.js', + 'lib/internal/loader/ModuleWrap.js', + 'lib/internal/loader/resolveRequestUrl.js', + 'lib/internal/loader/search.js', + 'lib/internal/safe_globals.js', 'lib/internal/net.js', 'lib/internal/module.js', 'lib/internal/os.js', @@ -177,6 +184,7 @@ 'src/fs_event_wrap.cc', 'src/handle_wrap.cc', 'src/js_stream.cc', + 'src/module_wrap.cc', 'src/node.cc', 'src/node_api.cc', 'src/node_api.h', @@ -230,6 +238,7 @@ 'src/env-inl.h', 'src/handle_wrap.h', 'src/js_stream.h', + 'src/module_wrap.h', 'src/node.h', 'src/node_http2_core.h', 'src/node_http2_core-inl.h', diff --git a/src/module_wrap.cc b/src/module_wrap.cc new file mode 100644 index 00000000000000..05bbe04ef2e605 --- /dev/null +++ b/src/module_wrap.cc @@ -0,0 +1,531 @@ +#include +#include // PATH_MAX +#include // S_IFDIR +#include "module_wrap.h" + +#include "env.h" +#include "node_url.h" +#include "util.h" +#include "util-inl.h" + +namespace node { +namespace loader { + +using node::url::URL; +using node::url::URL_FLAGS_FAILED; +using v8::Context; +using v8::EscapableHandleScope; +using v8::Function; +using v8::FunctionCallbackInfo; +using v8::FunctionTemplate; +using v8::Integer; +using v8::IntegrityLevel; +using v8::Isolate; +using v8::JSON; +using v8::Local; +using v8::MaybeLocal; +using v8::Module; +using v8::Object; +using v8::Persistent; +using v8::Promise; +using v8::ScriptCompiler; +using v8::ScriptOrigin; +using v8::String; +using v8::Value; + +static const char* EXTENSIONS[] = {".mjs", ".js", ".json", ".node"}; +std::map*> ModuleWrap::module_map_; + +ModuleWrap::ModuleWrap(Environment* env, + Local object, + Local module, + Local url) : BaseObject(env, object) { + Isolate* iso = Isolate::GetCurrent(); + module_.Reset(iso, module); + url_.Reset(iso, url); +} + +ModuleWrap::~ModuleWrap() { + Local module = module_.Get(Isolate::GetCurrent()); + std::vector* same_hash = module_map_[module->GetIdentityHash()]; + auto it = std::find(same_hash->begin(), same_hash->end(), this); + + if (it != same_hash->end()) { + same_hash->erase(it); + } + + module_.Reset(); +} + +void ModuleWrap::New(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + Isolate* iso = args.GetIsolate(); + + if (!args.IsConstructCall()) { + env->ThrowError("constructor must be called using new"); + return; + } + + if (args.Length() != 2) { + env->ThrowError("constructor must have exactly 2 arguments " + "(string, string)"); + return; + } + + if (!args[0]->IsString()) { + env->ThrowError("first argument is not a string"); + return; + } + + auto source_text = args[0].As(); + + if (!args[1]->IsString()) { + env->ThrowError("second argument is not a string"); + return; + } + + Local url = args[1].As(); + + Local mod; + + // compile + { + ScriptOrigin origin(url, + Integer::New(iso, 0), + Integer::New(iso, 0), + False(iso), + Integer::New(iso, 0), + FIXED_ONE_BYTE_STRING(iso, ""), + False(iso), + False(iso), + True(iso)); + ScriptCompiler::Source source(source_text, origin); + auto maybe_mod = ScriptCompiler::CompileModule(iso, &source); + if (maybe_mod.IsEmpty()) { + return; + } + mod = maybe_mod.ToLocalChecked(); + } + + auto that = args.This(); + auto ctx = that->CreationContext(); + auto url_str = FIXED_ONE_BYTE_STRING(iso, "url"); + + if (!that->Set(ctx, url_str, url).FromMaybe(false)) { + return; + } + + ModuleWrap* obj = + new ModuleWrap(Environment::GetCurrent(ctx), that, mod, url); + + if (ModuleWrap::module_map_.count(mod->GetIdentityHash()) == 0) { + ModuleWrap::module_map_[mod->GetIdentityHash()] = + new std::vector(); + } + + ModuleWrap::module_map_[mod->GetIdentityHash()]->push_back(obj); + Wrap(that, obj); + + that->SetIntegrityLevel(ctx, IntegrityLevel::kFrozen); + args.GetReturnValue().Set(that); +} + +void ModuleWrap::Link(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + Isolate* iso = args.GetIsolate(); + EscapableHandleScope handle_scope(iso); + if (!args[0]->IsFunction()) { + env->ThrowError("first argument is not a function"); + return; + } + + Local resolver_arg = args[0].As(); + + auto that = args.This(); + ModuleWrap* obj = Unwrap(that); + auto mod_context = that->CreationContext(); + if (obj->linked_) return; + obj->linked_ = true; + Local mod(obj->module_.Get(iso)); + + // call the dependency resolve callbacks + for (int i = 0; i < mod->GetModuleRequestsLength(); i++) { + Local specifier = mod->GetModuleRequest(i); + Utf8Value specifier_utf(env->isolate(), specifier); + std::string specifier_std(*specifier_utf, specifier_utf.length()); + + Local argv[] = { + specifier + }; + + MaybeLocal maybe_resolve_return_value = + resolver_arg->Call(mod_context, that, 1, argv); + if (maybe_resolve_return_value.IsEmpty()) { + return; + } + Local resolve_return_value = + maybe_resolve_return_value.ToLocalChecked(); + if (!resolve_return_value->IsPromise()) { + env->ThrowError("linking error, expected resolver to return a promise"); + } + Local resolve_promise = resolve_return_value.As(); + obj->resolve_cache_[specifier_std] = new Persistent(); + obj->resolve_cache_[specifier_std]->Reset(iso, resolve_promise); + } + + args.GetReturnValue().Set(handle_scope.Escape(that)); +} + +void ModuleWrap::Instantiate(const FunctionCallbackInfo& args) { + auto iso = args.GetIsolate(); + auto that = args.This(); + auto ctx = that->CreationContext(); + + ModuleWrap* obj = Unwrap(that); + Local mod = obj->module_.Get(iso); + bool ok = mod->Instantiate(ctx, ModuleWrap::ResolveCallback); + + // clear resolve cache on instantiate + obj->resolve_cache_.clear(); + + if (!ok) { + return; + } +} + +void ModuleWrap::Evaluate(const FunctionCallbackInfo& args) { + auto iso = args.GetIsolate(); + auto that = args.This(); + auto ctx = that->CreationContext(); + ModuleWrap* obj = Unwrap(that); + auto result = obj->module_.Get(iso)->Evaluate(ctx); + + if (result.IsEmpty()) { + return; + } + + auto ret = result.ToLocalChecked(); + args.GetReturnValue().Set(ret); +} + +MaybeLocal ModuleWrap::ResolveCallback(Local context, + Local specifier, + Local referrer) { + Environment* env = Environment::GetCurrent(context); + Isolate* iso = Isolate::GetCurrent(); + if (ModuleWrap::module_map_.count(referrer->GetIdentityHash()) == 0) { + env->ThrowError("linking error, unknown module"); + return MaybeLocal(); + } + + std::vector* possible_deps = + ModuleWrap::module_map_[referrer->GetIdentityHash()]; + ModuleWrap* dependent = nullptr; + + for (auto possible_dep : *possible_deps) { + if (possible_dep->module_ == referrer) { + dependent = possible_dep; + } + } + + if (dependent == nullptr) { + env->ThrowError("linking error, null dep"); + return MaybeLocal(); + } + + Utf8Value specifier_utf(env->isolate(), specifier); + std::string specifier_std(*specifier_utf, specifier_utf.length()); + + if (dependent->resolve_cache_.count(specifier_std) != 1) { + env->ThrowError("linking error, not in local cache"); + return MaybeLocal(); + } + + Local resolve_promise = + dependent->resolve_cache_[specifier_std]->Get(iso); + + if (resolve_promise->State() != Promise::kFulfilled) { + env->ThrowError("linking error, dependency promises must be resolved on " + "instantiate"); + return MaybeLocal(); + } + + auto module_object = resolve_promise->Result().As(); + if (module_object.IsEmpty() || !module_object->IsObject()) { + env->ThrowError("linking error, expected a valid module object from " + "resolver"); + return MaybeLocal(); + } + + ModuleWrap* mod; + ASSIGN_OR_RETURN_UNWRAP(&mod, module_object, MaybeLocal()); + return mod->module_.Get(env->isolate()); +} + +namespace { + +URL __init_cwd() { + std::string specifier = "file://"; +#ifdef _WIN32 + // MAX_PATH is in characters, not bytes. Make sure we have enough headroom. + char buf[MAX_PATH * 4]; +#else + char buf[PATH_MAX]; +#endif + + size_t cwd_len = sizeof(buf); + int err = uv_cwd(buf, &cwd_len); + if (err) { + return URL(""); + } + specifier += buf; + specifier += "/"; + return URL(specifier); +} +static URL INITIAL_CWD(__init_cwd()); +inline bool is_relative_or_absolute_path(std::string specifier) { + auto len = specifier.length(); + if (len <= 0) { + return false; + } else if (specifier[0] == '/') { + return true; + } else if (specifier[0] == '.') { + if (len == 1 || specifier[1] == '/') { + return true; + } else if (specifier[1] == '.') { + if (len == 2 || specifier[2] == '/') { + return true; + } + } + } + return false; +} +struct read_result { + bool had_error = false; + std::string source; +} read_result; +inline const struct read_result read_file(uv_file file) { + struct read_result ret; + std::string src; + uv_fs_t req; + void* base = malloc(4096); + if (base == nullptr) { + ret.had_error = true; + return ret; + } + uv_buf_t buf = uv_buf_init(static_cast(base), 4096); + uv_fs_read(uv_default_loop(), &req, file, &buf, 1, 0, nullptr); + while (req.result > 0) { + src += std::string(static_cast(buf.base), req.result); + uv_fs_read(uv_default_loop(), &req, file, &buf, 1, src.length(), nullptr); + } + ret.source = src; + return ret; +} +struct file_check { + bool failed = true; + uv_file file; +} file_check; +inline const struct file_check check_file(URL search, + bool close = false, + bool allow_dir = false) { + struct file_check ret; + uv_fs_t fs_req; + std::string path = search.ToFilePath(); + if (path.empty()) { + return ret; + } + uv_fs_open(nullptr, &fs_req, path.c_str(), O_RDONLY, 0, nullptr); + auto fd = fs_req.result; + if (fd < 0) { + return ret; + } + if (!allow_dir) { + uv_fs_fstat(nullptr, &fs_req, fd, nullptr); + if (fs_req.statbuf.st_mode & S_IFDIR) { + uv_fs_close(nullptr, &fs_req, fd, nullptr); + return ret; + } + } + ret.failed = false; + ret.file = fd; + if (close) uv_fs_close(nullptr, &fs_req, fd, nullptr); + return ret; +} +URL resolve_extensions(URL search, bool check_exact = true) { + if (check_exact) { + auto check = check_file(search, true); + if (!check.failed) { + return search; + } + } + for (auto extension : EXTENSIONS) { + URL guess(search.path() + extension, &search); + auto check = check_file(guess, true); + if (!check.failed) { + return guess; + } + } + return URL(""); +} +inline URL resolve_index(URL search) { + return resolve_extensions(URL("index", &search), false); +} +URL resolve_main(URL search) { + URL pkg("package.json", &search); + auto check = check_file(pkg); + if (!check.failed) { + auto iso = Isolate::GetCurrent(); + auto ctx = iso->GetCurrentContext(); + auto read = read_file(check.file); + uv_fs_t fs_req; + // if we fail to close :-/ + uv_fs_close(nullptr, &fs_req, check.file, nullptr); + if (read.had_error) return URL(""); + std::string pkg_src = read.source; + Local src = + String::NewFromUtf8(iso, pkg_src.c_str(), + String::kNormalString, pkg_src.length()); + if (src.IsEmpty()) return URL(""); + auto maybe_pkg_json = JSON::Parse(ctx, src); + if (maybe_pkg_json.IsEmpty()) return URL(""); + auto pkg_json_obj = maybe_pkg_json.ToLocalChecked().As(); + if (!pkg_json_obj->IsObject()) return URL(""); + auto maybe_pkg_main = pkg_json_obj->Get( + ctx, FIXED_ONE_BYTE_STRING(iso, "main")); + if (maybe_pkg_main.IsEmpty()) return URL(""); + auto pkg_main_str = maybe_pkg_main.ToLocalChecked().As(); + if (!pkg_main_str->IsString()) return URL(""); + Utf8Value main_utf8(iso, pkg_main_str); + std::string main_std(*main_utf8, main_utf8.length()); + if (!is_relative_or_absolute_path(main_std)) { + main_std.insert(0, "./"); + } + return Resolve(main_std, &search); + } + return URL(""); +} +URL resolve_module(std::string specifier, URL* base) { + URL parent(".", base); + URL dir(""); + do { + dir = parent; + auto check = Resolve("./node_modules/" + specifier, &dir, true); + if (!(check.flags() & URL_FLAGS_FAILED)) { + const auto limit = specifier.find('/'); + const auto spec_len = limit == std::string::npos ? + specifier.length() : + limit + 1; + std::string chroot = + dir.path() + "node_modules/" + specifier.substr(0, spec_len); + if (check.path().substr(0, chroot.length()) != chroot) { + return URL(""); + } + return check; + } else { + // TODO(bmeck) PREVENT FALLTHROUGH + } + parent = URL("..", &dir); + } while (parent.path() != dir.path()); + return URL(""); +} + +URL resolve_directory(URL search, bool read_pkg_json) { + if (read_pkg_json) { + auto main = resolve_main(search); + if (!(main.flags() & URL_FLAGS_FAILED)) return main; + } + return resolve_index(search); +} + +} // anonymous namespace + + +URL Resolve(std::string specifier, URL* base, bool read_pkg_json) { + URL pure_url(specifier); + if (!(pure_url.flags() & URL_FLAGS_FAILED)) { + return pure_url; + } + if (specifier.length() == 0) { + return URL(""); + } + if (is_relative_or_absolute_path(specifier)) { + URL resolved(specifier, base); + auto file = resolve_extensions(resolved); + if (!(file.flags() & URL_FLAGS_FAILED)) return file; + if (specifier.back() != '/') { + resolved = URL(specifier + "/", base); + } + return resolve_directory(resolved, read_pkg_json); + } else { + return resolve_module(specifier, base); + } + return URL(""); +} + +void ModuleWrap::Resolve(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + if (args.IsConstructCall()) { + env->ThrowError("resolve() must not be called as a constructor"); + return; + } + if (args.Length() != 2) { + env->ThrowError("resolve must have exactly 2 arguments (string, string)"); + return; + } + + if (!args[0]->IsString()) { + env->ThrowError("first argument is not a string"); + return; + } + Utf8Value specifier_utf(env->isolate(), args[0]); + + if (!args[1]->IsString()) { + env->ThrowError("second argument is not a string"); + return; + } + Utf8Value url_utf(env->isolate(), args[1]); + URL url(*url_utf, url_utf.length()); + + if (url.flags() & URL_FLAGS_FAILED) { + env->ThrowError("second argument is not a URL string"); + return; + } + + URL result = node::loader::Resolve(*specifier_utf, &url, true); + if (result.flags() & URL_FLAGS_FAILED) { + std::string msg = "module "; + msg += *specifier_utf; + msg += " not found"; + env->ThrowError(msg.c_str()); + return; + } + + args.GetReturnValue().Set(result.ToObject(env)); +} + +void ModuleWrap::Initialize(Local target, + Local unused, + Local context) { + Environment* env = Environment::GetCurrent(context); + Isolate* isolate = env->isolate(); + + Local tpl = env->NewFunctionTemplate(New); + tpl->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "ModuleWrap")); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + + env->SetProtoMethod(tpl, "link", Link); + env->SetProtoMethod(tpl, "instantiate", Instantiate); + env->SetProtoMethod(tpl, "evaluate", Evaluate); + + target->Set(FIXED_ONE_BYTE_STRING(isolate, "ModuleWrap"), tpl->GetFunction()); + env->SetMethod(target, "resolve", node::loader::ModuleWrap::Resolve); +} + +} // namespace loader +} // namespace node + +NODE_MODULE_CONTEXT_AWARE_BUILTIN(module_wrap, + node::loader::ModuleWrap::Initialize) diff --git a/src/module_wrap.h b/src/module_wrap.h new file mode 100644 index 00000000000000..c669834c6f3ce5 --- /dev/null +++ b/src/module_wrap.h @@ -0,0 +1,58 @@ +#ifndef SRC_MODULE_WRAP_H_ +#define SRC_MODULE_WRAP_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include +#include +#include +#include "node_url.h" +#include "base-object.h" +#include "base-object-inl.h" + +namespace node { +namespace loader { + +node::url::URL Resolve(std::string specifier, node::url::URL* base, + bool read_pkg_json = false); + +class ModuleWrap : public BaseObject { + public: + static const std::string EXTENSIONS[]; + static void Initialize(v8::Local target, + v8::Local unused, + v8::Local context); + + private: + ModuleWrap(node::Environment* env, + v8::Local object, + v8::Local module, + v8::Local url); + ~ModuleWrap(); + + static void New(const v8::FunctionCallbackInfo& args); + static void Link(const v8::FunctionCallbackInfo& args); + static void Instantiate(const v8::FunctionCallbackInfo& args); + static void Evaluate(const v8::FunctionCallbackInfo& args); + static void GetUrl(v8::Local property, + const v8::PropertyCallbackInfo& info); + static void Resolve(const v8::FunctionCallbackInfo& args); + static v8::MaybeLocal ResolveCallback( + v8::Local context, + v8::Local specifier, + v8::Local referrer); + + v8::Persistent module_; + v8::Persistent url_; + bool linked_ = false; + std::map*> resolve_cache_; + + static std::map*> module_map_; +}; + +} // namespace loader +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_MODULE_WRAP_H_ diff --git a/src/node.cc b/src/node.cc index bdff4527c67857..5cd0ffc29db1e9 100644 --- a/src/node.cc +++ b/src/node.cc @@ -225,6 +225,11 @@ bool trace_warnings = false; // that is used by lib/module.js bool config_preserve_symlinks = false; +// Set in node.cc by ParseArgs when --experimental-modules is used. +// Used in node_config.cc to set a constant on process.binding('config') +// that is used by lib/module.js +bool config_experimental_modules = false; + // Set by ParseArgs when --pending-deprecation or NODE_PENDING_DEPRECATION // is used. bool config_pending_deprecation = false; @@ -3711,6 +3716,7 @@ static void PrintHelp() { " note: linked-in ICU data is present\n" #endif " --preserve-symlinks preserve symbolic links when resolving\n" + " --experimental-modules experimental ES Module support\n" " and caching modules\n" #endif "\n" @@ -3947,6 +3953,8 @@ static void ParseArgs(int* argc, Revert(cve); } else if (strcmp(arg, "--preserve-symlinks") == 0) { config_preserve_symlinks = true; + } else if (strcmp(arg, "--experimental-modules") == 0) { + config_experimental_modules = true; } else if (strcmp(arg, "--prof-process") == 0) { prof_process = true; short_circuit = true; diff --git a/src/node_config.cc b/src/node_config.cc index 87110dd8c644f7..2f45a5e9712ce7 100644 --- a/src/node_config.cc +++ b/src/node_config.cc @@ -65,6 +65,9 @@ static void InitConfig(Local target, if (config_preserve_symlinks) READONLY_BOOLEAN_PROPERTY("preserveSymlinks"); + if (config_experimental_modules) + READONLY_BOOLEAN_PROPERTY("experimentalModules"); + if (config_pending_deprecation) READONLY_BOOLEAN_PROPERTY("pendingDeprecation"); diff --git a/src/node_internals.h b/src/node_internals.h index 05cfdbe9256a9e..c3c9a6518b8413 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -69,6 +69,10 @@ extern bool config_preserve_symlinks; // Set in node.cc by ParseArgs when --expose-http2 is used. extern bool config_expose_http2; +// Set in node.cc by ParseArgs when --experimental-modules is used. +// Used in node_config.cc to set a constant on process.binding('config') +// that is used by lib/module.js +extern bool config_experimental_modules; // Set in node.cc by ParseArgs when --expose-internals or --expose_internals is // used. diff --git a/src/node_url.cc b/src/node_url.cc index dd3da1133ebf2a..f8adc7d7af5509 100644 --- a/src/node_url.cc +++ b/src/node_url.cc @@ -2080,6 +2080,69 @@ static void DomainToUnicode(const FunctionCallbackInfo& args) { v8::NewStringType::kNormal).ToLocalChecked()); } +std::string URL::ToFilePath() { + if (context_.scheme != "file:") { + return ""; + } + +#ifdef _WIN32 + const char* slash = "\\"; + auto is_slash = [] (char ch) { + return ch == '/' || ch == '\\'; + }; +#else + const char* slash = "/"; + auto is_slash = [] (char ch) { + return ch == '/'; + }; + if ((context_.flags & URL_FLAGS_HAS_HOST) && + context_.host.length() > 0) { + return ""; + } +#endif + std::string decoded_path; + for (std::string& part : context_.path) { + std::string decoded; + PercentDecode(part.c_str(), part.length(), &decoded); + for (char& ch : decoded) { + if (is_slash(ch)) { + return ""; + } + } + decoded_path += slash + decoded; + } + +#ifdef _WIN32 + // TODO(TimothyGu): Use "\\?\" long paths on Windows. + + // If hostname is set, then we have a UNC path. Pass the hostname through + // ToUnicode just in case it is an IDN using punycode encoding. We do not + // need to worry about percent encoding because the URL parser will have + // already taken care of that for us. Note that this only causes IDNs with an + // appropriate `xn--` prefix to be decoded. + if ((context_.flags & URL_FLAGS_HAS_HOST) && + context_.host.length() > 0) { + std::string unicode_host; + if (!ToUnicode(&context_.host, &unicode_host)) { + return ""; + } + return "\\\\" + unicode_host + decoded_path; + } + // Otherwise, it's a local path that requires a drive letter. + if (decoded_path.length() < 3) { + return ""; + } + if (decoded_path[2] != ':' || + !IsASCIIAlpha(decoded_path[1])) { + return ""; + } + // Strip out the leading '\'. + return decoded_path.substr(1); +#else + return decoded_path; +#endif +} + // This function works by calling out to a JS function that creates and // returns the JS URL object. Be mindful of the JS<->Native boundary // crossing that is required. diff --git a/src/node_url.h b/src/node_url.h index 72ac366ec1386b..cb7bdca7f2cd6a 100644 --- a/src/node_url.h +++ b/src/node_url.h @@ -163,6 +163,10 @@ class URL { return ret; } + // Get the path of the file: URL in a format consumable by native file system + // APIs. Returns an empty string if something went wrong. + std::string ToFilePath(); + const Local ToObject(Environment* env) const; private: diff --git a/test/cctest/test_url.cc b/test/cctest/test_url.cc index 2cede1a8a3deb3..0b80d44caad807 100644 --- a/test/cctest/test_url.cc +++ b/test/cctest/test_url.cc @@ -79,3 +79,28 @@ TEST_F(URLTest, Base3) { EXPECT_EQ(simple.host(), "example.org"); EXPECT_EQ(simple.path(), "/baz"); } + +TEST_F(URLTest, ToFilePath) { +#define T(url, path) EXPECT_EQ(path, URL(url).ToFilePath()) + T("http://example.org/foo/bar", ""); + +#ifdef _WIN32 + T("file:///C:/Program%20Files/", "C:\\Program Files\\"); + T("file:///C:/a/b/c?query#fragment", "C:\\a\\b\\c"); + T("file://host/path/a/b/c?query#fragment", "\\\\host\\path\\a\\b\\c"); + T("file://xn--weird-prdj8vva.com/host/a", "\\\\wͪ͊eiͬ͋rd.com\\host\\a"); + T("file:///C:/a%2Fb", ""); + T("file:///", ""); + T("file:///home", ""); +#else + T("file:///", "/"); + T("file:///home/user?query#fragment", "/home/user"); + T("file:///home/user/?query#fragment", "/home/user/"); + T("file:///home/user/%20space", "/home/user/ space"); + T("file:///home/us%5Cer", "/home/us\\er"); + T("file:///home/us%2Fer", ""); + T("file://host/path", ""); +#endif + +#undef T +} diff --git a/test/es-module/es-module.status b/test/es-module/es-module.status new file mode 100644 index 00000000000000..343e622ca02ac4 --- /dev/null +++ b/test/es-module/es-module.status @@ -0,0 +1,7 @@ +prefix parallel + +# To mark a test as flaky, list the test name in the appropriate section +# below, without ".js", followed by ": PASS,FLAKY". Example: +# sample-test : PASS,FLAKY + +[true] # This section applies to all platforms diff --git a/test/es-module/esm-snapshot-mutator.js b/test/es-module/esm-snapshot-mutator.js new file mode 100644 index 00000000000000..a0dfa0c28a92bd --- /dev/null +++ b/test/es-module/esm-snapshot-mutator.js @@ -0,0 +1,5 @@ +/* eslint-disable required-modules */ +'use strict'; +const shouldSnapshotFilePath = require.resolve('./esm-snapshot.js'); +require('./esm-snapshot.js'); +require.cache[shouldSnapshotFilePath].exports++; diff --git a/test/es-module/esm-snapshot.js b/test/es-module/esm-snapshot.js new file mode 100644 index 00000000000000..2c3c3a459a738b --- /dev/null +++ b/test/es-module/esm-snapshot.js @@ -0,0 +1,3 @@ +/* eslint-disable required-modules */ +'use strict'; +module.exports = 1; diff --git a/test/es-module/test-esm-basic-imports.mjs b/test/es-module/test-esm-basic-imports.mjs new file mode 100644 index 00000000000000..23989bddd5b0bd --- /dev/null +++ b/test/es-module/test-esm-basic-imports.mjs @@ -0,0 +1,8 @@ +// Flags: --experimental-modules +import '../common'; +import assert from 'assert'; +import ok from './test-esm-ok.mjs'; +import okShebang from './test-esm-shebang.mjs'; + +assert(ok); +assert(okShebang); diff --git a/test/es-module/test-esm-encoded-path-native.js b/test/es-module/test-esm-encoded-path-native.js new file mode 100644 index 00000000000000..f32297efdb9d7b --- /dev/null +++ b/test/es-module/test-esm-encoded-path-native.js @@ -0,0 +1,10 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { spawn } = require('child_process'); + +const native = `${common.fixturesDir}/es-module-url/native.mjs`; +const child = spawn(process.execPath, ['--experimental-modules', native]); +child.on('exit', (code) => { + assert.strictEqual(code, 1); +}); diff --git a/test/es-module/test-esm-encoded-path.mjs b/test/es-module/test-esm-encoded-path.mjs new file mode 100644 index 00000000000000..2c6e145927a1de --- /dev/null +++ b/test/es-module/test-esm-encoded-path.mjs @@ -0,0 +1,7 @@ +// Flags: --experimental-modules +import '../common'; +import assert from 'assert'; +// ./test-esm-ok.mjs +import ok from './test-%65%73%6d-ok.mjs'; + +assert(ok); diff --git a/test/es-module/test-esm-forbidden-globals.mjs b/test/es-module/test-esm-forbidden-globals.mjs new file mode 100644 index 00000000000000..d3e92b9238adba --- /dev/null +++ b/test/es-module/test-esm-forbidden-globals.mjs @@ -0,0 +1,24 @@ +// Flags: --experimental-modules +/* eslint-disable required-modules */ + +if (typeof arguments !== 'undefined') { + throw new Error('not an ESM'); +} +if (typeof this !== 'undefined') { + throw new Error('not an ESM'); +} +if (typeof exports !== 'undefined') { + throw new Error('not an ESM'); +} +if (typeof require !== 'undefined') { + throw new Error('not an ESM'); +} +if (typeof module !== 'undefined') { + throw new Error('not an ESM'); +} +if (typeof __filename !== 'undefined') { + throw new Error('not an ESM'); +} +if (typeof __dirname !== 'undefined') { + throw new Error('not an ESM'); +} diff --git a/test/es-module/test-esm-namespace.mjs b/test/es-module/test-esm-namespace.mjs new file mode 100644 index 00000000000000..72b7fed4b33dfa --- /dev/null +++ b/test/es-module/test-esm-namespace.mjs @@ -0,0 +1,7 @@ +// Flags: --experimental-modules +/* eslint-disable required-modules */ + +import * as fs from 'fs'; +import assert from 'assert'; + +assert.deepStrictEqual(Object.keys(fs), ['default']); diff --git a/test/es-module/test-esm-ok.mjs b/test/es-module/test-esm-ok.mjs new file mode 100644 index 00000000000000..6712e1ab7dfca1 --- /dev/null +++ b/test/es-module/test-esm-ok.mjs @@ -0,0 +1,5 @@ +// Flags: --experimental-modules +/* eslint-disable required-modules */ + +const isJs = true; +export default isJs; diff --git a/test/es-module/test-esm-pkg-over-ext.mjs b/test/es-module/test-esm-pkg-over-ext.mjs new file mode 100644 index 00000000000000..7e47c4c326942f --- /dev/null +++ b/test/es-module/test-esm-pkg-over-ext.mjs @@ -0,0 +1,8 @@ +// Flags: --experimental-modules +/* eslint-disable required-modules */ + +import resolved from '../fixtures/module-pkg-over-ext/inner'; +import expected from '../fixtures/module-pkg-over-ext/inner/package.json'; +import assert from 'assert'; + +assert.strictEqual(resolved, expected); diff --git a/test/es-module/test-esm-preserve-symlinks.js b/test/es-module/test-esm-preserve-symlinks.js new file mode 100644 index 00000000000000..eea5bf061b2fa3 --- /dev/null +++ b/test/es-module/test-esm-preserve-symlinks.js @@ -0,0 +1,38 @@ +// Flags: --experimental-modules +'use strict'; + +const common = require('../common'); +const { spawn } = require('child_process'); +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +common.refreshTmpDir(); +const tmpDir = common.tmpDir; + +const entry = path.join(tmpDir, 'entry.js'); +const real = path.join(tmpDir, 'real.js'); +const link_absolute_path = path.join(tmpDir, 'link.js'); + +fs.writeFileSync(entry, ` +const assert = require('assert'); +global.x = 0; +require('./real.js'); +assert.strictEqual(x, 1); +require('./link.js'); +assert.strictEqual(x, 2); +`); +fs.writeFileSync(real, 'x++;'); + +try { + fs.symlinkSync(real, link_absolute_path); +} catch (err) { + if (err.code !== 'EPERM') throw err; + common.skip('insufficient privileges for symlinks'); +} + +spawn(process.execPath, + ['--experimental-modules', '--preserve-symlinks', entry], + { stdio: 'inherit' }).on('exit', (code) => { + assert.strictEqual(code, 0); +}); diff --git a/test/es-module/test-esm-require-cache.mjs b/test/es-module/test-esm-require-cache.mjs new file mode 100644 index 00000000000000..ff32cde36ff2a8 --- /dev/null +++ b/test/es-module/test-esm-require-cache.mjs @@ -0,0 +1,7 @@ +// Flags: --experimental-modules +import '../common'; +import '../fixtures/es-module-require-cache/preload.js'; +import '../fixtures/es-module-require-cache/counter.js'; +import assert from 'assert'; +assert.strictEqual(global.counter, 1); +delete global.counter; diff --git a/test/es-module/test-esm-shebang.mjs b/test/es-module/test-esm-shebang.mjs new file mode 100644 index 00000000000000..43cc0f8367d8a2 --- /dev/null +++ b/test/es-module/test-esm-shebang.mjs @@ -0,0 +1,6 @@ +#! }]) // isn't js +// Flags: --experimental-modules +/* eslint-disable required-modules */ + +const isJs = true; +export default isJs; diff --git a/test/es-module/test-esm-snapshot.mjs b/test/es-module/test-esm-snapshot.mjs new file mode 100644 index 00000000000000..89034f56681238 --- /dev/null +++ b/test/es-module/test-esm-snapshot.mjs @@ -0,0 +1,7 @@ +// Flags: --experimental-modules +/* eslint-disable required-modules */ +import './esm-snapshot-mutator'; +import one from './esm-snapshot'; +import assert from 'assert'; + +assert.strictEqual(one, 1); diff --git a/test/es-module/test-esm-symlink.js b/test/es-module/test-esm-symlink.js new file mode 100644 index 00000000000000..3b7d689bf8f5f2 --- /dev/null +++ b/test/es-module/test-esm-symlink.js @@ -0,0 +1,48 @@ +'use strict'; + +const common = require('../common'); +const { spawn } = require('child_process'); +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +common.refreshTmpDir(); +const tmpDir = common.tmpDir; + +const entry = path.join(tmpDir, 'entry.mjs'); +const real = path.join(tmpDir, 'index.mjs'); +const link_absolute_path = path.join(tmpDir, 'absolute'); +const link_relative_path = path.join(tmpDir, 'relative'); +const link_ignore_extension = path.join(tmpDir, + 'ignore_extension.json'); +const link_directory = path.join(tmpDir, 'directory'); + +fs.writeFileSync(real, 'export default [];'); +fs.writeFileSync(entry, ` +import assert from 'assert'; +import real from './index.mjs'; +import absolute from './absolute'; +import relative from './relative'; +import ignoreExtension from './ignore_extension.json'; +import directory from './directory'; + +assert.strictEqual(absolute, real); +assert.strictEqual(relative, real); +assert.strictEqual(ignoreExtension, real); +assert.strictEqual(directory, real); +`); + +try { + fs.symlinkSync(real, link_absolute_path); + fs.symlinkSync(path.basename(real), link_relative_path); + fs.symlinkSync(real, link_ignore_extension); + fs.symlinkSync(path.dirname(real), link_directory); +} catch (err) { + if (err.code !== 'EPERM') throw err; + common.skip('insufficient privileges for symlinks'); +} + +spawn(process.execPath, ['--experimental-modules', entry], + { stdio: 'inherit' }).on('exit', (code) => { + assert.strictEqual(code, 0); +}); diff --git a/test/es-module/testcfg.py b/test/es-module/testcfg.py new file mode 100644 index 00000000000000..0d8dfeed463ec8 --- /dev/null +++ b/test/es-module/testcfg.py @@ -0,0 +1,6 @@ +import sys, os +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +import testpy + +def GetConfiguration(context, root): + return testpy.SimpleTestConfiguration(context, root, 'es-module') diff --git a/test/fixtures/es-module-require-cache/counter.js b/test/fixtures/es-module-require-cache/counter.js new file mode 100644 index 00000000000000..2640d3e372dcd2 --- /dev/null +++ b/test/fixtures/es-module-require-cache/counter.js @@ -0,0 +1,2 @@ +global.counter = global.counter || 0; +global.counter++; \ No newline at end of file diff --git a/test/fixtures/es-module-require-cache/preload.js b/test/fixtures/es-module-require-cache/preload.js new file mode 100644 index 00000000000000..6090dc0d5821eb --- /dev/null +++ b/test/fixtures/es-module-require-cache/preload.js @@ -0,0 +1 @@ +require('./counter'); \ No newline at end of file diff --git a/test/fixtures/es-module-url/empty.js b/test/fixtures/es-module-url/empty.js new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/test/fixtures/es-module-url/native.mjs b/test/fixtures/es-module-url/native.mjs new file mode 100644 index 00000000000000..c8831f9bfee1be --- /dev/null +++ b/test/fixtures/es-module-url/native.mjs @@ -0,0 +1,2 @@ +// path +import 'p%61th'; diff --git a/test/testpy/__init__.py b/test/testpy/__init__.py index 37e5ac710bcdf8..f113c1253a477f 100644 --- a/test/testpy/__init__.py +++ b/test/testpy/__init__.py @@ -27,7 +27,7 @@ import test import os -from os.path import join, dirname, exists +from os.path import join, dirname, exists, splitext import re import ast @@ -109,18 +109,17 @@ def __init__(self, context, root, section, additional=None): self.additional_flags = [] def Ls(self, path): - def SelectTest(name): - return name.startswith('test-') and name.endswith('.js') - return [f[:-3] for f in os.listdir(path) if SelectTest(f)] + return [f for f in os.listdir(path) if re.match('^test-.*\.m?js$', f)] def ListTests(self, current_path, path, arch, mode): all_tests = [current_path + [t] for t in self.Ls(join(self.root))] result = [] for test in all_tests: if self.Contains(path, test): - file_path = join(self.root, reduce(join, test[1:], "") + ".js") - result.append(SimpleTestCase(test, file_path, arch, mode, self.context, - self, self.additional_flags)) + file_path = join(self.root, reduce(join, test[1:], "")) + test_name = test[:-1] + [splitext(test[-1])[0]] + result.append(SimpleTestCase(test_name, file_path, arch, mode, + self.context, self, self.additional_flags)) return result def GetBuildRequirements(self): diff --git a/tools/eslint-rules/required-modules.js b/tools/eslint-rules/required-modules.js index 47ade5cd9f9b42..948c46c036d99d 100644 --- a/tools/eslint-rules/required-modules.js +++ b/tools/eslint-rules/required-modules.js @@ -13,6 +13,7 @@ const path = require('path'); module.exports = function(context) { // trim required module names var requiredModules = context.options; + const isESM = context.parserOptions.sourceType === 'module'; const foundModules = []; @@ -39,39 +40,35 @@ module.exports = function(context) { return node.callee.type === 'Identifier' && node.callee.name === 'require'; } + /** + * Function to check if the path is a required module and return its name. + * @param {String} str The path to check + * @returns {undefined|String} required module name or undefined + */ + function getRequiredModuleName(str) { + var value = path.basename(str); + + // check if value is in required modules array + return requiredModules.indexOf(value) !== -1 ? value : undefined; + } + /** * Function to check if a node has an argument that is a required module and * return its name. * @param {ASTNode} node The node to check * @returns {undefined|String} required module name or undefined */ - function getRequiredModuleName(node) { - var moduleName; - + function getRequiredModuleNameFromCall(node) { // node has arguments and first argument is string if (node.arguments.length && isString(node.arguments[0])) { - var argValue = path.basename(node.arguments[0].value.trim()); - - // check if value is in required modules array - if (requiredModules.indexOf(argValue) !== -1) { - moduleName = argValue; - } + return getRequiredModuleName(node.arguments[0].value.trim()); } - return moduleName; + return undefined; } - return { - 'CallExpression': function(node) { - if (isRequireCall(node)) { - var requiredModuleName = getRequiredModuleName(node); - - if (requiredModuleName) { - foundModules.push(requiredModuleName); - } - } - }, - 'Program:exit': function(node) { + const rules = { + 'Program:exit'(node) { if (foundModules.length < requiredModules.length) { var missingModules = requiredModules.filter( function(module) { @@ -88,6 +85,27 @@ module.exports = function(context) { } } }; + + if (isESM) { + rules.ImportDeclaration = (node) => { + var requiredModuleName = getRequiredModuleName(node.source.value); + if (requiredModuleName) { + foundModules.push(requiredModuleName); + } + }; + } else { + rules.CallExpression = (node) => { + if (isRequireCall(node)) { + var requiredModuleName = getRequiredModuleNameFromCall(node); + + if (requiredModuleName) { + foundModules.push(requiredModuleName); + } + } + }; + } + + return rules; }; module.exports.schema = { diff --git a/tools/test.py b/tools/test.py index 5a50c7f2e6ccc6..6839f4e1b2acb1 100755 --- a/tools/test.py +++ b/tools/test.py @@ -279,9 +279,7 @@ def HasRun(self, output): # hard to decipher what test is running when only the filename is printed. prefix = abspath(join(dirname(__file__), '../test')) + os.sep command = output.command[-1] - if command.endswith('.js'): command = command[:-3] - if command.startswith(prefix): command = command[len(prefix):] - command = command.replace('\\', '/') + command = NormalizePath(command, prefix) if output.UnexpectedOutput(): status_line = 'not ok %i %s' % (self._done, command) @@ -352,9 +350,7 @@ def HasRun(self, output): # hard to decipher what test is running when only the filename is printed. prefix = abspath(join(dirname(__file__), '../test')) + os.sep command = output.command[-1] - if command.endswith('.js'): command = command[:-3] - if command.startswith(prefix): command = command[len(prefix):] - command = command.replace('\\', '/') + command = NormalizePath(command, prefix) stdout = output.output.stdout.strip() printed_file = False @@ -1509,12 +1505,16 @@ def SplitPath(s): stripped = [ c.strip() for c in s.split('/') ] return [ Pattern(s) for s in stripped if len(s) > 0 ] -def NormalizePath(path): +def NormalizePath(path, prefix='test/'): # strip the extra path information of the specified test - if path.startswith('test/'): - path = path[5:] + prefix = prefix.replace('\\', '/') + path = path.replace('\\', '/') + if path.startswith(prefix): + path = path[len(prefix):] if path.endswith('.js'): path = path[:-3] + elif path.endswith('.mjs'): + path = path[:-4] return path def GetSpecialCommandProcessor(value): From d6ad9d72f7c1f2c1919394cfd19cb829f5f274ab Mon Sep 17 00:00:00 2001 From: Yury Popov Date: Sat, 12 Aug 2017 19:06:35 +0300 Subject: [PATCH 218/231] tls: multiple PFX in createSecureContext Add support for multiple PFX files in tls.createSecureContext. Also added support for object-style PFX pass. PR-URL: https://github.com/nodejs/node/pull/14793 Fixes: https://github.com/nodejs/node/issues/14756 Reviewed-By: Ruben Bridgewater Reviewed-By: Fedor Indutny Reviewed-By: James M Snell --- doc/api/tls.md | 12 ++++--- lib/_tls_common.js | 29 ++++++++++------ test/fixtures/keys/Makefile | 8 +++++ test/fixtures/keys/ec-pfx.pem | Bin 0 -> 1006 bytes test/parallel/test-tls-multi-pfx.js | 50 ++++++++++++++++++++++++++++ 5 files changed, 85 insertions(+), 14 deletions(-) create mode 100644 test/fixtures/keys/ec-pfx.pem create mode 100644 test/parallel/test-tls-multi-pfx.js diff --git a/doc/api/tls.md b/doc/api/tls.md index e18bbb62b1908f..ebcf85438fdeb5 100644 --- a/doc/api/tls.md +++ b/doc/api/tls.md @@ -934,10 +934,14 @@ changes: --> * `options` {Object} - * `pfx` {string|Buffer} Optional PFX or PKCS12 encoded private key and - certificate chain. `pfx` is an alternative to providing `key` and `cert` - individually. PFX is usually encrypted, if it is, `passphrase` will be used - to decrypt it. + * `pfx` {string|string[]|Buffer|Buffer[]|Object[]} Optional PFX or PKCS12 + encoded private key and certificate chain. `pfx` is an alternative to + providing `key` and `cert` individually. PFX is usually encrypted, if it is, + `passphrase` will be used to decrypt it. Multiple PFX can be provided either + as an array of unencrypted PFX buffers, or an array of objects in the form + `{buf: [, passphrase: ]}`. The object form can only + occur in an array. `object.passphrase` is optional. Encrypted PFX will be + decrypted with `object.passphrase` if provided, or `options.passphrase` if it is not. * `key` {string|string[]|Buffer|Buffer[]|Object[]} Optional private keys in PEM format. PEM allows the option of private keys being encrypted. Encrypted keys will be decrypted with `options.passphrase`. Multiple keys using diff --git a/lib/_tls_common.js b/lib/_tls_common.js index 36b2ebdad68d0b..3d191b8244871e 100644 --- a/lib/_tls_common.js +++ b/lib/_tls_common.js @@ -137,20 +137,29 @@ exports.createSecureContext = function createSecureContext(options, context) { } if (options.pfx) { - var pfx = options.pfx; - var passphrase = options.passphrase; - if (!crypto) crypto = require('crypto'); - pfx = crypto._toBuf(pfx); - if (passphrase) - passphrase = crypto._toBuf(passphrase); - - if (passphrase) { - c.context.loadPKCS12(pfx, passphrase); + if (Array.isArray(options.pfx)) { + for (i = 0; i < options.pfx.length; i++) { + const pfx = options.pfx[i]; + const raw = pfx.buf ? pfx.buf : pfx; + const buf = crypto._toBuf(raw); + const passphrase = pfx.passphrase || options.passphrase; + if (passphrase) { + c.context.loadPKCS12(buf, crypto._toBuf(passphrase)); + } else { + c.context.loadPKCS12(buf); + } + } } else { - c.context.loadPKCS12(pfx); + const buf = crypto._toBuf(options.pfx); + const passphrase = options.passphrase; + if (passphrase) { + c.context.loadPKCS12(buf, crypto._toBuf(passphrase)); + } else { + c.context.loadPKCS12(buf); + } } } diff --git a/test/fixtures/keys/Makefile b/test/fixtures/keys/Makefile index c7390eda0eefc4..27fda1eef27c87 100644 --- a/test/fixtures/keys/Makefile +++ b/test/fixtures/keys/Makefile @@ -335,6 +335,14 @@ ec-cert.pem: ec-csr.pem ec-key.pem -signkey ec-key.pem \ -out ec-cert.pem +ec-pfx.pem: ec-cert.pem ec-key.pem + openssl pkcs12 -export \ + -descert \ + -in ec-cert.pem \ + -inkey ec-key.pem \ + -out ec-pfx.pem \ + -password pass: + dh512.pem: openssl dhparam -out dh512.pem 512 diff --git a/test/fixtures/keys/ec-pfx.pem b/test/fixtures/keys/ec-pfx.pem new file mode 100644 index 0000000000000000000000000000000000000000..3a4aa7dd696085b04b9a169fb0bfaccb296ee714 GIT binary patch literal 1006 zcmXqLVt&QM$ZXKWyn&5VtIebBJ1-+UqmM z*K!sZ)l})GJH80~Ic=GXHm8%&)%O~bJ0|n3Nx8|Ye$y=3<;wBn$NASS5r`?!`1tk3 zpDF9Ie;$6dXub2{7CmF9kgPBN@45!Jli2gAxf8BJi{rJ+qEyaU)T=Er z+@`r;v%buw-{CXoTOD(Y_)sBs;n<^ha@m!)e;m+PdOqR$jzU;o_O1(lKa)F zPjXtbMCUGD*5vVfkJZ72$A26zxthV>edq4CJ^VpyMS|n@I2qni-d>%3;KQzh)3OfJ zE;I1)*dO>(yYKzI%Z{eAr_8Ky;3{M6E4XGA9X#QL(XLH@tivMjiEywe+Sh4om$r zoVsvJ>eLxre*4&--2XT8g0+}JJa5d;ZMtE%41avu*_it{FnMysx+{01wbtEgESo!5 zXc3ThRE0rSYRd<2!@K*KFLd zB*?|cw4iZ|LE}cuba{I!OQP(WUDrVAlBIEigZbO4A*#k#_LYQq*{n$-l9PA??^zuAyK=GY;>M%L@=i<(+5Lyr zY3iC?^Nbw6eoWo`=DGf3E0N$WLgELz3{?%3;ojpEH56kJ3FMeuv(W$SOwF15I-@mo z-&@}}W@2DyplHC!#;VQ7%p}Fiz#?)*(m`|DvB|efFMWu6r0`Ams?6d|EF2!(#-Bbs J+{^&-EC37!#6 Date: Thu, 7 Sep 2017 23:53:11 +0300 Subject: [PATCH 219/231] doc: add ESM doc to _toc.md and all.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/15248 Reviewed-By: Michaël Zasso Reviewed-By: Ruben Bridgewater Reviewed-By: James M Snell --- doc/api/_toc.md | 1 + doc/api/all.md | 1 + 2 files changed, 2 insertions(+) diff --git a/doc/api/_toc.md b/doc/api/_toc.md index 67e0c62256fa14..b3987ed8e4e9f7 100644 --- a/doc/api/_toc.md +++ b/doc/api/_toc.md @@ -19,6 +19,7 @@ * [Deprecated APIs](deprecations.html) * [DNS](dns.html) * [Domain](domain.html) +* [ECMAScript Modules](esm.html) * [Errors](errors.html) * [Events](events.html) * [File System](fs.html) diff --git a/doc/api/all.md b/doc/api/all.md index 849a39a4bd7142..b11661d2b7e916 100644 --- a/doc/api/all.md +++ b/doc/api/all.md @@ -14,6 +14,7 @@ @include deprecations @include dns @include domain +@include esm @include errors @include events @include fs From 02260eab9858f4c49b0523d99cb7878900b685a6 Mon Sep 17 00:00:00 2001 From: Gabriel Schulhof Date: Tue, 5 Sep 2017 23:53:06 +0300 Subject: [PATCH 220/231] test,doc: make module name match gyp target name Currently the nm_modname does not match the file name of the resulting module. In fact, the nm_modname is pretty arbitrary. This seeks to introduce some consistency into the nm_modname property by having the name of the module appear in exactly one place: the "target_name" property of the gyp target that builds the module. PR-URL: https://github.com/nodejs/node/pull/15209 Reviewed-By: Refael Ackermann Reviewed-By: Colin Ihrig --- benchmark/misc/function_call/binding.cc | 2 +- doc/api/addons.md | 20 +++++++++---------- doc/api/n-api.md | 4 ++-- test/addons-napi/1_hello_world/binding.c | 2 +- .../2_function_arguments/binding.c | 2 +- test/addons-napi/3_callbacks/binding.c | 2 +- test/addons-napi/4_object_factory/binding.c | 2 +- test/addons-napi/5_function_factory/binding.c | 2 +- test/addons-napi/6_object_wrap/binding.cc | 2 +- test/addons-napi/7_factory_wrap/binding.cc | 2 +- test/addons-napi/8_passing_wrapped/binding.cc | 2 +- test/addons-napi/test_array/test_array.c | 2 +- test/addons-napi/test_async/test_async.cc | 2 +- test/addons-napi/test_buffer/test_buffer.c | 2 +- .../test_constructor/test_constructor.c | 2 +- .../test_conversions/test_conversions.c | 2 +- .../addons-napi/test_dataview/test_dataview.c | 2 +- .../test_env_sharing/compare_env.c | 2 +- test/addons-napi/test_env_sharing/store_env.c | 2 +- test/addons-napi/test_error/test_error.cc | 2 +- .../test_exception/test_exception.c | 2 +- test/addons-napi/test_fatal/test_fatal.c | 2 +- .../addons-napi/test_function/test_function.c | 2 +- test/addons-napi/test_general/test_general.c | 2 +- .../test_handle_scope/test_handle_scope.c | 2 +- .../addons-napi/test_make_callback/binding.cc | 2 +- .../test_make_callback_recurse/binding.cc | 2 +- test/addons-napi/test_number/test_number.c | 2 +- test/addons-napi/test_object/test_object.c | 2 +- test/addons-napi/test_promise/test_promise.c | 2 +- .../test_properties/test_properties.c | 2 +- .../test_reference/test_reference.c | 2 +- test/addons-napi/test_string/test_string.c | 2 +- test/addons-napi/test_symbol/test_symbol.c | 2 +- .../test_typedarray/test_typedarray.c | 2 +- test/addons/async-hello-world/binding.cc | 2 +- test/addons/async-hooks-id/binding.cc | 2 +- test/addons/async-hooks-promise/binding.cc | 2 +- test/addons/async-resource/binding.cc | 2 +- test/addons/at-exit/binding.cc | 2 +- test/addons/buffer-free-callback/binding.cc | 2 +- test/addons/heap-profiler/binding.cc | 2 +- .../hello-world-function-export/binding.cc | 2 +- test/addons/hello-world/binding.cc | 2 +- test/addons/load-long-path/binding.cc | 2 +- test/addons/make-callback-recurse/binding.cc | 2 +- test/addons/make-callback/binding.cc | 2 +- test/addons/new-target/binding.cc | 2 +- test/addons/null-buffer-neuter/binding.cc | 2 +- test/addons/parse-encoding/binding.cc | 2 +- test/addons/repl-domain-abort/binding.cc | 2 +- .../binding.cc | 2 +- test/addons/symlinked-module/binding.cc | 2 +- 53 files changed, 63 insertions(+), 63 deletions(-) diff --git a/benchmark/misc/function_call/binding.cc b/benchmark/misc/function_call/binding.cc index 9008f6c437052a..95d2a56de26e87 100644 --- a/benchmark/misc/function_call/binding.cc +++ b/benchmark/misc/function_call/binding.cc @@ -14,4 +14,4 @@ extern "C" void init (Local target) { NODE_SET_METHOD(target, "hello", Hello); } -NODE_MODULE(binding, init) +NODE_MODULE(NODE_GYP_MODULE_NAME, init) diff --git a/doc/api/addons.md b/doc/api/addons.md index bd175103330da7..b031c273e7e66b 100644 --- a/doc/api/addons.md +++ b/doc/api/addons.md @@ -74,7 +74,7 @@ void init(Local exports) { NODE_SET_METHOD(exports, "hello", Method); } -NODE_MODULE(addon, init) +NODE_MODULE(NODE_GYP_MODULE_NAME, init) } // namespace demo ``` @@ -84,7 +84,7 @@ the pattern: ```cpp void Initialize(Local exports); -NODE_MODULE(module_name, Initialize) +NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize) ``` There is no semi-colon after `NODE_MODULE` as it's not a function (see @@ -330,7 +330,7 @@ void Init(Local exports) { NODE_SET_METHOD(exports, "add", Add); } -NODE_MODULE(addon, Init) +NODE_MODULE(NODE_GYP_MODULE_NAME, Init) } // namespace demo ``` @@ -378,7 +378,7 @@ void Init(Local exports, Local module) { NODE_SET_METHOD(module, "exports", RunCallback); } -NODE_MODULE(addon, Init) +NODE_MODULE(NODE_GYP_MODULE_NAME, Init) } // namespace demo ``` @@ -434,7 +434,7 @@ void Init(Local exports, Local module) { NODE_SET_METHOD(module, "exports", CreateObject); } -NODE_MODULE(addon, Init) +NODE_MODULE(NODE_GYP_MODULE_NAME, Init) } // namespace demo ``` @@ -493,7 +493,7 @@ void Init(Local exports, Local module) { NODE_SET_METHOD(module, "exports", CreateFunction); } -NODE_MODULE(addon, Init) +NODE_MODULE(NODE_GYP_MODULE_NAME, Init) } // namespace demo ``` @@ -529,7 +529,7 @@ void InitAll(Local exports) { MyObject::Init(exports); } -NODE_MODULE(addon, InitAll) +NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll) } // namespace demo ``` @@ -713,7 +713,7 @@ void InitAll(Local exports, Local module) { NODE_SET_METHOD(module, "exports", CreateObject); } -NODE_MODULE(addon, InitAll) +NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll) } // namespace demo ``` @@ -926,7 +926,7 @@ void InitAll(Local exports) { NODE_SET_METHOD(exports, "add", Add); } -NODE_MODULE(addon, InitAll) +NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll) } // namespace demo ``` @@ -1117,7 +1117,7 @@ void init(Local exports) { AtExit(sanity_check); } -NODE_MODULE(addon, init) +NODE_MODULE(NODE_GYP_MODULE_NAME, init) } // namespace demo ``` diff --git a/doc/api/n-api.md b/doc/api/n-api.md index d80c3524b3564e..daac4c592fc2a7 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -878,7 +878,7 @@ except that instead of using the `NODE_MODULE` macro the following is used: ```C -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) ``` The next difference is the signature for the `Init` method. For a N-API @@ -2875,7 +2875,7 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { if (status != napi_ok) return; } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) ``` Given the above code, the add-on can be used from JavaScript as follows: diff --git a/test/addons-napi/1_hello_world/binding.c b/test/addons-napi/1_hello_world/binding.c index 57bac37fa0bfd1..785484d1676fa6 100644 --- a/test/addons-napi/1_hello_world/binding.c +++ b/test/addons-napi/1_hello_world/binding.c @@ -15,4 +15,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { NAPI_CALL_RETURN_VOID(env, napi_define_properties(env, exports, 1, &desc)); } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/2_function_arguments/binding.c b/test/addons-napi/2_function_arguments/binding.c index 92f89fd2ffa199..977547bb6f7cbc 100644 --- a/test/addons-napi/2_function_arguments/binding.c +++ b/test/addons-napi/2_function_arguments/binding.c @@ -34,4 +34,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { NAPI_CALL_RETURN_VOID(env, napi_define_properties(env, exports, 1, &desc)); } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/3_callbacks/binding.c b/test/addons-napi/3_callbacks/binding.c index 8640a936107d47..4249ebc7686a80 100644 --- a/test/addons-napi/3_callbacks/binding.c +++ b/test/addons-napi/3_callbacks/binding.c @@ -53,4 +53,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { NAPI_CALL_RETURN_VOID(env, napi_define_properties(env, exports, 2, desc)); } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/4_object_factory/binding.c b/test/addons-napi/4_object_factory/binding.c index d76f30b847a237..54c39d8515911b 100644 --- a/test/addons-napi/4_object_factory/binding.c +++ b/test/addons-napi/4_object_factory/binding.c @@ -20,4 +20,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { NAPI_CALL_RETURN_VOID(env, napi_define_properties(env, module, 1, &desc)); } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/5_function_factory/binding.c b/test/addons-napi/5_function_factory/binding.c index 4c20190f5047cd..789331bef22e21 100644 --- a/test/addons-napi/5_function_factory/binding.c +++ b/test/addons-napi/5_function_factory/binding.c @@ -22,4 +22,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { NAPI_CALL_RETURN_VOID(env, napi_define_properties(env, module, 1, &desc)); } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/6_object_wrap/binding.cc b/test/addons-napi/6_object_wrap/binding.cc index 99d8339bd09ef3..5681550541a3fa 100644 --- a/test/addons-napi/6_object_wrap/binding.cc +++ b/test/addons-napi/6_object_wrap/binding.cc @@ -4,4 +4,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { MyObject::Init(env, exports); } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/7_factory_wrap/binding.cc b/test/addons-napi/7_factory_wrap/binding.cc index c61e4094784ed7..16946b7520009a 100644 --- a/test/addons-napi/7_factory_wrap/binding.cc +++ b/test/addons-napi/7_factory_wrap/binding.cc @@ -20,4 +20,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { NAPI_CALL_RETURN_VOID(env, napi_define_properties(env, module, 1, &desc)); } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/8_passing_wrapped/binding.cc b/test/addons-napi/8_passing_wrapped/binding.cc index c22ac6442f6c4e..408d0617106a22 100644 --- a/test/addons-napi/8_passing_wrapped/binding.cc +++ b/test/addons-napi/8_passing_wrapped/binding.cc @@ -41,4 +41,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { napi_define_properties(env, exports, sizeof(desc) / sizeof(*desc), desc)); } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/test_array/test_array.c b/test/addons-napi/test_array/test_array.c index 4bb12d4e01e8fa..90448f3ebbd7b9 100644 --- a/test/addons-napi/test_array/test_array.c +++ b/test/addons-napi/test_array/test_array.c @@ -182,4 +182,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/test_async/test_async.cc b/test/addons-napi/test_async/test_async.cc index ca76fa2d33b132..fd5278d7d5cc20 100644 --- a/test/addons-napi/test_async/test_async.cc +++ b/test/addons-napi/test_async/test_async.cc @@ -170,4 +170,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { env, exports, sizeof(properties) / sizeof(*properties), properties)); } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/test_buffer/test_buffer.c b/test/addons-napi/test_buffer/test_buffer.c index 0e12bedfb5602f..546b306ec6527d 100644 --- a/test/addons-napi/test_buffer/test_buffer.c +++ b/test/addons-napi/test_buffer/test_buffer.c @@ -141,4 +141,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { env, exports, sizeof(methods) / sizeof(methods[0]), methods)); } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/test_constructor/test_constructor.c b/test/addons-napi/test_constructor/test_constructor.c index a991dab8533bf1..2d3c5dbe9ffd46 100644 --- a/test/addons-napi/test_constructor/test_constructor.c +++ b/test/addons-napi/test_constructor/test_constructor.c @@ -87,4 +87,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { napi_create_reference(env, cons, 1, &constructor_)); } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/test_conversions/test_conversions.c b/test/addons-napi/test_conversions/test_conversions.c index a8d526c763c9a2..36f4a11ac4d5b6 100644 --- a/test/addons-napi/test_conversions/test_conversions.c +++ b/test/addons-napi/test_conversions/test_conversions.c @@ -148,4 +148,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/test_dataview/test_dataview.c b/test/addons-napi/test_dataview/test_dataview.c index 4060e4d2666c44..66470552f28a7f 100644 --- a/test/addons-napi/test_dataview/test_dataview.c +++ b/test/addons-napi/test_dataview/test_dataview.c @@ -46,4 +46,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/test_env_sharing/compare_env.c b/test/addons-napi/test_env_sharing/compare_env.c index b209c8f6d8e968..6488789a0b5759 100644 --- a/test/addons-napi/test_env_sharing/compare_env.c +++ b/test/addons-napi/test_env_sharing/compare_env.c @@ -19,4 +19,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* context) { NAPI_CALL_RETURN_VOID(env, napi_define_properties(env, module, 1, &prop)); } -NAPI_MODULE(compare_env, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/test_env_sharing/store_env.c b/test/addons-napi/test_env_sharing/store_env.c index 391f6faa27648e..b2b1772a696dac 100644 --- a/test/addons-napi/test_env_sharing/store_env.c +++ b/test/addons-napi/test_env_sharing/store_env.c @@ -9,4 +9,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* context) { napi_set_named_property(env, module, "exports", external)); } -NAPI_MODULE(store_env, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/test_error/test_error.cc b/test/addons-napi/test_error/test_error.cc index 29aba1f1ef62f6..4ef279d1fe4f65 100644 --- a/test/addons-napi/test_error/test_error.cc +++ b/test/addons-napi/test_error/test_error.cc @@ -141,4 +141,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/test_exception/test_exception.c b/test/addons-napi/test_exception/test_exception.c index 191ac3f8ba5aec..3a4e91a731b407 100644 --- a/test/addons-napi/test_exception/test_exception.c +++ b/test/addons-napi/test_exception/test_exception.c @@ -56,4 +56,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/test_fatal/test_fatal.c b/test/addons-napi/test_fatal/test_fatal.c index 1e8a1cf7955d18..74fe3f132d56b6 100644 --- a/test/addons-napi/test_fatal/test_fatal.c +++ b/test/addons-napi/test_fatal/test_fatal.c @@ -15,4 +15,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { env, exports, sizeof(properties) / sizeof(*properties), properties)); } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/test_function/test_function.c b/test/addons-napi/test_function/test_function.c index 4ce0203e7232dd..b044de3b1e2c64 100644 --- a/test/addons-napi/test_function/test_function.c +++ b/test/addons-napi/test_function/test_function.c @@ -32,4 +32,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { NAPI_CALL_RETURN_VOID(env, napi_set_named_property(env, exports, "Test", fn)); } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/test_general/test_general.c b/test/addons-napi/test_general/test_general.c index 287c1d7cb8cb34..c58b868af5eab0 100644 --- a/test/addons-napi/test_general/test_general.c +++ b/test/addons-napi/test_general/test_general.c @@ -252,4 +252,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/test_handle_scope/test_handle_scope.c b/test/addons-napi/test_handle_scope/test_handle_scope.c index 6637da82316185..d6b0653f2aa9da 100644 --- a/test/addons-napi/test_handle_scope/test_handle_scope.c +++ b/test/addons-napi/test_handle_scope/test_handle_scope.c @@ -78,4 +78,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { env, exports, sizeof(properties) / sizeof(*properties), properties)); } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/test_make_callback/binding.cc b/test/addons-napi/test_make_callback/binding.cc index 987b024098598c..526131ab739308 100644 --- a/test/addons-napi/test_make_callback/binding.cc +++ b/test/addons-napi/test_make_callback/binding.cc @@ -45,4 +45,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { } // namespace -NAPI_MODULE(binding, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/test_make_callback_recurse/binding.cc b/test/addons-napi/test_make_callback_recurse/binding.cc index 3f5a4c28b43524..75c0399cdba0c4 100644 --- a/test/addons-napi/test_make_callback_recurse/binding.cc +++ b/test/addons-napi/test_make_callback_recurse/binding.cc @@ -29,4 +29,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { } // namespace -NAPI_MODULE(binding, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/test_number/test_number.c b/test/addons-napi/test_number/test_number.c index 6b28afe18ff9e8..bd7f0679372313 100644 --- a/test/addons-napi/test_number/test_number.c +++ b/test/addons-napi/test_number/test_number.c @@ -55,4 +55,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/test_object/test_object.c b/test/addons-napi/test_object/test_object.c index 88ac79c170f5ea..c6fa27e30c3e73 100644 --- a/test/addons-napi/test_object/test_object.c +++ b/test/addons-napi/test_object/test_object.c @@ -236,4 +236,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/test_promise/test_promise.c b/test/addons-napi/test_promise/test_promise.c index dc617f4592e520..d21564e93f5c8d 100644 --- a/test/addons-napi/test_promise/test_promise.c +++ b/test/addons-napi/test_promise/test_promise.c @@ -57,4 +57,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/test_properties/test_properties.c b/test/addons-napi/test_properties/test_properties.c index 3f4f0a6bcbba96..7c5d6b1d6f1532 100644 --- a/test/addons-napi/test_properties/test_properties.c +++ b/test/addons-napi/test_properties/test_properties.c @@ -97,4 +97,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { env, exports, sizeof(properties) / sizeof(*properties), properties)); } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/test_reference/test_reference.c b/test/addons-napi/test_reference/test_reference.c index b16e10d70361b5..f10b40bf5e3b51 100644 --- a/test/addons-napi/test_reference/test_reference.c +++ b/test/addons-napi/test_reference/test_reference.c @@ -150,4 +150,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/test_string/test_string.c b/test/addons-napi/test_string/test_string.c index ec80e2c7b57fa8..34285b3b5935f2 100644 --- a/test/addons-napi/test_string/test_string.c +++ b/test/addons-napi/test_string/test_string.c @@ -217,4 +217,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { env, exports, sizeof(properties) / sizeof(*properties), properties)); } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/test_symbol/test_symbol.c b/test/addons-napi/test_symbol/test_symbol.c index f85567c071e256..42425c052c22f8 100644 --- a/test/addons-napi/test_symbol/test_symbol.c +++ b/test/addons-napi/test_symbol/test_symbol.c @@ -57,4 +57,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { env, exports, sizeof(properties) / sizeof(*properties), properties)); } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/test_typedarray/test_typedarray.c b/test/addons-napi/test_typedarray/test_typedarray.c index ffc118681bad9e..a138297d49a61d 100644 --- a/test/addons-napi/test_typedarray/test_typedarray.c +++ b/test/addons-napi/test_typedarray/test_typedarray.c @@ -153,4 +153,4 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); } -NAPI_MODULE(addon, Init) +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons/async-hello-world/binding.cc b/test/addons/async-hello-world/binding.cc index 42291c49011fb3..f773bfffcdb95d 100644 --- a/test/addons/async-hello-world/binding.cc +++ b/test/addons/async-hello-world/binding.cc @@ -88,4 +88,4 @@ void init(v8::Local exports, v8::Local module) { NODE_SET_METHOD(exports, "runMakeCallback", Method); } -NODE_MODULE(binding, init) +NODE_MODULE(NODE_GYP_MODULE_NAME, init) diff --git a/test/addons/async-hooks-id/binding.cc b/test/addons/async-hooks-id/binding.cc index bb82e9ef1558bb..f0fbdcfcc72a62 100644 --- a/test/addons/async-hooks-id/binding.cc +++ b/test/addons/async-hooks-id/binding.cc @@ -32,4 +32,4 @@ void Initialize(Local exports) { } // namespace -NODE_MODULE(binding, Initialize) +NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize) diff --git a/test/addons/async-hooks-promise/binding.cc b/test/addons/async-hooks-promise/binding.cc index fb91b936e66d28..1571690edead24 100644 --- a/test/addons/async-hooks-promise/binding.cc +++ b/test/addons/async-hooks-promise/binding.cc @@ -38,6 +38,6 @@ inline void Initialize(v8::Local binding) { NODE_SET_METHOD(binding, "getPromiseField", GetPromiseField); } -NODE_MODULE(binding, Initialize) +NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize) } // anonymous namespace diff --git a/test/addons/async-resource/binding.cc b/test/addons/async-resource/binding.cc index 9d3ab37e12a838..83629e757dedfc 100644 --- a/test/addons/async-resource/binding.cc +++ b/test/addons/async-resource/binding.cc @@ -111,4 +111,4 @@ void Initialize(Local exports) { } // namespace -NODE_MODULE(binding, Initialize) +NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize) diff --git a/test/addons/at-exit/binding.cc b/test/addons/at-exit/binding.cc index d90b3f70c2560b..4dd9b0f304758f 100644 --- a/test/addons/at-exit/binding.cc +++ b/test/addons/at-exit/binding.cc @@ -39,4 +39,4 @@ void init(Local exports) { atexit(sanity_check); } -NODE_MODULE(binding, init) +NODE_MODULE(NODE_GYP_MODULE_NAME, init) diff --git a/test/addons/buffer-free-callback/binding.cc b/test/addons/buffer-free-callback/binding.cc index 24b5dea6025db6..4075fef50dcb0b 100644 --- a/test/addons/buffer-free-callback/binding.cc +++ b/test/addons/buffer-free-callback/binding.cc @@ -41,4 +41,4 @@ void init(v8::Local exports) { NODE_SET_METHOD(exports, "check", Check); } -NODE_MODULE(binding, init) +NODE_MODULE(NODE_GYP_MODULE_NAME, init) diff --git a/test/addons/heap-profiler/binding.cc b/test/addons/heap-profiler/binding.cc index 0830aae1beceac..09feefa66902aa 100644 --- a/test/addons/heap-profiler/binding.cc +++ b/test/addons/heap-profiler/binding.cc @@ -22,6 +22,6 @@ inline void Initialize(v8::Local binding) { v8::FunctionTemplate::New(isolate, Test)->GetFunction()); } -NODE_MODULE(test, Initialize) +NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize) } // anonymous namespace diff --git a/test/addons/hello-world-function-export/binding.cc b/test/addons/hello-world-function-export/binding.cc index 9a93a806e7156e..55d64b3d6c3513 100644 --- a/test/addons/hello-world-function-export/binding.cc +++ b/test/addons/hello-world-function-export/binding.cc @@ -10,4 +10,4 @@ void init(v8::Local exports, v8::Local module) { NODE_SET_METHOD(module, "exports", Method); } -NODE_MODULE(binding, init) +NODE_MODULE(NODE_GYP_MODULE_NAME, init) diff --git a/test/addons/hello-world/binding.cc b/test/addons/hello-world/binding.cc index 83823ae8363cd9..944f5631956d15 100644 --- a/test/addons/hello-world/binding.cc +++ b/test/addons/hello-world/binding.cc @@ -10,4 +10,4 @@ void init(v8::Local exports) { NODE_SET_METHOD(exports, "hello", Method); } -NODE_MODULE(binding, init) +NODE_MODULE(NODE_GYP_MODULE_NAME, init) diff --git a/test/addons/load-long-path/binding.cc b/test/addons/load-long-path/binding.cc index 83823ae8363cd9..944f5631956d15 100644 --- a/test/addons/load-long-path/binding.cc +++ b/test/addons/load-long-path/binding.cc @@ -10,4 +10,4 @@ void init(v8::Local exports) { NODE_SET_METHOD(exports, "hello", Method); } -NODE_MODULE(binding, init) +NODE_MODULE(NODE_GYP_MODULE_NAME, init) diff --git a/test/addons/make-callback-recurse/binding.cc b/test/addons/make-callback-recurse/binding.cc index 4ca5426f9ec28f..c42166c7455277 100644 --- a/test/addons/make-callback-recurse/binding.cc +++ b/test/addons/make-callback-recurse/binding.cc @@ -28,4 +28,4 @@ void Initialize(Local exports) { } // namespace -NODE_MODULE(binding, Initialize) +NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize) diff --git a/test/addons/make-callback/binding.cc b/test/addons/make-callback/binding.cc index 3b56c385ecdd00..a5dfe46d773e82 100644 --- a/test/addons/make-callback/binding.cc +++ b/test/addons/make-callback/binding.cc @@ -36,4 +36,4 @@ void Initialize(v8::Local exports) { } // namespace -NODE_MODULE(binding, Initialize) +NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize) diff --git a/test/addons/new-target/binding.cc b/test/addons/new-target/binding.cc index a7e18eff70ea0d..5491c18c6e99af 100644 --- a/test/addons/new-target/binding.cc +++ b/test/addons/new-target/binding.cc @@ -11,6 +11,6 @@ inline void Initialize(v8::Local binding) { v8::FunctionTemplate::New(isolate, NewClass)->GetFunction()); } -NODE_MODULE(binding, Initialize) +NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize) } // anonymous namespace diff --git a/test/addons/null-buffer-neuter/binding.cc b/test/addons/null-buffer-neuter/binding.cc index 0d3d850304d534..4c9dbf902589d9 100644 --- a/test/addons/null-buffer-neuter/binding.cc +++ b/test/addons/null-buffer-neuter/binding.cc @@ -38,4 +38,4 @@ void init(v8::Local exports) { NODE_SET_METHOD(exports, "run", Run); } -NODE_MODULE(binding, init) +NODE_MODULE(NODE_GYP_MODULE_NAME, init) diff --git a/test/addons/parse-encoding/binding.cc b/test/addons/parse-encoding/binding.cc index 65dba5b6734dea..1501544153ad62 100644 --- a/test/addons/parse-encoding/binding.cc +++ b/test/addons/parse-encoding/binding.cc @@ -35,4 +35,4 @@ void Initialize(v8::Local exports) { } // anonymous namespace -NODE_MODULE(binding, Initialize) +NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize) diff --git a/test/addons/repl-domain-abort/binding.cc b/test/addons/repl-domain-abort/binding.cc index b2f2c74348c655..d6a825dfdd5dd8 100644 --- a/test/addons/repl-domain-abort/binding.cc +++ b/test/addons/repl-domain-abort/binding.cc @@ -42,4 +42,4 @@ void init(Local exports) { NODE_SET_METHOD(exports, "method", Method); } -NODE_MODULE(binding, init) +NODE_MODULE(NODE_GYP_MODULE_NAME, init) diff --git a/test/addons/stringbytes-external-exceed-max/binding.cc b/test/addons/stringbytes-external-exceed-max/binding.cc index 2141d2c5b948b4..628a6b691376b3 100644 --- a/test/addons/stringbytes-external-exceed-max/binding.cc +++ b/test/addons/stringbytes-external-exceed-max/binding.cc @@ -21,4 +21,4 @@ void init(v8::Local exports) { NODE_SET_METHOD(exports, "ensureAllocation", EnsureAllocation); } -NODE_MODULE(binding, init) +NODE_MODULE(NODE_GYP_MODULE_NAME, init) diff --git a/test/addons/symlinked-module/binding.cc b/test/addons/symlinked-module/binding.cc index 83823ae8363cd9..944f5631956d15 100644 --- a/test/addons/symlinked-module/binding.cc +++ b/test/addons/symlinked-module/binding.cc @@ -10,4 +10,4 @@ void init(v8::Local exports) { NODE_SET_METHOD(exports, "hello", Method); } -NODE_MODULE(binding, init) +NODE_MODULE(NODE_GYP_MODULE_NAME, init) From 41bf40e209d0a8575e52205d95b2fe45164ddb0c Mon Sep 17 00:00:00 2001 From: Shigeki Ohtsu Date: Tue, 20 Jun 2017 23:44:53 +0900 Subject: [PATCH 221/231] crypto: warn if counter mode used in createCipher MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `crypto.createCipher()` sets the fixed IV derived from password and it leads to a security risk of nonce reuse when counter mode is used. A warning is emitted when CTR, GCM or CCM is used in `crypto.createCipher()` to notify users to avoid nonce reuse. Fixes: https://github.com/nodejs/node/issues/13801 PR-URL: https://github.com/nodejs/node/pull/13821 Reviewed-By: Ben Noordhuis Reviewed-By: Fedor Indutny Reviewed-By: James M Snell Reviewed-By: Tobias Nießen --- doc/api/crypto.md | 7 ++++++- src/node_crypto.cc | 8 ++++++++ test/parallel/test-crypto-cipher-decipher.js | 3 +++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/doc/api/crypto.md b/doc/api/crypto.md index 52c7ebe71cf64a..08d14e96975812 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -1201,7 +1201,11 @@ rapidly. In line with OpenSSL's recommendation to use pbkdf2 instead of [`EVP_BytesToKey`][] it is recommended that developers derive a key and IV on their own using [`crypto.pbkdf2()`][] and to use [`crypto.createCipheriv()`][] -to create the `Cipher` object. +to create the `Cipher` object. Users should not use ciphers with counter mode +(e.g. CTR, GCM or CCM) in `crypto.createCipher()`. A warning is emitted when +they are used in order to avoid the risk of IV reuse that causes +vulnerabilities. For the case when IV is reused in GCM, see [Nonce-Disrespecting +Adversaries][] for details. ### crypto.createCipheriv(algorithm, key, iv[, options]) - `algorithm` {string} @@ -2271,6 +2275,7 @@ the `crypto`, `tls`, and `https` modules and are generally specific to OpenSSL. [HTML5's `keygen` element]: http://www.w3.org/TR/html5/forms.html#the-keygen-element [NIST SP 800-131A]: http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar1.pdf [NIST SP 800-132]: http://csrc.nist.gov/publications/nistpubs/800-132/nist-sp800-132.pdf +[Nonce-Disrespecting Adversaries]: https://github.com/nonce-disrespect/nonce-disrespect [OpenSSL's SPKAC implementation]: https://www.openssl.org/docs/man1.0.2/apps/spkac.html [RFC 2412]: https://www.rfc-editor.org/rfc/rfc2412.txt [RFC 3526]: https://www.rfc-editor.org/rfc/rfc3526.txt diff --git a/src/node_crypto.cc b/src/node_crypto.cc index e935490971c5b2..1fa522d521acec 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -3341,6 +3341,14 @@ void CipherBase::Init(const char* cipher_type, EVP_CIPHER_CTX_init(&ctx_); const bool encrypt = (kind_ == kCipher); EVP_CipherInit_ex(&ctx_, cipher, nullptr, nullptr, nullptr, encrypt); + + int mode = EVP_CIPHER_CTX_mode(&ctx_); + if (encrypt && (mode == EVP_CIPH_CTR_MODE || mode == EVP_CIPH_GCM_MODE || + mode == EVP_CIPH_CCM_MODE)) { + ProcessEmitWarning(env(), "Use Cipheriv for counter mode of %s", + cipher_type); + } + if (!EVP_CIPHER_CTX_set_key_length(&ctx_, key_len)) { EVP_CIPHER_CTX_cleanup(&ctx_); return env()->ThrowError("Invalid key length"); diff --git a/test/parallel/test-crypto-cipher-decipher.js b/test/parallel/test-crypto-cipher-decipher.js index 0104341653f7f5..336ab3a07ba42e 100644 --- a/test/parallel/test-crypto-cipher-decipher.js +++ b/test/parallel/test-crypto-cipher-decipher.js @@ -155,6 +155,9 @@ testCipher2(Buffer.from('0123456789abcdef')); const aadbuf = Buffer.from('aadbuf'); const data = Buffer.from('test-crypto-cipher-decipher'); + common.expectWarning('Warning', + 'Use Cipheriv for counter mode of aes-256-gcm'); + const cipher = crypto.createCipher('aes-256-gcm', key); cipher.setAAD(aadbuf); cipher.setAutoPadding(); From 78a71aa123008f95485bee6bf719ea029881b736 Mon Sep 17 00:00:00 2001 From: Shigeki Ohtsu Date: Fri, 25 Aug 2017 01:42:55 +0900 Subject: [PATCH 222/231] crypto: fix error of createCipher in wrap mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EVP_CIPHER_CTX_FLAG_WRAP_ALLOW flag needs to be set in using wrap mode ciphers. In `crypto.createCipher()`, AES key wrap mode does not use a default IV defined in RFC3394 but a generated IV with `EVP_BytesToKey()` to be consistent API behaviors with other ciphers. The built-in AES wrap mode in OpenSSL is not supported in FIPS mode as http://openssl.6102.n7.nabble.com/AES-Key-Wrap-in-FIPS-Mode-td50238.html so its tests in FIPS mode are skipped. Fixes: https://github.com/nodejs/node/issues/15009 PR-URL: https://github.com/nodejs/node/pull/15037 Reviewed-By: Fedor Indutny Reviewed-By: Ben Noordhuis Reviewed-By: Tobias Nießen Reviewed-By: James M Snell --- src/node_crypto.cc | 10 +++++++- test/parallel/test-crypto-binary-default.js | 21 ++++++++++++++++ .../test-crypto-cipheriv-decipheriv.js | 24 +++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 1fa522d521acec..e6acb565d69ab1 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -3349,6 +3349,9 @@ void CipherBase::Init(const char* cipher_type, cipher_type); } + if (mode == EVP_CIPH_WRAP_MODE) + EVP_CIPHER_CTX_set_flags(&ctx_, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); + if (!EVP_CIPHER_CTX_set_key_length(&ctx_, key_len)) { EVP_CIPHER_CTX_cleanup(&ctx_); return env()->ThrowError("Invalid key length"); @@ -3396,13 +3399,18 @@ void CipherBase::InitIv(const char* cipher_type, } const int expected_iv_len = EVP_CIPHER_iv_length(cipher); - const bool is_gcm_mode = (EVP_CIPH_GCM_MODE == EVP_CIPHER_mode(cipher)); + const int mode = EVP_CIPHER_mode(cipher); + const bool is_gcm_mode = (EVP_CIPH_GCM_MODE == mode); if (is_gcm_mode == false && iv_len != expected_iv_len) { return env()->ThrowError("Invalid IV length"); } EVP_CIPHER_CTX_init(&ctx_); + + if (mode == EVP_CIPH_WRAP_MODE) + EVP_CIPHER_CTX_set_flags(&ctx_, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); + const bool encrypt = (kind_ == kCipher); EVP_CipherInit_ex(&ctx_, cipher, nullptr, nullptr, nullptr, encrypt); diff --git a/test/parallel/test-crypto-binary-default.js b/test/parallel/test-crypto-binary-default.js index 21a3233ad3bc43..68dab386c42cfc 100644 --- a/test/parallel/test-crypto-binary-default.js +++ b/test/parallel/test-crypto-binary-default.js @@ -530,12 +530,33 @@ function testCipher4(key, iv) { 'encryption and decryption with key and iv'); } + +function testCipher5(key, iv) { + // Test encryption and decryption with explicit key with aes128-wrap + const plaintext = + '32|RmVZZkFUVmpRRkp0TmJaUm56ZU9qcnJkaXNNWVNpTTU*|iXmckfRWZBGWWELw' + + 'eCBsThSsfUHLeRe0KCsK8ooHgxie0zOINpXxfZi/oNG7uq9JWFVCk70gfzQH8ZUJ' + + 'jAfaFg**'; + const cipher = crypto.createCipher('id-aes128-wrap', key); + let ciph = cipher.update(plaintext, 'utf8', 'buffer'); + ciph = Buffer.concat([ciph, cipher.final('buffer')]); + + const decipher = crypto.createDecipher('id-aes128-wrap', key); + let txt = decipher.update(ciph, 'buffer', 'utf8'); + txt += decipher.final('utf8'); + + assert.strictEqual(txt, plaintext, + 'encryption and decryption with key'); +} + if (!common.hasFipsCrypto) { testCipher1('MySecretKey123'); testCipher1(Buffer.from('MySecretKey123')); testCipher2('0123456789abcdef'); testCipher2(Buffer.from('0123456789abcdef')); + + testCipher5(Buffer.from('0123456789abcd0123456789')); } testCipher3('0123456789abcd0123456789', '12345678'); diff --git a/test/parallel/test-crypto-cipheriv-decipheriv.js b/test/parallel/test-crypto-cipheriv-decipheriv.js index 1ccfe8b3b8a267..8a5a05b82fa436 100644 --- a/test/parallel/test-crypto-cipheriv-decipheriv.js +++ b/test/parallel/test-crypto-cipheriv-decipheriv.js @@ -55,12 +55,36 @@ function testCipher2(key, iv) { assert.strictEqual(txt, plaintext, 'encryption/decryption with key and iv'); } + +function testCipher3(key, iv) { + // Test encryption and decryption with explicit key and iv. + // AES Key Wrap test vector comes from RFC3394 + const plaintext = Buffer.from('00112233445566778899AABBCCDDEEFF', 'hex'); + + const cipher = crypto.createCipheriv('id-aes128-wrap', key, iv); + let ciph = cipher.update(plaintext, 'utf8', 'buffer'); + ciph = Buffer.concat([ciph, cipher.final('buffer')]); + const ciph2 = Buffer.from('1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5', + 'hex'); + assert(ciph.equals(ciph2)); + const decipher = crypto.createDecipheriv('id-aes128-wrap', key, iv); + let deciph = decipher.update(ciph, 'buffer'); + deciph = Buffer.concat([deciph, decipher.final()]); + + assert(deciph.equals(plaintext), 'encryption/decryption with key and iv'); +} + testCipher1('0123456789abcd0123456789', '12345678'); testCipher1('0123456789abcd0123456789', Buffer.from('12345678')); testCipher1(Buffer.from('0123456789abcd0123456789'), '12345678'); testCipher1(Buffer.from('0123456789abcd0123456789'), Buffer.from('12345678')); testCipher2(Buffer.from('0123456789abcd0123456789'), Buffer.from('12345678')); +if (!common.hasFipsCrypto) { + testCipher3(Buffer.from('000102030405060708090A0B0C0D0E0F', 'hex'), + Buffer.from('A6A6A6A6A6A6A6A6', 'hex')); +} + // Zero-sized IV should be accepted in ECB mode. crypto.createCipheriv('aes-128-ecb', Buffer.alloc(16), Buffer.alloc(0)); From f81812b1ff2da76edbf2daffdfb9039f709506e4 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 27 Aug 2017 06:31:59 +0200 Subject: [PATCH 223/231] src: turn key length exception into CHECK This exception can logically never happen because of the key stretching that takes place first. Failure must therefore be a bug in Node.js and not in the executing script. PR-URL: https://github.com/nodejs/node/pull/15183 Reviewed-By: Colin Ihrig Reviewed-By: James M Snell --- src/node_crypto.cc | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index e6acb565d69ab1..d1418e8e689791 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -3352,10 +3352,7 @@ void CipherBase::Init(const char* cipher_type, if (mode == EVP_CIPH_WRAP_MODE) EVP_CIPHER_CTX_set_flags(&ctx_, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); - if (!EVP_CIPHER_CTX_set_key_length(&ctx_, key_len)) { - EVP_CIPHER_CTX_cleanup(&ctx_); - return env()->ThrowError("Invalid key length"); - } + CHECK_EQ(1, EVP_CIPHER_CTX_set_key_length(&ctx_, key_len)); EVP_CipherInit_ex(&ctx_, nullptr, From 67fde146e05c4968eabeba7e8f08ce0833eb7838 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Thu, 31 Aug 2017 01:13:04 +0800 Subject: [PATCH 224/231] net: check EADDRINUSE after binding localPort PR-URL: https://github.com/nodejs/node/pull/15097 Fixes: https://github.com/nodejs/node/issues/15084 Reviewed-By: Benjamin Gruenbaum Reviewed-By: James M Snell Reviewed-By: Ruben Bridgewater Reviewed-By: Refael Ackermann Reviewed-By: Luigi Pinca Reviewed-By: Colin Ihrig --- lib/net.js | 37 +++++++++++++-------- test/parallel/test-net-client-bind-twice.js | 26 +++++++++++++++ 2 files changed, 49 insertions(+), 14 deletions(-) create mode 100644 test/parallel/test-net-client-bind-twice.js diff --git a/lib/net.js b/lib/net.js index 946cc80a99e0e8..80fd1f82e5375c 100644 --- a/lib/net.js +++ b/lib/net.js @@ -871,6 +871,27 @@ function afterWrite(status, handle, req, err) { } +function checkBindError(err, port, handle) { + // EADDRINUSE may not be reported until we call listen() or connect(). + // To complicate matters, a failed bind() followed by listen() or connect() + // will implicitly bind to a random port. Ergo, check that the socket is + // bound to the expected port before calling listen() or connect(). + // + // FIXME(bnoordhuis) Doesn't work for pipe handles, they don't have a + // getsockname() method. Non-issue for now, the cluster module doesn't + // really support pipes anyway. + if (err === 0 && port > 0 && handle.getsockname) { + var out = {}; + err = handle.getsockname(out); + if (err === 0 && port !== out.port) { + debug(`checkBindError, bound to ${out.port} instead of ${port}`); + err = uv.UV_EADDRINUSE; + } + } + return err; +} + + function internalConnect( self, address, port, addressType, localAddress, localPort) { // TODO return promise from Socket.prototype.connect which @@ -894,6 +915,7 @@ function internalConnect( debug('binding to localAddress: %s and localPort: %d (addressType: %d)', localAddress, localPort, addressType); + err = checkBindError(err, localPort, self._handle); if (err) { const ex = exceptionWithHostPort(err, 'bind', localAddress, localPort); self.destroy(ex); @@ -1382,20 +1404,7 @@ function listenInCluster(server, address, port, addressType, cluster._getServer(server, serverQuery, listenOnMasterHandle); function listenOnMasterHandle(err, handle) { - // EADDRINUSE may not be reported until we call listen(). To complicate - // matters, a failed bind() followed by listen() will implicitly bind to - // a random port. Ergo, check that the socket is bound to the expected - // port before calling listen(). - // - // FIXME(bnoordhuis) Doesn't work for pipe handles, they don't have a - // getsockname() method. Non-issue for now, the cluster module doesn't - // really support pipes anyway. - if (err === 0 && port > 0 && handle.getsockname) { - var out = {}; - err = handle.getsockname(out); - if (err === 0 && port !== out.port) - err = uv.UV_EADDRINUSE; - } + err = checkBindError(err, port, handle); if (err) { var ex = exceptionWithHostPort(err, 'bind', address, port); diff --git a/test/parallel/test-net-client-bind-twice.js b/test/parallel/test-net-client-bind-twice.js new file mode 100644 index 00000000000000..ca7eb502d85ba5 --- /dev/null +++ b/test/parallel/test-net-client-bind-twice.js @@ -0,0 +1,26 @@ +'use strict'; + +// This tests that net.connect() from a used local port throws EADDRINUSE. + +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server1 = net.createServer(common.mustNotCall()); +server1.listen(0, common.localhostIPv4, common.mustCall(() => { + const server2 = net.createServer(common.mustNotCall()); + server2.listen(0, common.localhostIPv4, common.mustCall(() => { + const client = net.connect({ + host: common.localhostIPv4, + port: server1.address().port, + localAddress: common.localhostIPv4, + localPort: server2.address().port + }, common.mustNotCall()); + + client.on('error', common.mustCall((err) => { + assert.strictEqual(err.code, 'EADDRINUSE'); + server1.close(); + server2.close(); + })); + })); +})); From a46e59d52d487cec0b537183870bd1af7e3379e9 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Thu, 17 Aug 2017 22:41:14 -0700 Subject: [PATCH 225/231] console: implement minimal `console.group()` Node.js exposes `console.group()` and `console.groupEnd()` via the inspector. These functions have no apparent effect when called from Node.js without the inspector. We cannot easily hide them when Node.js is started without the inspector because we support opening the inspector during runtime via `inspector.port()`. Implement a minimal `console.group()`/`console.groupEnd()`. More sophisticated implementations are possible, but they can be done in userland and/or features can be added to this at a later time. `console.groupCollapsed()` is implemented as an alias for `console.group()`. PR-URL: https://github.com/nodejs/node/pull/14910 Fixes: https://github.com/nodejs/node/issues/1716 Ref: https://github.com/nodejs/node/issues/12675 Reviewed-By: James M Snell Reviewed-By: Matteo Collina Reviewed-By: Timothy Gu Reviewed-By: Anna Henningsen --- doc/api/console.md | 27 +++++++ lib/console.js | 24 +++++- test/parallel/test-console-group.js | 111 ++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 test/parallel/test-console-group.js diff --git a/doc/api/console.md b/doc/api/console.md index 2d8c44581a813f..7457abb29b49d5 100644 --- a/doc/api/console.md +++ b/doc/api/console.md @@ -288,6 +288,32 @@ If formatting elements (e.g. `%d`) are not found in the first string then [`util.inspect()`][] is called on each argument and the resulting string values are concatenated. See [`util.format()`][] for more information. +### console.group([...label]) + + +* `label` {any} + +Increases indentation of subsequent lines by two spaces. + +If one or more `label`s are provided, those are printed first without the +additional indentation. + +### console.groupCollapsed() + + +An alias for [`console.group()`][]. + +### console.groupEnd() + + +Decreases indentation of subsequent lines by two spaces. + ### console.info([data][, ...args]) + +* `src` {string|Buffer|URL} source filename to copy +* `dest` {string|Buffer|URL} destination filename of the copy operation +* `flags` {number} modifiers for copy operation. **Default:** `0` +* `callback` {Function} + +Asynchronously copies `src` to `dest`. By default, `dest` is overwritten if it +already exists. No arguments other than a possible exception are given to the +callback function. Node.js makes no guarantees about the atomicity of the copy +operation. If an error occurs after the destination file has been opened for +writing, Node.js will attempt to remove the destination. + +`flags` is an optional integer that specifies the behavior +of the copy operation. The only supported flag is `fs.constants.COPYFILE_EXCL`, +which causes the copy operation to fail if `dest` already exists. + +Example: + +```js +const fs = require('fs'); + +// destination.txt will be created or overwritten by default. +fs.copyFile('source.txt', 'destination.txt', (err) => { + if (err) throw err; + console.log('source.txt was copied to destination.txt'); +}); +``` + +If the third argument is a number, then it specifies `flags`, as shown in the +following example. + +```js +const fs = require('fs'); +const { COPYFILE_EXCL } = fs.constants; + +// By using COPYFILE_EXCL, the operation will fail if destination.txt exists. +fs.copyFile('source.txt', 'destination.txt', COPYFILE_EXCL, callback); +``` + +## fs.copyFileSync(src, dest[, flags]) + + +* `src` {string|Buffer|URL} source filename to copy +* `dest` {string|Buffer|URL} destination filename of the copy operation +* `flags` {number} modifiers for copy operation. **Default:** `0` + +Synchronously copies `src` to `dest`. By default, `dest` is overwritten if it +already exists. Returns `undefined`. Node.js makes no guarantees about the +atomicity of the copy operation. If an error occurs after the destination file +has been opened for writing, Node.js will attempt to remove the destination. + +`flags` is an optional integer that specifies the behavior +of the copy operation. The only supported flag is `fs.constants.COPYFILE_EXCL`, +which causes the copy operation to fail if `dest` already exists. + +Example: + +```js +const fs = require('fs'); + +// destination.txt will be created or overwritten by default. +fs.copyFileSync('source.txt', 'destination.txt'); +console.log('source.txt was copied to destination.txt'); +``` + +If the third argument is a number, then it specifies `flags`, as shown in the +following example. + +```js +const fs = require('fs'); +const { COPYFILE_EXCL } = fs.constants; + +// By using COPYFILE_EXCL, the operation will fail if destination.txt exists. +fs.copyFileSync('source.txt', 'destination.txt', COPYFILE_EXCL); +``` + ## fs.createReadStream(path[, options]) * `label` {any} @@ -302,14 +302,14 @@ additional indentation. ### console.groupCollapsed() An alias for [`console.group()`][]. ### console.groupEnd() Decreases indentation of subsequent lines by two spaces. diff --git a/doc/api/fs.md b/doc/api/fs.md index ce05469881bf42..0d9755d60900d3 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -752,7 +752,7 @@ operations. The specific constants currently defined are described in ## fs.copyFile(src, dest[, flags], callback) * `src` {string|Buffer|URL} source filename to copy @@ -795,7 +795,7 @@ fs.copyFile('source.txt', 'destination.txt', COPYFILE_EXCL, callback); ## fs.copyFileSync(src, dest[, flags]) * `src` {string|Buffer|URL} source filename to copy diff --git a/doc/api/http2.md b/doc/api/http2.md index 5f964d1643c1c3..43813feffdfb80 100644 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -851,7 +851,7 @@ the client. #### Event: 'continue' Emitted when the server sends a `100 Continue` status, usually because @@ -1257,7 +1257,7 @@ an `Http2Session` object. If no listener is registered for this event, an #### Event: 'streamError' * `socket` {http2.ServerHttp2Stream} @@ -1317,7 +1317,7 @@ a given number of milliseconds set using `http2server.setTimeout()`. #### Event: 'checkContinue' * `request` {http2.Http2ServerRequest} @@ -1422,7 +1422,7 @@ added: v8.4.0 #### Event: 'checkContinue' * `request` {http2.Http2ServerRequest} diff --git a/doc/api/n-api.md b/doc/api/n-api.md index daac4c592fc2a7..e3d7dcce32256b 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -3185,7 +3185,7 @@ then by calling `napi_unwrap()` on the wrapper object. ### *napi_remove_wrap* ```C napi_status napi_remove_wrap(napi_env env, @@ -3399,7 +3399,7 @@ support it: ### napi_adjust_external_memory ```C NAPI_EXTERN napi_status napi_adjust_external_memory(napi_env env, @@ -3478,7 +3478,7 @@ deferred = NULL; ### napi_create_promise ```C NAPI_EXTERN napi_status napi_create_promise(napi_env env, @@ -3498,7 +3498,7 @@ This API creates a deferred object and a JavaScript promise. ### napi_resolve_deferred ```C NAPI_EXTERN napi_status napi_resolve_deferred(napi_env env, @@ -3521,7 +3521,7 @@ The deferred object is freed upon successful completion. ### napi_reject_deferred ```C NAPI_EXTERN napi_status napi_reject_deferred(napi_env env, @@ -3544,7 +3544,7 @@ The deferred object is freed upon successful completion. ### napi_is_promise ```C NAPI_EXTERN napi_status napi_is_promise(napi_env env, @@ -3564,7 +3564,7 @@ underlying JavaScript engine. ### napi_run_script ```C NAPI_EXTERN napi_status napi_run_script(napi_env env, diff --git a/doc/api/perf_hooks.md b/doc/api/perf_hooks.md index f41caf6f53112f..bccc99c18e8447 100644 --- a/doc/api/perf_hooks.md +++ b/doc/api/perf_hooks.md @@ -1,6 +1,6 @@ # Performance Timing API > Stability: 1 - Experimental @@ -24,7 +24,7 @@ doSomeLongRunningProcess(() => { ## Class: Performance The `Performance` provides access to performance metric data. A single @@ -32,7 +32,7 @@ instance of this class is provided via the `performance` property. ### performance.clearFunctions([name]) * `name` {string} @@ -42,7 +42,7 @@ Performance Timeline. If `name` is provided, removes entries with `name`. ### performance.clearMarks([name]) * `name` {string} @@ -52,7 +52,7 @@ Performance Timeline. If `name` is provided, removes only the named mark. ### performance.clearMeasures([name]) * `name` {string} @@ -63,7 +63,7 @@ Performance Timeline. If `name` is provided, removes only objects whose ### performance.getEntries() * Returns: {Array} @@ -73,7 +73,7 @@ with respect to `performanceEntry.startTime`. ### performance.getEntriesByName(name[, type]) * `name` {string} @@ -87,7 +87,7 @@ equal to `name`, and optionally, whose `performanceEntry.entryType` is equal to ### performance.getEntriesByType(type) * `type` {string} @@ -99,7 +99,7 @@ is equal to `type`. ### performance.mark([name]) * `name` {string} @@ -112,7 +112,7 @@ to mark specific significant moments in the Performance Timeline. ### performance.measure(name, startMark, endMark) * `name` {string} @@ -137,7 +137,7 @@ error will be thrown. ### performance.nodeFrame * {PerformanceFrame} @@ -147,7 +147,7 @@ for the event loop. ### performance.nodeTiming * {PerformanceNodeTiming} @@ -157,7 +157,7 @@ metrics for specific Node.js operational milestones. ### performance.now() * Returns: {number} @@ -166,7 +166,7 @@ Returns the current high resolution millisecond timestamp. ### performance.timeOrigin * {number} @@ -176,7 +176,7 @@ which all performance metric durations are measured. ### performance.timerify(fn) * `fn` {Function} @@ -210,12 +210,12 @@ wrapped(); ## Class: PerformanceEntry ### performanceEntry.duration * {number} @@ -225,7 +225,7 @@ be meaningful for all Performance Entry types. ### performanceEntry.name * {string} @@ -234,7 +234,7 @@ The name of the performance entry. ### performanceEntry.startTime * {number} @@ -244,7 +244,7 @@ Performance Entry. ### performanceEntry.entryType * {string} @@ -254,7 +254,7 @@ The type of the performance entry. Current it may be one of: `'node'`, `'mark'`, ### performanceEntry.kind * {number} @@ -270,7 +270,7 @@ The value may be one of: ## Class: PerformanceNodeFrame extends PerformanceEntry Provides timing details for the Node.js event loop. @@ -301,14 +301,14 @@ current loop. ## Class: PerformanceNodeTiming extends PerformanceEntry Provides timing details for Node.js itself. ### performanceNodeTiming.bootstrapComplete * {number} @@ -318,7 +318,7 @@ completed bootstrap. ### performanceNodeTiming.clusterSetupEnd * {number} @@ -327,7 +327,7 @@ The high resolution millisecond timestamp at which cluster processing ended. ### performanceNodeTiming.clusterSetupStart * {number} @@ -336,7 +336,7 @@ The high resolution millisecond timestamp at which cluster processing started. ### performanceNodeTiming.loopExit * {number} @@ -346,7 +346,7 @@ exited. ### performanceNodeTiming.loopStart * {number} @@ -356,7 +356,7 @@ started. ### performanceNodeTiming.moduleLoadEnd * {number} @@ -365,7 +365,7 @@ The high resolution millisecond timestamp at which main module load ended. ### performanceNodeTiming.moduleLoadStart * {number} @@ -374,7 +374,7 @@ The high resolution millisecond timestamp at which main module load started. ### performanceNodeTiming.nodeStart * {number} @@ -384,7 +384,7 @@ initialized. ### performanceNodeTiming.preloadModuleLoadEnd * {number} @@ -393,7 +393,7 @@ The high resolution millisecond timestamp at which preload module load ended. ### performanceNodeTiming.preloadModuleLoadStart * {number} @@ -402,7 +402,7 @@ The high resolution millisecond timestamp at which preload module load started. ### performanceNodeTiming.thirdPartyMainEnd * {number} @@ -412,7 +412,7 @@ ended. ### performanceNodeTiming.thirdPartyMainStart * {number} @@ -422,7 +422,7 @@ started. ### performanceNodeTiming.v8Start * {number} @@ -433,7 +433,7 @@ initialized. ## Class: PerformanceObserver(callback) * `callback` {Function} A `PerformanceObserverCallback` callback function. @@ -463,7 +463,7 @@ longer needed. ### Callback: PerformanceObserverCallback(list, observer) * `list` {PerformanceObserverEntryList} @@ -476,7 +476,7 @@ notified about new `PerformanceEntry` instances. The callback receives a ### Class: PerformanceObserverEntryList The `PerformanceObserverEntryList` class is used to provide access to the @@ -484,7 +484,7 @@ The `PerformanceObserverEntryList` class is used to provide access to the #### performanceObserverEntryList.getEntries() * Returns: {Array} @@ -494,7 +494,7 @@ with respect to `performanceEntry.startTime`. #### performanceObserverEntryList.getEntriesByName(name[, type]) * `name` {string} @@ -508,7 +508,7 @@ equal to `name`, and optionally, whose `performanceEntry.entryType` is equal to #### performanceObserverEntryList.getEntriesByType(type) * `type` {string} @@ -520,13 +520,13 @@ is equal to `type`. ### performanceObserver.disconnect() Disconnects the `PerformanceObserver` instance from all notifications. ### performanceObserver.observe(options) * `options` {Object} * `entryTypes` {Array} An array of strings identifying the types of diff --git a/doc/changelogs/CHANGELOG_V8.md b/doc/changelogs/CHANGELOG_V8.md index 2413d874778933..02cf749e3cbf76 100644 --- a/doc/changelogs/CHANGELOG_V8.md +++ b/doc/changelogs/CHANGELOG_V8.md @@ -6,6 +6,7 @@ +8.5.0
    8.4.0
    8.3.0
    8.2.1
    @@ -30,6 +31,289 @@ * [io.js](CHANGELOG_IOJS.md) * [Archive](CHANGELOG_ARCHIVE.md) + +## 2017-09-12, Version 8.5.0 (Current), @MylesBorins + +### Notable Changes + +* **build** + * Snapshots are now re-enabled in V8 + [#14875](https://github.com/nodejs/node/pull/14875) +* **console** + * Implement minimal `console.group()`. + [#14910](https://github.com/nodejs/node/pull/14910) +* **deps** + * upgrade libuv to 1.14.1 + [#14866](https://github.com/nodejs/node/pull/14866) + * update nghttp2 to v1.25.0 + [#14955](https://github.com/nodejs/node/pull/14955) +* **dns** + * Add `verbatim` option to dns.lookup(). When true, results from the DNS + resolver are passed on as-is, without the reshuffling that Node.js + otherwise does that puts IPv4 addresses before IPv6 addresses. + [#14731](https://github.com/nodejs/node/pull/14731) +* **fs** + * add fs.copyFile and fs.copyFileSync which allows for more efficient + copying of files. + [#15034](https://github.com/nodejs/node/pull/15034) +* **inspector** + * Enable async stack traces [#13870](https://github.com/nodejs/node/pull/13870) +* **module** + * Add support for ESM. This is currently behind the `--experimental-modules` flag + and requires the .mjs extension. + `node --experimental-modules index.mjs` + [#14369](https://github.com/nodejs/node/pull/14369) +* **napi** + * implement promise + [#14365](https://github.com/nodejs/node/pull/14365) +* **os** + * Add support for CIDR notation to the output of the networkInterfaces() method. + [#14307](https://github.com/nodejs/node/pull/14307) +* **perf_hooks** + * An initial implementation of the Performance Timing API for Node.js. This is the + same Performance Timing API implemented by modern browsers with a number of Node.js + specific properties. The User Timing mark() and measure() APIs are implemented, + as is a Node.js specific flavor of the Frame Timing for measuring event loop duration. + [#14680](https://github.com/nodejs/node/pull/14680) +* **tls** + * multiple PFX in createSecureContext + [#14793](https://github.com/nodejs/node/pull/14793) +* **Added new collaborators** + * [BridgeAR](https://github.com/BridgeAR) – Ruben Bridgewater + +### Commits + +* [[`87c3e1d7de`](https://github.com/nodejs/node/commit/87c3e1d7de)] - fix --prof-process --preprocess flag (davidmarkclements) [#14966](https://github.com/nodejs/node/pull/14966) +* [[`bcf0e5d676`](https://github.com/nodejs/node/commit/bcf0e5d676)] - **assert**: handle errors properly with deep*Equal (Ruben Bridgewater) [#15001](https://github.com/nodejs/node/pull/15001) +* [[`7174dc2e8a`](https://github.com/nodejs/node/commit/7174dc2e8a)] - **assert**: handle sparse arrays in deepStrictEqual (Ruben Bridgewater) [#15027](https://github.com/nodejs/node/pull/15027) +* [[`b40105df3b`](https://github.com/nodejs/node/commit/b40105df3b)] - **async_hooks**: don't abort unnecessarily (Trevor Norris) [#14722](https://github.com/nodejs/node/pull/14722) +* [[`3e73ea8745`](https://github.com/nodejs/node/commit/3e73ea8745)] - **async_hooks**: improve comments and function names (Trevor Norris) [#14722](https://github.com/nodejs/node/pull/14722) +* [[`700d576962`](https://github.com/nodejs/node/commit/700d576962)] - **async_hooks**: emitAfter correctly on fatalException (Trevor Norris) [#14914](https://github.com/nodejs/node/pull/14914) +* [[`78a36e0dd1`](https://github.com/nodejs/node/commit/78a36e0dd1)] - **async_wrap**: unroll unnecessarily DRY code (Trevor Norris) [#14722](https://github.com/nodejs/node/pull/14722) +* [[`fadccbaa17`](https://github.com/nodejs/node/commit/fadccbaa17)] - **async_wrap**: return undefined if domain is disposed (Trevor Norris) [#14722](https://github.com/nodejs/node/pull/14722) +* [[`8d11220e0b`](https://github.com/nodejs/node/commit/8d11220e0b)] - **benchmark**: add default configs to buffer benchmark (Rich Trott) [#15175](https://github.com/nodejs/node/pull/15175) +* [[`7feb99455a`](https://github.com/nodejs/node/commit/7feb99455a)] - **benchmark**: fix issues in dns benchmark (Ian Perkins) [#14936](https://github.com/nodejs/node/pull/14936) +* [[`978889f8c0`](https://github.com/nodejs/node/commit/978889f8c0)] - **benchmark**: fix dgram/bind-params.js benchmark (Rich Trott) [#14948](https://github.com/nodejs/node/pull/14948) +* [[`7f1ea7c3af`](https://github.com/nodejs/node/commit/7f1ea7c3af)] - **benchmark**: removed unused arguments from callbacks (Abhishek Raj) [#14919](https://github.com/nodejs/node/pull/14919) +* [[`ca3ec90285`](https://github.com/nodejs/node/commit/ca3ec90285)] - **benchmark**: convert var to es6 const (Sebastian Murphy) [#12886](https://github.com/nodejs/node/pull/12886) +* [[`bda5585012`](https://github.com/nodejs/node/commit/bda5585012)] - **buffer**: fix MAX_LENGTH constant export (Anna Henningsen) [#14821](https://github.com/nodejs/node/pull/14821) +* [[`b9e1f60333`](https://github.com/nodejs/node/commit/b9e1f60333)] - **buffer**: increase coverage by removing dead code (Marcelo Gobelli) [#15100](https://github.com/nodejs/node/pull/15100) +* [[`5b8fa29649`](https://github.com/nodejs/node/commit/5b8fa29649)] - **build**: display HTTP2 configure --help options (Daniel Bevenius) [#15198](https://github.com/nodejs/node/pull/15198) +* [[`6de4e10c7a`](https://github.com/nodejs/node/commit/6de4e10c7a)] - **build**: add NetBSD support to opensslconf.h (Roy Marples) [#14313](https://github.com/nodejs/node/pull/14313) +* [[`ebb3c2ce6f`](https://github.com/nodejs/node/commit/ebb3c2ce6f)] - **build**: add npx to zip and 7z packages (Richard Lau) [#15033](https://github.com/nodejs/node/pull/15033) +* [[`b946693f4b`](https://github.com/nodejs/node/commit/b946693f4b)] - **build**: fix indentation in node.gyp (Alexey Orlenko) [#15051](https://github.com/nodejs/node/pull/15051) +* [[`c8be90cabf`](https://github.com/nodejs/node/commit/c8be90cabf)] - **build**: for --enable-static, run only cctest (Daniel Bevenius) [#14892](https://github.com/nodejs/node/pull/14892) +* [[`77dfa73cf2`](https://github.com/nodejs/node/commit/77dfa73cf2)] - **build**: better support for python3 systems (Ben Noordhuis) [#14737](https://github.com/nodejs/node/pull/14737) +* [[`8f3537f66a`](https://github.com/nodejs/node/commit/8f3537f66a)] - **build**: allow proper generation of html docs (Jon Moss) [#14932](https://github.com/nodejs/node/pull/14932) +* [[`838d3fef72`](https://github.com/nodejs/node/commit/838d3fef72)] - **build**: don't add libraries when --enable-static (Daniel Bevenius) [#14912](https://github.com/nodejs/node/pull/14912) +* [[`9d373981f4`](https://github.com/nodejs/node/commit/9d373981f4)] - **build**: remove duplicated code (Ruslan Bekenev) [#13482](https://github.com/nodejs/node/pull/13482) +* [[`e12a9c567c`](https://github.com/nodejs/node/commit/e12a9c567c)] - **build**: re-enable snapshots in v8.x (Myles Borins) [#14875](https://github.com/nodejs/node/pull/14875) +* [[`3a68b0bb98`](https://github.com/nodejs/node/commit/3a68b0bb98)] - **console**: improve console.group() (Rich Trott) [#14999](https://github.com/nodejs/node/pull/14999) +* [[`a46e59d52d`](https://github.com/nodejs/node/commit/a46e59d52d)] - **(SEMVER-MINOR)** **console**: implement minimal `console.group()` (Rich Trott) [#14910](https://github.com/nodejs/node/pull/14910) +* [[`78a71aa123`](https://github.com/nodejs/node/commit/78a71aa123)] - **crypto**: fix error of createCipher in wrap mode (Shigeki Ohtsu) [#15037](https://github.com/nodejs/node/pull/15037) +* [[`41bf40e209`](https://github.com/nodejs/node/commit/41bf40e209)] - **crypto**: warn if counter mode used in createCipher (Shigeki Ohtsu) [#13821](https://github.com/nodejs/node/pull/13821) +* [[`ba5a697bdb`](https://github.com/nodejs/node/commit/ba5a697bdb)] - **deps**: cherry-pick 5005faed5 from V8 upstream (Miguel Martins) [#15177](https://github.com/nodejs/node/pull/15177) +* [[`d18bb3d1dd`](https://github.com/nodejs/node/commit/d18bb3d1dd)] - **deps**: cherry-pick 1aead19 from upstream V8 (Ben Noordhuis) [#15184](https://github.com/nodejs/node/pull/15184) +* [[`acf9650730`](https://github.com/nodejs/node/commit/acf9650730)] - **deps**: upgrade libuv to 1.14.1 (cjihrig) [#14866](https://github.com/nodejs/node/pull/14866) +* [[`296729c41e`](https://github.com/nodejs/node/commit/296729c41e)] - **deps**: cherry-pick 0ef4a0c64b6 from c-ares upstream (Anna Henningsen) [#15023](https://github.com/nodejs/node/pull/15023) +* [[`3f7bdc5ab7`](https://github.com/nodejs/node/commit/3f7bdc5ab7)] - **deps**: cherry-pick e020aae394 from V8 upstream (Ben Noordhuis) [#14913](https://github.com/nodejs/node/pull/14913) +* [[`c46e7e1988`](https://github.com/nodejs/node/commit/c46e7e1988)] - **deps**: fixup nghttp2 version number (Anna Henningsen) [#14955](https://github.com/nodejs/node/pull/14955) +* [[`4eb907f26b`](https://github.com/nodejs/node/commit/4eb907f26b)] - **deps**: update nghttp2 to v1.25.0 (Anna Henningsen) [#14955](https://github.com/nodejs/node/pull/14955) +* [[`9f46bde440`](https://github.com/nodejs/node/commit/9f46bde440)] - **deps**: backport d727680 from V8 upstream (Matt Loring) [#14947](https://github.com/nodejs/node/pull/14947) +* [[`56bb199ef0`](https://github.com/nodejs/node/commit/56bb199ef0)] - **deps**: cherry-pick eb306f463e from nghttp2 upstream (Anna Henningsen) [#14808](https://github.com/nodejs/node/pull/14808) +* [[`55eed604a9`](https://github.com/nodejs/node/commit/55eed604a9)] - **deps**: backport f9c4b7a from upstream V8 (Matt Loring) [#14001](https://github.com/nodejs/node/pull/14001) +* [[`b7f7d67677`](https://github.com/nodejs/node/commit/b7f7d67677)] - **deps**: backport bca8409 from upstream V8 (Matt Loring) [#14001](https://github.com/nodejs/node/pull/14001) +* [[`a67e7f9b35`](https://github.com/nodejs/node/commit/a67e7f9b35)] - **deps**: backport 6e9e2e5 from upstream V8 (Matt Loring) [#14001](https://github.com/nodejs/node/pull/14001) +* [[`6e2f62262d`](https://github.com/nodejs/node/commit/6e2f62262d)] - **deps**: backport 3d8e87a from upstream V8 (Matt Loring) [#14001](https://github.com/nodejs/node/pull/14001) +* [[`6cb718b87a`](https://github.com/nodejs/node/commit/6cb718b87a)] - **deps**: backport 5152d97 from upstream V8 (Matt Loring) [#14001](https://github.com/nodejs/node/pull/14001) +* [[`c6e2b8adf7`](https://github.com/nodejs/node/commit/c6e2b8adf7)] - **deps**: backport c4852ea from upstream V8 (Matt Loring) [#14001](https://github.com/nodejs/node/pull/14001) +* [[`bfb97b71b6`](https://github.com/nodejs/node/commit/bfb97b71b6)] - **deps**: cherry-pick fa4ec9f from V8 upstream (Jaideep Bajwa) [#14608](https://github.com/nodejs/node/pull/14608) +* [[`1a2f749e16`](https://github.com/nodejs/node/commit/1a2f749e16)] - **deps**: fix inspector v8 test (Eugene Ostroukhov) [#14827](https://github.com/nodejs/node/pull/14827) +* [[`13577d4ada`](https://github.com/nodejs/node/commit/13577d4ada)] - **dns**: add `verbatim` option to dns.lookup() (Ben Noordhuis) [#14731](https://github.com/nodejs/node/pull/14731) +* [[`ffed33710c`](https://github.com/nodejs/node/commit/ffed33710c)] - **doc**: add ESM doc to _toc.md and all.md (Vse Mozhet Byt) [#15248](https://github.com/nodejs/node/pull/15248) +* [[`1b51287603`](https://github.com/nodejs/node/commit/1b51287603)] - **doc**: fix Error property markdown level (Sam Roberts) [#15247](https://github.com/nodejs/node/pull/15247) +* [[`af3b173e82`](https://github.com/nodejs/node/commit/af3b173e82)] - **doc**: add missing space in test/README.md (Vse Mozhet Byt) [#15278](https://github.com/nodejs/node/pull/15278) +* [[`c90c68e8a0`](https://github.com/nodejs/node/commit/c90c68e8a0)] - **doc**: document bytes to chars after setEncoding (Jessica Quynh Tran) [#13442](https://github.com/nodejs/node/pull/13442) +* [[`ea86cb59b9`](https://github.com/nodejs/node/commit/ea86cb59b9)] - **doc**: describe what security issues are (Sam Roberts) [#14485](https://github.com/nodejs/node/pull/14485) +* [[`ddbcc9e59d`](https://github.com/nodejs/node/commit/ddbcc9e59d)] - **doc**: add options argument to crypto docs (Adina Shanholtz) [#14846](https://github.com/nodejs/node/pull/14846) +* [[`da5e6d33d5`](https://github.com/nodejs/node/commit/da5e6d33d5)] - **doc**: instructions for generating coverage reports (Simon Brewster) [#15190](https://github.com/nodejs/node/pull/15190) +* [[`286111a2b0`](https://github.com/nodejs/node/commit/286111a2b0)] - **doc**: clarify async/asynchronous in deprecations.md (Rich Trott) [#15172](https://github.com/nodejs/node/pull/15172) +* [[`9542844feb`](https://github.com/nodejs/node/commit/9542844feb)] - **doc**: `readFileSync` instead of `fs.readFileSync` (Piotr Mionskowski) [#15137](https://github.com/nodejs/node/pull/15137) +* [[`959b270fe1`](https://github.com/nodejs/node/commit/959b270fe1)] - **doc**: /s/SHASUM256/SHASUMS256 (Jon Moss) [#15101](https://github.com/nodejs/node/pull/15101) +* [[`3697cd86c4`](https://github.com/nodejs/node/commit/3697cd86c4)] - **doc**: fix comment about http2.createSecureServer (creeperyang) [#15085](https://github.com/nodejs/node/pull/15085) +* [[`76780445b3`](https://github.com/nodejs/node/commit/76780445b3)] - **doc**: remove braces which shouldn't be there (Jan Schär) [#15094](https://github.com/nodejs/node/pull/15094) +* [[`2610ae326f`](https://github.com/nodejs/node/commit/2610ae326f)] - **doc**: clarify http.get data consumption requirement (AJ Jordan) [#15049](https://github.com/nodejs/node/pull/15049) +* [[`e7838d7077`](https://github.com/nodejs/node/commit/e7838d7077)] - **doc**: add 8.4.0 link to CHANGELOG.md (Ruslan Iusupov) [#15064](https://github.com/nodejs/node/pull/15064) +* [[`feeff48d5c`](https://github.com/nodejs/node/commit/feeff48d5c)] - **doc**: add links to alternative versions of doc (Chris Young) [#10958](https://github.com/nodejs/node/pull/10958) +* [[`a5242851b9`](https://github.com/nodejs/node/commit/a5242851b9)] - **doc**: update configure to require g++ 4.9.4 (Dave Olszewski) [#14204](https://github.com/nodejs/node/pull/14204) +* [[`87ff86b2d8`](https://github.com/nodejs/node/commit/87ff86b2d8)] - **doc**: building - note on Windows SDK 15063 (Refael Ackermann) [#14394](https://github.com/nodejs/node/pull/14394) +* [[`449549bc4f`](https://github.com/nodejs/node/commit/449549bc4f)] - **doc**: threadpool size, and APIs using the pool (Sam Roberts) [#14995](https://github.com/nodejs/node/pull/14995) +* [[`6bb8133638`](https://github.com/nodejs/node/commit/6bb8133638)] - **doc**: sort bottom-of-file dns markdown links (Sam Roberts) [#14992](https://github.com/nodejs/node/pull/14992) +* [[`a06d1295c5`](https://github.com/nodejs/node/commit/a06d1295c5)] - **doc**: crypto.randomBytes does not block when async (Sam Roberts) [#14993](https://github.com/nodejs/node/pull/14993) +* [[`83ba2aa46b`](https://github.com/nodejs/node/commit/83ba2aa46b)] - **doc**: environmental-\>environment & NodeJS-\>Node.js (Rod Vagg) [#14974](https://github.com/nodejs/node/pull/14974) +* [[`f1bc168ad5`](https://github.com/nodejs/node/commit/f1bc168ad5)] - **doc**: fix typo in Buffer.from(string, \[encoding\]) (Michał Wadas) [#15013](https://github.com/nodejs/node/pull/15013) +* [[`9b9e7b4044`](https://github.com/nodejs/node/commit/9b9e7b4044)] - **doc**: add note for Windows build path (Kyle Lamse) [#14354](https://github.com/nodejs/node/pull/14354) +* [[`57c7eae1df`](https://github.com/nodejs/node/commit/57c7eae1df)] - **doc**: rephrase text of child_process.execSync() (hafiz) [#14953](https://github.com/nodejs/node/pull/14953) +* [[`188713ca46`](https://github.com/nodejs/node/commit/188713ca46)] - **doc**: beautify net.md formats (sevenryze) [#14987](https://github.com/nodejs/node/pull/14987) +* [[`a8648e287c`](https://github.com/nodejs/node/commit/a8648e287c)] - **doc**: link to correct "OS Constants" heading in docs (James Kyle) [#14969](https://github.com/nodejs/node/pull/14969) +* [[`e187c98186`](https://github.com/nodejs/node/commit/e187c98186)] - **doc**: remove misterdjules from the CTC members list (Julien Gilli) [#1498](https://github.com/nodejs/node/pull/1498) +* [[`78b2bc77f2`](https://github.com/nodejs/node/commit/78b2bc77f2)] - **doc**: update http2.md example code (RefinedSoftwareLLC) [#14979](https://github.com/nodejs/node/pull/14979) +* [[`6179c2764a`](https://github.com/nodejs/node/commit/6179c2764a)] - **doc**: fix doc for napi_get_value_string_utf8 (Daniel Taveras) [#14529](https://github.com/nodejs/node/pull/14529) +* [[`daae6bc652`](https://github.com/nodejs/node/commit/daae6bc652)] - **doc**: fixed link definitions in http2.md footer (sharababy) [#14946](https://github.com/nodejs/node/pull/14946) +* [[`6c93d01fba`](https://github.com/nodejs/node/commit/6c93d01fba)] - **doc**: remove `you` and fixup note in stream.md (James M Snell) [#14938](https://github.com/nodejs/node/pull/14938) +* [[`96d95d4fed`](https://github.com/nodejs/node/commit/96d95d4fed)] - **doc**: minor fixes to http/2 docs (Anand Suresh) [#14877](https://github.com/nodejs/node/pull/14877) +* [[`bfa3cbe158`](https://github.com/nodejs/node/commit/bfa3cbe158)] - **doc**: remove redundant only from doc/api/stream.md (George Sapkin) [#14858](https://github.com/nodejs/node/pull/14858) +* [[`c5380c83c6`](https://github.com/nodejs/node/commit/c5380c83c6)] - **doc**: add missing word (Jon Moss) [#14924](https://github.com/nodejs/node/pull/14924) +* [[`abe014834e`](https://github.com/nodejs/node/commit/abe014834e)] - **doc**: fix http api document (陈刚) [#14625](https://github.com/nodejs/node/pull/14625) +* [[`050a2249c1`](https://github.com/nodejs/node/commit/050a2249c1)] - **doc**: explain what to do if git push is rejected (Rich Trott) [#14848](https://github.com/nodejs/node/pull/14848) +* [[`3d621393bd`](https://github.com/nodejs/node/commit/3d621393bd)] - **doc**: add BridgeAR to collaborators (Ruben Bridgewater) [#14862](https://github.com/nodejs/node/pull/14862) +* [[`c8f0e5ab82`](https://github.com/nodejs/node/commit/c8f0e5ab82)] - **doc**: fix typo in cli.md (hsmtkk) [#14855](https://github.com/nodejs/node/pull/14855) +* [[`0dc9d284a4`](https://github.com/nodejs/node/commit/0dc9d284a4)] - **doc**: added napi_get_value_string_latin1 (Kyle Farnung) [#14678](https://github.com/nodejs/node/pull/14678) +* [[`72cc2caf78`](https://github.com/nodejs/node/commit/72cc2caf78)] - **doc**: fix word wrapping for api stability boxes (Saad Quadri) [#14809](https://github.com/nodejs/node/pull/14809) +* [[`205d5f674a`](https://github.com/nodejs/node/commit/205d5f674a)] - **doc,fs**: rename defaultEncoding option to encoding (Aleh Zasypkin) [#14867](https://github.com/nodejs/node/pull/14867) +* [[`aaf55db95b`](https://github.com/nodejs/node/commit/aaf55db95b)] - **doc,lib,src,test**: strip executable bits off files (Anna Henningsen) [#15132](https://github.com/nodejs/node/pull/15132) +* [[`7f62378e76`](https://github.com/nodejs/node/commit/7f62378e76)] - **doc,stream**: remove wrong remark on readable.read (Jan Schär) [#15014](https://github.com/nodejs/node/pull/15014) +* [[`ea2b5760d5`](https://github.com/nodejs/node/commit/ea2b5760d5)] - **errors**: remove duplicated ERR_HTTP_INVALID_STATUS_CODE error (Jon Moss) [#15003](https://github.com/nodejs/node/pull/15003) +* [[`71f90c6f80`](https://github.com/nodejs/node/commit/71f90c6f80)] - **(SEMVER-MINOR)** **fs**: add fs.copyFile{Sync} (cjihrig) [#15034](https://github.com/nodejs/node/pull/15034) +* [[`3d9ad82729`](https://github.com/nodejs/node/commit/3d9ad82729)] - **gyp**: fix ninja build failure (GYP patch) (Daniel Bevenius) [#12484](https://github.com/nodejs/node/pull/12484) +* [[`12191f6ed8`](https://github.com/nodejs/node/commit/12191f6ed8)] - **gyp**: enable cctest to use objects (gyp part) (Daniel Bevenius) [#12450](https://github.com/nodejs/node/pull/12450) +* [[`538894978b`](https://github.com/nodejs/node/commit/538894978b)] - **gyp**: add compile_commands.json gyp generator (Ben Noordhuis) [#12450](https://github.com/nodejs/node/pull/12450) +* [[`7eb3679eea`](https://github.com/nodejs/node/commit/7eb3679eea)] - **gyp**: inherit parent for `*.host` (Johan Bergström) [#6173](https://github.com/nodejs/node/pull/6173) +* [[`5fb252a5a2`](https://github.com/nodejs/node/commit/5fb252a5a2)] - **gyp**: fix gyp to work on MacOSX without XCode (Shigeki Ohtsu) [iojs/io.js#1325](https://github.com/iojs/io.js/pull/1325) +* [[`0343eceda4`](https://github.com/nodejs/node/commit/0343eceda4)] - **http2**: fix refs to status 205, add tests (Anatoli Papirovski) [#15153](https://github.com/nodejs/node/pull/15153) +* [[`d8ff550528`](https://github.com/nodejs/node/commit/d8ff550528)] - **http2**: store headersSent after stream destroyed (Anatoli Papirovski) [#15232](https://github.com/nodejs/node/pull/15232) +* [[`4882f079f1`](https://github.com/nodejs/node/commit/4882f079f1)] - **http2**: set decodeStrings to false, test (Anatoli Papirovski) [#15140](https://github.com/nodejs/node/pull/15140) +* [[`93a4cf60ff`](https://github.com/nodejs/node/commit/93a4cf60ff)] - **http2**: use session not socket timeout, tests (Anatoli Papirovski) [#15188](https://github.com/nodejs/node/pull/15188) +* [[`764213cc7b`](https://github.com/nodejs/node/commit/764213cc7b)] - **http2**: add compat trailers, adjust multi-headers (Anatoli Papirovski) [#15193](https://github.com/nodejs/node/pull/15193) +* [[`cc82f541e5`](https://github.com/nodejs/node/commit/cc82f541e5)] - **http2**: fix closedCode NaN, increase test coverage (Anatoli Papirovski) [#15154](https://github.com/nodejs/node/pull/15154) +* [[`afa72dfdf3`](https://github.com/nodejs/node/commit/afa72dfdf3)] - **http2**: guard against destroyed session, timeouts (James M Snell) [#15106](https://github.com/nodejs/node/pull/15106) +* [[`f6c51888db`](https://github.com/nodejs/node/commit/f6c51888db)] - **http2**: correct emit error in onConnect, full tests (Anatoli Papirovski) [#15080](https://github.com/nodejs/node/pull/15080) +* [[`fd51cb8ca3`](https://github.com/nodejs/node/commit/fd51cb8ca3)] - **http2**: adjust error types, test coverage (Anatoli Papirovski) [#15109](https://github.com/nodejs/node/pull/15109) +* [[`f612a6dd5c`](https://github.com/nodejs/node/commit/f612a6dd5c)] - **http2**: handle 100-continue flow & writeContinue (Anatoli Papirovski) [#15039](https://github.com/nodejs/node/pull/15039) +* [[`989dfaf930`](https://github.com/nodejs/node/commit/989dfaf930)] - **http2**: refactor error handling (Matteo Collina) [#14991](https://github.com/nodejs/node/pull/14991) +* [[`d231ef645e`](https://github.com/nodejs/node/commit/d231ef645e)] - **http2**: ignore invalid headers explicitly (Anna Henningsen) [#14955](https://github.com/nodejs/node/pull/14955) +* [[`1b57c375aa`](https://github.com/nodejs/node/commit/1b57c375aa)] - **http2**: minor refactor of passing headers to JS (Anna Henningsen) [#14808](https://github.com/nodejs/node/pull/14808) +* [[`80fe40aabf`](https://github.com/nodejs/node/commit/80fe40aabf)] - **http2**: handful of http/2 src cleanups (James M Snell) [#14825](https://github.com/nodejs/node/pull/14825) +* [[`9589641c5c`](https://github.com/nodejs/node/commit/9589641c5c)] - **http2**: Expose Http2ServerRequest/Response (Pini Houri) [#14690](https://github.com/nodejs/node/pull/14690) +* [[`8c61b72f90`](https://github.com/nodejs/node/commit/8c61b72f90)] - **(SEMVER-MINOR)** **inspector**: enable async stack traces (Miroslav Bajtoš) [#13870](https://github.com/nodejs/node/pull/13870) +* [[`e2ae08b48d`](https://github.com/nodejs/node/commit/e2ae08b48d)] - **inspector**: rewrite inspector test helper (Eugene Ostroukhov) [#14797](https://github.com/nodejs/node/pull/14797) +* [[`105acf4af7`](https://github.com/nodejs/node/commit/105acf4af7)] - **inspector**: log exceptions in message handlers (Eugene Ostroukhov) [#14980](https://github.com/nodejs/node/pull/14980) +* [[`d5a376ab7a`](https://github.com/nodejs/node/commit/d5a376ab7a)] - **lib**: remove circular reference (Ruben Bridgewater) [#14885](https://github.com/nodejs/node/pull/14885) +* [[`605d625e62`](https://github.com/nodejs/node/commit/605d625e62)] - **lib**: simplify the readonly properties of icu (Jackson Tian) [#13221](https://github.com/nodejs/node/pull/13221) +* [[`ea0a882041`](https://github.com/nodejs/node/commit/ea0a882041)] - **lib**: remove the invalid command line options (Jackson Tian) [#13764](https://github.com/nodejs/node/pull/13764) +* [[`9129057e03`](https://github.com/nodejs/node/commit/9129057e03)] - **lib**: clean up usage of threw (Jackson Tian) [#10534](https://github.com/nodejs/node/pull/10534) +* [[`f34e0f97e7`](https://github.com/nodejs/node/commit/f34e0f97e7)] - **lib**: instantiate console methods eagerly (Ben Noordhuis) [#14791](https://github.com/nodejs/node/pull/14791) +* [[`01846a06c2`](https://github.com/nodejs/node/commit/01846a06c2)] - **meta**: merge TSC and CTC back into a single body (James M Snell) [#14973](https://github.com/nodejs/node/pull/14973) +* [[`859abe5169`](https://github.com/nodejs/node/commit/859abe5169)] - **meta**: considerations for new core modules (James M Snell) [#15022](https://github.com/nodejs/node/pull/15022) +* [[`cc72118e71`](https://github.com/nodejs/node/commit/cc72118e71)] - **meta**: improve definition of a collaborator (James M Snell) [#14981](https://github.com/nodejs/node/pull/14981) +* [[`865a3c3daf`](https://github.com/nodejs/node/commit/865a3c3daf)] - **(SEMVER-MINOR)** **module**: Allow runMain to be ESM (Bradley Farias) [#14369](https://github.com/nodejs/node/pull/14369) +* [[`4bf0d4e133`](https://github.com/nodejs/node/commit/4bf0d4e133)] - **n-api**: implement napi_run_script (Gabriel Schulhof) [#15216](https://github.com/nodejs/node/pull/15216) +* [[`3a18df0750`](https://github.com/nodejs/node/commit/3a18df0750)] - **n-api**: adds function to adjust external memory (Chris Young) [#14310](https://github.com/nodejs/node/pull/14310) +* [[`503370e2d3`](https://github.com/nodejs/node/commit/503370e2d3)] - **(SEMVER-MINOR)** **n-api**: implement promise (Gabriel Schulhof) [#14365](https://github.com/nodejs/node/pull/14365) +* [[`a6344d5a83`](https://github.com/nodejs/node/commit/a6344d5a83)] - **(SEMVER-MINOR)** **n-api**: add ability to remove a wrapping (Gabriel Schulhof) [#14658](https://github.com/nodejs/node/pull/14658) +* [[`67fde146e0`](https://github.com/nodejs/node/commit/67fde146e0)] - **net**: check EADDRINUSE after binding localPort (Joyee Cheung) [#15097](https://github.com/nodejs/node/pull/15097) +* [[`b4e8850576`](https://github.com/nodejs/node/commit/b4e8850576)] - **net**: move debug statement (Brian White) [#12616](https://github.com/nodejs/node/pull/12616) +* [[`136eea4bcb`](https://github.com/nodejs/node/commit/136eea4bcb)] - **(SEMVER-MINOR)** **os**: add CIDR support (Mudit Ameta) [#14307](https://github.com/nodejs/node/pull/14307) +* [[`29f9101a0f`](https://github.com/nodejs/node/commit/29f9101a0f)] - **path**: fix normalize on directories with two dots (Michaël Zasso) [#14107](https://github.com/nodejs/node/pull/14107) +* [[`e3f5c58423`](https://github.com/nodejs/node/commit/e3f5c58423)] - **perf_hooks**: fix presumed typo in node_perf.cc (Anna Henningsen) [#15019](https://github.com/nodejs/node/pull/15019) +* [[`69e3bc64cc`](https://github.com/nodejs/node/commit/69e3bc64cc)] - **perf_hooks**: mark as experimental (James M Snell) [#14997](https://github.com/nodejs/node/pull/14997) +* [[`f75faddb1f`](https://github.com/nodejs/node/commit/f75faddb1f)] - **(SEMVER-MINOR)** **perf_hooks**: implementation of the perf timing API (James M Snell) [#14680](https://github.com/nodejs/node/pull/14680) +* [[`4d2aa16d33`](https://github.com/nodejs/node/commit/4d2aa16d33)] - **process**: keep process prototype in inheritance chain (Jimmy Thomson) [#14715](https://github.com/nodejs/node/pull/14715) +* [[`ae85d5f024`](https://github.com/nodejs/node/commit/ae85d5f024)] - **promises**: more robust stringification (Timothy Gu) [#13784](https://github.com/nodejs/node/pull/13784) +* [[`eee2aa693b`](https://github.com/nodejs/node/commit/eee2aa693b)] - **repl**: force editorMode in .load (Lance Ball) [#14861](https://github.com/nodejs/node/pull/14861) +* [[`f81812b1ff`](https://github.com/nodejs/node/commit/f81812b1ff)] - **src**: turn key length exception into CHECK (Ben Noordhuis) [#15183](https://github.com/nodejs/node/pull/15183) +* [[`f113d7332f`](https://github.com/nodejs/node/commit/f113d7332f)] - **src**: fix compiler warnings in node_perf.cc (Daniel Bevenius) [#15112](https://github.com/nodejs/node/pull/15112) +* [[`a83d427091`](https://github.com/nodejs/node/commit/a83d427091)] - **src**: remove unused persistent properties from env (Anna Henningsen) [#15096](https://github.com/nodejs/node/pull/15096) +* [[`391855c252`](https://github.com/nodejs/node/commit/391855c252)] - **src**: fix build on certain platforms (Anna Henningsen) [#14996](https://github.com/nodejs/node/pull/14996) +* [[`8cee5d66bd`](https://github.com/nodejs/node/commit/8cee5d66bd)] - **src**: reduce code duplication (James M Snell) [#14937](https://github.com/nodejs/node/pull/14937) +* [[`5a05dfe0a7`](https://github.com/nodejs/node/commit/5a05dfe0a7)] - **src**: fixup strings, reduce duplication (James M Snell) [#14937](https://github.com/nodejs/node/pull/14937) +* [[`1c3cb49f00`](https://github.com/nodejs/node/commit/1c3cb49f00)] - **src**: miscellaneous cleanups for node_config (James M Snell) [#14868](https://github.com/nodejs/node/pull/14868) +* [[`7213be9f59`](https://github.com/nodejs/node/commit/7213be9f59)] - **src**: fix DEBUG_HTTP2 type arguments (Daniel Bevenius) [#15197](https://github.com/nodejs/node/pull/15197) +* [[`ffe572addd`](https://github.com/nodejs/node/commit/ffe572addd)] - **src**: replace assert() with CHECK() (Ben Noordhuis) [#14663](https://github.com/nodejs/node/pull/14663) +* [[`abc5cdc923`](https://github.com/nodejs/node/commit/abc5cdc923)] - **src**: remove unnecessary helper function (Brian White) [#14959](https://github.com/nodejs/node/pull/14959) +* [[`992d1dd956`](https://github.com/nodejs/node/commit/992d1dd956)] - **src**: detect nul bytes in InternalModuleReadFile() (Ben Noordhuis) [#14854](https://github.com/nodejs/node/pull/14854) +* [[`4570fa16c7`](https://github.com/nodejs/node/commit/4570fa16c7)] - **src**: remove extra copy from Copy() in node_url.cc (Anna Henningsen) [#14907](https://github.com/nodejs/node/pull/14907) +* [[`081c3e107d`](https://github.com/nodejs/node/commit/081c3e107d)] - **src**: minor cleanup for node_revert (James M Snell) [#14864](https://github.com/nodejs/node/pull/14864) +* [[`dcd7817fbc`](https://github.com/nodejs/node/commit/dcd7817fbc)] - **src**: use `unordered_set` instead of custom rb tree (Anna Henningsen) [#14826](https://github.com/nodejs/node/pull/14826) +* [[`fadcbab617`](https://github.com/nodejs/node/commit/fadcbab617)] - **src**: Node implementation of v8::Platform (Matt Loring) [#14001](https://github.com/nodejs/node/pull/14001) +* [[`c861462faa`](https://github.com/nodejs/node/commit/c861462faa)] - **stream**: fix Writable instanceof for subclasses (Anna Henningsen) [#14945](https://github.com/nodejs/node/pull/14945) +* [[`2adabe6777`](https://github.com/nodejs/node/commit/2adabe6777)] - **test**: fix single test runner regression (Timothy Gu) [#15329](https://github.com/nodejs/node/pull/15329) +* [[`e3d0ff901b`](https://github.com/nodejs/node/commit/e3d0ff901b)] - **test**: split test-cli-node-options (Refael Ackermann) [#14195](https://github.com/nodejs/node/pull/14195) +* [[`e87cb32db2`](https://github.com/nodejs/node/commit/e87cb32db2)] - **test**: remove envPlus, use Object.assign everywhere (Gibson Fahnestock) [#14845](https://github.com/nodejs/node/pull/14845) +* [[`dea959e841`](https://github.com/nodejs/node/commit/dea959e841)] - **test**: fix flaky test-readline-interface (Rich Trott) [#15066](https://github.com/nodejs/node/pull/15066) +* [[`ae91b1efc0`](https://github.com/nodejs/node/commit/ae91b1efc0)] - **test**: continue normalizing fixtures use (Miguel Angel Asencio Hurtado) [#14716](https://github.com/nodejs/node/pull/14716) +* [[`77bc72ad54`](https://github.com/nodejs/node/commit/77bc72ad54)] - **(SEMVER-MINOR)** **test**: fix inspector helper port sniffing (Timothy Gu) [#13870](https://github.com/nodejs/node/pull/13870) +* [[`7facfaab66`](https://github.com/nodejs/node/commit/7facfaab66)] - **test**: preserve env in test cases (Beth Griggs) [#14822](https://github.com/nodejs/node/pull/14822) +* [[`2310cfcea1`](https://github.com/nodejs/node/commit/2310cfcea1)] - **test**: exclude write-coverage from coverage report (Benjamin Coe) [#15194](https://github.com/nodejs/node/pull/15194) +* [[`6fa05e671c`](https://github.com/nodejs/node/commit/6fa05e671c)] - **test**: use no-save and no-package-lock flags (Simon Brewster) [#15196](https://github.com/nodejs/node/pull/15196) +* [[`ac71d99253`](https://github.com/nodejs/node/commit/ac71d99253)] - **test**: add http2 compat setTimeout tests (Anatoli Papirovski) [#15156](https://github.com/nodejs/node/pull/15156) +* [[`7106734773`](https://github.com/nodejs/node/commit/7106734773)] - **test**: add test-benchmark-buffer (Rich Trott) [#15175](https://github.com/nodejs/node/pull/15175) +* [[`0b9fde4d4a`](https://github.com/nodejs/node/commit/0b9fde4d4a)] - **test**: refactor test-fs-readfile-unlink (Rich Trott) [#15173](https://github.com/nodejs/node/pull/15173) +* [[`9f79bd8fba`](https://github.com/nodejs/node/commit/9f79bd8fba)] - **test**: http2 test coverage for NghttpError (James M Snell) [#15105](https://github.com/nodejs/node/pull/15105) +* [[`c0dba0f3f4`](https://github.com/nodejs/node/commit/c0dba0f3f4)] - **test**: http2 test coverage for assertValidPseudoHeader (James M Snell) [#15105](https://github.com/nodejs/node/pull/15105) +* [[`837c29c73b`](https://github.com/nodejs/node/commit/837c29c73b)] - **test**: http2 test coverage for updateOptionsBuffer (James M Snell) [#15105](https://github.com/nodejs/node/pull/15105) +* [[`e3e9e5039d`](https://github.com/nodejs/node/commit/e3e9e5039d)] - **test**: increase Http2ServerResponse test coverage (Anatoli Papirovski) [#15074](https://github.com/nodejs/node/pull/15074) +* [[`72aae0417c`](https://github.com/nodejs/node/commit/72aae0417c)] - **test**: split path tests into multiple files (Michaël Zasso) [#15093](https://github.com/nodejs/node/pull/15093) +* [[`d176a18547`](https://github.com/nodejs/node/commit/d176a18547)] - **test**: add a test for Expect & checkExpectation (Anatoli Papirovski) [#15040](https://github.com/nodejs/node/pull/15040) +* [[`cfbf5057d6`](https://github.com/nodejs/node/commit/cfbf5057d6)] - **test**: add http2 test for method CONNECT (Anatoli Papirovski) [#15052](https://github.com/nodejs/node/pull/15052) +* [[`5b13add028`](https://github.com/nodejs/node/commit/5b13add028)] - **test**: remove unused param in test-graph.pipe (Simon Brewster) [#15007](https://github.com/nodejs/node/pull/15007) +* [[`5cb6500de9`](https://github.com/nodejs/node/commit/5cb6500de9)] - **test**: increase coverage for http2 response headers (Anatoli Papirovski) [#15035](https://github.com/nodejs/node/pull/15035) +* [[`7050608593`](https://github.com/nodejs/node/commit/7050608593)] - **test**: fix hijackStdout behavior in console (XadillaX) [#14647](https://github.com/nodejs/node/pull/14647) +* [[`458b8ab5df`](https://github.com/nodejs/node/commit/458b8ab5df)] - **test**: add regression test for 14814 (Anna Henningsen) [#15023](https://github.com/nodejs/node/pull/15023) +* [[`f89ef77144`](https://github.com/nodejs/node/commit/f89ef77144)] - **test**: run abort tests (Rich Trott) [#14013](https://github.com/nodejs/node/pull/14013) +* [[`a91a3fe6c4`](https://github.com/nodejs/node/commit/a91a3fe6c4)] - **test**: improve test-abort-backtrace (Rich Trott) [#14013](https://github.com/nodejs/node/pull/14013) +* [[`b85a73407b`](https://github.com/nodejs/node/commit/b85a73407b)] - **test**: improve test-abort-uncaught-exception (Rich Trott) [#14013](https://github.com/nodejs/node/pull/14013) +* [[`f694ea6f2b`](https://github.com/nodejs/node/commit/f694ea6f2b)] - **test**: pipe some error output if npm fails (Jeremiah Senkpiel) [#12490](https://github.com/nodejs/node/pull/12490) +* [[`f1284d32a5`](https://github.com/nodejs/node/commit/f1284d32a5)] - **test**: simplify test-tls-client-default-ciphers (Jon Moss) [#14928](https://github.com/nodejs/node/pull/14928) +* [[`d4c2eba376`](https://github.com/nodejs/node/commit/d4c2eba376)] - **test**: remove unused function args (Mohd Maqbool Alam) [#14971](https://github.com/nodejs/node/pull/14971) +* [[`9c7f27b91b`](https://github.com/nodejs/node/commit/9c7f27b91b)] - **test**: extend async addon test (Anna Henningsen) [#14922](https://github.com/nodejs/node/pull/14922) +* [[`8c927dd71f`](https://github.com/nodejs/node/commit/8c927dd71f)] - **test**: fix async-hooks tests (Bartosz Sosnowski) [#14865](https://github.com/nodejs/node/pull/14865) +* [[`1849c519ca`](https://github.com/nodejs/node/commit/1849c519ca)] - **test**: add test-benchmark-process (Rich Trott) [#14951](https://github.com/nodejs/node/pull/14951) +* [[`b480b20e02`](https://github.com/nodejs/node/commit/b480b20e02)] - **test**: add test-benchmark-path (Rich Trott) [#14951](https://github.com/nodejs/node/pull/14951) +* [[`2e3e136519`](https://github.com/nodejs/node/commit/2e3e136519)] - **test**: add test-benchmark-os (Rich Trott) [#14951](https://github.com/nodejs/node/pull/14951) +* [[`7e541d6a97`](https://github.com/nodejs/node/commit/7e541d6a97)] - **test**: add test-benchmark-events (Rich Trott) [#14951](https://github.com/nodejs/node/pull/14951) +* [[`981ef464e2`](https://github.com/nodejs/node/commit/981ef464e2)] - **test**: add test-benchmark-domain (Rich Trott) [#14951](https://github.com/nodejs/node/pull/14951) +* [[`34d1a779b1`](https://github.com/nodejs/node/commit/34d1a779b1)] - **test**: add known issue for vm module (Franziska Hinkelmann) [#14661](https://github.com/nodejs/node/pull/14661) +* [[`ae27cb8ea3`](https://github.com/nodejs/node/commit/ae27cb8ea3)] - **test**: do not modify fixtures in test-fs-chmod (Rich Trott) [#14926](https://github.com/nodejs/node/pull/14926) +* [[`eb46609622`](https://github.com/nodejs/node/commit/eb46609622)] - **test**: improve assertion fail messages (Refael Ackermann) [#14949](https://github.com/nodejs/node/pull/14949) +* [[`36b8b46443`](https://github.com/nodejs/node/commit/36b8b46443)] - **test**: remove unused parameters (Daniil Shakir) [#14968](https://github.com/nodejs/node/pull/14968) +* [[`6421a9cb9a`](https://github.com/nodejs/node/commit/6421a9cb9a)] - **test**: remove unused arguments from function (Ankit Parashar) [#14931](https://github.com/nodejs/node/pull/14931) +* [[`e244f8433e`](https://github.com/nodejs/node/commit/e244f8433e)] - **test**: update windows module load error message (cjihrig) [#14950](https://github.com/nodejs/node/pull/14950) +* [[`8f61bf2cda`](https://github.com/nodejs/node/commit/8f61bf2cda)] - **test**: increase coverage for http2.connect (Michael Albert) [#14832](https://github.com/nodejs/node/pull/14832) +* [[`c0312dc781`](https://github.com/nodejs/node/commit/c0312dc781)] - **test**: make timers-blocking-callback more reliable (Rich Trott) [#14831](https://github.com/nodejs/node/pull/14831) +* [[`762155578a`](https://github.com/nodejs/node/commit/762155578a)] - **test**: remove erroneous assert message from test (Beth Griggs) [#14918](https://github.com/nodejs/node/pull/14918) +* [[`1217b1a556`](https://github.com/nodejs/node/commit/1217b1a556)] - **test**: add test for cluster benchmarks (Rich Trott) [#14812](https://github.com/nodejs/node/pull/14812) +* [[`03fd38c1bb`](https://github.com/nodejs/node/commit/03fd38c1bb)] - **test**: Mark test-stop-profile-after-done flaky (Eugene Ostroukhov) +* [[`4f49ae52f8`](https://github.com/nodejs/node/commit/4f49ae52f8)] - **test**: check util.inspect circular Set and Map refs (Ruben Bridgewater) [#14790](https://github.com/nodejs/node/pull/14790) +* [[`4dd095c982`](https://github.com/nodejs/node/commit/4dd095c982)] - **test**: refactor async-hooks/test-httparser tests (Runite618) [#14818](https://github.com/nodejs/node/pull/14818) +* [[`27ec693a53`](https://github.com/nodejs/node/commit/27ec693a53)] - **test**: add missing console.error to exec-maxBuffer (Beth Griggs) [#14796](https://github.com/nodejs/node/pull/14796) +* [[`7f02c36c4f`](https://github.com/nodejs/node/commit/7f02c36c4f)] - **test**: fix test-cluster-send-handle-large-payload (Rich Trott) [#14780](https://github.com/nodejs/node/pull/14780) +* [[`4205648216`](https://github.com/nodejs/node/commit/4205648216)] - **test**: invoke callback with common.mustCall() (Griffith Tchenpan) [#8597](https://github.com/nodejs/node/pull/8597) +* [[`a3feb54c7f`](https://github.com/nodejs/node/commit/a3feb54c7f)] - **test**: make test-tls-alert-handling more strict (Rich Trott) [#14650](https://github.com/nodejs/node/pull/14650) +* [[`d4f2a52953`](https://github.com/nodejs/node/commit/d4f2a52953)] - **test**: check crypto before requiring tls module (Daniel Bevenius) [#14708](https://github.com/nodejs/node/pull/14708) +* [[`868b441f3e`](https://github.com/nodejs/node/commit/868b441f3e)] - **test**: begin normalizing fixtures use (James M Snell) [#14332](https://github.com/nodejs/node/pull/14332) +* [[`c76ec7130e`](https://github.com/nodejs/node/commit/c76ec7130e)] - **test**: improve multiple zlib tests (James M Snell) [#14455](https://github.com/nodejs/node/pull/14455) +* [[`8fb0895176`](https://github.com/nodejs/node/commit/8fb0895176)] - **test**: improve multiple vm tests (James M Snell) [#14458](https://github.com/nodejs/node/pull/14458) +* [[`4d6da3f770`](https://github.com/nodejs/node/commit/4d6da3f770)] - **test, win**: fix IPv6 detection on Windows (Bartosz Sosnowski) [#14865](https://github.com/nodejs/node/pull/14865) +* [[`02260eab98`](https://github.com/nodejs/node/commit/02260eab98)] - **test,doc**: make module name match gyp target name (Gabriel Schulhof) [#15209](https://github.com/nodejs/node/pull/15209) +* [[`dae86e4cf5`](https://github.com/nodejs/node/commit/dae86e4cf5)] - **timers**: fix outdated comment (Tim Costa) [#14314](https://github.com/nodejs/node/pull/14314) +* [[`d6ad9d72f7`](https://github.com/nodejs/node/commit/d6ad9d72f7)] - **(SEMVER-MINOR)** **tls**: multiple PFX in createSecureContext (Yury Popov) [#14793](https://github.com/nodejs/node/pull/14793) +* [[`97908ea4d0`](https://github.com/nodejs/node/commit/97908ea4d0)] - **tools**: bump vswhere helper to 2.0.0 (Refael Ackermann) [#14557](https://github.com/nodejs/node/pull/14557) +* [[`87e44d8651`](https://github.com/nodejs/node/commit/87e44d8651)] - **tools**: add eslint rule for inspector checking (Daniel Bevenius) [#13813](https://github.com/nodejs/node/pull/13813) +* [[`1d97ff4800`](https://github.com/nodejs/node/commit/1d97ff4800)] - **tools**: add eslint rule for hasCrypto checking (Daniel Bevenius) [#13813](https://github.com/nodejs/node/pull/13813) +* [[`bc250a1e38`](https://github.com/nodejs/node/commit/bc250a1e38)] - **tools**: fix linter error in html.js (Michaël Zasso) [#15063](https://github.com/nodejs/node/pull/15063) +* [[`5ee4e86efc`](https://github.com/nodejs/node/commit/5ee4e86efc)] - **tools**: add custom private key option (Ruslan Bekenev) [#14401](https://github.com/nodejs/node/pull/14401) +* [[`8f34b834b7`](https://github.com/nodejs/node/commit/8f34b834b7)] - **tools**: update GYP to 324dd166 (Refael Ackermann) [#14718](https://github.com/nodejs/node/pull/14718) +* [[`e4ea45412e`](https://github.com/nodejs/node/commit/e4ea45412e)] - **tools**: remove stray package-lock.json file (Rich Trott) [#14873](https://github.com/nodejs/node/pull/14873) +* [[`37c43ede43`](https://github.com/nodejs/node/commit/37c43ede43)] - **tools**: fix update-eslint.sh (Myles Borins) [#14850](https://github.com/nodejs/node/pull/14850) +* [[`b0f4539ce5`](https://github.com/nodejs/node/commit/b0f4539ce5)] - **tools**: delete an unused argument (phisixersai) [#14251](https://github.com/nodejs/node/pull/14251) +* [[`9da6c1056c`](https://github.com/nodejs/node/commit/9da6c1056c)] - **tools**: checkout for unassigned DEP00XX codes (James M Snell) [#14702](https://github.com/nodejs/node/pull/14702) +* [[`bd40cc6ef8`](https://github.com/nodejs/node/commit/bd40cc6ef8)] - **tracing**: Update to use new Platform tracing apis (Matt Loring) [#14001](https://github.com/nodejs/node/pull/14001) +* [[`a4fc43202e`](https://github.com/nodejs/node/commit/a4fc43202e)] - **url**: remove unused code from autoEscapeStr (Cyril Lakech) [#15086](https://github.com/nodejs/node/pull/15086) +* [[`2aec977fa2`](https://github.com/nodejs/node/commit/2aec977fa2)] - **util**: remove duplicate code in format (Anatoli Papirovski) [#15098](https://github.com/nodejs/node/pull/15098) +* [[`de10c0f515`](https://github.com/nodejs/node/commit/de10c0f515)] - **util**: fix inspect array w. negative maxArrayLength (Ruben Bridgewater) [#14880](https://github.com/nodejs/node/pull/14880) +* [[`c3c6cb1c13`](https://github.com/nodejs/node/commit/c3c6cb1c13)] - **util**: use proper circular reference checking (Anna Henningsen) [#14790](https://github.com/nodejs/node/pull/14790) + ## 2017-08-15, Version 8.4.0 (Current), @addaleax diff --git a/src/node_version.h b/src/node_version.h index 4fae29617d155e..fe9a93cfc81c4e 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -23,10 +23,10 @@ #define SRC_NODE_VERSION_H_ #define NODE_MAJOR_VERSION 8 -#define NODE_MINOR_VERSION 4 -#define NODE_PATCH_VERSION 1 +#define NODE_MINOR_VERSION 5 +#define NODE_PATCH_VERSION 0 -#define NODE_VERSION_IS_RELEASE 0 +#define NODE_VERSION_IS_RELEASE 1 #ifndef NODE_STRINGIFY #define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n)