diff --git a/.eslintrc.yaml b/.eslintrc.yaml index 0839e352cd399d..0e7e9ccfcb73b8 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -47,6 +47,7 @@ rules: accessor-pairs: error array-callback-return: error dot-location: [error, property] + dot-notation: error eqeqeq: [error, smart] no-fallthrough: error no-global-assign: error @@ -74,6 +75,7 @@ rules: message: __defineSetter__ is deprecated. no-return-await: error no-self-assign: error + no-self-compare: error no-throw-literal: error no-unused-labels: error no-useless-call: error diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 4f1ff30d83411c..23c1ac8bb5106e 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -38,7 +38,7 @@ responsibility for the change. In the case of pull requests proposed by an existing Collaborator, an additional Collaborator is required for sign-off. -If one or more Collaborators oppose a proposed change, then the change can not +If one or more Collaborators oppose a proposed change, then the change cannot be accepted unless: * Discussions and/or additional changes result in no Collaborators objecting to @@ -73,7 +73,7 @@ may request that the TSC restore them to active status. ## Technical Steering Committee -A subset of the Collaborators form the Technical Steering Committee (TSC). +A subset of the Collaborators forms the Technical Steering Committee (TSC). The TSC has final authority over this project, including: * Technical direction diff --git a/Makefile b/Makefile index b69b8786a30701..d7158eed378f97 100644 --- a/Makefile +++ b/Makefile @@ -616,7 +616,7 @@ doc-only: $(apidoc_dirs) $(apiassets) ## Builds the docs with the local or the if [ ! -d doc/api/assets ]; then \ $(MAKE) tools/doc/node_modules/js-yaml/package.json; \ fi; - @$(MAKE) -s $(apidocs_html) $(apidocs_json) + @$(MAKE) $(apidocs_html) $(apidocs_json) .PHONY: doc doc: $(NODE_EXE) doc-only diff --git a/README.md b/README.md index 9767b71d0919c0..687f0fb4723862 100644 --- a/README.md +++ b/README.md @@ -246,8 +246,6 @@ For more information about the governance of the Node.js project, see **Jeremiah Senkpiel** <fishrock123@rocketmail.com> * [gibfahn](https://github.com/gibfahn) - **Gibson Fahnestock** <gibfahn@gmail.com> (he/him) -* [indutny](https://github.com/indutny) - -**Fedor Indutny** <fedor.indutny@gmail.com> * [jasnell](https://github.com/jasnell) - **James M Snell** <jasnell@gmail.com> (he/him) * [joyeecheung](https://github.com/joyeecheung) - @@ -277,6 +275,8 @@ For more information about the governance of the Node.js project, see **Ben Noordhuis** <info@bnoordhuis.nl> * [chrisdickinson](https://github.com/chrisdickinson) - **Chris Dickinson** <christopher.s.dickinson@gmail.com> +* [indutny](https://github.com/indutny) - +**Fedor Indutny** <fedor.indutny@gmail.com> * [isaacs](https://github.com/isaacs) - **Isaac Z. Schlueter** <i@izs.me> * [joshgav](https://github.com/joshgav) - @@ -340,6 +340,8 @@ For more information about the governance of the Node.js project, see **Daniel Bevenius** <daniel.bevenius@gmail.com> * [DavidCai1993](https://github.com/DavidCai1993) - **David Cai** <davidcai1993@yahoo.com> (he/him) +* [devsnek](https://github.com/devsnek) - +**Gus Caplan** <me@gus.host> (he/him) * [edsadr](https://github.com/edsadr) - **Adrian Estrada** <edsadr@gmail.com> (he/him) * [eljefedelrodeodeljefe](https://github.com/eljefedelrodeodeljefe) - @@ -424,6 +426,8 @@ For more information about the governance of the Node.js project, see **Mikeal Rogers** <mikeal.rogers@gmail.com> * [misterdjules](https://github.com/misterdjules) - **Julien Gilli** <jgilli@nodejs.org> +* [mmarchini](https://github.com/mmarchini) - +**Matheus Marchini** <matheus@sthima.com> * [mscdex](https://github.com/mscdex) - **Brian White** <mscdex@mscdex.net> * [MylesBorins](https://github.com/MylesBorins) - @@ -510,6 +514,8 @@ For more information about the governance of the Node.js project, see **Jeremy Whitlock** <jwhitlock@apache.org> * [XadillaX](https://github.com/XadillaX) - **Khaidi Chu** <i@2333.moe> (he/him) +* [yhwang](https://github.com/yhwang) - +**Yihong Wang** <yh.wang@ibm.com> * [yorkie](https://github.com/yorkie) - **Yorkie Liu** <yorkiefixer@gmail.com> * [yosuke-furukawa](https://github.com/yosuke-furukawa) - diff --git a/benchmark/compare.js b/benchmark/compare.js index 6b51a70eb9a41b..e7866b60e36418 100644 --- a/benchmark/compare.js +++ b/benchmark/compare.js @@ -1,6 +1,7 @@ 'use strict'; -const fork = require('child_process').fork; +const { fork } = require('child_process'); +const { inspect } = require('util'); const path = require('path'); const CLI = require('./_cli.js'); const BenchmarkProgress = require('./_benchmark_progress.js'); @@ -76,7 +77,7 @@ if (showProgress) { // Construct configuration string, " A=a, B=b, ..." let conf = ''; for (const key of Object.keys(data.conf)) { - conf += ` ${key}=${JSON.stringify(data.conf[key])}`; + conf += ` ${key}=${inspect(data.conf[key])}`; } conf = conf.slice(1); // Escape quotes (") for correct csv formatting diff --git a/benchmark/http/check_invalid_header_char.js b/benchmark/http/check_invalid_header_char.js index 399e71b2dfcc88..c70b0d39db2ffc 100644 --- a/benchmark/http/check_invalid_header_char.js +++ b/benchmark/http/check_invalid_header_char.js @@ -3,43 +3,66 @@ const common = require('../common.js'); const _checkInvalidHeaderChar = require('_http_common')._checkInvalidHeaderChar; -// Put it here so the benchmark result lines will not be super long. -const LONG_AND_INVALID = 'Here is a value that is really a folded header ' + - 'value\r\n this should be supported, but it is not currently'; +const groupedInputs = { + // Representative set of inputs from an AcmeAir benchmark run: + // all valid strings, average length 14.4, stdev 13.0 + group_acmeair: [ + 'W/"2-d4cbb29"', 'OK', 'Express', 'X-HTTP-Method-Override', 'Express', + 'application/json', 'application/json; charset=utf-8', '206', 'OK', + 'sessionid=; Path=/', 'text/html; charset=utf-8', + 'text/html; charset=utf-8', '10', 'W/"a-eda64de5"', 'OK', 'Express', + 'application/json', 'application/json; charset=utf-8', '2', 'W/"2-d4cbb29"', + 'OK', 'Express', 'X-HTTP-Method-Override', 'sessionid=; Path=/', 'Express', + 'sessionid=; Path=/,sessionid=6b059402-d62f-4e6f-b3dd-ce5b9e487c39; Path=/', + 'text/html; charset=utf-8', 'text/html; charset=utf-8', '9', 'OK', + 'sessionid=; Path=/', 'text/html; charset=utf-8', + 'text/html; charset=utf-8', '10', 'W/"a-eda64de5"', 'OK', 'Express', + 'Express', 'X-HTTP-Method-Override', 'sessionid=; Path=/', + 'application/json' + ], + + // Put it here so the benchmark result lines will not be super long. + LONG_AND_INVALID: ['Here is a value that is really a folded header ' + + 'value\r\n this should be supported, but it is not currently'] +}; + +const inputs = [ + // Valid + '', + '1', + '\t\t\t\t\t\t\t\t\t\tFoo bar baz', + 'keep-alive', + 'close', + 'gzip', + '20091', + 'private', + 'text/html; charset=utf-8', + 'text/plain', + 'Sat, 07 May 2016 16:54:48 GMT', + 'SAMEORIGIN', + 'en-US', + + // Invalid + '中文呢', // unicode + 'foo\nbar', + '\x7F' +]; const bench = common.createBenchmark(main, { - key: [ - // Valid - '', - '1', - '\t\t\t\t\t\t\t\t\t\tFoo bar baz', - 'keep-alive', - 'close', - 'gzip', - '20091', - 'private', - 'text/html; charset=utf-8', - 'text/plain', - 'Sat, 07 May 2016 16:54:48 GMT', - 'SAMEORIGIN', - 'en-US', - - // Invalid - 'LONG_AND_INVALID', - '中文呢', // unicode - 'foo\nbar', - '\x7F' - ], + input: inputs.concat(Object.keys(groupedInputs)), n: [1e6], }); -function main({ n, key }) { - if (key === 'LONG_AND_INVALID') { - key = LONG_AND_INVALID; +function main({ n, input }) { + let inputs = [input]; + if (groupedInputs.hasOwnProperty(input)) { + inputs = groupedInputs[input]; } + + const len = inputs.length; bench.start(); for (var i = 0; i < n; i++) { - _checkInvalidHeaderChar(key); + _checkInvalidHeaderChar(inputs[i % len]); } bench.end(n); } diff --git a/benchmark/misc/object-property-bench.js b/benchmark/misc/object-property-bench.js index ddc6faed7fc8b7..3a181ed0a540f8 100644 --- a/benchmark/misc/object-property-bench.js +++ b/benchmark/misc/object-property-bench.js @@ -1,5 +1,7 @@ 'use strict'; +/* eslint-disable dot-notation */ + const common = require('../common.js'); const bench = common.createBenchmark(main, { diff --git a/common.gypi b/common.gypi index 08ef48ccedcccf..5752c17168d50a 100644 --- a/common.gypi +++ b/common.gypi @@ -27,7 +27,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.19', + 'v8_embedder_string': '-node.20', # Enable disassembler for `--print-code` v8 options 'v8_enable_disassembler': 1, @@ -44,29 +44,29 @@ 'conditions': [ ['GENERATOR=="ninja"', { - 'OBJ_DIR': '<(PRODUCT_DIR)/obj', - 'V8_BASE': '<(PRODUCT_DIR)/obj/deps/v8/src/libv8_base.a', + 'obj_dir': '<(PRODUCT_DIR)/obj', + 'v8_base': '<(PRODUCT_DIR)/obj/deps/v8/src/libv8_base.a', }, { - 'OBJ_DIR%': '<(PRODUCT_DIR)/obj.target', - 'V8_BASE%': '<(PRODUCT_DIR)/obj.target/deps/v8/src/libv8_base.a', + 'obj_dir%': '<(PRODUCT_DIR)/obj.target', + 'v8_base%': '<(PRODUCT_DIR)/obj.target/deps/v8/src/libv8_base.a', }], ['OS == "win"', { 'os_posix': 0, 'v8_postmortem_support%': 'false', - 'OBJ_DIR': '<(PRODUCT_DIR)/obj', - 'V8_BASE': '<(PRODUCT_DIR)/lib/v8_libbase.lib', + 'obj_dir': '<(PRODUCT_DIR)/obj', + 'v8_base': '<(PRODUCT_DIR)/lib/v8_libbase.lib', }, { 'os_posix': 1, 'v8_postmortem_support%': 'true', }], ['OS== "mac"', { - 'OBJ_DIR%': '<(PRODUCT_DIR)/obj.target', - 'V8_BASE': '<(PRODUCT_DIR)/libv8_base.a', + 'obj_dir%': '<(PRODUCT_DIR)/obj.target', + 'v8_base': '<(PRODUCT_DIR)/libv8_base.a', }], ['openssl_fips != ""', { - 'OPENSSL_PRODUCT': '<(STATIC_LIB_PREFIX)crypto<(STATIC_LIB_SUFFIX)', + 'openssl_product': '<(STATIC_LIB_PREFIX)crypto<(STATIC_LIB_SUFFIX)', }, { - 'OPENSSL_PRODUCT': '<(STATIC_LIB_PREFIX)openssl<(STATIC_LIB_SUFFIX)', + 'openssl_product': '<(STATIC_LIB_PREFIX)openssl<(STATIC_LIB_SUFFIX)', }], ['OS=="mac"', { 'clang%': 1, diff --git a/deps/v8/src/profiler/profiler-listener.cc b/deps/v8/src/profiler/profiler-listener.cc index 540d93002459fb..b90f5a4894fc76 100644 --- a/deps/v8/src/profiler/profiler-listener.cc +++ b/deps/v8/src/profiler/profiler-listener.cc @@ -226,11 +226,18 @@ void ProfilerListener::RecordInliningInfo(CodeEntry* entry, SharedFunctionInfo* shared_info = SharedFunctionInfo::cast( deopt_input_data->LiteralArray()->get(shared_info_id)); if (!depth++) continue; // Skip the current function itself. - CodeEntry* inline_entry = new CodeEntry( - entry->tag(), GetFunctionName(shared_info->DebugName()), - CodeEntry::kEmptyNamePrefix, entry->resource_name(), - CpuProfileNode::kNoLineNumberInfo, - CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start()); + const char* resource_name = + (shared_info->script()->IsScript() && + Script::cast(shared_info->script())->name()->IsName()) + ? GetName(Name::cast(Script::cast(shared_info->script())->name())) + : CodeEntry::kEmptyResourceName; + + CodeEntry* inline_entry = + new CodeEntry(entry->tag(), GetFunctionName(shared_info->DebugName()), + CodeEntry::kEmptyNamePrefix, resource_name, + CpuProfileNode::kNoLineNumberInfo, + CpuProfileNode::kNoColumnNumberInfo, nullptr, + code->instruction_start()); inline_entry->FillFunctionInfo(shared_info); inline_stack.push_back(inline_entry); } diff --git a/deps/v8/test/cctest/test-cpu-profiler.cc b/deps/v8/test/cctest/test-cpu-profiler.cc index f22a42a977d5be..b441d04fdd31db 100644 --- a/deps/v8/test/cctest/test-cpu-profiler.cc +++ b/deps/v8/test/cctest/test-cpu-profiler.cc @@ -1745,6 +1745,85 @@ TEST(FunctionDetails) { script_a->GetUnboundScript()->GetId(), 5, 14); } +TEST(FunctionDetailsInlining) { + if (!CcTest::i_isolate()->use_optimizer() || i::FLAG_always_opt) return; + i::FLAG_allow_natives_syntax = true; + v8::HandleScope scope(CcTest::isolate()); + v8::Local env = CcTest::NewContext(PROFILER_EXTENSION); + v8::Context::Scope context_scope(env); + ProfilerHelper helper(env); + + // alpha is in a_script, beta in b_script. beta is + // inlined in alpha, but it should be attributed to b_script. + + v8::Local script_b = CompileWithOrigin( + "function beta(k) {\n" + " let sum = 2;\n" + " for(let i = 0; i < k; i ++) {\n" + " sum += i;\n" + " sum = sum + 'a';\n" + " }\n" + " return sum;\n" + "}\n" + "\n", + "script_b"); + + v8::Local script_a = CompileWithOrigin( + "function alpha(p) {\n" + " let res = beta(p);\n" + " res = res + res;\n" + " return res;\n" + "}\n" + "let p = 2;\n" + "\n" + "\n" + "// Warm up before profiling or the inlining doesn't happen.\n" + "p = alpha(p);\n" + "p = alpha(p);\n" + "%OptimizeFunctionOnNextCall(alpha);\n" + "p = alpha(p);\n" + "\n" + "\n" + "startProfiling();\n" + "for(let i = 0; i < 10000; i++) {\n" + " p = alpha(p);\n" + "}\n" + "stopProfiling();\n" + "\n" + "\n", + "script_a"); + + script_b->Run(env).ToLocalChecked(); + script_a->Run(env).ToLocalChecked(); + + const v8::CpuProfile* profile = i::ProfilerExtension::last_profile; + const v8::CpuProfileNode* current = profile->GetTopDownRoot(); + reinterpret_cast(const_cast(current)) + ->Print(0); + // The tree should look like this: + // 0 (root) 0 #1 + // 5 (program) 0 #6 + // 2 14 #2 script_a:1 + // ;;; deopted at script_id: 14 position: 299 with reason 'Insufficient + // type feedback for call'. + // 1 alpha 14 #4 script_a:1 + // 9 beta 13 #5 script_b:0 + // 0 startProfiling 0 #3 + + const v8::CpuProfileNode* root = profile->GetTopDownRoot(); + const v8::CpuProfileNode* script = GetChild(env, root, ""); + CheckFunctionDetails(env->GetIsolate(), script, "", "script_a", + script_a->GetUnboundScript()->GetId(), 1, 1); + const v8::CpuProfileNode* alpha = FindChild(env, script, "alpha"); + // Return early if profiling didn't sample alpha. + if (!alpha) return; + CheckFunctionDetails(env->GetIsolate(), alpha, "alpha", "script_a", + script_a->GetUnboundScript()->GetId(), 1, 15); + const v8::CpuProfileNode* beta = FindChild(env, alpha, "beta"); + if (!beta) return; + CheckFunctionDetails(env->GetIsolate(), beta, "beta", "script_b", + script_b->GetUnboundScript()->GetId(), 0, 0); +} TEST(DontStopOnFinishedProfileDelete) { v8::HandleScope scope(CcTest::isolate()); diff --git a/doc/.eslintrc.yaml b/doc/.eslintrc.yaml index 8038836fe310db..c8c1612e3a100b 100644 --- a/doc/.eslintrc.yaml +++ b/doc/.eslintrc.yaml @@ -12,3 +12,6 @@ rules: no-var: error prefer-const: error prefer-rest-params: error + + # Stylistic Issues + no-multiple-empty-lines: [error, {max: 1, maxEOF: 0, maxBOF: 0}] diff --git a/doc/api/assert.md b/doc/api/assert.md index 796c9c07d2431b..6935a9db4bda64 100644 --- a/doc/api/assert.md +++ b/doc/api/assert.md @@ -113,7 +113,9 @@ changes: description: Enumerable symbol properties are now compared. - version: v9.0.0 pr-url: https://github.com/nodejs/node/pull/15036 - description: NaN is now compared using the [SameValueZero][] comparison. + description: NaN is now compared using the + [SameValueZero](https://tc39.github.io/ecma262/#sec-samevaluezero) + comparison. - version: v8.5.0 pr-url: https://github.com/nodejs/node/pull/15001 description: Error names and messages are now properly compared @@ -461,7 +463,9 @@ changes: description: -0 and +0 are not considered equal anymore. - version: v9.0.0 pr-url: https://github.com/nodejs/node/pull/15036 - description: NaN is now compared using the [SameValueZero][] comparison. + description: NaN is now compared using the + [SameValueZero](https://tc39.github.io/ecma262/#sec-samevaluezero) + comparison. - version: v9.0.0 pr-url: https://github.com/nodejs/node/pull/15001 description: Error names and messages are now properly compared diff --git a/doc/api/async_hooks.md b/doc/api/async_hooks.md index e0922fcd1134f5..99ee7f4f769ea6 100644 --- a/doc/api/async_hooks.md +++ b/doc/api/async_hooks.md @@ -301,10 +301,10 @@ and document their own resource objects. For example, such a resource object could contain the SQL query being executed. In the case of Promises, the `resource` object will have `promise` property -that refers to the Promise that is being initialized, and a `parentId` property -set to the `asyncId` of a parent Promise, if there is one, and `undefined` +that refers to the Promise that is being initialized, and a `isChainedPromise` +property, set to `true` if the promise has a parent promise, and `false` otherwise. For example, in the case of `b = a.then(handler)`, `a` is considered -a parent Promise of `b`. +a parent Promise of `b`. Here, `b` is considered a chained promise. *Note*: In some cases the resource object is reused for performance reasons, it is thus not safe to use it as a key in a `WeakMap` or add properties to it. @@ -599,10 +599,6 @@ own resources. The `init` hook will trigger when an `AsyncResource` is instantiated. -*Note*: `before` and `after` calls must be unwound in the same order that they -are called. Otherwise, an unrecoverable exception will occur and the process -will abort. - The following is an overview of the `AsyncResource` API. ```js @@ -615,11 +611,13 @@ const asyncResource = new AsyncResource( type, { triggerAsyncId: executionAsyncId(), requireManualDestroy: false } ); -// Call AsyncHooks before callbacks. -asyncResource.emitBefore(); - -// Call AsyncHooks after callbacks. -asyncResource.emitAfter(); +// Run a function in the execution context of the resource. This will +// * establish the context of the resource +// * trigger the AsyncHooks before callbacks +// * call the provided function `fn` with the supplied arguments +// * trigger the AsyncHooks after callbacks +// * restore the original execution context +asyncResource.runInAsyncScope(fn, thisArg, ...args); // Call AsyncHooks destroy callbacks. asyncResource.emitDestroy(); @@ -629,6 +627,14 @@ asyncResource.asyncId(); // Return the trigger ID for the AsyncResource instance. asyncResource.triggerAsyncId(); + +// Call AsyncHooks before callbacks. +// Deprecated: Use asyncResource.runInAsyncScope instead. +asyncResource.emitBefore(); + +// Call AsyncHooks after callbacks. +// Deprecated: Use asyncResource.runInAsyncScope instead. +asyncResource.emitAfter(); ``` #### `AsyncResource(type[, options])` @@ -654,9 +660,7 @@ class DBQuery extends AsyncResource { getInfo(query, callback) { this.db.get(query, (err, data) => { - this.emitBefore(); - callback(err, data); - this.emitAfter(); + this.runInAsyncScope(callback, null, err, data); }); } @@ -667,7 +671,26 @@ class DBQuery extends AsyncResource { } ``` +#### `asyncResource.runInAsyncScope(fn[, thisArg, ...args])` + + +* `fn` {Function} The function to call in the execution context of this async + resource. +* `thisArg` {any} The receiver to be used for the function call. +* `...args` {any} Optional arguments to pass to the function. + +Call the provided function with the provided arguments in the execution context +of the async resource. This will establish the context, trigger the AsyncHooks +before callbacks, call the function, trigger the AsyncHooks after callbacks, and +then restore the original execution context. + #### `asyncResource.emitBefore()` + +> Stability: 0 - Deprecated: Use [`asyncResource.runInAsyncScope()`][] instead. * Returns: {undefined} @@ -675,7 +698,17 @@ Call all `before` callbacks to notify that a new asynchronous execution context is being entered. If nested calls to `emitBefore()` are made, the stack of `asyncId`s will be tracked and properly unwound. +`before` and `after` calls must be unwound in the same order that they +are called. Otherwise, an unrecoverable exception will occur and the process +will abort. For this reason, the `emitBefore` and `emitAfter` APIs are +considered deprecated. Please use `runInAsyncScope`, as it provides a much safer +alternative. + #### `asyncResource.emitAfter()` + +> Stability: 0 - Deprecated: Use [`asyncResource.runInAsyncScope()`][] instead. * Returns: {undefined} @@ -686,6 +719,12 @@ If the user's callback throws an exception, `emitAfter()` will automatically be called for all `asyncId`s on the stack if the error is handled by a domain or `'uncaughtException'` handler. +`before` and `after` calls must be unwound in the same order that they +are called. Otherwise, an unrecoverable exception will occur and the process +will abort. For this reason, the `emitBefore` and `emitAfter` APIs are +considered deprecated. Please use `runInAsyncScope`, as it provides a much safer +alternative. + #### `asyncResource.emitDestroy()` * Returns: {undefined} @@ -705,6 +744,7 @@ never be called. constructor. [`after` callback]: #async_hooks_after_asyncid +[`asyncResource.runInAsyncScope()`]: #async_hooks_asyncresource_runinasyncscope_fn_thisarg_args [`before` callback]: #async_hooks_before_asyncid [`destroy` callback]: #async_hooks_destroy_asyncid [`init` callback]: #async_hooks_init_asyncid_type_triggerasyncid_resource diff --git a/doc/api/buffer.md b/doc/api/buffer.md index 1e5b23cb4b9982..439e9917803814 100644 --- a/doc/api/buffer.md +++ b/doc/api/buffer.md @@ -500,7 +500,6 @@ console.log(buf1.toString()); // Prints: this is a tC)st console.log(buf1.toString('ascii')); - const buf2 = new Buffer('7468697320697320612074c3a97374', 'hex'); // Prints: this is a tést @@ -908,7 +907,6 @@ console.log(buf1.toString()); // Prints: this is a tC)st console.log(buf1.toString('ascii')); - const buf2 = Buffer.from('7468697320697320612074c3a97374', 'hex'); // Prints: this is a tést @@ -1364,7 +1362,6 @@ console.log(buf.indexOf(Buffer.from('a buffer example'))); // Prints: 8 console.log(buf.indexOf(Buffer.from('a buffer example').slice(0, 8))); - const utf16Buffer = Buffer.from('\u039a\u0391\u03a3\u03a3\u0395', 'ucs2'); // Prints: 4 @@ -1475,7 +1472,6 @@ console.log(buf.lastIndexOf('buffer', 5)); // Prints: -1 console.log(buf.lastIndexOf('buffer', 4)); - const utf16Buffer = Buffer.from('\u039a\u0391\u03a3\u03a3\u0395', 'ucs2'); // Prints: 6 @@ -1998,7 +1994,6 @@ buf1.swap16(); // Prints: console.log(buf1); - const buf2 = Buffer.from([0x1, 0x2, 0x3]); // Throws an exception: RangeError: Buffer size must be a multiple of 16-bits @@ -2028,7 +2023,6 @@ buf1.swap32(); // Prints: console.log(buf1); - const buf2 = Buffer.from([0x1, 0x2, 0x3]); // Throws an exception: RangeError: Buffer size must be a multiple of 32-bits @@ -2058,7 +2052,6 @@ buf1.swap64(); // Prints: console.log(buf1); - const buf2 = Buffer.from([0x1, 0x2, 0x3]); // Throws an exception: RangeError: Buffer size must be a multiple of 64-bits @@ -2130,7 +2123,6 @@ console.log(buf1.toString('ascii')); // Prints: abcde console.log(buf1.toString('ascii', 0, 5)); - const buf2 = Buffer.from('tést'); // Prints: 74c3a97374 diff --git a/doc/api/child_process.md b/doc/api/child_process.md index 7ae5fd3d751e6a..b3a2864c67a9f8 100644 --- a/doc/api/child_process.md +++ b/doc/api/child_process.md @@ -620,6 +620,10 @@ pipes between the parent and child. The value is one of the following: will enable [`process.send()`][], [`process.disconnect()`][], [`process.on('disconnect')`][], and [`process.on('message')`] within the child. + + Accessing the IPC channel fd in any way other than [`process.send()`][] + or using the IPC channel with a child process that is not a Node.js instance + is not supported. 3. `'ignore'` - Instructs Node.js to ignore the fd in the child. While Node.js will always open fds 0 - 2 for the processes it spawns, setting the fd to `'ignore'` will cause Node.js to open `/dev/null` and attach it to the diff --git a/doc/api/crypto.md b/doc/api/crypto.md index ac1c709429ff36..00128cebfb8154 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -42,7 +42,10 @@ added: v0.11.8 --> SPKAC is a Certificate Signing Request mechanism originally implemented by -Netscape and now specified formally as part of [HTML5's `keygen` element][]. +Netscape and was specified formally as part of [HTML5's `keygen` element][]. + +Note that `` is deprecated since [HTML 5.2][] and new projects +should not use this element anymore. The `crypto` module provides the `Certificate` class for working with SPKAC data. The most common usage is handling output generated by the HTML5 @@ -1268,6 +1271,9 @@ vulnerabilities. For the case when IV is reused in GCM, see [Nonce-Disrespecting Adversaries][] for details. ### crypto.createCipheriv(algorithm, key, iv[, options]) + - `algorithm` {string} - `key` {string | Buffer | TypedArray | DataView} - `iv` {string | Buffer | TypedArray | DataView} @@ -1343,8 +1349,8 @@ recent OpenSSL releases, `openssl list-cipher-algorithms` will display the available cipher algorithms. The `key` is the raw key used by the `algorithm` and `iv` is an -[initialization vector][]. Both arguments must be `'utf8'` encoded strings or -[buffers][`Buffer`]. +[initialization vector][]. Both arguments must be `'utf8'` encoded strings, +[Buffers][`Buffer`], `TypedArray`, or `DataView`s. ### crypto.createDiffieHellman(prime[, primeEncoding][, generator][, generatorEncoding]) - -> Stability: 1 - Experimental - For most `fs` module functions, the `path` or `filename` argument may be passed as a WHATWG [`URL`][] object. Only [`URL`][] objects using the `file:` protocol are supported. @@ -1397,6 +1394,7 @@ fs.open('myfile', 'wx', (err, fd) => { fs.exists('myfile', (exists) => { if (exists) { fs.open('myfile', 'r', (err, fd) => { + if (err) throw err; readMyData(fd); }); } else { @@ -2738,8 +2736,21 @@ changes: * `callback` {Function} * `err` {Error} -Asynchronous unlink(2). No arguments other than a possible exception are given -to the completion callback. +Asynchronously removes a file or symbolic link. No arguments other than a +possible exception are given to the completion callback. + +```js +// Assuming that 'path/file.txt' is a regular file. +fs.unlink('path/file.txt', (err) => { + if (err) throw err; + console.log('path/file.txt was deleted'); +}); +``` + +`fs.unlink()` will not work on a directory, empty or otherwise. To remove a +directory, use [`fs.rmdir()`][]. + +See also: unlink(2) ## fs.unlinkSync(path) -An Agent object for HTTPS similar to [`http.Agent`][]. See [`https.request()`][] +An [`Agent`][] object for HTTPS similar to [`http.Agent`][]. See [`https.request()`][] for more information. ## Class: https.Server @@ -168,9 +168,10 @@ changes: Makes a request to a secure web server. -The following additional `options` from [`tls.connect()`][] are also accepted -when using a custom [`Agent`][]: `ca`, `cert`, `ciphers`, `clientCertEngine`, -`key`, `passphrase`, `pfx`, `rejectUnauthorized`, `secureProtocol`, `servername` +The following additional `options` from [`tls.connect()`][] are also accepted: +`ca`, `cert`, `ciphers`, `clientCertEngine`, `crl`, `dhparam`, `ecdhCurve`, +`honorCipherOrder`, `key`, `passphrase`, `pfx`, `rejectUnauthorized`, +`secureOptions`, `secureProtocol`, `servername`, `sessionIdContext` `options` can be an object, a string, or a [`URL`][] object. If `options` is a string, it is automatically parsed with [`url.parse()`][]. If it is a [`URL`][] @@ -220,7 +221,7 @@ const req = https.request(options, (res) => { }); ``` -Alternatively, opt out of connection pooling by not using an `Agent`. +Alternatively, opt out of connection pooling by not using an [`Agent`][]. Example: diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 38cf7a88420be5..1b94e71d1a1ade 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -554,10 +554,10 @@ NAPI_NO_RETURN void napi_fatal_error(const char* location, - `[in] location`: Optional location at which the error occurred. - `[in] location_len`: The length of the location in bytes, or -NAPI_AUTO_LENGTH if it is null-terminated. +`NAPI_AUTO_LENGTH` if it is null-terminated. - `[in] message`: The message associated with the error. - `[in] message_len`: The length of the message in bytes, or -NAPI_AUTO_LENGTH if it is +`NAPI_AUTO_LENGTH` if it is null-terminated. The function call does not return, the process will be terminated. @@ -918,7 +918,7 @@ For example, to set a function to be returned by the `require()` for the addon: napi_value Init(napi_env env, napi_value exports) { napi_value method; napi_status status; - status = napi_create_function(env, "exports", Method, NULL, &method); + status = napi_create_function(env, "exports", NAPI_AUTO_LENGTH, Method, NULL, &method); if (status != napi_ok) return NULL; return method; } @@ -1255,7 +1255,7 @@ napi_status napi_create_function(napi_env env, - `[in] utf8name`: A string representing the name of the function encoded as UTF8. - `[in] length`: The length of the utf8name in bytes, or -NAPI_AUTO_LENGTH if it is null-terminated. +`NAPI_AUTO_LENGTH` if it is null-terminated. - `[in] cb`: A function pointer to the native function to be invoked when the created function is invoked from JavaScript. - `[in] data`: Optional arbitrary context data to be passed into the native @@ -1483,7 +1483,7 @@ napi_status napi_create_string_latin1(napi_env env, - `[in] env`: The environment that the API is invoked under. - `[in] str`: Character buffer representing a ISO-8859-1-encoded string. - `[in] length`: The length of the string in bytes, or -NAPI_AUTO_LENGTH if it is null-terminated. +`NAPI_AUTO_LENGTH` if it is null-terminated. - `[out] result`: A `napi_value` representing a JavaScript String. Returns `napi_ok` if the API succeeded. @@ -1507,7 +1507,7 @@ napi_status napi_create_string_utf16(napi_env env, - `[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 -NAPI_AUTO_LENGTH if it is null-terminated. +`NAPI_AUTO_LENGTH` if it is null-terminated. - `[out] result`: A `napi_value` representing a JavaScript String. Returns `napi_ok` if the API succeeded. @@ -1530,7 +1530,7 @@ napi_status napi_create_string_utf8(napi_env env, - `[in] env`: The environment that the API is invoked under. - `[in] str`: Character buffer representing a UTF8-encoded string. -- `[in] length`: The length of the string in bytes, or NAPI_AUTO_LENGTH +- `[in] length`: The length of the string in bytes, or `NAPI_AUTO_LENGTH` if it is null-terminated. - `[out] result`: A `napi_value` representing a JavaScript String. @@ -3053,7 +3053,7 @@ napi_status napi_define_class(napi_env env, - `[in] utf8name`: Name of the JavaScript constructor function; this is not required to be the same as the C++ class name, though it is recommended for clarity. - - `[in] length`: The length of the utf8name in bytes, or NAPI_AUTO_LENGTH + - `[in] length`: The length of the utf8name in bytes, or `NAPI_AUTO_LENGTH` if it is null-terminated. - `[in] constructor`: Callback function that handles constructing instances of the class. (This should be a static method on the class, not an actual diff --git a/doc/api/url.md b/doc/api/url.md index d015b4e35ce077..38cb95d6097ae9 100644 --- a/doc/api/url.md +++ b/doc/api/url.md @@ -1030,6 +1030,11 @@ The formatting process operates as follows: ### url.parse(urlString[, parseQueryString[, slashesDenoteHost]]) * `urlString` {string} The URL string to parse. diff --git a/doc/api/vm.md b/doc/api/vm.md index a26ee4ed94d090..26ec6d62270aca 100644 --- a/doc/api/vm.md +++ b/doc/api/vm.md @@ -43,6 +43,318 @@ console.log(x); // 1; y is not defined. *Note*: The vm module is not a security mechanism. **Do not use it to run untrusted code**. +## Class: vm.Module + + +> Stability: 1 - Experimental + +*This feature is only available with the `--experimental-vm-modules` command +flag enabled.* + +The `vm.Module` class provides a low-level interface for using ECMAScript +modules in VM contexts. It is the counterpart of the `vm.Script` class that +closely mirrors [Source Text Module Record][]s as defined in the ECMAScript +specification. + +Unlike `vm.Script` however, every `vm.Module` object is bound to a context from +its creation. Operations on `vm.Module` objects are intrinsically asynchronous, +in contrast with the synchronous nature of `vm.Script` objects. With the help +of async functions, however, manipulating `vm.Module` objects is fairly +straightforward. + +Using a `vm.Module` object requires four distinct steps: creation/parsing, +linking, instantiation, and evaluation. These four steps are illustrated in the +following example. + +*Note*: This implementation lies at a lower level than the [ECMAScript Module +loader][]. There is also currently no way to interact with the Loader, though +support is planned. + +```js +const vm = require('vm'); + +const contextifiedSandbox = vm.createContext({ secret: 42 }); + +(async () => { + // Step 1 + // + // Create a Module by constructing a new `vm.Module` object. This parses the + // provided source text, throwing a `SyntaxError` if anything goes wrong. By + // default, a Module is created in the top context. But here, we specify + // `contextifiedSandbox` as the context this Module belongs to. + // + // Here, we attempt to obtain the default export from the module "foo", and + // put it into local binding "secret". + + const bar = new vm.Module(` + import s from 'foo'; + s; + `, { context: contextifiedSandbox }); + + // Step 2 + // + // "Link" the imported dependencies of this Module to it. + // + // The provided linking callback (the "linker") accepts two arguments: the + // parent module (`bar` in this case) and the string that is the specifier of + // the imported module. The callback is expected to return a Module that + // corresponds to the provided specifier, with certain requirements documented + // in `module.link()`. + // + // If linking has not started for the returned Module, the same linker + // callback will be called on the returned Module. + // + // Even top-level Modules without dependencies must be explicitly linked. The + // callback provided would never be called, however. + // + // The link() method returns a Promise that will be resolved when all the + // Promises returned by the linker resolve. + // + // Note: This is a contrived example in that the linker function creates a new + // "foo" module every time it is called. In a full-fledged module system, a + // cache would probably be used to avoid duplicated modules. + + async function linker(specifier, referencingModule) { + if (specifier === 'foo') { + return new vm.Module(` + // The "secret" variable refers to the global variable we added to + // "contextifiedSandbox" when creating the context. + export default secret; + `, { context: referencingModule.context }); + + // Using `contextifiedSandbox` instead of `referencingModule.context` + // here would work as well. + } + throw new Error(`Unable to resolve dependency: ${specifier}`); + } + await bar.link(linker); + + // Step 3 + // + // Instantiate the top-level Module. + // + // Only the top-level Module needs to be explicitly instantiated; its + // dependencies will be recursively instantiated by instantiate(). + + bar.instantiate(); + + // Step 4 + // + // Evaluate the Module. The evaluate() method returns a Promise with a single + // property "result" that contains the result of the very last statement + // executed in the Module. In the case of `bar`, it is `s;`, which refers to + // the default export of the `foo` module, the `secret` we set in the + // beginning to 42. + + const { result } = await bar.evaluate(); + + console.log(result); + // Prints 42. +})(); +``` + +### Constructor: new vm.Module(code[, options]) + +* `code` {string} JavaScript Module code to parse +* `options` + * `url` {string} URL used in module resolution and stack traces. **Default**: + `'vm:module(i)'` where `i` is a context-specific ascending index. + * `context` {Object} The [contextified][] object as returned by the + `vm.createContext()` method, to compile and evaluate this Module in. + * `lineOffset` {integer} Specifies the line number offset that is displayed + in stack traces produced by this Module. + * `columnOffset` {integer} Spcifies the column number offset that is displayed + in stack traces produced by this Module. + +Creates a new ES `Module` object. + +### module.dependencySpecifiers + +* {string[]} + +The specifiers of all dependencies of this module. The returned array is frozen +to disallow any changes to it. + +Corresponds to the [[RequestedModules]] field of [Source Text Module Record][]s +in the ECMAScript specification. + +### module.error + +* {any} + +If the `module.status` is `'errored'`, this property contains the exception thrown +by the module during evaluation. If the status is anything else, accessing this +property will result in a thrown exception. + +*Note*: `undefined` cannot be used for cases where there is not a thrown +exception due to possible ambiguity with `throw undefined;`. + +Corresponds to the [[EvaluationError]] field of [Source Text Module Record][]s +in the ECMAScript specification. + +### module.linkingStatus + +* {string} + +The current linking status of `module`. It will be one of the following values: + +- `'unlinked'`: `module.link()` has not yet been called. +- `'linking'`: `module.link()` has been called, but not all Promises returned by + the linker function have been resolved yet. +- `'linked'`: `module.link()` has been called, and all its dependencies have + been successfully linked. +- `'errored'`: `module.link()` has been called, but at least one of its + dependencies failed to link, either because the callback returned a Promise + that is rejected, or because the Module the callback returned is invalid. + +### module.namespace + +* {Object} + +The namespace object of the module. This is only available after instantiation +(`module.instantiate()`) has completed. + +Corresponds to the [GetModuleNamespace][] abstract operation in the ECMAScript +specification. + +### module.status + +* {string} + +The current status of the module. Will be one of: + +- `'uninstantiated'`: The module is not instantiated. It may because of any of + the following reasons: + + - The module was just created. + - `module.instantiate()` has been called on this module, but it failed for + some reason. + + This status does not convey any information regarding if `module.link()` has + been called. See `module.linkingStatus` for that. + +- `'instantiating'`: The module is currently being instantiated through a + `module.instantiate()` call on itself or a parent module. + +- `'instantiated'`: The module has been instantiated successfully, but + `module.evaluate()` has not yet been called. + +- `'evaluating'`: The module is being evaluated through a `module.evaluate()` on + itself or a parent module. + +- `'evaluated'`: The module has been successfully evaluated. + +- `'errored'`: The module has been evaluated, but an exception was thrown. + +Other than `'errored'`, this status string corresponds to the specification's +[Source Text Module Record][]'s [[Status]] field. `'errored'` corresponds to +`'evaluated'` in the specification, but with [[EvaluationError]] set to a value +that is not `undefined`. + +### module.url + +* {string} + +The URL of the current module, as set in the constructor. + +### module.evaluate([options]) + +* `options` {Object} + * `timeout` {number} Specifies the number of milliseconds to evaluate + before terminating execution. If execution is interrupted, an [`Error`][] + will be thrown. + * `breakOnSigint` {boolean} If `true`, the execution will be terminated when + `SIGINT` (Ctrl+C) is received. Existing handlers for the event that have + been attached via `process.on("SIGINT")` will be disabled during script + execution, but will continue to work after that. If execution is + interrupted, an [`Error`][] will be thrown. +* Returns: {Promise} + +Evaluate the module. + +This must be called after the module has been instantiated; otherwise it will +throw an error. It could be called also when the module has already been +evaluated, in which case it will do one of the following two things: + +- return `undefined` if the initial evaluation ended in success (`module.status` + is `'evaluated'`) +- rethrow the same exception the initial evaluation threw if the initial + evaluation ended in an error (`module.status` is `'errored'`) + +This method cannot be called while the module is being evaluated +(`module.status` is `'evaluating'`) to prevent infinite recursion. + +Corresponds to the [Evaluate() concrete method][] field of [Source Text Module +Record][]s in the ECMAScript specification. + +### module.instantiate() + +Instantiate the module. This must be called after linking has completed +(`linkingStatus` is `'linked'`); otherwise it will throw an error. It may also +throw an exception if one of the dependencies does not provide an export the +parent module requires. + +However, if this function succeeded, further calls to this function after the +initial instantiation will be no-ops, to be consistent with the ECMAScript +specification. + +Unlike other methods operating on `Module`, this function completes +synchronously and returns nothing. + +Corresponds to the [Instantiate() concrete method][] field of [Source Text +Module Record][]s in the ECMAScript specification. + +### module.link(linker) + +* `linker` {Function} +* Returns: {Promise} + +Link module dependencies. This method must be called before instantiation, and +can only be called once per module. + +Two parameters will be passed to the `linker` function: + +- `specifier` The specifier of the requested module: + + ```js + import foo from 'foo'; + // ^^^^^ the module specifier + ``` +- `referencingModule` The `Module` object `link()` is called on. + +The function is expected to return a `Module` object or a `Promise` that +eventually resolves to a `Module` object. The returned `Module` must satisfy the +following two invariants: + +- It must belong to the same context as the parent `Module`. +- Its `linkingStatus` must not be `'errored'`. + +If the returned `Module`'s `linkingStatus` is `'unlinked'`, this method will be +recursively called on the returned `Module` with the same provided `linker` +function. + +`link()` returns a `Promise` that will either get resolved when all linking +instances resolve to a valid `Module`, or rejected if the linker function either +throws an exception or returns an invalid `Module`. + +The linker function roughly corresponds to the implementation-defined +[HostResolveImportedModule][] abstract operation in the ECMAScript +specification, with a few key differences: + +- The linker function is allowed to be asynchronous while + [HostResolveImportedModule][] is synchronous. +- The linker function is executed during linking, a Node.js-specific stage + before instantiation, while [HostResolveImportedModule][] is called during + instantiation. + +The actual [HostResolveImportedModule][] implementation used during module +instantiation is one that returns the modules linked during linking. Since at +that point all modules would have been fully linked already, the +[HostResolveImportedModule][] implementation is fully synchronous per +specification. + ## Class: vm.Script ```js diff --git a/test/common/countdown.js b/test/common/countdown.js index 93bdbbfb16da5d..5fcb77c4ed66a0 100644 --- a/test/common/countdown.js +++ b/test/common/countdown.js @@ -4,13 +4,14 @@ const assert = require('assert'); const kLimit = Symbol('limit'); const kCallback = Symbol('callback'); +const common = require('./'); class Countdown { constructor(limit, cb) { assert.strictEqual(typeof limit, 'number'); assert.strictEqual(typeof cb, 'function'); this[kLimit] = limit; - this[kCallback] = cb; + this[kCallback] = common.mustCall(cb); } dec() { diff --git a/test/common/index.js b/test/common/index.js index b4aa51b1f8d171..30b6aca88e05e3 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -54,6 +54,7 @@ exports.isLinuxPPCBE = (process.platform === 'linux') && (os.endianness() === 'BE'); exports.isSunOS = process.platform === 'sunos'; exports.isFreeBSD = process.platform === 'freebsd'; +exports.isOpenBSD = process.platform === 'openbsd'; exports.isLinux = process.platform === 'linux'; exports.isOSX = process.platform === 'darwin'; @@ -493,6 +494,14 @@ exports.fileExists = function(pathname) { } }; +exports.skipIfEslintMissing = function() { + if (!exports.fileExists( + path.join('..', '..', 'tools', 'node_modules', 'eslint') + )) { + exports.skip('missing ESLint'); + } +}; + exports.canCreateSymLink = function() { // On Windows, creating symlinks requires admin privileges. // We'll only try to run symlink test if we have enough privileges. @@ -501,7 +510,7 @@ exports.canCreateSymLink = function() { // whoami.exe needs to be the one from System32 // If unix tools are in the path, they can shadow the one we want, // so use the full path while executing whoami - const whoamiPath = path.join(process.env['SystemRoot'], + const whoamiPath = path.join(process.env.SystemRoot, 'System32', 'whoami.exe'); let err = false; diff --git a/test/common/inspector-helper.js b/test/common/inspector-helper.js index 52e9d01b87dd7e..de0723933f573a 100644 --- a/test/common/inspector-helper.js +++ b/test/common/inspector-helper.js @@ -168,9 +168,7 @@ class InspectorSession { reject(message.error); } else { if (message.method === 'Debugger.scriptParsed') { - const script = message['params']; - const scriptId = script['scriptId']; - const url = script['url']; + const { scriptId, url } = message.params; this._scriptsIdsByUrl.set(scriptId, url); const fileUrl = url.startsWith('file:') ? url : getURLFromFilePath(url).toString(); @@ -192,12 +190,12 @@ class InspectorSession { _sendMessage(message) { const msg = JSON.parse(JSON.stringify(message)); // Clone! - msg['id'] = this._nextId++; + msg.id = this._nextId++; if (DEBUG) console.log('[sent]', JSON.stringify(msg)); const responsePromise = new Promise((resolve, reject) => { - this._commandResponsePromises.set(msg['id'], { resolve, reject }); + this._commandResponsePromises.set(msg.id, { resolve, reject }); }); return new Promise( @@ -243,14 +241,14 @@ class InspectorSession { } _isBreakOnLineNotification(message, line, expectedScriptPath) { - if ('Debugger.paused' === message['method']) { - const callFrame = message['params']['callFrames'][0]; - const location = callFrame['location']; - const scriptPath = this._scriptsIdsByUrl.get(location['scriptId']); + if ('Debugger.paused' === message.method) { + const callFrame = message.params.callFrames[0]; + const location = callFrame.location; + const scriptPath = this._scriptsIdsByUrl.get(location.scriptId); assert.strictEqual(scriptPath.toString(), expectedScriptPath.toString(), `${scriptPath} !== ${expectedScriptPath}`); - assert.strictEqual(line, location['lineNumber']); + assert.strictEqual(line, location.lineNumber); return true; } } @@ -266,12 +264,12 @@ class InspectorSession { _matchesConsoleOutputNotification(notification, type, values) { if (!Array.isArray(values)) values = [ values ]; - if ('Runtime.consoleAPICalled' === notification['method']) { - const params = notification['params']; - if (params['type'] === type) { + 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++]) + for (const value of params.args) { + if (value.value !== values[i++]) return false; } return i === values.length; @@ -392,7 +390,7 @@ class NodeInstance { async sendUpgradeRequest() { const response = await this.httpGet(null, '/json/list'); - const devtoolsUrl = response[0]['webSocketDebuggerUrl']; + const devtoolsUrl = response[0].webSocketDebuggerUrl; const port = await this.portPromise; return http.get({ port, diff --git a/test/common/shared-lib-util.js b/test/common/shared-lib-util.js new file mode 100644 index 00000000000000..7ff7518ac31e6d --- /dev/null +++ b/test/common/shared-lib-util.js @@ -0,0 +1,29 @@ +/* eslint-disable required-modules */ +'use strict'; +const path = require('path'); + +// If node executable is linked to shared lib, need to take care about the +// shared lib path. +exports.addLibraryPath = function(env) { + if (!process.config.variables.node_shared) { + return; + } + + env = env || process.env; + + env.LD_LIBRARY_PATH = + (env.LD_LIBRARY_PATH ? env.LD_LIBRARY_PATH + path.delimiter : '') + + path.join(path.dirname(process.execPath), 'lib.target'); + // For AIX. + env.LIBPATH = + (env.LIBPATH ? env.LIBPATH + path.delimiter : '') + + path.join(path.dirname(process.execPath), 'lib.target'); + // For Mac OSX. + env.DYLD_LIBRARY_PATH = + (env.DYLD_LIBRARY_PATH ? env.DYLD_LIBRARY_PATH + path.delimiter : '') + + path.dirname(process.execPath); + // For Windows. + env.PATH = + (env.PATH ? env.PATH + path.delimiter : '') + + path.dirname(process.execPath); +}; diff --git a/test/fixtures/failcounter.js b/test/fixtures/failcounter.js new file mode 100644 index 00000000000000..f3bc34a7308334 --- /dev/null +++ b/test/fixtures/failcounter.js @@ -0,0 +1,2 @@ +const Countdown = require('../common/countdown'); +new Countdown(2, () => {}); diff --git a/test/fixtures/packages/is-dir/package.json/.placeholder b/test/fixtures/packages/is-dir/package.json/.placeholder new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/test/parallel/test-async-hooks-promise.js b/test/parallel/test-async-hooks-promise.js index d712fd616c647b..4b36f6026b36c6 100644 --- a/test/parallel/test-async-hooks-promise.js +++ b/test/parallel/test-async-hooks-promise.js @@ -21,8 +21,8 @@ const a = Promise.resolve(42); const b = a.then(common.mustCall()); assert.strictEqual(initCalls[0].triggerId, 1); -assert.strictEqual(initCalls[0].resource.parentId, undefined); +assert.strictEqual(initCalls[0].resource.isChainedPromise, false); assert.strictEqual(initCalls[0].resource.promise, a); assert.strictEqual(initCalls[1].triggerId, initCalls[0].id); -assert.strictEqual(initCalls[1].resource.parentId, initCalls[0].id); +assert.strictEqual(initCalls[1].resource.isChainedPromise, true); assert.strictEqual(initCalls[1].resource.promise, b); diff --git a/test/parallel/test-async-hooks-recursive-stack-runInAsyncScope.js b/test/parallel/test-async-hooks-recursive-stack-runInAsyncScope.js new file mode 100644 index 00000000000000..bc4ac86e7f1ca1 --- /dev/null +++ b/test/parallel/test-async-hooks-recursive-stack-runInAsyncScope.js @@ -0,0 +1,20 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const async_hooks = require('async_hooks'); + +// This test verifies that the async ID stack can grow indefinitely. + +function recurse(n) { + const a = new async_hooks.AsyncResource('foobar'); + a.runInAsyncScope(() => { + assert.strictEqual(a.asyncId(), async_hooks.executionAsyncId()); + assert.strictEqual(a.triggerAsyncId(), async_hooks.triggerAsyncId()); + if (n >= 0) + recurse(n - 1); + assert.strictEqual(a.asyncId(), async_hooks.executionAsyncId()); + assert.strictEqual(a.triggerAsyncId(), async_hooks.triggerAsyncId()); + }); +} + +recurse(1000); diff --git a/test/parallel/test-benchmark-timers.js b/test/parallel/test-benchmark-timers.js index cca9ede3a01c41..4cbcb1f11b9327 100644 --- a/test/parallel/test-benchmark-timers.js +++ b/test/parallel/test-benchmark-timers.js @@ -7,6 +7,7 @@ const runBenchmark = require('../common/benchmark'); runBenchmark('timers', [ 'type=depth', + 'n=1', 'millions=0.000001', 'thousands=0.001' ], diff --git a/test/parallel/test-buffer-arraybuffer.js b/test/parallel/test-buffer-arraybuffer.js index 5ecf0279e02c88..41af1eecbea4b2 100644 --- a/test/parallel/test-buffer-arraybuffer.js +++ b/test/parallel/test-buffer-arraybuffer.js @@ -148,3 +148,6 @@ b.writeDoubleBE(11.11, 0, true); message: '"length" is outside of buffer bounds' }); } + +// Test an array like entry with the length set to NaN. +assert.deepStrictEqual(Buffer.from({ length: NaN }), Buffer.alloc(0)); diff --git a/test/parallel/test-child-process-exec-timeout.js b/test/parallel/test-child-process-exec-timeout.js index ed25d9bff825a9..e08aff908522f2 100644 --- a/test/parallel/test-child-process-exec-timeout.js +++ b/test/parallel/test-child-process-exec-timeout.js @@ -16,15 +16,22 @@ const cmd = `"${process.execPath}" "${__filename}" child`; // Test the case where a timeout is set, and it expires. cp.exec(cmd, { timeout: 1 }, common.mustCall((err, stdout, stderr) => { + let sigterm = 'SIGTERM'; assert.strictEqual(err.killed, true); - assert.strictEqual(err.code, null); + // TODO OpenBSD returns a null signal and 143 for code + if (common.isOpenBSD) { + assert.strictEqual(err.code, 143); + sigterm = null; + } else { + assert.strictEqual(err.code, null); + } // At least starting with Darwin Kernel Version 16.4.0, sending a SIGTERM to a // process that is still starting up kills it with SIGKILL instead of SIGTERM. // See: https://github.com/libuv/libuv/issues/1226 if (common.isOSX) assert.ok(err.signal === 'SIGTERM' || err.signal === 'SIGKILL'); else - assert.strictEqual(err.signal, 'SIGTERM'); + assert.strictEqual(err.signal, sigterm); assert.strictEqual(err.cmd, cmd); assert.strictEqual(stdout.trim(), ''); assert.strictEqual(stderr.trim(), ''); diff --git a/test/parallel/test-child-process-fork-exec-path.js b/test/parallel/test-child-process-fork-exec-path.js index 42855cd663e826..8b94ef62a93bc8 100644 --- a/test/parallel/test-child-process-fork-exec-path.js +++ b/test/parallel/test-child-process-fork-exec-path.js @@ -28,6 +28,9 @@ const tmpdir = require('../common/tmpdir'); const msg = { test: 'this' }; const nodePath = process.execPath; const copyPath = path.join(tmpdir.path, 'node-copy.exe'); +const { addLibraryPath } = require('../common/shared-lib-util'); + +addLibraryPath(process.env); if (process.env.FORK) { assert(process.send); diff --git a/test/parallel/test-cluster-fork-env.js b/test/parallel/test-cluster-fork-env.js index 5dd28163084a21..57e7881013d0fd 100644 --- a/test/parallel/test-cluster-fork-env.js +++ b/test/parallel/test-cluster-fork-env.js @@ -31,8 +31,8 @@ const cluster = require('cluster'); if (cluster.isWorker) { const result = cluster.worker.send({ - prop: process.env['cluster_test_prop'], - overwrite: process.env['cluster_test_overwrite'] + prop: process.env.cluster_test_prop, + overwrite: process.env.cluster_test_overwrite }); assert.strictEqual(result, true); @@ -45,7 +45,7 @@ if (cluster.isWorker) { // To check that the cluster extend on the process.env we will overwrite a // property - process.env['cluster_test_overwrite'] = 'old'; + process.env.cluster_test_overwrite = 'old'; // Fork worker const worker = cluster.fork({ diff --git a/test/parallel/test-common-countdown.js b/test/parallel/test-common-countdown.js index ec0543f36fec74..6a1a2f1bbce98a 100644 --- a/test/parallel/test-common-countdown.js +++ b/test/parallel/test-common-countdown.js @@ -3,13 +3,31 @@ const common = require('../common'); const assert = require('assert'); const Countdown = require('../common/countdown'); +const fixtures = require('../common/fixtures'); +const { execFile } = require('child_process'); let done = ''; - -const countdown = new Countdown(2, common.mustCall(() => done = true)); +const countdown = new Countdown(2, () => done = true); assert.strictEqual(countdown.remaining, 2); countdown.dec(); assert.strictEqual(countdown.remaining, 1); countdown.dec(); assert.strictEqual(countdown.remaining, 0); assert.strictEqual(done, true); + +const failFixtures = [ + [ + fixtures.path('failcounter.js'), + 'Mismatched function calls. Expected exactly 1, actual 0.', + ] +]; + +for (const p of failFixtures) { + const [file, expected] = p; + execFile(process.argv[0], [file], common.mustCall((ex, stdout, stderr) => { + assert.ok(ex); + assert.strictEqual(stderr, ''); + const firstLine = stdout.split('\n').shift(); + assert.strictEqual(firstLine, expected); + })); +} diff --git a/test/parallel/test-crypto-hmac.js b/test/parallel/test-crypto-hmac.js index 6c2102fe23c82c..92fa16f98cda44 100644 --- a/test/parallel/test-crypto-hmac.js +++ b/test/parallel/test-crypto-hmac.js @@ -87,13 +87,13 @@ const wikipedia = [ ]; for (let i = 0, l = wikipedia.length; i < l; i++) { - for (const hash in wikipedia[i]['hmac']) { + for (const hash in wikipedia[i].hmac) { // FIPS does not support MD5. if (common.hasFipsCrypto && hash === 'md5') continue; - const expected = wikipedia[i]['hmac'][hash]; - const actual = crypto.createHmac(hash, wikipedia[i]['key']) - .update(wikipedia[i]['data']) + const expected = wikipedia[i].hmac[hash]; + const actual = crypto.createHmac(hash, wikipedia[i].key) + .update(wikipedia[i].data) .digest('hex'); assert.strictEqual( actual, @@ -252,18 +252,18 @@ const rfc4231 = [ ]; for (let i = 0, l = rfc4231.length; i < l; i++) { - for (const hash in rfc4231[i]['hmac']) { + for (const hash in rfc4231[i].hmac) { const str = crypto.createHmac(hash, rfc4231[i].key); str.end(rfc4231[i].data); let strRes = str.read().toString('hex'); - let actual = crypto.createHmac(hash, rfc4231[i]['key']) - .update(rfc4231[i]['data']) + let actual = crypto.createHmac(hash, rfc4231[i].key) + .update(rfc4231[i].data) .digest('hex'); - if (rfc4231[i]['truncate']) { + if (rfc4231[i].truncate) { actual = actual.substr(0, 32); // first 128 bits == 32 hex chars strRes = strRes.substr(0, 32); } - const expected = rfc4231[i]['hmac'][hash]; + const expected = rfc4231[i].hmac[hash]; assert.strictEqual( actual, expected, @@ -384,10 +384,10 @@ const rfc2202_sha1 = [ if (!common.hasFipsCrypto) { for (let i = 0, l = rfc2202_md5.length; i < l; i++) { - const actual = crypto.createHmac('md5', rfc2202_md5[i]['key']) - .update(rfc2202_md5[i]['data']) + const actual = crypto.createHmac('md5', rfc2202_md5[i].key) + .update(rfc2202_md5[i].data) .digest('hex'); - const expected = rfc2202_md5[i]['hmac']; + const expected = rfc2202_md5[i].hmac; assert.strictEqual( actual, expected, @@ -396,10 +396,10 @@ if (!common.hasFipsCrypto) { } } for (let i = 0, l = rfc2202_sha1.length; i < l; i++) { - const actual = crypto.createHmac('sha1', rfc2202_sha1[i]['key']) - .update(rfc2202_sha1[i]['data']) + const actual = crypto.createHmac('sha1', rfc2202_sha1[i].key) + .update(rfc2202_sha1[i].data) .digest('hex'); - const expected = rfc2202_sha1[i]['hmac']; + const expected = rfc2202_sha1[i].hmac; assert.strictEqual( actual, expected, diff --git a/test/parallel/test-dgram-createSocket-type.js b/test/parallel/test-dgram-createSocket-type.js index 54f3aaa0603621..19f4dad9208212 100644 --- a/test/parallel/test-dgram-createSocket-type.js +++ b/test/parallel/test-dgram-createSocket-type.js @@ -52,10 +52,12 @@ validTypes.forEach((validType) => { // note: linux will double the buffer size assert.ok(socket.getRecvBufferSize() === 10000 || socket.getRecvBufferSize() === 20000, - 'SO_RCVBUF not 1300 or 2600'); + 'SO_RCVBUF not 10000 or 20000, ' + + `was ${socket.getRecvBufferSize()}`); assert.ok(socket.getSendBufferSize() === 15000 || socket.getSendBufferSize() === 30000, - 'SO_SNDBUF not 1800 or 3600'); + 'SO_SNDBUF not 15000 or 30000, ' + + `was ${socket.getRecvBufferSize()}`); socket.close(); })); } diff --git a/test/parallel/test-domain-stack-empty-in-process-uncaughtexception.js b/test/parallel/test-domain-stack-empty-in-process-uncaughtexception.js index 79ded2dd9d1424..e9e8ab12d59d2c 100644 --- a/test/parallel/test-domain-stack-empty-in-process-uncaughtexception.js +++ b/test/parallel/test-domain-stack-empty-in-process-uncaughtexception.js @@ -6,15 +6,18 @@ const assert = require('assert'); const d = domain.create(); -process.on('uncaughtException', common.mustCall(function onUncaught() { +process.once('uncaughtException', common.mustCall(function onUncaught() { assert.strictEqual( process.domain, null, - 'domains stack should be empty in uncaughtException handler'); + 'Domains stack should be empty in uncaughtException handler ' + + `but the value of process.domain is ${JSON.stringify(process.domain)}`); })); process.on('beforeExit', common.mustCall(function onBeforeExit() { - assert.strictEqual(process.domain, null, - 'domains stack should be empty in beforeExit handler'); + assert.strictEqual( + process.domain, null, + 'Domains stack should be empty in beforeExit handler ' + + `but the value of process.domain is ${JSON.stringify(process.domain)}`); })); d.run(function() { diff --git a/test/parallel/test-emit-after-uncaught-exception-runInAsyncScope.js b/test/parallel/test-emit-after-uncaught-exception-runInAsyncScope.js new file mode 100644 index 00000000000000..5003972e9984aa --- /dev/null +++ b/test/parallel/test-emit-after-uncaught-exception-runInAsyncScope.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.runInAsyncScope(() => { + const ar2 = new async_hooks.AsyncResource('Mine'); + ar2.runInAsyncScope(() => { + 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? +})); diff --git a/test/parallel/test-eslint-crypto-check.js b/test/parallel/test-eslint-crypto-check.js index 325fb7a057c98b..86c28d21218154 100644 --- a/test/parallel/test-eslint-crypto-check.js +++ b/test/parallel/test-eslint-crypto-check.js @@ -1,6 +1,8 @@ 'use strict'; -require('../common'); +const common = require('../common'); + +common.skipIfEslintMissing(); const RuleTester = require('../../tools/node_modules/eslint').RuleTester; const rule = require('../../tools/eslint-rules/crypto-check'); @@ -13,20 +15,34 @@ new RuleTester().run('crypto-check', rule, { 'foo', 'crypto', ` - if (!common.hasCrypto) { - common.skip(); - } - require('crypto'); + if (!common.hasCrypto) { + common.skip("missing crypto"); + } + require("crypto"); ` ], invalid: [ { - code: 'require("crypto")', - errors: [{ message }] + code: 'require("common")\n' + + 'require("crypto")', + errors: [{ message }], + output: 'require("common")\n' + + 'if (!common.hasCrypto) {' + + ' common.skip("missing crypto");' + + '}\n' + + 'require("crypto")' }, { - code: 'if (common.foo) {} require("crypto")', - errors: [{ message }] + code: 'require("common")\n' + + 'if (common.foo) {}\n' + + 'require("crypto")', + errors: [{ message }], + output: 'require("common")\n' + + 'if (!common.hasCrypto) {' + + ' common.skip("missing crypto");' + + '}\n' + + 'if (common.foo) {}\n' + + 'require("crypto")' } ] }); diff --git a/test/parallel/test-eslint-inspector-check.js b/test/parallel/test-eslint-inspector-check.js index ab8314b63818c7..bdec596f8d128e 100644 --- a/test/parallel/test-eslint-inspector-check.js +++ b/test/parallel/test-eslint-inspector-check.js @@ -12,12 +12,18 @@ const message = 'Please add a skipIfInspectorDisabled() call to allow this ' + new RuleTester().run('inspector-check', rule, { valid: [ 'foo;', - 'common.skipIfInspectorDisabled(); require("inspector");' + 'require("common")\n' + + 'common.skipIfInspectorDisabled();\n' + + 'require("inspector")' ], invalid: [ { - code: 'require("inspector")', - errors: [{ message }] + code: 'require("common")\n' + + 'require("inspector")', + errors: [{ message }], + output: 'require("common")\n' + + 'common.skipIfInspectorDisabled();\n' + + 'require("inspector")' } ] }); diff --git a/test/parallel/test-eslint-lowercase-name-for-primitive.js b/test/parallel/test-eslint-lowercase-name-for-primitive.js index 3449095d6639f9..929ff03c656cd6 100644 --- a/test/parallel/test-eslint-lowercase-name-for-primitive.js +++ b/test/parallel/test-eslint-lowercase-name-for-primitive.js @@ -1,6 +1,8 @@ 'use strict'; -require('../common'); +const common = require('../common'); + +common.skipIfEslintMissing(); const RuleTester = require('../../tools/node_modules/eslint').RuleTester; const rule = require('../../tools/eslint-rules/lowercase-name-for-primitive'); diff --git a/test/parallel/test-eslint-no-let-in-for-declaration.js b/test/parallel/test-eslint-no-let-in-for-declaration.js index ea5b681854621c..ed8f306eea8ad5 100644 --- a/test/parallel/test-eslint-no-let-in-for-declaration.js +++ b/test/parallel/test-eslint-no-let-in-for-declaration.js @@ -1,6 +1,8 @@ 'use strict'; -require('../common'); +const common = require('../common'); + +common.skipIfEslintMissing(); const RuleTester = require('../../tools/node_modules/eslint').RuleTester; const rule = require('../../tools/eslint-rules/no-let-in-for-declaration'); diff --git a/test/parallel/test-eslint-no-unescaped-regexp-dot.js b/test/parallel/test-eslint-no-unescaped-regexp-dot.js index 38a9f3efcc6ff5..e6f09291b0838d 100644 --- a/test/parallel/test-eslint-no-unescaped-regexp-dot.js +++ b/test/parallel/test-eslint-no-unescaped-regexp-dot.js @@ -1,6 +1,8 @@ 'use strict'; -require('../common'); +const common = require('../common'); + +common.skipIfEslintMissing(); const RuleTester = require('../../tools/node_modules/eslint').RuleTester; const rule = require('../../tools/eslint-rules/no-unescaped-regexp-dot'); diff --git a/test/parallel/test-eslint-number-isnan.js b/test/parallel/test-eslint-number-isnan.js index 9f6af407e469a6..b290a48a8957e7 100644 --- a/test/parallel/test-eslint-number-isnan.js +++ b/test/parallel/test-eslint-number-isnan.js @@ -1,6 +1,8 @@ 'use strict'; -require('../common'); +const common = require('../common'); + +common.skipIfEslintMissing(); const RuleTester = require('../../tools/node_modules/eslint').RuleTester; const rule = require('../../tools/eslint-rules/number-isnan'); diff --git a/test/parallel/test-eslint-prefer-assert-iferror.js b/test/parallel/test-eslint-prefer-assert-iferror.js index 8a9723f4b21f6a..7dc3bbe7bcda4d 100644 --- a/test/parallel/test-eslint-prefer-assert-iferror.js +++ b/test/parallel/test-eslint-prefer-assert-iferror.js @@ -1,6 +1,8 @@ 'use strict'; -require('../common'); +const common = require('../common'); + +common.skipIfEslintMissing(); const RuleTester = require('../../tools/node_modules/eslint').RuleTester; const rule = require('../../tools/eslint-rules/prefer-assert-iferror'); diff --git a/test/parallel/test-eslint-prefer-assert-methods.js b/test/parallel/test-eslint-prefer-assert-methods.js index 2d05a4851bf781..3ad1cd5c376e9b 100644 --- a/test/parallel/test-eslint-prefer-assert-methods.js +++ b/test/parallel/test-eslint-prefer-assert-methods.js @@ -1,6 +1,8 @@ 'use strict'; -require('../common'); +const common = require('../common'); + +common.skipIfEslintMissing(); const RuleTester = require('../../tools/node_modules/eslint').RuleTester; const rule = require('../../tools/eslint-rules/prefer-assert-methods'); diff --git a/test/parallel/test-eslint-prefer-common-expectserror.js b/test/parallel/test-eslint-prefer-common-expectserror.js index 1fe7b1bc9761ac..e0d208b68bb743 100644 --- a/test/parallel/test-eslint-prefer-common-expectserror.js +++ b/test/parallel/test-eslint-prefer-common-expectserror.js @@ -1,6 +1,8 @@ 'use strict'; -require('../common'); +const common = require('../common'); + +common.skipIfEslintMissing(); const RuleTester = require('../../tools/node_modules/eslint').RuleTester; const rule = require('../../tools/eslint-rules/prefer-common-expectserror'); diff --git a/test/parallel/test-eslint-prefer-common-mustnotcall.js b/test/parallel/test-eslint-prefer-common-mustnotcall.js index c2e298cde2153c..e6e9d1d2f88332 100644 --- a/test/parallel/test-eslint-prefer-common-mustnotcall.js +++ b/test/parallel/test-eslint-prefer-common-mustnotcall.js @@ -1,6 +1,8 @@ 'use strict'; -require('../common'); +const common = require('../common'); + +common.skipIfEslintMissing(); const RuleTester = require('../../tools/node_modules/eslint').RuleTester; const rule = require('../../tools/eslint-rules/prefer-common-mustnotcall'); diff --git a/test/parallel/test-eslint-prefer-util-format-errors.js b/test/parallel/test-eslint-prefer-util-format-errors.js index 7ccbb8a1335dfa..560222438b9aee 100644 --- a/test/parallel/test-eslint-prefer-util-format-errors.js +++ b/test/parallel/test-eslint-prefer-util-format-errors.js @@ -2,7 +2,9 @@ /* eslint-disable no-template-curly-in-string */ -require('../common'); +const common = require('../common'); + +common.skipIfEslintMissing(); const RuleTester = require('../../tools/node_modules/eslint').RuleTester; const rule = require('../../tools/eslint-rules/prefer-util-format-errors'); diff --git a/test/parallel/test-eslint-require-buffer.js b/test/parallel/test-eslint-require-buffer.js index 23bf7c0afa05dd..ca2c44cb322515 100644 --- a/test/parallel/test-eslint-require-buffer.js +++ b/test/parallel/test-eslint-require-buffer.js @@ -1,6 +1,8 @@ 'use strict'; -require('../common'); +const common = require('../common'); + +common.skipIfEslintMissing(); const RuleTester = require('../../tools/node_modules/eslint').RuleTester; const rule = require('../../tools/eslint-rules/require-buffer'); diff --git a/test/parallel/test-eslint-required-modules.js b/test/parallel/test-eslint-required-modules.js index 1ef96521ec7037..dbdc135ccf633f 100644 --- a/test/parallel/test-eslint-required-modules.js +++ b/test/parallel/test-eslint-required-modules.js @@ -1,6 +1,8 @@ 'use strict'; -require('../common'); +const common = require('../common'); + +common.skipIfEslintMissing(); const RuleTester = require('../../tools/node_modules/eslint').RuleTester; const rule = require('../../tools/eslint-rules/required-modules'); diff --git a/test/parallel/test-event-emitter-check-listener-leaks.js b/test/parallel/test-event-emitter-check-listener-leaks.js index e24c07506c372e..7688c61a435cfb 100644 --- a/test/parallel/test-event-emitter-check-listener-leaks.js +++ b/test/parallel/test-event-emitter-check-listener-leaks.js @@ -32,9 +32,9 @@ const events = require('events'); for (let i = 0; i < 10; i++) { e.on('default', common.mustNotCall()); } - assert.ok(!e._events['default'].hasOwnProperty('warned')); + assert.ok(!e._events.default.hasOwnProperty('warned')); e.on('default', common.mustNotCall()); - assert.ok(e._events['default'].warned); + assert.ok(e._events.default.warned); // symbol const symbol = Symbol('symbol'); @@ -49,9 +49,9 @@ const events = require('events'); for (let i = 0; i < 5; i++) { e.on('specific', common.mustNotCall()); } - assert.ok(!e._events['specific'].hasOwnProperty('warned')); + assert.ok(!e._events.specific.hasOwnProperty('warned')); e.on('specific', common.mustNotCall()); - assert.ok(e._events['specific'].warned); + assert.ok(e._events.specific.warned); // only one e.setMaxListeners(1); @@ -65,7 +65,7 @@ const events = require('events'); for (let i = 0; i < 1000; i++) { e.on('unlimited', common.mustNotCall()); } - assert.ok(!e._events['unlimited'].hasOwnProperty('warned')); + assert.ok(!e._events.unlimited.hasOwnProperty('warned')); } // process-wide @@ -76,16 +76,16 @@ const events = require('events'); for (let i = 0; i < 42; ++i) { e.on('fortytwo', common.mustNotCall()); } - assert.ok(!e._events['fortytwo'].hasOwnProperty('warned')); + assert.ok(!e._events.fortytwo.hasOwnProperty('warned')); e.on('fortytwo', common.mustNotCall()); - assert.ok(e._events['fortytwo'].hasOwnProperty('warned')); - delete e._events['fortytwo'].warned; + assert.ok(e._events.fortytwo.hasOwnProperty('warned')); + delete e._events.fortytwo.warned; events.EventEmitter.defaultMaxListeners = 44; e.on('fortytwo', common.mustNotCall()); - assert.ok(!e._events['fortytwo'].hasOwnProperty('warned')); + assert.ok(!e._events.fortytwo.hasOwnProperty('warned')); e.on('fortytwo', common.mustNotCall()); - assert.ok(e._events['fortytwo'].hasOwnProperty('warned')); + assert.ok(e._events.fortytwo.hasOwnProperty('warned')); } // but _maxListeners still has precedence over defaultMaxListeners @@ -94,9 +94,9 @@ const events = require('events'); const e = new events.EventEmitter(); e.setMaxListeners(1); e.on('uno', common.mustNotCall()); - assert.ok(!e._events['uno'].hasOwnProperty('warned')); + assert.ok(!e._events.uno.hasOwnProperty('warned')); e.on('uno', common.mustNotCall()); - assert.ok(e._events['uno'].hasOwnProperty('warned')); + assert.ok(e._events.uno.hasOwnProperty('warned')); // chainable assert.strictEqual(e, e.setMaxListeners(1)); diff --git a/test/parallel/test-fs-readdir-stack-overflow.js b/test/parallel/test-fs-readdir-stack-overflow.js new file mode 100644 index 00000000000000..b7dea52cc37ec5 --- /dev/null +++ b/test/parallel/test-fs-readdir-stack-overflow.js @@ -0,0 +1,18 @@ +'use strict'; + +const common = require('../common'); + +const fs = require('fs'); + +function recurse() { + fs.readdirSync('.'); + recurse(); +} + +common.expectsError( + () => recurse(), + { + type: RangeError, + message: 'Maximum call stack size exceeded' + } +); diff --git a/test/parallel/test-fs-utimes.js b/test/parallel/test-fs-utimes.js index 103b4640961937..3dc0bb59def6a2 100644 --- a/test/parallel/test-fs-utimes.js +++ b/test/parallel/test-fs-utimes.js @@ -172,8 +172,8 @@ process.on('exit', function() { const path = `${tmpdir.path}/test-utimes-precision`; fs.writeFileSync(path, ''); -// test Y2K38 for all platforms [except 'arm', and 'SunOS'] -if (!process.arch.includes('arm') && !common.isSunOS) { +// test Y2K38 for all platforms [except 'arm', 'OpenBSD' and 'SunOS'] +if (!process.arch.includes('arm') && !common.isOpenBSD && !common.isSunOS) { // because 2 ** 31 doesn't look right // eslint-disable-next-line space-infix-ops const Y2K38_mtime = 2**31; diff --git a/test/parallel/test-http-after-connect.js b/test/parallel/test-http-after-connect.js index 324186bab72324..d209c82083e639 100644 --- a/test/parallel/test-http-after-connect.js +++ b/test/parallel/test-http-after-connect.js @@ -32,7 +32,7 @@ const server = http.createServer(common.mustCall((req, res) => { setTimeout(() => res.end(req.url), 50); }, 2)); -const countdown = new Countdown(2, common.mustCall(() => server.close())); +const countdown = new Countdown(2, () => server.close()); server.on('connect', common.mustCall((req, socket) => { socket.write('HTTP/1.1 200 Connection established\r\n\r\n'); diff --git a/test/parallel/test-http-agent-destroyed-socket.js b/test/parallel/test-http-agent-destroyed-socket.js index 7970ce8bae0fcb..39f4ebef5374c1 100644 --- a/test/parallel/test-http-agent-destroyed-socket.js +++ b/test/parallel/test-http-agent-destroyed-socket.js @@ -80,7 +80,7 @@ const server = http.createServer(common.mustCall((req, res) => { assert(request1.socket.destroyed); // assert not reusing the same socket, since it was destroyed. assert.notStrictEqual(request1.socket, request2.socket); - const countdown = new Countdown(2, common.mustCall(() => server.close())); + const countdown = new Countdown(2, () => server.close()); request2.socket.on('close', common.mustCall(() => countdown.dec())); response.on('end', common.mustCall(() => countdown.dec())); response.resume(); diff --git a/test/parallel/test-http-agent-maxsockets-regress-4050.js b/test/parallel/test-http-agent-maxsockets-regress-4050.js index 57a90e4b05c79f..eb1c95d5200694 100644 --- a/test/parallel/test-http-agent-maxsockets-regress-4050.js +++ b/test/parallel/test-http-agent-maxsockets-regress-4050.js @@ -17,7 +17,7 @@ const server = http.createServer(common.mustCall((req, res) => { res.end('hello world'); }, 6)); -const countdown = new Countdown(6, common.mustCall(() => server.close())); +const countdown = new Countdown(6, () => server.close()); function get(path, callback) { return http.get({ diff --git a/test/parallel/test-http-agent-maxsockets.js b/test/parallel/test-http-agent-maxsockets.js index 4d422f4a90b4f9..267f820e0435eb 100644 --- a/test/parallel/test-http-agent-maxsockets.js +++ b/test/parallel/test-http-agent-maxsockets.js @@ -26,13 +26,13 @@ function get(path, callback) { }, callback); } -const countdown = new Countdown(2, common.mustCall(() => { +const countdown = new Countdown(2, () => { const freepool = agent.freeSockets[Object.keys(agent.freeSockets)[0]]; assert.strictEqual(freepool.length, 2, `expect keep 2 free sockets, but got ${freepool.length}`); agent.destroy(); server.close(); -})); +}); function dec() { process.nextTick(() => countdown.dec()); diff --git a/test/parallel/test-http-automatic-headers.js b/test/parallel/test-http-automatic-headers.js index 5a6a8e524c76ee..5e99f1ee39dd6b 100644 --- a/test/parallel/test-http-automatic-headers.js +++ b/test/parallel/test-http-automatic-headers.js @@ -22,8 +22,8 @@ server.on('listening', common.mustCall(() => { assert.strictEqual(res.headers['x-date'], 'foo'); assert.strictEqual(res.headers['x-connection'], 'bar'); assert.strictEqual(res.headers['x-content-length'], 'baz'); - assert(res.headers['date']); - assert.strictEqual(res.headers['connection'], 'keep-alive'); + assert(res.headers.date); + assert.strictEqual(res.headers.connection, 'keep-alive'); assert.strictEqual(res.headers['content-length'], '0'); server.close(); agent.destroy(); diff --git a/test/parallel/test-http-client-abort.js b/test/parallel/test-http-client-abort.js index 71fd2ad64cabdd..f767189ea9d593 100644 --- a/test/parallel/test-http-client-abort.js +++ b/test/parallel/test-http-client-abort.js @@ -26,7 +26,7 @@ const Countdown = require('../common/countdown'); const N = 8; -const countdown = new Countdown(N, common.mustCall(() => server.close())); +const countdown = new Countdown(N, () => server.close()); const server = http.Server(common.mustCall((req, res) => { res.writeHead(200); @@ -37,9 +37,9 @@ const server = http.Server(common.mustCall((req, res) => { server.listen(0, common.mustCall(() => { const requests = []; - const reqCountdown = new Countdown(N, common.mustCall(() => { + const reqCountdown = new Countdown(N, () => { requests.forEach((req) => req.abort()); - })); + }); const options = { port: server.address().port }; diff --git a/test/parallel/test-http-client-parse-error.js b/test/parallel/test-http-client-parse-error.js index 60f1ee45c8efc1..cb4e3ff08434dd 100644 --- a/test/parallel/test-http-client-parse-error.js +++ b/test/parallel/test-http-client-parse-error.js @@ -25,7 +25,7 @@ const http = require('http'); const net = require('net'); const Countdown = require('../common/countdown'); -const countdown = new Countdown(2, common.mustCall(() => server.close())); +const countdown = new Countdown(2, () => server.close()); const payloads = [ 'HTTP/1.1 302 Object Moved\r\nContent-Length: 0\r\n\r\nhi world', diff --git a/test/parallel/test-http-dns-error.js b/test/parallel/test-http-dns-error.js index 900cf40e6b209b..06a15c89fb46b3 100644 --- a/test/parallel/test-http-dns-error.js +++ b/test/parallel/test-http-dns-error.js @@ -32,6 +32,10 @@ const https = require('https'); const host = '*'.repeat(256); const MAX_TRIES = 5; +let errCode = 'ENOTFOUND'; +if (common.isOpenBSD) + errCode = 'EAI_FAIL'; + function tryGet(mod, tries) { // Bad host name should not throw an uncatchable exception. // Ensure that there is time to attach an error listener. @@ -41,7 +45,7 @@ function tryGet(mod, tries) { tryGet(mod, ++tries); return; } - assert.strictEqual(err.code, 'ENOTFOUND'); + assert.strictEqual(err.code, errCode); })); // http.get() called req1.end() for us } @@ -57,7 +61,7 @@ function tryRequest(mod, tries) { tryRequest(mod, ++tries); return; } - assert.strictEqual(err.code, 'ENOTFOUND'); + assert.strictEqual(err.code, errCode); })); req.end(); } diff --git a/test/parallel/test-http-exceptions.js b/test/parallel/test-http-exceptions.js index 0b6ac5bdc1f7d0..03e30b67e18b57 100644 --- a/test/parallel/test-http-exceptions.js +++ b/test/parallel/test-http-exceptions.js @@ -21,7 +21,12 @@ 'use strict'; require('../common'); +const Countdown = require('../common/countdown'); const http = require('http'); +const NUMBER_OF_EXCEPTIONS = 4; +const countdown = new Countdown(NUMBER_OF_EXCEPTIONS, () => { + process.exit(0); +}); const server = http.createServer(function(req, res) { intentionally_not_defined(); // eslint-disable-line no-undef @@ -30,16 +35,16 @@ const server = http.createServer(function(req, res) { res.end(); }); +function onUncaughtException(err) { + console.log(`Caught an exception: ${err}`); + if (err.name === 'AssertionError') throw err; + countdown.dec(); +} + +process.on('uncaughtException', onUncaughtException); + server.listen(0, function() { - for (let i = 0; i < 4; i += 1) { + for (let i = 0; i < NUMBER_OF_EXCEPTIONS; i += 1) { http.get({ port: this.address().port, path: `/busy/${i}` }); } }); - -let exception_count = 0; - -process.on('uncaughtException', function(err) { - console.log(`Caught an exception: ${err}`); - if (err.name === 'AssertionError') throw err; - if (++exception_count === 4) process.exit(0); -}); diff --git a/test/parallel/test-http-flush-headers.js b/test/parallel/test-http-flush-headers.js index 8ca5e92e5e02bc..88e8bddaed9e54 100644 --- a/test/parallel/test-http-flush-headers.js +++ b/test/parallel/test-http-flush-headers.js @@ -5,7 +5,7 @@ const http = require('http'); const server = http.createServer(); server.on('request', function(req, res) { - assert.strictEqual(req.headers['foo'], 'bar'); + assert.strictEqual(req.headers.foo, 'bar'); res.end('ok'); server.close(); }); diff --git a/test/parallel/test-http-flush-response-headers.js b/test/parallel/test-http-flush-response-headers.js index b8045568d49ac7..0f0a1387b56733 100644 --- a/test/parallel/test-http-flush-response-headers.js +++ b/test/parallel/test-http-flush-response-headers.js @@ -20,7 +20,7 @@ server.listen(0, common.localhostIPv4, function() { req.end(); function onResponse(res) { - assert.strictEqual(res.headers['foo'], 'bar'); + assert.strictEqual(res.headers.foo, 'bar'); res.destroy(); server.close(); } diff --git a/test/parallel/test-http-proxy.js b/test/parallel/test-http-proxy.js index a6267faaa6715c..8781679c0a45b8 100644 --- a/test/parallel/test-http-proxy.js +++ b/test/parallel/test-http-proxy.js @@ -50,7 +50,7 @@ const proxy = http.createServer(function(req, res) { console.error(`proxy res headers: ${JSON.stringify(proxy_res.headers)}`); - assert.strictEqual('world', proxy_res.headers['hello']); + assert.strictEqual('world', proxy_res.headers.hello); assert.strictEqual('text/plain', proxy_res.headers['content-type']); assert.deepStrictEqual(cookies, proxy_res.headers['set-cookie']); @@ -81,7 +81,7 @@ function startReq() { console.error('got res'); assert.strictEqual(200, res.statusCode); - assert.strictEqual('world', res.headers['hello']); + assert.strictEqual('world', res.headers.hello); assert.strictEqual('text/plain', res.headers['content-type']); assert.deepStrictEqual(cookies, res.headers['set-cookie']); diff --git a/test/parallel/test-http-server-multiheaders.js b/test/parallel/test-http-server-multiheaders.js index 201a95c346ae65..bf918ad24cc168 100644 --- a/test/parallel/test-http-server-multiheaders.js +++ b/test/parallel/test-http-server-multiheaders.js @@ -38,7 +38,7 @@ const srv = http.createServer(function(req, res) { assert.strictEqual(req.headers['sec-websocket-protocol'], 'chat, share'); assert.strictEqual(req.headers['sec-websocket-extensions'], 'foo; 1, bar; 2, baz'); - assert.strictEqual(req.headers['constructor'], 'foo, bar, baz'); + assert.strictEqual(req.headers.constructor, 'foo, bar, baz'); res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('EOF'); diff --git a/test/parallel/test-http-write-head.js b/test/parallel/test-http-write-head.js index 2d1b8ff5504df0..0846168db98e93 100644 --- a/test/parallel/test-http-write-head.js +++ b/test/parallel/test-http-write-head.js @@ -68,7 +68,7 @@ s.listen(0, common.mustCall(runTest)); function runTest() { http.get({ port: this.address().port }, common.mustCall((response) => { response.on('end', common.mustCall(() => { - assert.strictEqual(response.headers['test'], '2'); + assert.strictEqual(response.headers.test, '2'); assert(response.rawHeaders.includes('Test')); s.close(); })); diff --git a/test/parallel/test-http.js b/test/parallel/test-http.js index 52bebc476e1510..e9a48c0fba3826 100644 --- a/test/parallel/test-http.js +++ b/test/parallel/test-http.js @@ -33,8 +33,8 @@ const server = http.Server(common.mustCall(function(req, res) { switch (req.url) { case '/hello': assert.strictEqual(req.method, 'GET'); - assert.strictEqual(req.headers['accept'], '*/*'); - assert.strictEqual(req.headers['foo'], 'bar'); + assert.strictEqual(req.headers.accept, '*/*'); + assert.strictEqual(req.headers.foo, 'bar'); assert.strictEqual(req.headers.cookie, 'foo=bar; bar=baz; baz=quux'); break; case '/there': diff --git a/test/parallel/test-http2-client-destroy.js b/test/parallel/test-http2-client-destroy.js index fab8a4fc24d652..eab413e2327d8f 100644 --- a/test/parallel/test-http2-client-destroy.js +++ b/test/parallel/test-http2-client-destroy.js @@ -59,7 +59,6 @@ const Countdown = require('../common/countdown'); assert(socket.destroyed); })); - const req = client.request(); req.on('error', common.expectsError({ code: 'ERR_HTTP2_STREAM_CANCEL', @@ -77,15 +76,21 @@ const Countdown = require('../common/countdown'); message: 'The session has been destroyed' }; - common.expectsError(() => client.request(), sessionError); + common.expectsError(() => client.setNextStreamID(), sessionError); + common.expectsError(() => client.ping(), sessionError); common.expectsError(() => client.settings({}), sessionError); + common.expectsError(() => client.goaway(), sessionError); + common.expectsError(() => client.request(), sessionError); client.close(); // should be a non-op at this point // Wait for setImmediate call from destroy() to complete // so that state.destroyed is set to true setImmediate(() => { - common.expectsError(() => client.request(), sessionError); + common.expectsError(() => client.setNextStreamID(), sessionError); + common.expectsError(() => client.ping(), sessionError); common.expectsError(() => client.settings({}), sessionError); + common.expectsError(() => client.goaway(), sessionError); + common.expectsError(() => client.request(), sessionError); client.close(); // should be a non-op at this point }); diff --git a/test/parallel/test-http2-client-settings-before-connect.js b/test/parallel/test-http2-client-settings-before-connect.js index 4642bf5220f554..ceea1bc39ad7d3 100644 --- a/test/parallel/test-http2-client-settings-before-connect.js +++ b/test/parallel/test-http2-client-settings-before-connect.js @@ -35,13 +35,26 @@ server.listen(0, common.mustCall(() => { ['enablePush', 0, TypeError], ['enablePush', null, TypeError], ['enablePush', {}, TypeError] - ].forEach((i) => { + ].forEach(([name, value, errorType]) => common.expectsError( - () => client.settings({ [i[0]]: i[1] }), + () => client.settings({ [name]: value }), { code: 'ERR_HTTP2_INVALID_SETTING_VALUE', - type: i[2] }); - }); + type: errorType + } + ) + ); + + [1, true, {}, []].forEach((invalidCallback) => + common.expectsError( + () => client.settings({}, invalidCallback), + { + type: TypeError, + code: 'ERR_INVALID_CALLBACK', + message: 'Callback must be a function' + } + ) + ); client.settings({ maxFrameSize: 1234567 }); diff --git a/test/parallel/test-http2-compat-expect-handling.js b/test/parallel/test-http2-compat-expect-handling.js index f36032c972fc45..e987118476337d 100644 --- a/test/parallel/test-http2-compat-expect-handling.js +++ b/test/parallel/test-http2-compat-expect-handling.js @@ -11,7 +11,7 @@ const expectValue = 'meoww'; const server = http2.createServer(common.mustNotCall()); server.once('checkExpectation', common.mustCall((req, res) => { - assert.strictEqual(req.headers['expect'], expectValue); + assert.strictEqual(req.headers.expect, expectValue); res.statusCode = 417; res.end(); })); diff --git a/test/parallel/test-http2-compat-serverresponse-createpushresponse.js b/test/parallel/test-http2-compat-serverresponse-createpushresponse.js index b168c3563c32e2..e0ce51bfc7ac50 100644 --- a/test/parallel/test-http2-compat-serverresponse-createpushresponse.js +++ b/test/parallel/test-http2-compat-serverresponse-createpushresponse.js @@ -77,7 +77,7 @@ server.listen(0, common.mustCall(() => { let actual = ''; pushStream.on('push', common.mustCall((headers) => { assert.strictEqual(headers[':status'], 200); - assert(headers['date']); + assert(headers.date); })); pushStream.setEncoding('utf8'); pushStream.on('data', (chunk) => actual += chunk); @@ -89,7 +89,7 @@ server.listen(0, common.mustCall(() => { req.on('response', common.mustCall((headers) => { assert.strictEqual(headers[':status'], 200); - assert(headers['date']); + assert(headers.date); })); let actual = ''; diff --git a/test/parallel/test-http2-create-client-secure-session.js b/test/parallel/test-http2-create-client-secure-session.js index b0111e15b69c15..1f20ec8e42a871 100644 --- a/test/parallel/test-http2-create-client-secure-session.js +++ b/test/parallel/test-http2-create-client-secure-session.js @@ -62,7 +62,7 @@ function verifySecureSession(key, cert, ca, opts) { req.on('response', common.mustCall((headers) => { assert.strictEqual(headers[':status'], 200); assert.strictEqual(headers['content-type'], 'application/json'); - assert(headers['date']); + assert(headers.date); })); let data = ''; diff --git a/test/parallel/test-http2-create-client-session.js b/test/parallel/test-http2-create-client-session.js index 963db2faa173b7..34e4e975d92d81 100644 --- a/test/parallel/test-http2-create-client-session.js +++ b/test/parallel/test-http2-create-client-session.js @@ -58,7 +58,7 @@ server.on('listening', common.mustCall(() => { assert.strictEqual(headers[':status'], 200, 'status code is set'); assert.strictEqual(headers['content-type'], 'text/html', 'content type is set'); - assert(headers['date'], 'there is a date'); + assert(headers.date); })); let data = ''; diff --git a/test/parallel/test-http2-multiheaders-raw.js b/test/parallel/test-http2-multiheaders-raw.js index 50486450d5aeb7..da9aa3a68eaa51 100644 --- a/test/parallel/test-http2-multiheaders-raw.js +++ b/test/parallel/test-http2-multiheaders-raw.js @@ -12,7 +12,7 @@ const src = Object.create(null); src['www-authenticate'] = 'foo'; src['WWW-Authenticate'] = 'bar'; src['WWW-AUTHENTICATE'] = 'baz'; -src['test'] = 'foo, bar, baz'; +src.test = 'foo, bar, baz'; server.on('stream', common.mustCall((stream, headers, flags, rawHeaders) => { const expected = [ diff --git a/test/parallel/test-http2-multiheaders.js b/test/parallel/test-http2-multiheaders.js index 9bf8f76d22e60e..6611dcf054d42a 100644 --- a/test/parallel/test-http2-multiheaders.js +++ b/test/parallel/test-http2-multiheaders.js @@ -24,21 +24,21 @@ src.constructor = 'foo'; src.Constructor = 'bar'; src.CONSTRUCTOR = 'baz'; // eslint-disable-next-line no-proto -src['__proto__'] = 'foo'; -src['__PROTO__'] = 'bar'; -src['__Proto__'] = 'baz'; +src.__proto__ = 'foo'; +src.__PROTO__ = 'bar'; +src.__Proto__ = 'baz'; function checkHeaders(headers) { - assert.deepStrictEqual(headers['accept'], + assert.deepStrictEqual(headers.accept, 'abc, def, ghijklmnop'); assert.deepStrictEqual(headers['www-authenticate'], 'foo, bar, baz'); assert.deepStrictEqual(headers['proxy-authenticate'], 'foo, bar, baz'); assert.deepStrictEqual(headers['x-foo'], 'foo, bar, baz'); - assert.deepStrictEqual(headers['constructor'], 'foo, bar, baz'); + assert.deepStrictEqual(headers.constructor, 'foo, bar, baz'); // eslint-disable-next-line no-proto - assert.deepStrictEqual(headers['__proto__'], 'foo, bar, baz'); + assert.deepStrictEqual(headers.__proto__, 'foo, bar, baz'); } server.on('stream', common.mustCall((stream, headers) => { diff --git a/test/parallel/test-http2-no-more-streams.js b/test/parallel/test-http2-no-more-streams.js index dd06a709f23023..ff0c8baa14ccdb 100644 --- a/test/parallel/test-http2-no-more-streams.js +++ b/test/parallel/test-http2-no-more-streams.js @@ -23,10 +23,10 @@ server.listen(0, common.mustCall(() => { assert.strictEqual(client.state.nextStreamID, nextID); - const countdown = new Countdown(2, common.mustCall(() => { + const countdown = new Countdown(2, () => { server.close(); client.close(); - })); + }); { // This one will be ok diff --git a/test/parallel/test-http2-options-server-request.js b/test/parallel/test-http2-options-server-request.js new file mode 100644 index 00000000000000..2143d379823d51 --- /dev/null +++ b/test/parallel/test-http2-options-server-request.js @@ -0,0 +1,40 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const h2 = require('http2'); + +class MyServerRequest extends h2.Http2ServerRequest { + getUserAgent() { + return this.headers['user-agent'] || 'unknown'; + } +} + +const server = h2.createServer({ + Http2ServerRequest: MyServerRequest +}, (req, res) => { + assert.strictEqual(req.getUserAgent(), 'node-test'); + + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end(); +}); +server.listen(0); + +server.on('listening', common.mustCall(() => { + + const client = h2.connect(`http://localhost:${server.address().port}`); + const req = client.request({ + ':path': '/', + 'User-Agent': 'node-test' + }); + + req.on('response', common.mustCall()); + + req.resume(); + req.on('end', common.mustCall(() => { + server.close(); + client.destroy(); + })); +})); diff --git a/test/parallel/test-http2-options-server-response.js b/test/parallel/test-http2-options-server-response.js new file mode 100644 index 00000000000000..6f1ae1881d22d8 --- /dev/null +++ b/test/parallel/test-http2-options-server-response.js @@ -0,0 +1,34 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const h2 = require('http2'); + +class MyServerResponse extends h2.Http2ServerResponse { + status(code) { + return this.writeHead(code, { 'Content-Type': 'text/plain' }); + } +} + +const server = h2.createServer({ + Http2ServerResponse: MyServerResponse +}, (req, res) => { + res.status(200); + res.end(); +}); +server.listen(0); + +server.on('listening', common.mustCall(() => { + + const client = h2.connect(`http://localhost:${server.address().port}`); + const req = client.request({ ':path': '/' }); + + req.on('response', common.mustCall()); + + req.resume(); + req.on('end', common.mustCall(() => { + server.close(); + client.destroy(); + })); +})); diff --git a/test/parallel/test-http2-ping.js b/test/parallel/test-http2-ping.js index 32fb8926e4716c..0280f656d065db 100644 --- a/test/parallel/test-http2-ping.js +++ b/test/parallel/test-http2-ping.js @@ -71,12 +71,60 @@ server.listen(0, common.mustCall(() => { assert.deepStrictEqual(payload, ret); }))); } + // Only max 2 pings at a time based on the maxOutstandingPings option assert(!client.ping(common.expectsError({ code: 'ERR_HTTP2_PING_CANCEL', type: Error, message: 'HTTP2 ping cancelled' }))); + + // should throw if payload is not of type ArrayBufferView + { + [1, true, {}, []].forEach((invalidPayload) => + common.expectsError( + () => client.ping(invalidPayload), + { + type: TypeError, + code: 'ERR_INVALID_ARG_TYPE', + message: 'The "payload" argument must be one of type' + + ' Buffer, TypedArray, or DataView' + } + ) + ); + } + + // should throw if payload length is not 8 + { + const shortPayload = Buffer.from('abcdefg'); + const longPayload = Buffer.from('abcdefghi'); + [shortPayload, longPayload].forEach((payloadWithInvalidLength) => + common.expectsError( + () => client.ping(payloadWithInvalidLength), + { + type: RangeError, + code: 'ERR_HTTP2_PING_LENGTH', + message: 'HTTP2 ping payload must be 8 bytes' + } + ) + ); + } + + // should throw error is callback is not of type function + { + const payload = Buffer.from('abcdefgh'); + [1, true, {}, []].forEach((invalidCallback) => + common.expectsError( + () => client.ping(payload, invalidCallback), + { + type: TypeError, + code: 'ERR_INVALID_CALLBACK', + message: 'Callback must be a function' + } + ) + ); + } + const req = client.request(); req.resume(); req.on('end', common.mustCall(() => { diff --git a/test/parallel/test-http2-server-rst-stream.js b/test/parallel/test-http2-server-rst-stream.js index c2d938c22f4483..4a58091aa61d7f 100644 --- a/test/parallel/test-http2-server-rst-stream.js +++ b/test/parallel/test-http2-server-rst-stream.js @@ -26,16 +26,16 @@ const tests = [ const server = http2.createServer(); server.on('stream', (stream, headers) => { - stream.close(headers['rstcode'] | 0); + stream.close(headers.rstcode | 0); }); server.listen(0, common.mustCall(() => { const client = http2.connect(`http://localhost:${server.address().port}`); - const countdown = new Countdown(tests.length, common.mustCall(() => { + const countdown = new Countdown(tests.length, () => { client.close(); server.close(); - })); + }); tests.forEach((test) => { const req = client.request({ diff --git a/test/parallel/test-http2-server-set-header.js b/test/parallel/test-http2-server-set-header.js index 4b6228053f8ece..83f373ec21b314 100644 --- a/test/parallel/test-http2-server-set-header.js +++ b/test/parallel/test-http2-server-set-header.js @@ -20,7 +20,7 @@ server.listen(0, common.mustCall(() => { const req = client.request(headers); req.setEncoding('utf8'); req.on('response', common.mustCall(function(headers) { - assert.strictEqual(headers['foobar'], 'baz'); + assert.strictEqual(headers.foobar, 'baz'); assert.strictEqual(headers['x-powered-by'], 'node-test'); })); diff --git a/test/parallel/test-https-agent-additional-options.js b/test/parallel/test-https-agent-additional-options.js new file mode 100644 index 00000000000000..8d10524d902ca7 --- /dev/null +++ b/test/parallel/test-https-agent-additional-options.js @@ -0,0 +1,87 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const crypto = require('crypto'); +const https = require('https'); +const fixtures = require('../common/fixtures'); + +const options = { + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem'), + ca: fixtures.readKey('ca1-cert.pem') +}; + +const server = https.Server(options, function(req, res) { + res.writeHead(200); + res.end('hello world\n'); +}); + +function getBaseOptions(port) { + return { + path: '/', + port: port, + ca: options.ca, + rejectUnautorized: true, + servername: 'agent1', + }; +} + +const updatedValues = new Map([ + ['dhparam', fixtures.readKey('dh2048.pem')], + ['ecdhCurve', 'secp384r1'], + ['honorCipherOrder', true], + ['secureOptions', crypto.constants.SSL_OP_CIPHER_SERVER_PREFERENCE], + ['secureProtocol', 'TLSv1_method'], + ['sessionIdContext', 'sessionIdContext'], +]); + +function variations(iter, port, cb) { + const { done, value } = iter.next(); + if (done) { + return common.mustCall(cb); + } else { + const [key, val] = value; + return common.mustCall(function(res) { + res.resume(); + https.globalAgent.once('free', common.mustCall(function() { + https.get( + Object.assign({}, getBaseOptions(port), { [key]: val }), + variations(iter, port, cb) + ); + })); + }); + } +} + +server.listen(0, common.mustCall(function() { + const port = this.address().port; + const globalAgent = https.globalAgent; + globalAgent.keepAlive = true; + https.get(getBaseOptions(port), variations( + updatedValues.entries(), + port, + common.mustCall(function(res) { + res.resume(); + globalAgent.once('free', common.mustCall(function() { + // Verify that different keep-alived connections are created + // for the base call and each variation + const keys = Object.keys(globalAgent.freeSockets); + assert.strictEqual(keys.length, 1 + updatedValues.size); + let i = 1; + for (const [, value] of updatedValues) { + assert.ok( + keys[i].startsWith(value.toString() + ':') || + keys[i].endsWith(':' + value.toString()) || + keys[i].includes(':' + value.toString() + ':') + ); + i++; + } + globalAgent.destroy(); + server.close(); + })); + }) + )); +})); diff --git a/test/parallel/test-https-agent-getname.js b/test/parallel/test-https-agent-getname.js index 0cdc9568d84470..c29e09731df0b2 100644 --- a/test/parallel/test-https-agent-getname.js +++ b/test/parallel/test-https-agent-getname.js @@ -12,7 +12,7 @@ const agent = new https.Agent(); // empty options assert.strictEqual( agent.getName({}), - 'localhost:::::::::::' + 'localhost:::::::::::::::::' ); // pass all options arguments @@ -23,13 +23,21 @@ const options = { ca: 'ca', cert: 'cert', ciphers: 'ciphers', + crl: [Buffer.from('c'), Buffer.from('r'), Buffer.from('l')], + dhparam: 'dhparam', + ecdhCurve: 'ecdhCurve', + honorCipherOrder: false, key: 'key', pfx: 'pfx', rejectUnauthorized: false, + secureOptions: 0, + secureProtocol: 'secureProtocol', servername: 'localhost', + sessionIdContext: 'sessionIdContext' }; assert.strictEqual( agent.getName(options), - '0.0.0.0:443:192.168.1.1:ca:cert::ciphers:key:pfx:false:localhost:' + '0.0.0.0:443:192.168.1.1:ca:cert::ciphers:key:pfx:false:localhost:' + + 'secureProtocol:c,r,l:false:ecdhCurve:dhparam:0:sessionIdContext' ); diff --git a/test/parallel/test-https-agent-secure-protocol.js b/test/parallel/test-https-agent-secure-protocol.js deleted file mode 100644 index 82554952e8446b..00000000000000 --- a/test/parallel/test-https-agent-secure-protocol.js +++ /dev/null @@ -1,57 +0,0 @@ -'use strict'; -const common = require('../common'); -if (!common.hasCrypto) - common.skip('missing crypto'); - -const assert = require('assert'); -const https = require('https'); -const fixtures = require('../common/fixtures'); - -const options = { - key: fixtures.readKey('agent1-key.pem'), - cert: fixtures.readKey('agent1-cert.pem'), - ca: fixtures.readKey('ca1-cert.pem') -}; - -const server = https.Server(options, function(req, res) { - res.writeHead(200); - res.end('hello world\n'); -}); - -server.listen(0, common.mustCall(function() { - const port = this.address().port; - const globalAgent = https.globalAgent; - globalAgent.keepAlive = true; - https.get({ - path: '/', - port: port, - ca: options.ca, - rejectUnauthorized: true, - servername: 'agent1', - secureProtocol: 'SSLv23_method' - }, common.mustCall(function(res) { - res.resume(); - globalAgent.once('free', common.mustCall(function() { - https.get({ - path: '/', - port: port, - ca: options.ca, - rejectUnauthorized: true, - servername: 'agent1', - secureProtocol: 'TLSv1_method' - }, common.mustCall(function(res) { - res.resume(); - globalAgent.once('free', common.mustCall(function() { - // Verify that two keep-alive connections are created - // due to the different secureProtocol settings: - const keys = Object.keys(globalAgent.freeSockets); - assert.strictEqual(keys.length, 2); - assert.ok(keys[0].includes(':SSLv23_method')); - assert.ok(keys[1].includes(':TLSv1_method')); - globalAgent.destroy(); - server.close(); - })); - })); - })); - })); -})); diff --git a/test/parallel/test-https-agent-session-reuse.js b/test/parallel/test-https-agent-session-reuse.js index 0330e111e9bd47..0717a3f8165d67 100644 --- a/test/parallel/test-https-agent-session-reuse.js +++ b/test/parallel/test-https-agent-session-reuse.js @@ -112,11 +112,11 @@ const server = https.createServer(options, function(req, res) { process.on('exit', function() { assert.strictEqual(serverRequests, 6); - assert.strictEqual(clientSessions['first'].toString('hex'), + assert.strictEqual(clientSessions.first.toString('hex'), clientSessions['first-reuse'].toString('hex')); - assert.notStrictEqual(clientSessions['first'].toString('hex'), + assert.notStrictEqual(clientSessions.first.toString('hex'), clientSessions['cipher-change'].toString('hex')); - assert.notStrictEqual(clientSessions['first'].toString('hex'), + assert.notStrictEqual(clientSessions.first.toString('hex'), clientSessions['before-drop'].toString('hex')); assert.notStrictEqual(clientSessions['cipher-change'].toString('hex'), clientSessions['before-drop'].toString('hex')); diff --git a/test/parallel/test-inspector-esm.js b/test/parallel/test-inspector-esm.js index b5a1365212a4ac..696f2af9a77462 100644 --- a/test/parallel/test-inspector-esm.js +++ b/test/parallel/test-inspector-esm.js @@ -17,9 +17,9 @@ function assertNoUrlsWhileConnected(response) { function assertScopeValues({ result }, expected) { const unmatched = new Set(Object.keys(expected)); for (const actual of result) { - const value = expected[actual['name']]; - assert.strictEqual(actual['value']['value'], value); - unmatched.delete(actual['name']); + const value = expected[actual.name]; + assert.strictEqual(actual.value.value, value); + unmatched.delete(actual.name); } assert.deepStrictEqual(Array.from(unmatched.values()), []); } @@ -93,14 +93,14 @@ async function testBreakpoint(session) { } }); - assert.strictEqual(result['value'], 1002); + assert.strictEqual(result.value, 1002); result = (await session.send({ 'method': 'Runtime.evaluate', 'params': { 'expression': '5 * 5' } })).result; - assert.strictEqual(result['value'], 25); + assert.strictEqual(result.value, 25); } async function runTest() { diff --git a/test/parallel/test-internal-module-wrap.js b/test/parallel/test-internal-module-wrap.js new file mode 100644 index 00000000000000..634d1ebc6f678e --- /dev/null +++ b/test/parallel/test-internal-module-wrap.js @@ -0,0 +1,29 @@ +'use strict'; + +// Flags: --expose-internals + +const common = require('../common'); +common.crashOnUnhandledRejection(); +const assert = require('assert'); + +const ModuleWrap = require('internal/loader/ModuleWrap'); +const { getPromiseDetails, isPromise } = process.binding('util'); +const setTimeoutAsync = require('util').promisify(setTimeout); + +const foo = new ModuleWrap('export * from "bar"; 6;', 'foo'); +const bar = new ModuleWrap('export const five = 5', 'bar'); + +(async () => { + const promises = foo.link(() => setTimeoutAsync(1000).then(() => bar)); + assert.strictEqual(promises.length, 1); + assert(isPromise(promises[0])); + + await Promise.all(promises); + + assert.strictEqual(getPromiseDetails(promises[0])[1], bar); + + foo.instantiate(); + + assert.strictEqual(await foo.evaluate(), 6); + assert.strictEqual(foo.namespace().five, 5); +})(); diff --git a/test/parallel/test-internal-util-decorate-error-stack.js b/test/parallel/test-internal-util-decorate-error-stack.js index b5670135a028ad..a0682161ae2c69 100644 --- a/test/parallel/test-internal-util-decorate-error-stack.js +++ b/test/parallel/test-internal-util-decorate-error-stack.js @@ -7,8 +7,8 @@ const internalUtil = require('internal/util'); const binding = process.binding('util'); const spawnSync = require('child_process').spawnSync; -const kArrowMessagePrivateSymbolIndex = binding['arrow_message_private_symbol']; -const kDecoratedPrivateSymbolIndex = binding['decorated_private_symbol']; +const kArrowMessagePrivateSymbolIndex = binding.arrow_message_private_symbol; +const kDecoratedPrivateSymbolIndex = binding.decorated_private_symbol; const decorateErrorStack = internalUtil.decorateErrorStack; diff --git a/test/parallel/test-memory-usage-emfile.js b/test/parallel/test-memory-usage-emfile.js index c5345079a7f47b..f8d1fc90da7aec 100644 --- a/test/parallel/test-memory-usage-emfile.js +++ b/test/parallel/test-memory-usage-emfile.js @@ -10,4 +10,4 @@ while (files.length < 256) files.push(fs.openSync(__filename, 'r')); const r = process.memoryUsage(); -assert.strictEqual(true, r['rss'] > 0); +assert.strictEqual(true, r.rss > 0); diff --git a/test/parallel/test-module-globalpaths-nodepath.js b/test/parallel/test-module-globalpaths-nodepath.js index dce5bf12c1604e..c4492169ad7e81 100644 --- a/test/parallel/test-module-globalpaths-nodepath.js +++ b/test/parallel/test-module-globalpaths-nodepath.js @@ -30,11 +30,11 @@ const partC = ''; if (common.isWindows) { partA = 'C:\\Users\\Rocko Artischocko\\AppData\\Roaming\\npm'; partB = 'C:\\Program Files (x86)\\nodejs\\'; - process.env['NODE_PATH'] = `${partA};${partB};${partC}`; + process.env.NODE_PATH = `${partA};${partB};${partC}`; } else { partA = '/usr/test/lib/node_modules'; partB = '/usr/test/lib/node'; - process.env['NODE_PATH'] = `${partA}:${partB}:${partC}`; + process.env.NODE_PATH = `${partA}:${partB}:${partC}`; } mod._initPaths(); diff --git a/test/parallel/test-module-loading-error.js b/test/parallel/test-module-loading-error.js index f1c457af2b2c9e..11af0225830257 100644 --- a/test/parallel/test-module-loading-error.js +++ b/test/parallel/test-module-loading-error.js @@ -72,3 +72,10 @@ common.expectsError( code: 'ERR_ASSERTION', message: /^path must be a string$/ }); + +common.expectsError( + () => { require('../fixtures/packages/is-dir'); }, + { + code: 'MODULE_NOT_FOUND', + message: 'Cannot find module \'../fixtures/packages/is-dir\'' + }); diff --git a/test/parallel/test-module-loading-globalpaths.js b/test/parallel/test-module-loading-globalpaths.js index e3c36cb21c202e..aff96543735428 100644 --- a/test/parallel/test-module-loading-globalpaths.js +++ b/test/parallel/test-module-loading-globalpaths.js @@ -6,6 +6,9 @@ const path = require('path'); const fs = require('fs'); const child_process = require('child_process'); const pkgName = 'foo'; +const { addLibraryPath } = require('../common/shared-lib-util'); + +addLibraryPath(process.env); if (process.argv[2] === 'child') { console.log(require(pkgName).string); @@ -39,14 +42,14 @@ if (process.argv[2] === 'child') { const env = Object.assign({}, process.env); // Turn on module debug to aid diagnosing failures. - env['NODE_DEBUG'] = 'module'; + env.NODE_DEBUG = 'module'; // Unset NODE_PATH. - delete env['NODE_PATH']; + delete env.NODE_PATH; // Test empty global path. const noPkgHomeDir = path.join(tmpdir.path, 'home-no-pkg'); fs.mkdirSync(noPkgHomeDir); - env['HOME'] = env['USERPROFILE'] = noPkgHomeDir; + env.HOME = env.USERPROFILE = noPkgHomeDir; assert.throws( () => { child_process.execFileSync(testExecPath, [ __filename, 'child' ], @@ -56,17 +59,17 @@ if (process.argv[2] === 'child') { // Test module in $HOME/.node_modules. const modHomeDir = path.join(testFixturesDir, 'home-pkg-in-node_modules'); - env['HOME'] = env['USERPROFILE'] = modHomeDir; + env.HOME = env.USERPROFILE = modHomeDir; runTest('$HOME/.node_modules', env); // Test module in $HOME/.node_libraries. const libHomeDir = path.join(testFixturesDir, 'home-pkg-in-node_libraries'); - env['HOME'] = env['USERPROFILE'] = libHomeDir; + env.HOME = env.USERPROFILE = libHomeDir; runTest('$HOME/.node_libraries', env); // Test module both $HOME/.node_modules and $HOME/.node_libraries. const bothHomeDir = path.join(testFixturesDir, 'home-pkg-in-both'); - env['HOME'] = env['USERPROFILE'] = bothHomeDir; + env.HOME = env.USERPROFILE = bothHomeDir; runTest('$HOME/.node_modules', env); // Test module in $PREFIX/lib/node. @@ -79,22 +82,22 @@ if (process.argv[2] === 'child') { const pkgPath = path.join(prefixLibNodePath, `${pkgName}.js`); fs.writeFileSync(pkgPath, `exports.string = '${expectedString}';`); - env['HOME'] = env['USERPROFILE'] = noPkgHomeDir; + env.HOME = env.USERPROFILE = noPkgHomeDir; runTest(expectedString, env); // Test module in all global folders. - env['HOME'] = env['USERPROFILE'] = bothHomeDir; + env.HOME = env.USERPROFILE = bothHomeDir; runTest('$HOME/.node_modules', env); // Test module in NODE_PATH is loaded ahead of global folders. - env['HOME'] = env['USERPROFILE'] = bothHomeDir; - env['NODE_PATH'] = path.join(testFixturesDir, 'node_path'); + env.HOME = env.USERPROFILE = bothHomeDir; + env.NODE_PATH = path.join(testFixturesDir, 'node_path'); runTest('$NODE_PATH', env); // Test module in local folder is loaded ahead of global folders. const localDir = path.join(testFixturesDir, 'local-pkg'); - env['HOME'] = env['USERPROFILE'] = bothHomeDir; - env['NODE_PATH'] = path.join(testFixturesDir, 'node_path'); + env.HOME = env.USERPROFILE = bothHomeDir; + env.NODE_PATH = path.join(testFixturesDir, 'node_path'); const child = child_process.execFileSync(testExecPath, [ path.join(localDir, 'test.js') ], { encoding: 'utf8', env: env }); diff --git a/test/parallel/test-net-connect-handle-econnrefused.js b/test/parallel/test-net-connect-handle-econnrefused.js index 569aded7bf5def..3c7310b4752d72 100644 --- a/test/parallel/test-net-connect-handle-econnrefused.js +++ b/test/parallel/test-net-connect-handle-econnrefused.js @@ -27,11 +27,10 @@ const assert = require('assert'); const server = net.createServer(); server.listen(0); const port = server.address().port; -const c = net.createConnection(port); - -c.on('connect', common.mustNotCall()); - -c.on('error', common.mustCall((e) => { - assert.strictEqual('ECONNREFUSED', e.code); +server.close(common.mustCall(() => { + const c = net.createConnection(port); + c.on('connect', common.mustNotCall()); + c.on('error', common.mustCall((e) => { + assert.strictEqual('ECONNREFUSED', e.code); + })); })); -server.close(); diff --git a/test/parallel/test-net-dns-error.js b/test/parallel/test-net-dns-error.js index beebcd8cb9cf44..a5ae415592fed4 100644 --- a/test/parallel/test-net-dns-error.js +++ b/test/parallel/test-net-dns-error.js @@ -27,17 +27,21 @@ const net = require('net'); const host = '*'.repeat(256); +let errCode = 'ENOTFOUND'; +if (common.isOpenBSD) + errCode = 'EAI_FAIL'; + function do_not_call() { throw new Error('This function should not have been called.'); } const socket = net.connect(42, host, do_not_call); socket.on('error', common.mustCall(function(err) { - assert.strictEqual(err.code, 'ENOTFOUND'); + assert.strictEqual(err.code, errCode); })); socket.on('lookup', function(err, ip, type) { assert(err instanceof Error); - assert.strictEqual(err.code, 'ENOTFOUND'); + assert.strictEqual(err.code, errCode); assert.strictEqual(ip, undefined); assert.strictEqual(type, undefined); }); diff --git a/test/parallel/test-performanceobserver.js b/test/parallel/test-performanceobserver.js index 779e9740d7cc62..af83d199da9186 100644 --- a/test/parallel/test-performanceobserver.js +++ b/test/parallel/test-performanceobserver.js @@ -65,10 +65,10 @@ assert.strictEqual(counts[NODE_PERFORMANCE_ENTRY_TYPE_FUNCTION], 0); new PerformanceObserver(common.mustCall(callback, 3)); const countdown = - new Countdown(3, common.mustCall(() => { + new Countdown(3, () => { observer.disconnect(); assert.strictEqual(counts[NODE_PERFORMANCE_ENTRY_TYPE_MARK], 1); - })); + }); function callback(list, obs) { assert.strictEqual(obs, observer); diff --git a/test/parallel/test-postmortem-metadata.js b/test/parallel/test-postmortem-metadata.js index 95d2ff67e1fb1d..3ee7b5dc3cee1a 100644 --- a/test/parallel/test-postmortem-metadata.js +++ b/test/parallel/test-postmortem-metadata.js @@ -12,6 +12,9 @@ const args = [process.execPath]; if (common.isAIX) args.unshift('-Xany', '-B'); +if (common.isOpenBSD) + common.skip('no v8 debug symbols on OpenBSD'); + const nm = spawnSync('nm', args); if (nm.error && nm.error.errno === 'ENOENT') diff --git a/test/parallel/test-process-kill-pid.js b/test/parallel/test-process-kill-pid.js index 2ff35c6e4f866f..594a2a6a0bd8e6 100644 --- a/test/parallel/test-process-kill-pid.js +++ b/test/parallel/test-process-kill-pid.js @@ -57,16 +57,19 @@ assert.throws(function() { process.kill(1 / 0); }, assert.throws(function() { process.kill(-1 / 0); }, invalidPidArgument); -// Test that kill throws an error for invalid signal -const unknownSignal = common.expectsError({ +// Test that kill throws an error for unknown signal names +common.expectsError(() => process.kill(0, 'test'), { code: 'ERR_UNKNOWN_SIGNAL', type: TypeError, message: 'Unknown signal: test' }); - -assert.throws(function() { process.kill(1, 'test'); }, - unknownSignal); +// Test that kill throws an error for invalid signal numbers +common.expectsError(() => process.kill(0, 987), { + code: 'EINVAL', + type: Error, + message: 'kill EINVAL' +}); // Test kill argument processing in valid cases. // @@ -99,6 +102,11 @@ kill(0, undefined, 0, 15); kill('0', 'SIGHUP', 0, 1); kill('0', undefined, 0, 15); +// Confirm that numeric signal arguments are supported + +kill(0, 1, 0, 1); +kill(0, 15, 0, 15); + // negative numbers are meaningful on unix kill(-1, 'SIGHUP', -1, 1); kill(-1, undefined, -1, 15); diff --git a/test/parallel/test-repl-multiline.js b/test/parallel/test-repl-multiline.js new file mode 100644 index 00000000000000..54048bf31f2f6f --- /dev/null +++ b/test/parallel/test-repl-multiline.js @@ -0,0 +1,35 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const repl = require('repl'); +const inputStream = new common.ArrayStream(); +const outputStream = new common.ArrayStream(); +const input = ['var foo = {', '};', 'foo;']; +let output = ''; + +outputStream.write = (data) => { output += data.replace('\r', ''); }; + +const r = repl.start({ + prompt: '', + input: inputStream, + output: outputStream, + terminal: true, + useColors: false +}); + +r.on('exit', common.mustCall(() => { + const actual = output.split('\n'); + + // Validate the output, which contains terminal escape codes. + assert.strictEqual(actual.length, 6); + assert.ok(actual[0].endsWith(input[0])); + assert.ok(actual[1].includes('... ')); + assert.ok(actual[1].endsWith(input[1])); + assert.strictEqual(actual[2], 'undefined'); + assert.ok(actual[3].endsWith(input[2])); + assert.strictEqual(actual[4], '{}'); + // Ignore the last line, which is nothing but escape codes. +})); + +inputStream.run(input); +r.close(); diff --git a/test/parallel/test-setproctitle.js b/test/parallel/test-setproctitle.js index 4bb88c4ba06922..1ab6bff6a30848 100644 --- a/test/parallel/test-setproctitle.js +++ b/test/parallel/test-setproctitle.js @@ -34,7 +34,7 @@ exec(cmd, common.mustCall((error, stdout, stderr) => { assert.strictEqual(stderr, ''); // freebsd always add ' (procname)' to the process title - if (common.isFreeBSD) + if (common.isFreeBSD || common.isOpenBSD) title += ` (${path.basename(process.execPath)})`; // omitting trailing whitespace and \n diff --git a/test/parallel/test-stdio-pipe-access.js b/test/parallel/test-stdio-pipe-access.js new file mode 100644 index 00000000000000..ef84bb83803b26 --- /dev/null +++ b/test/parallel/test-stdio-pipe-access.js @@ -0,0 +1,35 @@ +'use strict'; +require('../common'); + +// Test if Node handles acessing process.stdin if it is a redirected +// pipe without deadlocking +const { spawn, spawnSync } = require('child_process'); + +const numTries = 5; +const who = process.argv.length <= 2 ? 'runner' : process.argv[2]; + +switch (who) { + case 'runner': + for (let num = 0; num < numTries; ++num) { + spawnSync(process.argv0, + [process.argv[1], 'parent'], + { 'stdio': 'inherit' }); + } + break; + case 'parent': + const middle = spawn(process.argv0, + [process.argv[1], 'middle'], + { 'stdio': 'pipe' }); + middle.stdout.on('data', () => {}); + break; + case 'middle': + spawn(process.argv0, + [process.argv[1], 'bottom'], + { 'stdio': [ process.stdin, + process.stdout, + process.stderr ] }); + break; + case 'bottom': + process.stdin; + break; +} diff --git a/test/parallel/test-stdio-pipe-redirect.js b/test/parallel/test-stdio-pipe-redirect.js new file mode 100644 index 00000000000000..b47f5b9cf44af5 --- /dev/null +++ b/test/parallel/test-stdio-pipe-redirect.js @@ -0,0 +1,38 @@ +'use strict'; +require('../common'); + +// Test if Node handles redirecting one child process stdout to another +// process stdin without crashing. +const spawn = require('child_process').spawn; + +const writeSize = 100; +const totalDots = 10000; + +const who = process.argv.length <= 2 ? 'parent' : process.argv[2]; + +switch (who) { + case 'parent': + const consumer = spawn(process.argv0, [process.argv[1], 'consumer'], { + stdio: ['pipe', 'ignore', 'inherit'], + }); + const producer = spawn(process.argv0, [process.argv[1], 'producer'], { + stdio: ['pipe', consumer.stdin, 'inherit'], + }); + process.stdin.on('data', () => {}); + producer.on('exit', process.exit); + break; + case 'producer': + const buffer = Buffer.alloc(writeSize, '.'); + let written = 0; + const write = () => { + if (written < totalDots) { + written += writeSize; + process.stdout.write(buffer, write); + } + }; + write(); + break; + case 'consumer': + process.stdin.on('data', () => {}); + break; +} diff --git a/test/parallel/test-stream-transform-constructor-set-methods.js b/test/parallel/test-stream-transform-constructor-set-methods.js index 31b4c6c9bd0509..14c173b4ccfec5 100644 --- a/test/parallel/test-stream-transform-constructor-set-methods.js +++ b/test/parallel/test-stream-transform-constructor-set-methods.js @@ -1,19 +1,19 @@ 'use strict'; const common = require('../common'); -const assert = require('assert'); -const Transform = require('stream').Transform; +const { strictEqual } = require('assert'); +const { Transform } = require('stream'); -const _transform = common.mustCall(function _transform(d, e, n) { - n(); +const _transform = common.mustCall((chunk, _, next) => { + next(); }); -const _final = common.mustCall(function _final(n) { - n(); +const _final = common.mustCall((next) => { + next(); }); -const _flush = common.mustCall(function _flush(n) { - n(); +const _flush = common.mustCall((next) => { + next(); }); const t = new Transform({ @@ -22,11 +22,15 @@ const t = new Transform({ final: _final }); -const t2 = new Transform({}); +strictEqual(t._transform, _transform); +strictEqual(t._flush, _flush); +strictEqual(t._final, _final); t.end(Buffer.from('blerg')); t.resume(); +const t2 = new Transform({}); + common.expectsError(() => { t2.end(Buffer.from('blerg')); }, { @@ -34,9 +38,3 @@ common.expectsError(() => { code: 'ERR_METHOD_NOT_IMPLEMENTED', message: 'The _transform method is not implemented' }); - -process.on('exit', () => { - assert.strictEqual(t._transform, _transform); - assert.strictEqual(t._flush, _flush); - assert.strictEqual(t._final, _final); -}); diff --git a/test/parallel/test-stream-writable-constructor-set-methods.js b/test/parallel/test-stream-writable-constructor-set-methods.js index a12112d1ecc266..425cd88ed7a336 100644 --- a/test/parallel/test-stream-writable-constructor-set-methods.js +++ b/test/parallel/test-stream-writable-constructor-set-methods.js @@ -1,35 +1,37 @@ 'use strict'; -require('../common'); -const assert = require('assert'); - -const Writable = require('stream').Writable; - -let _writeCalled = false; -function _write(d, e, n) { - _writeCalled = true; -} - -const w = new Writable({ write: _write }); -w.end(Buffer.from('blerg')); - -let _writevCalled = false; -let dLength = 0; -function _writev(d, n) { - dLength = d.length; - _writevCalled = true; -} - -const w2 = new Writable({ writev: _writev }); -w2.cork(); - -w2.write(Buffer.from('blerg')); -w2.write(Buffer.from('blerg')); -w2.end(); - -process.on('exit', function() { - assert.strictEqual(w._write, _write); - assert(_writeCalled); - assert.strictEqual(w2._writev, _writev); - assert.strictEqual(dLength, 2); - assert(_writevCalled); +const common = require('../common'); + +const { strictEqual } = require('assert'); +const { Writable } = require('stream'); + +const _write = common.mustCall((chunk, _, next) => { + next(); +}); + +const _writev = common.mustCall((chunks, next) => { + strictEqual(chunks.length, 2); + next(); }); + +const w = new Writable({ write: _write, writev: _writev }); + +strictEqual(w._write, _write); +strictEqual(w._writev, _writev); + +w.write(Buffer.from('blerg')); + +w.cork(); +w.write(Buffer.from('blerg')); +w.write(Buffer.from('blerg')); + +w.end(); + +const w2 = new Writable(); + +w2.on('error', common.expectsError({ + type: Error, + code: 'ERR_METHOD_NOT_IMPLEMENTED', + message: 'The _write method is not implemented' +})); + +w2.end(Buffer.from('blerg')); diff --git a/test/parallel/test-timers-socket-timeout-removes-other-socket-unref-timer.js b/test/parallel/test-timers-socket-timeout-removes-other-socket-unref-timer.js index 842c8180701aa1..5dceb386fdbdf4 100644 --- a/test/parallel/test-timers-socket-timeout-removes-other-socket-unref-timer.js +++ b/test/parallel/test-timers-socket-timeout-removes-other-socket-unref-timer.js @@ -33,7 +33,7 @@ const server = net.createServer(function onClient(client) { }); server.listen(0, common.localhostIPv4, common.mustCall(() => { - const countdown = new Countdown(2, common.mustCall(() => server.close())); + const countdown = new Countdown(2, () => server.close()); { const client = net.connect({ port: server.address().port }); diff --git a/test/parallel/test-tls-0-dns-altname.js b/test/parallel/test-tls-0-dns-altname.js index 5952d6b02cb239..4bc87e44cb60c1 100644 --- a/test/parallel/test-tls-0-dns-altname.js +++ b/test/parallel/test-tls-0-dns-altname.js @@ -33,15 +33,15 @@ const fixtures = require('../common/fixtures'); const server = tls.createServer({ key: fixtures.readSync(['0-dns', '0-dns-key.pem']), cert: fixtures.readSync(['0-dns', '0-dns-cert.pem']) -}, function(c) { - c.once('data', function() { +}, common.mustCall((c) => { + c.once('data', common.mustCall(() => { c.destroy(); server.close(); - }); -}).listen(0, common.mustCall(function() { - const c = tls.connect(this.address().port, { + })); +})).listen(0, common.mustCall(() => { + const c = tls.connect(server.address().port, { rejectUnauthorized: false - }, common.mustCall(function() { + }, common.mustCall(() => { const cert = c.getPeerCertificate(); assert.strictEqual(cert.subjectaltname, 'DNS:good.example.org\0.evil.example.com, ' + diff --git a/test/parallel/test-tls-addca.js b/test/parallel/test-tls-addca.js index 034334a7e41bf3..8eb88db6291457 100644 --- a/test/parallel/test-tls-addca.js +++ b/test/parallel/test-tls-addca.js @@ -1,5 +1,5 @@ 'use strict'; -require('../common'); +const common = require('../common'); const fixtures = require('../common/fixtures'); // Adding a CA certificate to contextWithCert should not also add it to @@ -32,7 +32,7 @@ clientOptions.secureContext = contextWithoutCert; connect({ client: clientOptions, server: serverOptions, -}, function(err, pair, cleanup) { +}, common.mustCall((err, pair, cleanup) => { assert(err); assert.strictEqual(err.message, 'unable to verify the first certificate'); cleanup(); @@ -43,8 +43,8 @@ connect({ connect({ client: clientOptions, server: serverOptions, - }, function(err, pair, cleanup) { + }, common.mustCall((err, pair, cleanup) => { assert.ifError(err); cleanup(); - }); -}); + })); +})); diff --git a/test/parallel/test-tls-client-getephemeralkeyinfo.js b/test/parallel/test-tls-client-getephemeralkeyinfo.js index ed628204025b16..a50a0354c74014 100644 --- a/test/parallel/test-tls-client-getephemeralkeyinfo.js +++ b/test/parallel/test-tls-client-getephemeralkeyinfo.js @@ -24,7 +24,7 @@ const cipherlist = { }; function test(size, type, name, next) { - const cipher = type ? cipherlist[type] : cipherlist['NOT_PFS']; + const cipher = type ? cipherlist[type] : cipherlist.NOT_PFS; if (name) tls.DEFAULT_ECDH_CURVE = name; diff --git a/test/parallel/test-tls-connect-no-host.js b/test/parallel/test-tls-connect-no-host.js index d685ba90ccdd84..f6384743ac7081 100644 --- a/test/parallel/test-tls-connect-no-host.js +++ b/test/parallel/test-tls-connect-no-host.js @@ -6,7 +6,6 @@ if (!common.hasCrypto) common.skip('missing crypto'); const tls = require('tls'); - const assert = require('assert'); const cert = fixtures.readSync('test_cert.pem'); @@ -15,10 +14,10 @@ const key = fixtures.readSync('test_key.pem'); // https://github.com/nodejs/node/issues/1489 // tls.connect(options) with no options.host should accept a cert with // CN:'localhost' -tls.createServer({ +const server = tls.createServer({ key, cert -}).listen(0, function() { +}).listen(0, common.mustCall(function() { const socket = tls.connect({ port: this.address().port, ca: cert, @@ -26,8 +25,9 @@ tls.createServer({ // but tls.checkServerIdentity() breaks before the fix with: // Error: Hostname/IP doesn't match certificate's altnames: // "Host: undefined. is not cert's CN: localhost" - }, function() { + }, common.mustCall(function() { assert(socket.authorized); - process.exit(); - }); -}); + socket.destroy(); + server.close(); + })); +})); diff --git a/test/parallel/test-tls-over-http-tunnel.js b/test/parallel/test-tls-over-http-tunnel.js index 8e9bafda5394d6..f3f2bb3f2726ed 100644 --- a/test/parallel/test-tls-over-http-tunnel.js +++ b/test/parallel/test-tls-over-http-tunnel.js @@ -24,6 +24,9 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); +// This test ensures that the data received through tls over http tunnel +// is same as what is sent. + const assert = require('assert'); const https = require('https'); const net = require('net'); @@ -37,21 +40,21 @@ const cert = fixtures.readKey('agent1-cert.pem'); const options = { key, cert }; -const server = https.createServer(options, function(req, res) { +const server = https.createServer(options, common.mustCall((req, res) => { console.log('SERVER: got request'); res.writeHead(200, { 'content-type': 'text/plain' }); console.log('SERVER: sending response'); res.end('hello world\n'); -}); +})); -const proxy = net.createServer(function(clientSocket) { +const proxy = net.createServer((clientSocket) => { console.log('PROXY: got a client connection'); let serverSocket = null; - clientSocket.on('data', function(chunk) { + clientSocket.on('data', (chunk) => { if (!serverSocket) { // Verify the CONNECT request assert.strictEqual(`CONNECT localhost:${server.address().port} ` + @@ -65,39 +68,39 @@ const proxy = net.createServer(function(clientSocket) { console.log('PROXY: creating a tunnel'); // create the tunnel - serverSocket = net.connect(server.address().port, function() { + serverSocket = net.connect(server.address().port, common.mustCall(() => { console.log('PROXY: replying to client CONNECT request'); // Send the response clientSocket.write('HTTP/1.1 200 OK\r\nProxy-Connections: keep' + - '-alive\r\nConnections: keep-alive\r\nVia: ' + - `localhost:${proxy.address().port}\r\n\r\n`); - }); + '-alive\r\nConnections: keep-alive\r\nVia: ' + + `localhost:${proxy.address().port}\r\n\r\n`); + })); - serverSocket.on('data', function(chunk) { + serverSocket.on('data', (chunk) => { clientSocket.write(chunk); }); - serverSocket.on('end', function() { + serverSocket.on('end', common.mustCall(() => { clientSocket.destroy(); - }); + })); } else { serverSocket.write(chunk); } }); - clientSocket.on('end', function() { + clientSocket.on('end', () => { serverSocket.destroy(); }); }); server.listen(0); -proxy.listen(0, function() { +proxy.listen(0, common.mustCall(() => { console.log('CLIENT: Making CONNECT request'); const req = http.request({ - port: this.address().port, + port: proxy.address().port, method: 'CONNECT', path: `localhost:${server.address().port}`, headers: { @@ -117,7 +120,7 @@ proxy.listen(0, function() { function onUpgrade(res, socket, head) { // Hacky. - process.nextTick(function() { + process.nextTick(() => { onConnect(res, socket, head); }); } @@ -145,20 +148,20 @@ proxy.listen(0, function() { socket: socket, // reuse the socket agent: false, rejectUnauthorized: false - }, function(res) { + }, (res) => { assert.strictEqual(200, res.statusCode); - res.on('data', function(chunk) { + res.on('data', common.mustCall((chunk) => { assert.strictEqual('hello world\n', chunk.toString()); console.log('CLIENT: got HTTPS response'); gotRequest = true; - }); + })); - res.on('end', function() { + res.on('end', common.mustCall(() => { proxy.close(); server.close(); - }); - }).on('error', function(er) { + })); + }).on('error', (er) => { // We're ok with getting ECONNRESET in this test, but it's // timing-dependent, and thus unreliable. Any other errors // are just failures, though. @@ -166,8 +169,8 @@ proxy.listen(0, function() { throw er; }).end(); } -}); +})); -process.on('exit', function() { +process.on('exit', () => { assert.ok(gotRequest); }); diff --git a/test/parallel/test-tls-pause.js b/test/parallel/test-tls-pause.js index 75c2832f7332f8..a6130cd4331f62 100644 --- a/test/parallel/test-tls-pause.js +++ b/test/parallel/test-tls-pause.js @@ -24,6 +24,9 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); +// This test ensures that the data received over tls-server after pause +// is same as what it was sent + const assert = require('assert'); const tls = require('tls'); const fixtures = require('../common/fixtures'); @@ -37,24 +40,23 @@ const bufSize = 1024 * 1024; let sent = 0; let received = 0; -const server = tls.Server(options, function(socket) { +const server = tls.Server(options, common.mustCall((socket) => { socket.pipe(socket); - socket.on('data', function(c) { + socket.on('data', (c) => { console.error('data', c.length); }); -}); +})); -server.listen(0, function() { +server.listen(0, common.mustCall(() => { let resumed = false; const client = tls.connect({ - port: this.address().port, + port: server.address().port, rejectUnauthorized: false - }, function() { + }, common.mustCall(() => { console.error('connected'); client.pause(); console.error('paused'); - send(); - function send() { + const send = (() => { console.error('sending'); const ret = client.write(Buffer.allocUnsafe(bufSize)); console.error(`write => ${ret}`); @@ -69,9 +71,9 @@ server.listen(0, function() { resumed = true; client.resume(); console.error('resumed', client); - } - }); - client.on('data', function(data) { + })(); + })); + client.on('data', (data) => { console.error('data'); assert.ok(resumed); received += data.length; @@ -83,8 +85,8 @@ server.listen(0, function() { server.close(); } }); -}); +})); -process.on('exit', function() { +process.on('exit', () => { assert.strictEqual(sent, received); }); diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 5520e8bc2ed4fb..a99583d454b3f4 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -284,7 +284,7 @@ assert.strictEqual( '{ writeonly: [Setter] }'); const value = {}; - value['a'] = value; + value.a = value; assert.strictEqual(util.inspect(value), '{ a: [Circular] }'); } diff --git a/test/parallel/test-util-internal.js b/test/parallel/test-util-internal.js index 63e782f15de1e8..cb3d3122f9be4e 100644 --- a/test/parallel/test-util-internal.js +++ b/test/parallel/test-util-internal.js @@ -6,7 +6,7 @@ const assert = require('assert'); const fixtures = require('../common/fixtures'); const binding = process.binding('util'); -const kArrowMessagePrivateSymbolIndex = binding['arrow_message_private_symbol']; +const kArrowMessagePrivateSymbolIndex = binding.arrow_message_private_symbol; function getHiddenValue(obj, index) { return function() { diff --git a/test/parallel/test-vm-module-basic.js b/test/parallel/test-vm-module-basic.js new file mode 100644 index 00000000000000..4bbe0a95ee6724 --- /dev/null +++ b/test/parallel/test-vm-module-basic.js @@ -0,0 +1,54 @@ +'use strict'; + +// Flags: --experimental-vm-modules + +const common = require('../common'); +const assert = require('assert'); +const { Module, createContext } = require('vm'); + +common.crashOnUnhandledRejection(); + +(async function test1() { + const context = createContext({ + foo: 'bar', + baz: undefined, + typeofProcess: undefined, + }); + const m = new Module( + 'baz = foo; typeofProcess = typeof process; typeof Object;', + { context } + ); + assert.strictEqual(m.status, 'uninstantiated'); + await m.link(common.mustNotCall()); + m.instantiate(); + assert.strictEqual(m.status, 'instantiated'); + const result = await m.evaluate(); + assert.strictEqual(m.status, 'evaluated'); + assert.strictEqual(Object.getPrototypeOf(result), null); + assert.deepStrictEqual(context, { + foo: 'bar', + baz: 'bar', + typeofProcess: 'undefined' + }); + assert.strictEqual(result.result, 'function'); +}()); + +(async () => { + const m = new Module( + 'global.vmResult = "foo"; Object.prototype.toString.call(process);' + ); + await m.link(common.mustNotCall()); + m.instantiate(); + const { result } = await m.evaluate(); + assert.strictEqual(global.vmResult, 'foo'); + assert.strictEqual(result, '[object process]'); + delete global.vmResult; +})(); + +(async () => { + const m = new Module('while (true) {}'); + await m.link(common.mustNotCall()); + m.instantiate(); + await m.evaluate({ timeout: 500 }) + .then(() => assert(false), () => {}); +})(); diff --git a/test/parallel/test-vm-module-dynamic-import.js b/test/parallel/test-vm-module-dynamic-import.js new file mode 100644 index 00000000000000..ca4dceb5def731 --- /dev/null +++ b/test/parallel/test-vm-module-dynamic-import.js @@ -0,0 +1,27 @@ +'use strict'; + +// Flags: --experimental-vm-modules --experimental-modules --harmony-dynamic-import + +const common = require('../common'); +common.crashOnUnhandledRejection(); + +const assert = require('assert'); +const { Module, createContext } = require('vm'); + +const finished = common.mustCall(); + +(async function() { + const m = new Module('import("foo")', { context: createContext() }); + await m.link(common.mustNotCall()); + m.instantiate(); + const { result } = await m.evaluate(); + let threw = false; + try { + await result; + } catch (err) { + threw = true; + assert.strictEqual(err.message, 'import() called outside of main context'); + } + assert(threw); + finished(); +}()); diff --git a/test/parallel/test-vm-module-errors.js b/test/parallel/test-vm-module-errors.js new file mode 100644 index 00000000000000..1c7e01cf30e51b --- /dev/null +++ b/test/parallel/test-vm-module-errors.js @@ -0,0 +1,264 @@ +'use strict'; + +// Flags: --experimental-vm-modules + +const common = require('../common'); +common.crashOnUnhandledRejection(); + +const assert = require('assert'); + +const { Module, createContext } = require('vm'); + +async function expectsRejection(fn, settings) { + const validateError = common.expectsError(settings); + // Retain async context. + const storedError = new Error('Thrown from:'); + try { + await fn(); + } catch (err) { + try { + validateError(err); + } catch (validationError) { + console.error(validationError); + console.error('Original error:'); + console.error(err); + throw storedError; + } + return; + } + assert.fail('Missing expected exception'); +} + +async function createEmptyLinkedModule() { + const m = new Module(''); + await m.link(common.mustNotCall()); + return m; +} + +async function checkArgType() { + common.expectsError(() => { + new Module(); + }, { + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError + }); + + for (const invalidOptions of [ + 0, 1, null, true, 'str', () => {}, Symbol.iterator + ]) { + common.expectsError(() => { + new Module('', invalidOptions); + }, { + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError + }); + } + + for (const invalidLinker of [ + 0, 1, undefined, null, true, 'str', {}, Symbol.iterator + ]) { + await expectsRejection(async () => { + const m = new Module(''); + await m.link(invalidLinker); + }, { + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError + }); + } +} + +// Check methods/properties can only be used under a specific state. +async function checkModuleState() { + await expectsRejection(async () => { + const m = new Module(''); + await m.link(common.mustNotCall()); + assert.strictEqual(m.linkingStatus, 'linked'); + await m.link(common.mustNotCall()); + }, { + code: 'ERR_VM_MODULE_ALREADY_LINKED' + }); + + await expectsRejection(async () => { + const m = new Module(''); + m.link(common.mustNotCall()); + assert.strictEqual(m.linkingStatus, 'linking'); + await m.link(common.mustNotCall()); + }, { + code: 'ERR_VM_MODULE_ALREADY_LINKED' + }); + + common.expectsError(() => { + const m = new Module(''); + m.instantiate(); + }, { + code: 'ERR_VM_MODULE_NOT_LINKED' + }); + + await expectsRejection(async () => { + const m = new Module('import "foo";'); + try { + await m.link(common.mustCall(() => ({}))); + } catch (err) { + assert.strictEqual(m.linkingStatus, 'errored'); + m.instantiate(); + } + assert.fail('Unreachable'); + }, { + code: 'ERR_VM_MODULE_NOT_LINKED' + }); + + { + const m = new Module('import "foo";'); + await m.link(common.mustCall(async (specifier, module) => { + assert.strictEqual(module, m); + assert.strictEqual(specifier, 'foo'); + assert.strictEqual(m.linkingStatus, 'linking'); + common.expectsError(() => { + m.instantiate(); + }, { + code: 'ERR_VM_MODULE_NOT_LINKED' + }); + return new Module(''); + })); + m.instantiate(); + await m.evaluate(); + } + + await expectsRejection(async () => { + const m = new Module(''); + await m.evaluate(); + }, { + code: 'ERR_VM_MODULE_STATUS', + message: 'Module status must be one of instantiated, evaluated, and errored' + }); + + await expectsRejection(async () => { + const m = await createEmptyLinkedModule(); + await m.evaluate(); + }, { + code: 'ERR_VM_MODULE_STATUS', + message: 'Module status must be one of instantiated, evaluated, and errored' + }); + + common.expectsError(() => { + const m = new Module(''); + m.error; + }, { + code: 'ERR_VM_MODULE_STATUS', + message: 'Module status must be errored' + }); + + await expectsRejection(async () => { + const m = await createEmptyLinkedModule(); + m.instantiate(); + await m.evaluate(); + m.error; + }, { + code: 'ERR_VM_MODULE_STATUS', + message: 'Module status must be errored' + }); + + common.expectsError(() => { + const m = new Module(''); + m.namespace; + }, { + code: 'ERR_VM_MODULE_STATUS', + message: 'Module status must not be uninstantiated or instantiating' + }); + + await expectsRejection(async () => { + const m = await createEmptyLinkedModule(); + m.namespace; + }, { + code: 'ERR_VM_MODULE_STATUS', + message: 'Module status must not be uninstantiated or instantiating' + }); +} + +// Check link() fails when the returned module is not valid. +async function checkLinking() { + await expectsRejection(async () => { + const m = new Module('import "foo";'); + try { + await m.link(common.mustCall(() => ({}))); + } catch (err) { + assert.strictEqual(m.linkingStatus, 'errored'); + throw err; + } + assert.fail('Unreachable'); + }, { + code: 'ERR_VM_MODULE_NOT_MODULE' + }); + + await expectsRejection(async () => { + const c = createContext({ a: 1 }); + const foo = new Module('', { context: c }); + await foo.link(common.mustNotCall()); + const bar = new Module('import "foo";'); + try { + await bar.link(common.mustCall(() => foo)); + } catch (err) { + assert.strictEqual(bar.linkingStatus, 'errored'); + throw err; + } + assert.fail('Unreachable'); + }, { + code: 'ERR_VM_MODULE_DIFFERENT_CONTEXT' + }); + + await expectsRejection(async () => { + const erroredModule = new Module('import "foo";'); + try { + await erroredModule.link(common.mustCall(() => ({}))); + } catch (err) { + // ignored + } finally { + assert.strictEqual(erroredModule.linkingStatus, 'errored'); + } + + const rootModule = new Module('import "errored";'); + await rootModule.link(common.mustCall(() => erroredModule)); + }, { + code: 'ERR_VM_MODULE_LINKING_ERRORED' + }); +} + +// Check the JavaScript engine deals with exceptions correctly +async function checkExecution() { + await (async () => { + const m = new Module('import { nonexistent } from "module";'); + await m.link(common.mustCall(() => new Module(''))); + + // There is no code for this exception since it is thrown by the JavaScript + // engine. + assert.throws(() => { + m.instantiate(); + }, SyntaxError); + })(); + + await (async () => { + const m = new Module('throw new Error();'); + await m.link(common.mustNotCall()); + m.instantiate(); + const evaluatePromise = m.evaluate(); + await evaluatePromise.catch(() => {}); + assert.strictEqual(m.status, 'errored'); + try { + await evaluatePromise; + } catch (err) { + assert.strictEqual(m.error, err); + return; + } + assert.fail('Missing expected exception'); + })(); +} + +const finished = common.mustCall(); + +(async function main() { + await checkArgType(); + await checkModuleState(); + await checkLinking(); + await checkExecution(); + finished(); +})(); diff --git a/test/parallel/test-vm-module-link.js b/test/parallel/test-vm-module-link.js new file mode 100644 index 00000000000000..843c1fdc517787 --- /dev/null +++ b/test/parallel/test-vm-module-link.js @@ -0,0 +1,135 @@ +'use strict'; + +// Flags: --experimental-vm-modules + +const common = require('../common'); +common.crashOnUnhandledRejection(); + +const assert = require('assert'); +const { URL } = require('url'); + +const { Module } = require('vm'); + +async function simple() { + const foo = new Module('export default 5;'); + await foo.link(common.mustNotCall()); + + const bar = new Module('import five from "foo"; five'); + + assert.deepStrictEqual(bar.dependencySpecifiers, ['foo']); + + await bar.link(common.mustCall((specifier, module) => { + assert.strictEqual(module, bar); + assert.strictEqual(specifier, 'foo'); + return foo; + })); + + bar.instantiate(); + + assert.strictEqual((await bar.evaluate()).result, 5); +} + +async function depth() { + const foo = new Module('export default 5'); + await foo.link(common.mustNotCall()); + + async function getProxy(parentName, parentModule) { + const mod = new Module(` + import ${parentName} from '${parentName}'; + export default ${parentName}; + `); + await mod.link(common.mustCall((specifier, module) => { + assert.strictEqual(module, mod); + assert.strictEqual(specifier, parentName); + return parentModule; + })); + return mod; + } + + const bar = await getProxy('foo', foo); + const baz = await getProxy('bar', bar); + const barz = await getProxy('baz', baz); + + barz.instantiate(); + await barz.evaluate(); + + assert.strictEqual(barz.namespace.default, 5); +} + +async function circular() { + const foo = new Module(` + import getFoo from 'bar'; + export let foo = 42; + export default getFoo(); + `); + const bar = new Module(` + import { foo } from 'foo'; + export default function getFoo() { + return foo; + } + `); + await foo.link(common.mustCall(async (fooSpecifier, fooModule) => { + assert.strictEqual(fooModule, foo); + assert.strictEqual(fooSpecifier, 'bar'); + await bar.link(common.mustCall((barSpecifier, barModule) => { + assert.strictEqual(barModule, bar); + assert.strictEqual(barSpecifier, 'foo'); + assert.strictEqual(foo.linkingStatus, 'linking'); + return foo; + })); + assert.strictEqual(bar.linkingStatus, 'linked'); + return bar; + })); + + foo.instantiate(); + await foo.evaluate(); + assert.strictEqual(foo.namespace.default, 42); +} + +async function circular2() { + const sourceMap = { + root: ` + import * as a from './a.mjs'; + import * as b from './b.mjs'; + if (!('fromA' in a)) + throw new Error(); + if (!('fromB' in a)) + throw new Error(); + if (!('fromA' in b)) + throw new Error(); + if (!('fromB' in b)) + throw new Error(); + `, + './a.mjs': ` + export * from './b.mjs'; + export var fromA; + `, + './b.mjs': ` + export * from './a.mjs'; + export var fromB; + ` + }; + const moduleMap = new Map(); + const rootModule = new Module(sourceMap.root, { url: 'vm:root' }); + async function link(specifier, referencingModule) { + if (moduleMap.has(specifier)) { + return moduleMap.get(specifier); + } + const mod = new Module(sourceMap[specifier], { url: new URL(specifier, 'file:///').href }); + moduleMap.set(specifier, mod); + return mod; + } + await rootModule.link(link); + rootModule.instantiate(); + await rootModule.evaluate(); +} + +const finished = common.mustCall(); + +(async function main() { + await simple(); + await depth(); + await circular(); + await circular2(); + finished(); +})(); diff --git a/test/parallel/test-vm-module-reevaluate.js b/test/parallel/test-vm-module-reevaluate.js new file mode 100644 index 00000000000000..e4f5858800e297 --- /dev/null +++ b/test/parallel/test-vm-module-reevaluate.js @@ -0,0 +1,49 @@ +'use strict'; + +// Flags: --experimental-vm-modules + +const common = require('../common'); +common.crashOnUnhandledRejection(); + +const assert = require('assert'); + +const { Module } = require('vm'); + +const finished = common.mustCall(); + +(async function main() { + { + const m = new Module('1'); + await m.link(common.mustNotCall()); + m.instantiate(); + assert.strictEqual((await m.evaluate()).result, 1); + assert.strictEqual((await m.evaluate()).result, undefined); + assert.strictEqual((await m.evaluate()).result, undefined); + } + + { + const m = new Module('throw new Error()'); + await m.link(common.mustNotCall()); + m.instantiate(); + + let threw = false; + try { + await m.evaluate(); + } catch (err) { + assert(err instanceof Error); + threw = true; + } + assert(threw); + + threw = false; + try { + await m.evaluate(); + } catch (err) { + assert(err instanceof Error); + threw = true; + } + assert(threw); + } + + finished(); +})(); diff --git a/test/sequential/test-benchmark-buffer.js b/test/sequential/test-benchmark-buffer.js index 26600cf4f26a87..47e4df63e67aff 100644 --- a/test/sequential/test-benchmark-buffer.js +++ b/test/sequential/test-benchmark-buffer.js @@ -11,6 +11,7 @@ runBenchmark('buffers', 'buffer=fast', 'encoding=utf8', 'len=2', + 'millions=0.000001', 'method=', 'n=1', 'noAssert=true', diff --git a/test/sequential/test-benchmark-tls.js b/test/sequential/test-benchmark-tls.js index 7c87aa3cbcd89e..3545955e3ab5b0 100644 --- a/test/sequential/test-benchmark-tls.js +++ b/test/sequential/test-benchmark-tls.js @@ -2,6 +2,9 @@ const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + if (!common.enoughTestMem) common.skip('Insufficient memory for TLS benchmark test'); diff --git a/test/sequential/test-init.js b/test/sequential/test-init.js index 1f1679b5e149f8..5dd8d9ab14ea4f 100644 --- a/test/sequential/test-init.js +++ b/test/sequential/test-init.js @@ -25,7 +25,7 @@ const assert = require('assert'); const child = require('child_process'); const fixtures = require('../common/fixtures'); -if (process.env['TEST_INIT']) { +if (process.env.TEST_INIT) { return process.stdout.write('Loaded successfully!'); } diff --git a/test/sequential/test-inspector-bindings.js b/test/sequential/test-inspector-bindings.js index b2140c11a3329a..bf97d8b5124e86 100644 --- a/test/sequential/test-inspector-bindings.js +++ b/test/sequential/test-inspector-bindings.js @@ -27,9 +27,9 @@ function checkScope(session, scopeId) { } function debuggerPausedCallback(session, notification) { - const params = notification['params']; - const callFrame = params['callFrames'][0]; - const scopeId = callFrame['scopeChain'][0]['object']['objectId']; + const params = notification.params; + const callFrame = params.callFrames[0]; + const scopeId = callFrame.scopeChain[0].object.objectId; checkScope(session, scopeId); } @@ -65,11 +65,11 @@ function testSampleDebugSession() { scopeCallback = function(error, result) { const i = cur++; let v, actual, expected; - for (v of result['result']) { - actual = v['value']['value']; - expected = expects[v['name']][i]; + for (v of result.result) { + actual = v.value.value; + expected = expects[v.name][i]; if (actual !== expected) { - failures.push(`Iteration ${i} variable: ${v['name']} ` + + failures.push(`Iteration ${i} variable: ${v.name} ` + `expected: ${expected} actual: ${actual}`); } } diff --git a/test/sequential/test-inspector-ip-detection.js b/test/sequential/test-inspector-ip-detection.js index a69a57f55fcbe3..14be5e6824a4a2 100644 --- a/test/sequential/test-inspector-ip-detection.js +++ b/test/sequential/test-inspector-ip-detection.js @@ -15,12 +15,12 @@ if (!ip) function checkIpAddress(ip, response) { const res = response[0]; - const wsUrl = res['webSocketDebuggerUrl']; + 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]); + assert.strictEqual(res.id, match[2]); + assert.strictEqual(ip, res.devtoolsFrontendUrl.match(/.*ws=(.*):\d+/)[1]); } function pickIPv4Address() { diff --git a/test/sequential/test-inspector.js b/test/sequential/test-inspector.js index c34c953006276a..23b4dcb9618a44 100644 --- a/test/sequential/test-inspector.js +++ b/test/sequential/test-inspector.js @@ -9,10 +9,10 @@ const { NodeInstance } = require('../common/inspector-helper.js'); function checkListResponse(response) { assert.strictEqual(1, response.length); - assert.ok(response[0]['devtoolsFrontendUrl']); + assert.ok(response[0].devtoolsFrontendUrl); assert.ok( /ws:\/\/127\.0\.0\.1:\d+\/[0-9A-Fa-f]{8}-/ - .test(response[0]['webSocketDebuggerUrl'])); + .test(response[0].webSocketDebuggerUrl)); } function checkVersion(response) { @@ -32,7 +32,7 @@ function checkBadPath(err) { } function checkException(message) { - assert.strictEqual(message['exceptionDetails'], undefined, + assert.strictEqual(message.exceptionDetails, undefined, 'An exception occurred during execution'); } @@ -45,10 +45,10 @@ function assertNoUrlsWhileConnected(response) { function assertScopeValues({ result }, expected) { const unmatched = new Set(Object.keys(expected)); for (const actual of result) { - const value = expected[actual['name']]; + const value = expected[actual.name]; if (value) { - assert.strictEqual(value, actual['value']['value']); - unmatched.delete(actual['name']); + assert.strictEqual(value, actual.value.value); + unmatched.delete(actual.name); } } if (unmatched.size) @@ -124,14 +124,14 @@ async function testBreakpoint(session) { } }); - assert.strictEqual(1002, result['value']); + assert.strictEqual(1002, result.value); result = (await session.send({ 'method': 'Runtime.evaluate', 'params': { 'expression': '5 * 5' } })).result; - assert.strictEqual(25, result['value']); + assert.strictEqual(25, result.value); } async function testI18NCharacters(session) { @@ -168,7 +168,7 @@ async function testCommandLineAPI(session) { } }); checkException(result); - assert.strictEqual(result['result']['value'], true); + assert.strictEqual(result.result.value, true); // the global require has the same properties as a normal `require` result = await session.send( @@ -183,7 +183,7 @@ async function testCommandLineAPI(session) { } }); checkException(result); - assert.strictEqual(result['result']['value'], true); + assert.strictEqual(result.result.value, true); // `require` twice returns the same value result = await session.send( { @@ -199,7 +199,7 @@ async function testCommandLineAPI(session) { } }); checkException(result); - assert.strictEqual(result['result']['value'], true); + assert.strictEqual(result.result.value, true); // after require the module appears in require.cache result = await session.send( { @@ -211,7 +211,7 @@ async function testCommandLineAPI(session) { } }); checkException(result); - assert.deepStrictEqual(JSON.parse(result['result']['value']), + assert.deepStrictEqual(JSON.parse(result.result.value), { old: 'yes' }); // remove module from require.cache result = await session.send( @@ -222,7 +222,7 @@ async function testCommandLineAPI(session) { } }); checkException(result); - assert.strictEqual(result['result']['value'], true); + assert.strictEqual(result.result.value, true); // require again, should get fresh (empty) exports result = await session.send( { @@ -232,7 +232,7 @@ async function testCommandLineAPI(session) { } }); checkException(result); - assert.deepStrictEqual(JSON.parse(result['result']['value']), {}); + assert.deepStrictEqual(JSON.parse(result.result.value), {}); // require 2nd module, exports an empty object result = await session.send( { @@ -242,7 +242,7 @@ async function testCommandLineAPI(session) { } }); checkException(result); - assert.deepStrictEqual(JSON.parse(result['result']['value']), {}); + assert.deepStrictEqual(JSON.parse(result.result.value), {}); // both modules end up with the same module.parent result = await session.send( { @@ -257,7 +257,7 @@ async function testCommandLineAPI(session) { } }); checkException(result); - assert.deepStrictEqual(JSON.parse(result['result']['value']), { + assert.deepStrictEqual(JSON.parse(result.result.value), { parentsEqual: true, parentId: '' }); @@ -274,7 +274,7 @@ async function testCommandLineAPI(session) { } }); checkException(result); - assert.notStrictEqual(result['result']['value'], + assert.notStrictEqual(result.result.value, ''); } diff --git a/test/sequential/test-module-loading.js b/test/sequential/test-module-loading.js index 8b097142698e6f..3d0de954c58e8a 100644 --- a/test/sequential/test-module-loading.js +++ b/test/sequential/test-module-loading.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 path = require('path'); const fs = require('fs'); @@ -197,7 +197,10 @@ try { require(`${loadOrder}file3`); } catch (e) { // Not a real .node module, but we know we require'd the right thing. - assert.ok(/file3\.node/.test(e.message.replace(backslash, '/'))); + if (common.isOpenBSD) // OpenBSD errors with non-ELF object error + assert.ok(/File not an ELF object/.test(e.message.replace(backslash, '/'))); + else + assert.ok(/file3\.node/.test(e.message.replace(backslash, '/'))); } assert.strictEqual(require(`${loadOrder}file4`).file4, 'file4.reg', msg); assert.strictEqual(require(`${loadOrder}file5`).file5, 'file5.reg2', msg); @@ -205,7 +208,10 @@ try { try { require(`${loadOrder}file7`); } catch (e) { - assert.ok(/file7\/index\.node/.test(e.message.replace(backslash, '/'))); + if (common.isOpenBSD) + assert.ok(/File not an ELF object/.test(e.message.replace(backslash, '/'))); + else + assert.ok(/file7\/index\.node/.test(e.message.replace(backslash, '/'))); } assert.strictEqual(require(`${loadOrder}file8`).file8, 'file8/index.reg', msg); diff --git a/test/sequential/test-repl-timeout-throw.js b/test/sequential/test-repl-timeout-throw.js index aa933394b42ae7..3636b93ddfc2b2 100644 --- a/test/sequential/test-repl-timeout-throw.js +++ b/test/sequential/test-repl-timeout-throw.js @@ -1,5 +1,5 @@ 'use strict'; -const common = require('../common'); +require('../common'); const assert = require('assert'); const spawn = require('child_process').spawn; @@ -13,6 +13,8 @@ child.stdout.setEncoding('utf8'); child.stdout.on('data', function(c) { process.stdout.write(c); stdout += c; + if (stdout.includes('> THROW 2')) + child.stdin.end(); }); child.stdin.write = function(original) { @@ -46,8 +48,6 @@ child.stdout.once('data', function() { ' });\n' + ' });\n' + '});"";\n'); - - setTimeout(child.stdin.end.bind(child.stdin), common.platformTimeout(200)); } }); diff --git a/test/sequential/test-timers-block-eventloop.js b/test/sequential/test-timers-block-eventloop.js index f6426e454e0882..811216fcb29e7d 100644 --- a/test/sequential/test-timers-block-eventloop.js +++ b/test/sequential/test-timers-block-eventloop.js @@ -1,24 +1,18 @@ 'use strict'; const common = require('../common'); -const fs = require('fs'); -const platformTimeout = common.platformTimeout; +const assert = require('assert'); +let called = false; const t1 = setInterval(() => { - common.busyLoop(platformTimeout(12)); -}, platformTimeout(10)); - -const t2 = setInterval(() => { - common.busyLoop(platformTimeout(15)); -}, platformTimeout(10)); - -const t3 = - setTimeout(common.mustNotCall('eventloop blocked!'), platformTimeout(200)); - -setTimeout(function() { - fs.stat('/dev/nonexistent', () => { + assert(!called); + called = true; + setImmediate(common.mustCall(() => { clearInterval(t1); clearInterval(t2); - clearTimeout(t3); - }); -}, platformTimeout(50)); + })); +}, 10); + +const t2 = setInterval(() => { + common.busyLoop(20); +}, 10); diff --git a/tools/doc/README.md b/tools/doc/README.md index 0535243e92f63b..2d5317db9e6fd7 100644 --- a/tools/doc/README.md +++ b/tools/doc/README.md @@ -1,56 +1,55 @@ Here's how the node docs work. -1:1 relationship from `lib/.js` to `doc/api/.md` +1:1 relationship from `lib/.js` to `doc/api/.md`. Each type of heading has a description block. ```md -## module - +# module + + -> Stability: 3 - Stable +> Stability: 2 - Stable -description and examples. +A description and examples. -### module.property +## module.property -* Type +* {type} -description of the property. +A description of the property. -### module.someFunction(x, y, [z=100]) +## module.someFunction(x, y, [z=100]) -* `x` {String} the description of the string -* `y` {Boolean} Should I stay or should I go? -* `z` {Number} How many zebras to bring. +* `x` {string} The description of the string. +* `y` {boolean} Should I stay or should I go? +* `z` {number} How many zebras to bring. A description of the function. -### module.someNewFunction(x) +## module.someNewFunction(x) -* `x` {String} the description of the string +* `x` {string} The description of the string. This feature is not in a release yet. -### Event: 'blerg' +## Event: 'blerg' -* Argument: SomeClass object. +* `anArg` {type} A description of the listener argument. -Modules don't usually raise events on themselves. `cluster` is the +Modules don't usually raise events on themselves. `cluster` is the only exception. ## Class: SomeClass @@ -58,50 +57,50 @@ only exception. added: v0.10.0 --> -description of the class. +A description of the class. -### Class Method: SomeClass.classMethod(anArg) +### SomeClass.classMethod(anArg) -* `anArg` {Object} Just an argument - * `field` {String} anArg can have this field. - * `field2` {Boolean} Another field. Default: `false`. -* Return: {Boolean} `true` if it worked. +* `anArg` {Object} Just an argument. + * `field` {string} `anArg` can have this field. + * `field2` {boolean} Another field. Default: `false`. +* Returns: {boolean} `true` if it worked. -Description of the method for humans. +A description of the method for humans. -### someClass.nextSibling() +### SomeClass.nextSibling() -* Return: {SomeClass object | null} The next someClass in line. +* Returns: {SomeClass | null} The next `SomeClass` in line. -### someClass.someProperty +### SomeClass.someProperty -* String +* {string} -The indication of what someProperty is. +The indication of what `someProperty` is. ### Event: 'grelb' -* `isBlerg` {Boolean} +* `isBlerg` {boolean} -This event is emitted on instances of SomeClass, not on the module itself. +This event is emitted on instances of `SomeClass`, not on the module itself. ``` -* Classes have (description, Properties, Methods, Events) -* Events have (list of arguments, description) -* Functions have (list of arguments, description) -* Methods have (list of arguments, description) -* Modules have (description, Properties, Functions, Classes, Examples) -* Properties have (type, description) +* Classes have (description, Properties, Methods, Events). +* Events have (list of arguments, description). +* Functions have (list of arguments, description). +* Methods have (list of arguments, description). +* Modules have (description, Properties, Functions, Classes, Examples). +* Properties have (type, description). diff --git a/tools/eslint-rules/crypto-check.js b/tools/eslint-rules/crypto-check.js index 9570c24c030ef4..42b17b0c80a225 100644 --- a/tools/eslint-rules/crypto-check.js +++ b/tools/eslint-rules/crypto-check.js @@ -23,6 +23,7 @@ const bindingModules = cryptoModules.concat(['tls_wrap']); module.exports = function(context) { const missingCheckNodes = []; const requireNodes = []; + var commonModuleNode = null; var hasSkipCall = false; function testCryptoUsage(node) { @@ -30,6 +31,10 @@ module.exports = function(context) { utils.isBinding(node, bindingModules)) { requireNodes.push(node); } + + if (utils.isCommonModule(node)) { + commonModuleNode = node; + } } function testIfStatement(node) { @@ -75,7 +80,20 @@ module.exports = function(context) { function report(nodes) { nodes.forEach((node) => { - context.report(node, msg); + context.report({ + node, + message: msg, + fix: (fixer) => { + if (commonModuleNode) { + return fixer.insertTextAfter( + commonModuleNode, + '\nif (!common.hasCrypto) {' + + ' common.skip("missing crypto");' + + '}' + ); + } + } + }); }); } diff --git a/tools/eslint-rules/inspector-check.js b/tools/eslint-rules/inspector-check.js index bb40a98183250c..00a2dd02963558 100644 --- a/tools/eslint-rules/inspector-check.js +++ b/tools/eslint-rules/inspector-check.js @@ -15,12 +15,17 @@ const msg = 'Please add a skipIfInspectorDisabled() call to allow this ' + module.exports = function(context) { const missingCheckNodes = []; + var commonModuleNode = null; var hasInspectorCheck = false; function testInspectorUsage(context, node) { if (utils.isRequired(node, ['inspector'])) { missingCheckNodes.push(node); } + + if (utils.isCommonModule(node)) { + commonModuleNode = node; + } } function checkMemberExpression(context, node) { @@ -32,7 +37,18 @@ module.exports = function(context) { function reportIfMissing(context) { if (!hasInspectorCheck) { missingCheckNodes.forEach((node) => { - context.report(node, msg); + context.report({ + node, + message: msg, + fix: (fixer) => { + if (commonModuleNode) { + return fixer.insertTextAfter( + commonModuleNode, + '\ncommon.skipIfInspectorDisabled();' + ); + } + } + }); }); } } diff --git a/tools/eslint-rules/rules-utils.js b/tools/eslint-rules/rules-utils.js index 2bfab1c6399ee8..88ecf658ce37f0 100644 --- a/tools/eslint-rules/rules-utils.js +++ b/tools/eslint-rules/rules-utils.js @@ -8,10 +8,21 @@ * require calls. */ module.exports.isRequired = function(node, modules) { - return node.callee.name === 'require' && + return node.callee.name === 'require' && node.arguments.length !== 0 && modules.includes(node.arguments[0].value); }; +/** +* Return true if common module is required +* in AST Node under inspection +*/ +var commonModuleRegExp = new RegExp(/^(\.\.\/)*common(\.js)?$/); +module.exports.isCommonModule = function(node) { + return node.callee.name === 'require' && + node.arguments.length !== 0 && + commonModuleRegExp.test(node.arguments[0].value); +}; + /** * Returns true if any of the passed in modules are used in * binding calls. diff --git a/tools/icu/iculslocs.cc b/tools/icu/iculslocs.cc index ca312b783565c4..3ceb8d2a4d81d0 100644 --- a/tools/icu/iculslocs.cc +++ b/tools/icu/iculslocs.cc @@ -64,7 +64,7 @@ int VERBOSE = 0; #define RES_INDEX "res_index" #define INSTALLEDLOCALES "InstalledLocales" -CharString packageName; +icu::CharString packageName; const char* locale = RES_INDEX; // locale referring to our index void usage() { @@ -147,7 +147,7 @@ int localeExists(const char* loc, UBool* exists) { if (VERBOSE > 1) { printf("Trying to open %s:%s\n", packageName.data(), loc); } - LocalUResourceBundlePointer aResource( + icu::LocalUResourceBundlePointer aResource( ures_openDirect(packageName.data(), loc, &status)); *exists = FALSE; if (U_SUCCESS(status)) { @@ -189,11 +189,11 @@ void printIndent(FILE* bf, int indent) { * @return 0 for OK, 1 for err */ int dumpAllButInstalledLocales(int lev, - LocalUResourceBundlePointer* bund, + icu::LocalUResourceBundlePointer* bund, FILE* bf, UErrorCode* status) { ures_resetIterator(bund->getAlias()); - LocalUResourceBundlePointer t; + icu::LocalUResourceBundlePointer t; while (U_SUCCESS(*status) && ures_hasNext(bund->getAlias())) { t.adoptInstead(ures_getNextResource(bund->getAlias(), t.orphan(), status)); ASSERT_SUCCESS(status, "while processing table"); @@ -254,10 +254,10 @@ int list(const char* toBundle) { printf("\"locale\": %s\n", locale); } - LocalUResourceBundlePointer bund( + icu::LocalUResourceBundlePointer bund( ures_openDirect(packageName.data(), locale, &status)); ASSERT_SUCCESS(&status, "while opening the bundle"); - LocalUResourceBundlePointer installedLocales( + icu::LocalUResourceBundlePointer installedLocales( // NOLINTNEXTLINE (readability/null_usage) ures_getByKey(bund.getAlias(), INSTALLEDLOCALES, NULL, &status)); ASSERT_SUCCESS(&status, "while fetching installed locales"); @@ -295,7 +295,7 @@ int list(const char* toBundle) { } // OK, now list them. - LocalUResourceBundlePointer subkey; + icu::LocalUResourceBundlePointer subkey; int validCount = 0; for (int32_t i = 0; i < count; i++) { diff --git a/vcbuild.bat b/vcbuild.bat index 6b631fb922d614..8758fb826138d6 100644 --- a/vcbuild.bat +++ b/vcbuild.bat @@ -169,6 +169,9 @@ if "%target%"=="Clean" echo deleting %~dp0deps\icu if "%target%"=="Clean" rmdir /S /Q %~dp0deps\icu :no-depsicu +call tools\msvs\find_python.cmd +if errorlevel 1 echo Could not find python2 & goto :exit + call :getnodeversion || exit /b 1 if defined TAG set configure_flags=%configure_flags% --tag=%TAG% @@ -265,7 +268,8 @@ goto run if defined noprojgen goto msbuild @rem Generate the VS project. -call :run-python configure %configure_flags% +echo configure %configure_flags% +python configure %configure_flags% if errorlevel 1 goto create-msvs-files-failed if not exist node.sln goto create-msvs-files-failed echo Project files generated. @@ -457,7 +461,7 @@ if defined test_node_inspect goto node-test-inspect goto node-tests :node-check-deopts -call :run-python tools\test.py --mode=release --check-deopts parallel sequential -J +python tools\test.py --mode=release --check-deopts parallel sequential -J if defined test_node_inspect goto node-test-inspect goto node-tests @@ -481,7 +485,8 @@ if defined no_cctest echo Skipping cctest because no-cctest was specified && got echo running 'cctest %cctest_args%' "%config%\cctest" %cctest_args% :run-test-py -call :run-python tools\test.py %test_args% +echo running 'python tools\test.py %test_args%' +python tools\test.py %test_args% goto test-v8 :test-v8 @@ -493,7 +498,7 @@ goto lint-cpp :lint-cpp if not defined lint_cpp goto lint-js call :run-lint-cpp src\*.c src\*.cc src\*.h test\addons\*.cc test\addons\*.h test\addons-napi\*.cc test\addons-napi\*.h test\cctest\*.cc test\cctest\*.h test\gc\binding.cc tools\icu\*.cc tools\icu\*.h -call :run-python tools/check-imports.py +python tools/check-imports.py goto lint-js :run-lint-cpp @@ -509,7 +514,7 @@ for /f "tokens=*" %%G in ('dir /b /s /a %*') do ( ( endlocal set cppfilelist=%localcppfilelist% ) -call :run-python tools/cpplint.py %cppfilelist% > nul +python tools/cpplint.py %cppfilelist% > nul goto exit :add-to-list @@ -571,14 +576,6 @@ echo vcbuild.bat lint : runs the C++ and JavaScript linter echo vcbuild.bat no-cctest : skip building cctest.exe goto exit -:run-python -call tools\msvs\find_python.cmd -if errorlevel 1 echo Could not find python2 & goto :exit -set cmd1="%VCBUILD_PYTHON_LOCATION%" %* -echo %cmd1% -%cmd1% -exit /b %ERRORLEVEL% - :exit goto :EOF @@ -591,9 +588,8 @@ rem *************** set NODE_VERSION= set TAG= set FULLVERSION= -:: Call as subroutine for validation of python -call :run-python tools\getnodeversion.py > nul -for /F "tokens=*" %%i in ('"%VCBUILD_PYTHON_LOCATION%" tools\getnodeversion.py') do set NODE_VERSION=%%i + +for /F "usebackq tokens=*" %%i in (`python "%~dp0tools\getnodeversion.py"`) do set NODE_VERSION=%%i if not defined NODE_VERSION ( echo Cannot determine current version of Node.js exit /b 1