diff --git a/.eslintignore b/.eslintignore index 6dca769b45ccf6..3b9a2f9462f299 100644 --- a/.eslintignore +++ b/.eslintignore @@ -10,3 +10,4 @@ tools/remark-* node_modules benchmark/tmp doc/**/*.js +!.eslintrc.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000000000..d3f8d19878368a --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,278 @@ +'use strict'; + +const Module = require('module'); +const path = require('path'); + +const NodePlugin = require('./tools/node_modules/eslint-plugin-node-core'); +NodePlugin.RULES_DIR = path.resolve(__dirname, 'tools', 'eslint-rules'); + +const ModuleFindPath = Module._findPath; +const hacks = [ + 'eslint-plugin-node-core', + 'eslint-plugin-markdown', + 'babel-eslint', +]; +Module._findPath = (request, paths, isMain) => { + const r = ModuleFindPath(request, paths, isMain); + if (!r && hacks.includes(request)) { + try { + return require.resolve(`./tools/node_modules/${request}`); + } catch (err) { + return require.resolve( + `./tools/node_modules/eslint/node_modules/${request}`); + } + } + return r; +}; + +module.exports = { + root: true, + plugins: ['markdown', 'node-core'], + env: { node: true, es6: true }, + parser: 'babel-eslint', + parserOptions: { sourceType: 'script' }, + overrides: [ + { + files: [ + 'doc/api/esm.md', + '*.mjs', + 'test/es-module/test-esm-example-loader.js', + ], + parserOptions: { sourceType: 'module' }, + }, + ], + rules: { + // Possible Errors + // http://eslint.org/docs/rules/#possible-errors + 'for-direction': 'error', + 'no-control-regex': 'error', + 'no-debugger': 'error', + 'no-dupe-args': 'error', + 'no-dupe-keys': 'error', + 'no-duplicate-case': 'error', + 'no-empty-character-class': 'error', + 'no-ex-assign': 'error', + 'no-extra-boolean-cast': 'error', + 'no-extra-parens': ['error', 'functions'], + 'no-extra-semi': 'error', + 'no-func-assign': 'error', + 'no-invalid-regexp': 'error', + 'no-irregular-whitespace': 'error', + 'no-obj-calls': 'error', + 'no-template-curly-in-string': 'error', + 'no-unexpected-multiline': 'error', + 'no-unreachable': 'error', + 'no-unsafe-negation': 'error', + 'use-isnan': 'error', + 'valid-typeof': 'error', + + // Best Practices + // http://eslint.org/docs/rules/#best-practices + 'accessor-pairs': 'error', + 'array-callback-return': 'error', + 'dot-location': ['error', 'property'], + 'dot-notation': 'error', + eqeqeq: ['error', 'smart'], + 'no-fallthrough': 'error', + 'no-global-assign': 'error', + 'no-multi-spaces': ['error', { ignoreEOLComments: true }], + 'no-octal': 'error', + 'no-proto': 'error', + 'no-redeclare': 'error', + 'no-restricted-properties': [ + 'error', + { + object: 'assert', + property: 'deepEqual', + message: 'Use assert.deepStrictEqual().', + }, + { + object: 'assert', + property: 'notDeepEqual', + message: 'Use assert.notDeepStrictEqual().', + }, + { + object: 'assert', + property: 'equal', + message: 'Use assert.astrictEqual() rather than assert.equal().', + }, + { + object: 'assert', + property: 'notEqual', + message: 'Use assert.notStrictEqual() rather than assert.notEqual().', + }, + { + property: '__defineGetter__', + message: '__defineGetter__ is deprecated.', + }, + { + property: '__defineSetter__', + 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', + 'no-useless-concat': 'error', + 'no-useless-escape': 'error', + 'no-useless-return': 'error', + 'no-void': 'error', + 'no-with': 'error', + + // Strict Mode + // http://eslint.org/docs/rules/#strict-mode + strict: ['error', 'global'], + + // Variables + // http://eslint.org/docs/rules/#variables + 'no-delete-var': 'error', + 'no-undef': ['error', { typeof: true }], + 'no-unused-vars': ['error', { args: 'none' }], + 'no-use-before-define': ['error', { + classes: true, + functions: false, + variables: false, + }], + + // Node.js and CommonJS + // http://eslint.org/docs/rules/#nodejs-and-commonjs + 'no-mixed-requires': 'error', + 'no-new-require': 'error', + 'no-path-concat': 'error', + 'no-restricted-modules': ['error', 'sys'], + + // Stylistic Issues + // http://eslint.org/docs/rules/#stylistic-issues' + 'block-spacing': 'error', + 'brace-style': ['error', '1tbs', { allowSingleLine: true }], + 'comma-dangle': ['error', 'only-multiline'], + 'comma-spacing': 'error', + 'comma-style': 'error', + 'computed-property-spacing': 'error', + 'eol-last': 'error', + 'func-call-spacing': 'error', + 'func-name-matching': 'error', + 'func-style': ['error', 'declaration', { allowArrowFunctions: true }], + indent: ['error', 2, { + ArrayExpression: 'first', + CallExpression: { arguments: 'first' }, + FunctionDeclaration: { parameters: 'first' }, + FunctionExpression: { parameters: 'first' }, + MemberExpression: 'off', + ObjectExpression: 'first', + SwitchCase: 1, + }], + 'key-spacing': ['error', { mode: 'minimum' }], + 'keyword-spacing': 'error', + 'linebreak-style': ['error', 'unix'], + 'max-len': ['error', { + code: 80, + ignorePattern: '^// Flags:', + ignoreRegExpLiterals: true, + ignoreUrls: true, + tabWidth: 2, + }], + 'new-parens': 'error', + 'no-lonely-if': 'error', + 'no-mixed-spaces-and-tabs': 'error', + 'no-multiple-empty-lines': ['error', { max: 2, maxEOF: 0, maxBOF: 0 }], + /* eslint-disable max-len, quotes */ + 'no-restricted-syntax': [ + 'error', + { + selector: "CallExpression[callee.object.name='assert'][callee.property.name='doesNotThrow']", + message: "Please replace `assert.doesNotThrow()` and add a comment next to the code instead." + }, + { + selector: `CallExpression[callee.object.name='assert'][callee.property.name='rejects'][arguments.length<2]`, + message: 'assert.rejects() must be invoked with at least two arguments.', + }, + { + selector: `CallExpression[callee.object.name='assert'][callee.property.name='throws'][arguments.1.type='Literal']:not([arguments.1.regex])`, + message: 'Use an object as second argument of assert.throws()', + }, + { + selector: `CallExpression[callee.object.name='assert'][callee.property.name='throws'][arguments.length<2]`, + message: 'assert.throws() must be invoked with at least two arguments.', + }, + { + selector: `CallExpression[callee.name='setTimeout'][arguments.length<2]`, + message: 'setTimeout() must be invoked with at least two arguments.', + }, + { + selector: `CallExpression[callee.name='setInterval'][arguments.length<2]`, + message: 'setInterval() must be invoked with at least 2 arguments.', + }, + { + selector: 'ThrowStatement > CallExpression[callee.name=/Error$/]', + message: 'Use new keyword when throwing an Error.', + } + ], + /* eslint-enable max-len, quotes */ + 'no-tabs': 'error', + 'no-trailing-spaces': 'error', + 'no-unsafe-finally': 'error', + 'object-curly-spacing': ['error', 'always'], + 'one-var-declaration-per-line': 'error', + 'operator-linebreak': ['error', 'after'], + quotes: ['error', 'single', 'avoid-escape'], + semi: 'error', + 'semi-spacing': 'error', + 'space-before-blocks': ['error', 'always'], + 'space-before-function-paren': ['error', { + anonymous: 'never', + named: 'never', + asyncArrow: 'always', + }], + 'space-in-parens': ['error', 'never'], + 'space-infix-ops': 'error', + 'space-unary-ops': 'error', + 'spaced-comment': ['error', 'always', { + 'block': { 'balanced': true }, + 'exceptions': ['-'] + }], + 'unicode-bom': 'error', + + // ECMAScript 6 + // http://eslint.org/docs/rules/#ecmascript-6 + 'arrow-parens': ['error', 'always'], + 'arrow-spacing': ['error', { before: true, after: true }], + 'constructor-super': 'error', + 'no-class-assign': 'error', + 'no-confusing-arrow': 'error', + 'no-const-assign': 'error', + 'no-dupe-class-members': 'error', + 'no-new-symbol': 'error', + 'no-this-before-super': 'error', + 'prefer-const': ['error', { ignoreReadBeforeAssign: true }], + 'rest-spread-spacing': 'error', + 'symbol-description': 'error', + 'template-curly-spacing': 'error', + + // Custom rules in from eslint-plugin-node-core + 'node-core/no-unescaped-regexp-dot': 'error', + }, + globals: { + COUNTER_HTTP_CLIENT_REQUEST: false, + COUNTER_HTTP_CLIENT_RESPONSE: false, + COUNTER_HTTP_SERVER_REQUEST: false, + COUNTER_HTTP_SERVER_RESPONSE: false, + COUNTER_NET_SERVER_CONNECTION: false, + COUNTER_NET_SERVER_CONNECTION_CLOSE: false, + DTRACE_HTTP_CLIENT_REQUEST: false, + DTRACE_HTTP_CLIENT_RESPONSE: false, + DTRACE_HTTP_SERVER_REQUEST: false, + DTRACE_HTTP_SERVER_RESPONSE: false, + DTRACE_NET_SERVER_CONNECTION: false, + DTRACE_NET_STREAM_END: false, + LTTNG_HTTP_CLIENT_REQUEST: false, + LTTNG_HTTP_CLIENT_RESPONSE: false, + LTTNG_HTTP_SERVER_REQUEST: false, + LTTNG_HTTP_SERVER_RESPONSE: false, + LTTNG_NET_SERVER_CONNECTION: false, + LTTNG_NET_STREAM_END: false + }, +}; diff --git a/.eslintrc.yaml b/.eslintrc.yaml deleted file mode 100644 index 201af11f017733..00000000000000 --- a/.eslintrc.yaml +++ /dev/null @@ -1,216 +0,0 @@ -root: true - -plugins: - - markdown - -env: - node: true - es6: true - -parser: babel-eslint - -parserOptions: - sourceType: script - -overrides: - - files: ["doc/api/esm.md", "*.mjs", "test/es-module/test-esm-example-loader.js"] - parserOptions: - sourceType: module - -rules: - # Possible Errors - # http://eslint.org/docs/rules/#possible-errors - for-direction: error - no-control-regex: error - no-debugger: error - no-dupe-args: error - no-dupe-keys: error - no-duplicate-case: error - no-empty-character-class: error - no-ex-assign: error - no-extra-boolean-cast: error - no-extra-parens: [error, functions] - no-extra-semi: error - no-func-assign: error - no-invalid-regexp: error - no-irregular-whitespace: error - no-obj-calls: error - no-template-curly-in-string: error - no-unexpected-multiline: error - no-unreachable: error - no-unsafe-negation: error - use-isnan: error - valid-typeof: error - - # Best Practices - # http://eslint.org/docs/rules/#best-practices - accessor-pairs: error - array-callback-return: error - dot-location: [error, property] - dot-notation: error - eqeqeq: [error, smart] - no-fallthrough: error - no-global-assign: error - no-multi-spaces: [error, {ignoreEOLComments: true}] - no-octal: error - no-proto: error - no-redeclare: error - no-restricted-properties: - - error - - object: assert - property: deepEqual - message: Use assert.deepStrictEqual(). - - object: assert - property: notDeepEqual - message: Use assert.notDeepStrictEqual(). - - object: assert - property: equal - message: Use assert.strictEqual() rather than assert.equal(). - - object: assert - property: notEqual - message: Use assert.notStrictEqual() rather than assert.notEqual(). - - property: __defineGetter__ - message: __defineGetter__ is deprecated. - - property: __defineSetter__ - 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 - no-useless-concat: error - no-useless-escape: error - no-useless-return: error - no-void: error - no-with: error - - # Strict Mode - # http://eslint.org/docs/rules/#strict-mode - strict: [error, global] - - # Variables - # http://eslint.org/docs/rules/#variables - no-delete-var: error - no-undef: error - no-unused-vars: [error, {args: none}] - no-use-before-define: [error, {classes: true, - functions: false, - variables: false}] - - # Node.js and CommonJS - # http://eslint.org/docs/rules/#nodejs-and-commonjs - no-mixed-requires: error - no-new-require: error - no-path-concat: error - no-restricted-modules: [error, sys] - - # Stylistic Issues - # http://eslint.org/docs/rules/#stylistic-issues - block-spacing: error - brace-style: [error, 1tbs, {allowSingleLine: true}] - comma-dangle: [error, only-multiline] - comma-spacing: error - comma-style: error - computed-property-spacing: error - eol-last: error - func-call-spacing: error - func-name-matching: error - func-style: [error, declaration, {allowArrowFunctions: true}] - indent: [error, 2, {ArrayExpression: first, - CallExpression: {arguments: first}, - FunctionDeclaration: {parameters: first}, - FunctionExpression: {parameters: first}, - MemberExpression: off, - ObjectExpression: first, - SwitchCase: 1}] - key-spacing: [error, {mode: minimum}] - keyword-spacing: error - linebreak-style: [error, unix] - max-len: [error, {code: 80, - ignorePattern: "^\/\/ Flags:", - ignoreRegExpLiterals: true, - ignoreUrls: true, - tabWidth: 2}] - new-parens: error - no-lonely-if: error - no-mixed-spaces-and-tabs: error - no-multiple-empty-lines: [error, {max: 2, maxEOF: 0, maxBOF: 0}] - no-restricted-syntax: [error, { - selector: "CallExpression[callee.object.name='assert'][callee.property.name='doesNotThrow']", - message: "Please replace `assert.doesNotThrow()` and add a comment next to the code instead." - }, { - selector: "CallExpression[callee.object.name='assert'][callee.property.name='throws'][arguments.1.type='Literal']:not([arguments.1.regex])", - message: "Use a regular expression for second argument of assert.throws()" - }, { - selector: "CallExpression[callee.object.name='assert'][callee.property.name='throws'][arguments.length<2]", - message: "assert.throws() must be invoked with at least two arguments." - }, { - selector: "CallExpression[callee.name='setTimeout'][arguments.length<2]", - message: "setTimeout() must be invoked with at least two arguments." - }, { - selector: "CallExpression[callee.name='setInterval'][arguments.length<2]", - message: "setInterval() must be invoked with at least 2 arguments." - }, { - selector: "ThrowStatement > CallExpression[callee.name=/Error$/]", - message: "Use new keyword when throwing an Error." - }] - no-tabs: error - no-trailing-spaces: error - no-unsafe-finally: error - object-curly-spacing: [error, always] - one-var-declaration-per-line: error - operator-linebreak: [error, after] - quotes: [error, single, avoid-escape] - semi: error - semi-spacing: error - space-before-blocks: [error, always] - space-before-function-paren: [error, { - anonymous: never, - named: never, - asyncArrow: always - }] - space-in-parens: [error, never] - space-infix-ops: error - space-unary-ops: error - unicode-bom: error - - # ECMAScript 6 - # http://eslint.org/docs/rules/#ecmascript-6 - arrow-parens: [error, always] - arrow-spacing: [error, {before: true, after: true}] - constructor-super: error - no-class-assign: error - no-confusing-arrow: error - no-const-assign: error - no-dupe-class-members: error - no-new-symbol: error - no-this-before-super: error - prefer-const: [error, {ignoreReadBeforeAssign: true}] - rest-spread-spacing: error - symbol-description: error - template-curly-spacing: error - - # Custom rules in tools/eslint-rules - no-unescaped-regexp-dot: error - -# Global scoped method and vars -globals: - COUNTER_HTTP_CLIENT_REQUEST: false - COUNTER_HTTP_CLIENT_RESPONSE: false - COUNTER_HTTP_SERVER_REQUEST: false - COUNTER_HTTP_SERVER_RESPONSE: false - COUNTER_NET_SERVER_CONNECTION: false - COUNTER_NET_SERVER_CONNECTION_CLOSE: false - DTRACE_HTTP_CLIENT_REQUEST: false - DTRACE_HTTP_CLIENT_RESPONSE: false - DTRACE_HTTP_SERVER_REQUEST: false - DTRACE_HTTP_SERVER_RESPONSE: false - DTRACE_NET_SERVER_CONNECTION: false - DTRACE_NET_STREAM_END: false - LTTNG_HTTP_CLIENT_REQUEST: false - LTTNG_HTTP_CLIENT_RESPONSE: false - LTTNG_HTTP_SERVER_REQUEST: false - LTTNG_HTTP_SERVER_RESPONSE: false - LTTNG_NET_SERVER_CONNECTION: false - LTTNG_NET_STREAM_END: false diff --git a/.gitignore b/.gitignore index a9d428ad6abafc..7be817acce2349 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ !tools/doc/node_modules/**/.* !.editorconfig !.eslintignore -!.eslintrc.yaml +!.eslintrc.js !.gitattributes !.github !.gitignore diff --git a/BUILDING.md b/BUILDING.md index 5f45dbaa81d5a2..29045855ab4995 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -34,7 +34,7 @@ Support is divided into three tiers: ### Supported platforms The community does not build or test against end-of-life distributions (EoL). -Thus we do not recommend that you use Node on end-of-life or unsupported +Thus we do not recommend that you use Node on end-of-life or unsupported platforms in production. | System | Support type | Version | Architectures | Notes | @@ -107,18 +107,20 @@ On macOS, you will need to install the `Xcode Command Line Tools` by running installed, you can find them under the menu `Xcode -> Open Developer Tool -> More Developer Tools...`. This step will install `clang`, `clang++`, and `make`. -* After building, you may want to setup [firewall rules](tools/macosx-firewall.sh) -to avoid popups asking to accept incoming network connections when running -tests: -If the path to your build directory contains a space, the build will likely +If the path to your build directory contains a space, the build will likely fail. +After building, setting up [firewall rules](tools/macosx-firewall.sh) can avoid +popups asking to accept incoming network connections when running tests. + +Running the following script on macOS will add the firewall rules for the +executable `node` in the `out` directory and the symbolic `node` link in the +project's root directory. + ```console $ sudo ./tools/macosx-firewall.sh ``` -Running this script will add rules for the executable `node` in the `out` -directory and the symbolic `node` link in the project's root directory. On FreeBSD and OpenBSD, you may also need: * libexecinfo @@ -244,7 +246,7 @@ Prerequisites: * **Optional** (to build the MSI): the [WiX Toolset v3.11](http://wixtoolset.org/releases/) and the [Wix Toolset Visual Studio 2017 Extension](https://marketplace.visualstudio.com/items?itemName=RobMensching.WixToolsetVisualStudio2017Extension). -If the path to your build directory contains a space or a non-ASCII character, +If the path to your build directory contains a space or a non-ASCII character, the build will likely fail. ```console diff --git a/COLLABORATOR_GUIDE.md b/COLLABORATOR_GUIDE.md index cc213f502a8862..dbb00ee5fddbb5 100644 --- a/COLLABORATOR_GUIDE.md +++ b/COLLABORATOR_GUIDE.md @@ -208,19 +208,24 @@ confirm that the changes are not resulting in an unreliable test. #### Useful CI Jobs * [`node-test-pull-request`](https://ci.nodejs.org/job/node-test-pull-request/) -is the standard CI run we do to check Pull Requests. It triggers -`node-test-commit`, which runs the `build-ci` and `test-ci` targets on all +is the standard CI run we do to check Pull Requests. It triggers +`node-test-commit`, which runs the `build-ci` and `test-ci` targets on all supported platforms. +* [`node-test-linter`](https://ci.nodejs.org/job/node-test-linter/) +only runs the linter targets, which is useful for changes that only affect +comments or documentation. + * [`node-test-pull-request-lite`](https://ci.nodejs.org/job/node-test-pull-request-lite/) only runs the linter job, as well as the tests on LinuxONE, which is very fast. This is useful for changes that only affect comments or documentation. * [`citgm-smoker`](https://ci.nodejs.org/job/citgm-smoker/) -uses [`CitGM`](https://github.com/nodejs/citgm) to allow you to run `npm -install && npm test` on a large selection of common modules. This is useful to -check whether a change will cause breakage in the ecosystem. To test Node.js -ABI changes you can run [`citgm-abi-smoker`](https://ci.nodejs.org/job/citgm-abi-smoker/). +uses [`CitGM`](https://github.com/nodejs/citgm) to allow you to run +`npm install && npm test` on a large selection of common modules. This is useful +to check whether a change will cause breakage in the ecosystem. To test Node.js +ABI changes you can run +[`citgm-abi-smoker`](https://ci.nodejs.org/job/citgm-abi-smoker/). * [`node-stress-single-test`](https://ci.nodejs.org/job/node-stress-single-test/) is designed to allow one to run a group of tests over and over on a specific @@ -561,7 +566,7 @@ Apply external patches: $ curl -L https://github.com/nodejs/node/pull/xxx.patch | git am --whitespace=fix ``` -If the merge fails even though recent CI runs were successful, then a 3-way +If the merge fails even though recent CI runs were successful, then a 3-way merge may be required. In this case try: ```text @@ -570,8 +575,8 @@ $ curl -L https://github.com/nodejs/node/pull/xxx.patch | git am -3 --whitespace ``` If the 3-way merge succeeds you can proceed, but make sure to check the changes against the original PR carefully and build/test on at least one platform -before landing. If the 3-way merge fails, then it is most likely that a -conflicting PR has landed since the CI run and you will have to ask the author +before landing. If the 3-way merge fails, then it is most likely that a +conflicting PR has landed since the CI run and you will have to ask the author to rebase. Check and re-review the changes: diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 695e01cd7e35bc..83d4d9b50a7abe 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -160,7 +160,7 @@ repository, with a summary of the nominee's contributions, for example: Node.js organization * Can be shown using the links `https://github.com/search?q=author%3A${GITHUB_ID}++org%3Anodejs&type=Issues` - and + and `https://github.com/search?q=commenter%3A${GITHUB_ID}++org%3Anodejs&type=Issues` * Other participation in the wider Node.js community diff --git a/Makefile b/Makefile index bde51631ff5871..964866359bdda2 100644 --- a/Makefile +++ b/Makefile @@ -1108,7 +1108,7 @@ endif LINT_JS_TARGETS = benchmark doc lib test tools run-lint-js = tools/node_modules/eslint/bin/eslint.js --cache \ - --rulesdir=tools/eslint-rules --ext=.js,.mjs,.md $(LINT_JS_TARGETS) + --ext=.js,.mjs,.md $(LINT_JS_TARGETS) --ignore-pattern '!.eslintrc.js' run-lint-js-fix = $(run-lint-js) --fix .PHONY: lint-js-fix @@ -1137,7 +1137,8 @@ lint-js-ci: jslint-ci: lint-js-ci @echo "Please use lint-js-ci instead of jslint-ci" -LINT_CPP_ADDON_DOC_FILES = $(wildcard test/addons/??_*/*.cc test/addons/??_*/*.h) +LINT_CPP_ADDON_DOC_FILES_GLOB = test/addons/??_*/*.cc test/addons/??_*/*.h +LINT_CPP_ADDON_DOC_FILES = $(wildcard $(LINT_CPP_ADDON_DOC_FILES_GLOB)) LINT_CPP_EXCLUDE ?= LINT_CPP_EXCLUDE += src/node_root_certs.h LINT_CPP_EXCLUDE += $(LINT_CPP_ADDON_DOC_FILES) @@ -1180,7 +1181,7 @@ tools/.cpplintstamp: $(LINT_CPP_FILES) lint-addon-docs: test/addons/.docbuildstamp @echo "Running C++ linter on addon docs..." - @$(PYTHON) tools/cpplint.py --filter=$(ADDON_DOC_LINT_FLAGS) $(LINT_CPP_ADDON_DOC_FILES) + @$(PYTHON) tools/cpplint.py --filter=$(ADDON_DOC_LINT_FLAGS) $(LINT_CPP_ADDON_DOC_FILES_GLOB) cpplint: lint-cpp @echo "Please use lint-cpp instead of cpplint" diff --git a/README.md b/README.md index 7b2ed7ce2b7ef7..57d1e02fc97b5a 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@

- Node.js @@ -213,7 +213,7 @@ us a report nonetheless. - [#5507](https://github.com/nodejs/node/pull/5507): _Fix a defect that makes the CacheBleed Attack possible_. Many, though not all, OpenSSL vulnerabilities - in the TLS/SSL protocols also effect Node.js. + in the TLS/SSL protocols also affect Node.js. - [CVE-2016-2216](https://nodejs.org/en/blog/vulnerability/february-2016-security-releases/): _Fix defects in HTTP header parsing for requests and responses that can allow @@ -257,15 +257,13 @@ For more information about the governance of the Node.js project, see * [MylesBorins](https://github.com/MylesBorins) - **Myles Borins** <myles.borins@gmail.com> (he/him) * [ofrobots](https://github.com/ofrobots) - -**Ali Ijaz Sheikh** <ofrobots@google.com> +**Ali Ijaz Sheikh** <ofrobots@google.com> (he/him) * [rvagg](https://github.com/rvagg) - **Rod Vagg** <rod@vagg.org> * [targos](https://github.com/targos) - **Michaël Zasso** <targos@protonmail.com> (he/him) * [thefourtheye](https://github.com/thefourtheye) - **Sakthipriyan Vairamani** <thechargingvolcano@gmail.com> (he/him) -* [trevnorris](https://github.com/trevnorris) - -**Trevor Norris** <trev.norris@gmail.com> * [Trott](https://github.com/Trott) - **Rich Trott** <rtrott@gmail.com> (he/him) @@ -293,6 +291,8 @@ For more information about the governance of the Node.js project, see **Bert Belder** <bertbelder@gmail.com> * [shigeki](https://github.com/shigeki) - **Shigeki Ohtsu** <ohtsu@ohtsu.org> (he/him) +* [trevnorris](https://github.com/trevnorris) - +**Trevor Norris** <trev.norris@gmail.com> ### Collaborators @@ -443,7 +443,7 @@ For more information about the governance of the Node.js project, see * [not-an-aardvark](https://github.com/not-an-aardvark) - **Teddy Katz** <teddy.katz@gmail.com> * [ofrobots](https://github.com/ofrobots) - -**Ali Ijaz Sheikh** <ofrobots@google.com> +**Ali Ijaz Sheikh** <ofrobots@google.com> (he/him) * [orangemocha](https://github.com/orangemocha) - **Alexis Campailla** <orangemocha@nodejs.org> * [othiym23](https://github.com/othiym23) - diff --git a/benchmark/buffers/buffer-base64-decode-wrapped.js b/benchmark/buffers/buffer-base64-decode-wrapped.js index 61e3bb654ee7c0..1e6f1fde0ae3c8 100644 --- a/benchmark/buffers/buffer-base64-decode-wrapped.js +++ b/benchmark/buffers/buffer-base64-decode-wrapped.js @@ -13,7 +13,7 @@ function main({ n }) { const line = `${'abcd'.repeat(charsPerLine / 4)}\n`; const data = line.repeat(linesCount); - // eslint-disable-next-line no-unescaped-regexp-dot + // eslint-disable-next-line node-core/no-unescaped-regexp-dot data.match(/./); // Flatten the string const buffer = Buffer.alloc(bytesCount, line, 'base64'); diff --git a/benchmark/buffers/buffer-base64-decode.js b/benchmark/buffers/buffer-base64-decode.js index 492922fb2b6eac..1631ed4f089fd7 100644 --- a/benchmark/buffers/buffer-base64-decode.js +++ b/benchmark/buffers/buffer-base64-decode.js @@ -8,7 +8,7 @@ const bench = common.createBenchmark(main, { function main({ n }) { const s = 'abcd'.repeat(8 << 20); - // eslint-disable-next-line no-unescaped-regexp-dot + // eslint-disable-next-line node-core/no-unescaped-regexp-dot s.match(/./); // Flatten string. assert.strictEqual(s.length % 4, 0); const b = Buffer.allocUnsafe(s.length / 4 * 3); diff --git a/benchmark/buffers/buffer-swap.js b/benchmark/buffers/buffer-swap.js index 8f6e1f51d3a0f2..a85bcf32307313 100644 --- a/benchmark/buffers/buffer-swap.js +++ b/benchmark/buffers/buffer-swap.js @@ -4,7 +4,7 @@ const common = require('../common.js'); const bench = common.createBenchmark(main, { aligned: ['true', 'false'], - method: ['swap16', 'swap32', 'swap64'/*, 'htons', 'htonl', 'htonll'*/], + method: ['swap16', 'swap32', 'swap64'/* , 'htons', 'htonl', 'htonll' */], len: [8, 64, 128, 256, 512, 768, 1024, 1536, 2056, 4096, 8192], n: [5e7] }); diff --git a/deps/cares/cares.gyp b/deps/cares/cares.gyp index 03ae185fcce85a..fce52acb31f495 100644 --- a/deps/cares/cares.gyp +++ b/deps/cares/cares.gyp @@ -40,6 +40,7 @@ 'include/ares_rules.h', 'include/ares_version.h', 'include/nameser.h', + 'src/ares_android.c', 'src/ares_cancel.c', 'src/ares__close_sockets.c', 'src/ares_create_query.c', diff --git a/deps/cares/include/ares.h b/deps/cares/include/ares.h index cfd72b0c51be3e..65a82cb5b7eb47 100644 --- a/deps/cares/include/ares.h +++ b/deps/cares/include/ares.h @@ -68,6 +68,10 @@ # include #endif +#if defined(ANDROID) || defined(__ANDROID__) +#include +#endif + #ifdef __cplusplus extern "C" { #endif @@ -307,6 +311,12 @@ CARES_EXTERN int ares_library_init_mem(int flags, void (*afree)(void *ptr), void *(*arealloc)(void *ptr, size_t size)); +#if defined(ANDROID) || defined(__ANDROID__) +CARES_EXTERN void ares_library_init_jvm(JavaVM *jvm); +CARES_EXTERN int ares_library_init_android(jobject connectivity_manager); +CARES_EXTERN int ares_library_android_initialized(void); +#endif + CARES_EXTERN int ares_library_initialized(void); CARES_EXTERN void ares_library_cleanup(void); diff --git a/deps/cares/include/ares_version.h b/deps/cares/include/ares_version.h index afa46c632fd991..61b2b98a8d3683 100644 --- a/deps/cares/include/ares_version.h +++ b/deps/cares/include/ares_version.h @@ -3,15 +3,15 @@ #define ARES__VERSION_H /* This is the global package copyright */ -#define ARES_COPYRIGHT "2004 - 2016 Daniel Stenberg, ." +#define ARES_COPYRIGHT "2004 - 2017 Daniel Stenberg, ." #define ARES_VERSION_MAJOR 1 -#define ARES_VERSION_MINOR 13 +#define ARES_VERSION_MINOR 14 #define ARES_VERSION_PATCH 0 #define ARES_VERSION ((ARES_VERSION_MAJOR<<16)|\ (ARES_VERSION_MINOR<<8)|\ (ARES_VERSION_PATCH)) -#define ARES_VERSION_STR "1.13.0" +#define ARES_VERSION_STR "1.14.0" #if (ARES_VERSION >= 0x010700) # define CARES_HAVE_ARES_LIBRARY_INIT 1 diff --git a/deps/cares/src/AUTHORS b/deps/cares/src/AUTHORS index af29ec86471fa0..7db6584c91f34f 100644 --- a/deps/cares/src/AUTHORS +++ b/deps/cares/src/AUTHORS @@ -37,6 +37,7 @@ Frederic Germain Geert Uytterhoeven George Neill Gisle Vanem +Google LLC Gregor Jasny Guenter Knauf Guilherme Balena Versiani @@ -45,6 +46,7 @@ Henrik Stoerner Jakub Hrozek James Bursa Jérémy Lal +John Schember Keith Shaw Lei Shi Marko Kreen diff --git a/deps/cares/src/README.md b/deps/cares/src/README.md index 6ad0168dc9a842..93c1f5f812de4c 100644 --- a/deps/cares/src/README.md +++ b/deps/cares/src/README.md @@ -5,6 +5,7 @@ c-ares [![Windows Build Status](https://ci.appveyor.com/api/projects/status/03i7151772eq3wn3/branch/master?svg=true)](https://ci.appveyor.com/project/c-ares/c-ares) [![Coverage Status](https://coveralls.io/repos/c-ares/c-ares/badge.svg?branch=master&service=github)](https://coveralls.io/github/c-ares/c-ares?branch=master) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/291/badge)](https://bestpractices.coreinfrastructure.org/projects/291) +[![Releases](https://coderelease.io/badge/c-ares/c-ares)](https://coderelease.io/github/repository/c-ares/c-ares) This is c-ares, an asynchronous resolver library. It is intended for applications which need to perform DNS queries without blocking, or need to diff --git a/deps/cares/src/RELEASE-NOTES b/deps/cares/src/RELEASE-NOTES index 122390473ab59b..91230a325b9a73 100644 --- a/deps/cares/src/RELEASE-NOTES +++ b/deps/cares/src/RELEASE-NOTES @@ -1,53 +1,47 @@ -c-ares version 1.13.0 +c-ares version 1.14.0 Changes: - - o cmake build system support added - o Add virtual function set for socket IO: ares_set_socket_functions [5] + o android: Introduce new ares_library_init_android() call for Oreo support. [5] Bug fixes: - o CVE-2017-1000381: c-ares NAPTR parser out of bounds access [1] - o macos: do not set HAVE_CLOCK_GETTIME_MONOTONIC - o test: check ares_create_query with too-long name - o dist: add ares_library_initialized.* to the tarball - o fix build on OpenBSD - o dist: ship msvc_ver.inc too [2] - o test: Add gTest/gMock files to SOURCES - o test: add fuzz entrypoint for ares_create_query() - o configure: clock_gettime workaround [3] - o docs: convert INSTALL to MarkDown & tweak [4] - o ares_process: fix return type of socket_create function (win32 warning) - o docs: fixed references to ares_set_local_ip4 and ares_set_local_ip6 - o Windows DNS server sorting [6] - o Use ares_socklen_t instead of socket_t [7] - o ares_create_query: use ares_free not naked free - o msvc_ver.inc support most recent Visual Studio 2017 [8] - o acountry: Convert char from ISO-8859-1 to UTF-8 [9] - o ares_expand_name: limit number of indirections - o configure: do not check for ar if specified manually [10] - o Added support for Windows DNS Suffix Search List [11] - o ares.h: support compiling with QNX [12] + o Fix patch for CVE-2017-1000381 to not be overly aggressive. [1] + o win32: Preserve DNS server order returned by Windows when sorting and exclude + DNS servers in legacy subnets. [2] [4] + o win32: Support most recent Visual Studio 2017 + o gethostbyaddr should fail with ECANCELLED not ENOTFOUND when ares_cancel + is called. [3] + o win32: Exclude legacy ipv6 subnets [4] + o android: Applications compiled for Oreo can no longer use + __system_property_get and must use Java calls to retrieve DNS servers. + [5] [7] + o win32: Force use of ANSI functions [6] + o CMake minimum version is now 3.1 + o ares_gethostbyname.3: fix callback status values [8] + o docs: Document WSAStartup requirement [9] + o Fix a typo in init_by_resolv_conf [10] + o Android JNI code leaks local references in some cases [11] + o Force using the ANSI versions of WinAPI functions [12] Thanks go to these friendly people for their efforts and contributions: - Aaron Bieber, Andrew Sullivan, Brad House, Bruce Stephens, Calle Wilund, - Chris Araman, Christian Ammer, Daniel Stenberg, David Drysdale, David Hotham, - Dionna Glaze, Gregor Jasny, Michael Osei, Mulle kybernetiK, noiz at github, - Sergii Pylypenko, Stephen Sorley, Thomas Köckerbauer, + AC Thompson, Anna Henningsen, Antonio Tajuelo, Brad House, Brad Spencer, + Christian Ammer, Daniel Stenberg, David Drysdale, David Hotham, Felix Yan, + Gergely Nagy, Gregor Jasny, Jakub Hrozek, John Schember, + Konstantinos Sofokleous, Roman Teterin, Sergey Kolomenkin, Sheel Bedi, (18 contributors) References to bug reports and discussions on issues: - [1] = https://c-ares.haxx.se/adv_20170620.html - [2] = https://github.com/c-ares/c-ares/issues/69 - [3] = https://github.com/c-ares/c-ares/issues/71 - [4] = https://github.com/c-ares/c-ares/issues/83 - [5] = https://github.com/c-ares/c-ares/issues/72 - [6] = https://github.com/c-ares/c-ares/issues/81 - [7] = https://github.com/c-ares/c-ares/issues/92 - [8] = https://github.com/c-ares/c-ares/issues/101 - [9] = https://github.com/c-ares/c-ares/issues/97 - [10] = https://github.com/c-ares/c-ares/issues/62 - [11] = https://github.com/c-ares/c-ares/issues/93 - [12] = https://github.com/c-ares/c-ares/issues/113 + [1] = https://github.com/c-ares/c-ares/commit/18ea99 + [2] = https://github.com/c-ares/c-ares/issues/150 + [3] = https://github.com/c-ares/c-ares/pull/138 + [4] = https://github.com/c-ares/c-ares/pull/144 + [5] = https://github.com/c-ares/c-ares/pull/148 + [6] = https://github.com/c-ares/c-ares/pull/142 + [7] = https://github.com/c-ares/c-ares/pull/175 + [8] = https://c-ares.haxx.se/mail/c-ares-archive-2011-06/0012.shtml + [9] = https://github.com/c-ares/c-ares/pull/180 + [10] = https://github.com/c-ares/c-ares/pull/160 + [11] = https://github.com/c-ares/c-ares/pull/175 + [12] = https://github.com/c-ares/c-ares/pull/142 diff --git a/deps/cares/src/ares_android.c b/deps/cares/src/ares_android.c new file mode 100644 index 00000000000000..ab388110bdeff9 --- /dev/null +++ b/deps/cares/src/ares_android.c @@ -0,0 +1,347 @@ +/* Copyright (C) 2017 by John Schember + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of M.I.T. not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ +#if defined(ANDROID) || defined(__ANDROID__) + +#include + +#include "ares_setup.h" +#include "ares.h" +#include "ares_android.h" +#include "ares_private.h" + +static JavaVM *android_jvm = NULL; +static jobject android_connectivity_manager = NULL; + +/* ConnectivityManager.getActiveNetwork */ +static jmethodID android_cm_active_net_mid = NULL; +/* ConnectivityManager.getLinkProperties */ +static jmethodID android_cm_link_props_mid = NULL; +/* LinkProperties.getDnsServers */ +static jmethodID android_lp_dns_servers_mid = NULL; +/* List.size */ +static jmethodID android_list_size_mid = NULL; +/* List.get */ +static jmethodID android_list_get_mid = NULL; +/* InetAddress.getHostAddress */ +static jmethodID android_ia_host_addr_mid = NULL; + +static jclass jni_get_class(JNIEnv *env, const char *path) +{ + jclass cls = NULL; + + if (env == NULL || path == NULL || *path == '\0') + return NULL; + + cls = (*env)->FindClass(env, path); + if ((*env)->ExceptionOccurred(env)) { + (*env)->ExceptionClear(env); + return NULL; + } + return cls; +} + +static jmethodID jni_get_method_id(JNIEnv *env, jclass cls, + const char *func_name, const char *signature) +{ + jmethodID mid = NULL; + + if (env == NULL || cls == NULL || func_name == NULL || *func_name == '\0' || + signature == NULL || *signature == '\0') + { + return NULL; + } + + mid = (*env)->GetMethodID(env, cls, func_name, signature); + if ((*env)->ExceptionOccurred(env)) + { + (*env)->ExceptionClear(env); + return NULL; + } + + return mid; +} + +void ares_library_init_jvm(JavaVM *jvm) +{ + android_jvm = jvm; +} + +int ares_library_init_android(jobject connectivity_manager) +{ + JNIEnv *env = NULL; + int need_detatch = 0; + int res; + int ret = ARES_ENOTINITIALIZED; + jclass obj_cls = NULL; + + if (android_jvm == NULL) + goto cleanup; + + res = (*android_jvm)->GetEnv(android_jvm, (void **)&env, JNI_VERSION_1_6); + if (res == JNI_EDETACHED) + { + env = NULL; + res = (*android_jvm)->AttachCurrentThread(android_jvm, &env, NULL); + need_detatch = 1; + } + if (res != JNI_OK || env == NULL) + goto cleanup; + + android_connectivity_manager = + (*env)->NewGlobalRef(env, connectivity_manager); + if (android_connectivity_manager == NULL) + goto cleanup; + + /* Initialization has succeeded. Now attempt to cache the methods that will be + * called by ares_get_android_server_list. */ + ret = ARES_SUCCESS; + + /* ConnectivityManager in API 1. */ + obj_cls = jni_get_class(env, "android/net/ConnectivityManager"); + if (obj_cls == NULL) + goto cleanup; + + /* ConnectivityManager.getActiveNetwork in API 23. */ + android_cm_active_net_mid = + jni_get_method_id(env, obj_cls, "getActiveNetwork", + "()Landroid/net/Network;"); + if (android_cm_active_net_mid == NULL) + goto cleanup; + + /* ConnectivityManager.getLinkProperties in API 21. */ + android_cm_link_props_mid = + jni_get_method_id(env, obj_cls, "getLinkProperties", + "(Landroid/net/Network;)Landroid/net/LinkProperties;"); + if (android_cm_link_props_mid == NULL) + goto cleanup; + + /* LinkProperties in API 21. */ + (*env)->DeleteLocalRef(env, obj_cls); + obj_cls = jni_get_class(env, "android/net/LinkProperties"); + if (obj_cls == NULL) + goto cleanup; + + /* getDnsServers in API 21. */ + android_lp_dns_servers_mid = jni_get_method_id(env, obj_cls, "getDnsServers", + "()Ljava/util/List;"); + if (android_lp_dns_servers_mid == NULL) + goto cleanup; + + (*env)->DeleteLocalRef(env, obj_cls); + obj_cls = jni_get_class(env, "java/util/List"); + if (obj_cls == NULL) + goto cleanup; + + android_list_size_mid = jni_get_method_id(env, obj_cls, "size", "()I"); + if (android_list_size_mid == NULL) + goto cleanup; + + android_list_get_mid = jni_get_method_id(env, obj_cls, "get", + "(I)Ljava/lang/Object;"); + if (android_list_get_mid == NULL) + goto cleanup; + + (*env)->DeleteLocalRef(env, obj_cls); + obj_cls = jni_get_class(env, "java/net/InetAddress"); + if (obj_cls == NULL) + goto cleanup; + + android_ia_host_addr_mid = jni_get_method_id(env, obj_cls, "getHostAddress", + "()Ljava/lang/String;"); + if (android_ia_host_addr_mid == NULL) + goto cleanup; + + (*env)->DeleteLocalRef(env, obj_cls); + goto done; + +cleanup: + if (obj_cls != NULL) + (*env)->DeleteLocalRef(env, obj_cls); + + android_cm_active_net_mid = NULL; + android_cm_link_props_mid = NULL; + android_lp_dns_servers_mid = NULL; + android_list_size_mid = NULL; + android_list_get_mid = NULL; + android_ia_host_addr_mid = NULL; + +done: + if (need_detatch) + (*android_jvm)->DetachCurrentThread(android_jvm); + + return ret; +} + +int ares_library_android_initialized(void) +{ + if (android_jvm == NULL || android_connectivity_manager == NULL) + return ARES_ENOTINITIALIZED; + return ARES_SUCCESS; +} + +void ares_library_cleanup_android(void) +{ + JNIEnv *env = NULL; + int need_detatch = 0; + int res; + + if (android_jvm == NULL || android_connectivity_manager == NULL) + return; + + res = (*android_jvm)->GetEnv(android_jvm, (void **)&env, JNI_VERSION_1_6); + if (res == JNI_EDETACHED) + { + env = NULL; + res = (*android_jvm)->AttachCurrentThread(android_jvm, &env, NULL); + need_detatch = 1; + } + if (res != JNI_OK || env == NULL) + return; + + android_cm_active_net_mid = NULL; + android_cm_link_props_mid = NULL; + android_lp_dns_servers_mid = NULL; + android_list_size_mid = NULL; + android_list_get_mid = NULL; + android_ia_host_addr_mid = NULL; + + (*env)->DeleteGlobalRef(env, android_connectivity_manager); + android_connectivity_manager = NULL; + + if (need_detatch) + (*android_jvm)->DetachCurrentThread(android_jvm); +} + +char **ares_get_android_server_list(size_t max_servers, + size_t *num_servers) +{ + JNIEnv *env = NULL; + jobject active_network = NULL; + jobject link_properties = NULL; + jobject server_list = NULL; + jobject server = NULL; + jstring str = NULL; + jint nserv; + const char *ch_server_address; + int res; + size_t i; + char **dns_list = NULL; + int need_detatch = 0; + + if (android_jvm == NULL || android_connectivity_manager == NULL || + max_servers == 0 || num_servers == NULL) + { + return NULL; + } + + if (android_cm_active_net_mid == NULL || android_cm_link_props_mid == NULL || + android_lp_dns_servers_mid == NULL || android_list_size_mid == NULL || + android_list_get_mid == NULL || android_ia_host_addr_mid == NULL) + { + return NULL; + } + + res = (*android_jvm)->GetEnv(android_jvm, (void **)&env, JNI_VERSION_1_6); + if (res == JNI_EDETACHED) + { + env = NULL; + res = (*android_jvm)->AttachCurrentThread(android_jvm, &env, NULL); + need_detatch = 1; + } + if (res != JNI_OK || env == NULL) + goto done; + + /* JNI below is equivalent to this Java code. + import android.content.Context; + import android.net.ConnectivityManager; + import android.net.LinkProperties; + import android.net.Network; + import java.net.InetAddress; + import java.util.List; + + ConnectivityManager cm = (ConnectivityManager)this.getApplicationContext() + .getSystemService(Context.CONNECTIVITY_SERVICE); + Network an = cm.getActiveNetwork(); + LinkProperties lp = cm.getLinkProperties(an); + List dns = lp.getDnsServers(); + for (InetAddress ia: dns) { + String ha = ia.getHostAddress(); + } + + Note: The JNI ConnectivityManager object and all method IDs were previously + initialized in ares_library_init_android. + */ + + active_network = (*env)->CallObjectMethod(env, android_connectivity_manager, + android_cm_active_net_mid); + if (active_network == NULL) + goto done; + + link_properties = + (*env)->CallObjectMethod(env, android_connectivity_manager, + android_cm_link_props_mid, active_network); + if (link_properties == NULL) + goto done; + + server_list = (*env)->CallObjectMethod(env, link_properties, + android_lp_dns_servers_mid); + if (server_list == NULL) + goto done; + + nserv = (*env)->CallIntMethod(env, server_list, android_list_size_mid); + if (nserv > (jint)max_servers) + nserv = (jint)max_servers; + if (nserv <= 0) + goto done; + *num_servers = (size_t)nserv; + + dns_list = ares_malloc(sizeof(*dns_list)*(*num_servers)); + for (i=0; i<*num_servers; i++) + { + server = (*env)->CallObjectMethod(env, server_list, android_list_get_mid, + (jint)i); + dns_list[i] = ares_malloc(64); + dns_list[i][0] = 0; + if (server == NULL) + { + continue; + } + str = (*env)->CallObjectMethod(env, server, android_ia_host_addr_mid); + ch_server_address = (*env)->GetStringUTFChars(env, str, 0); + strncpy(dns_list[i], ch_server_address, 64); + (*env)->ReleaseStringUTFChars(env, str, ch_server_address); + (*env)->DeleteLocalRef(env, str); + (*env)->DeleteLocalRef(env, server); + } + +done: + if ((*env)->ExceptionOccurred(env)) + (*env)->ExceptionClear(env); + + if (server_list != NULL) + (*env)->DeleteLocalRef(env, server_list); + if (link_properties != NULL) + (*env)->DeleteLocalRef(env, link_properties); + if (active_network != NULL) + (*env)->DeleteLocalRef(env, active_network); + + if (need_detatch) + (*android_jvm)->DetachCurrentThread(android_jvm); + return dns_list; +} +#else +/* warning: ISO C forbids an empty translation unit */ +typedef int dummy_make_iso_compilers_happy; +#endif diff --git a/deps/cares/src/ares_data.c b/deps/cares/src/ares_data.c index f89111381511a0..18dd650c7b91fb 100644 --- a/deps/cares/src/ares_data.c +++ b/deps/cares/src/ares_data.c @@ -40,10 +40,9 @@ void ares_free_data(void *dataptr) { - struct ares_data *ptr; - - if (!dataptr) - return; + while (dataptr != NULL) { + struct ares_data *ptr; + void *next_data = NULL; #ifdef __INTEL_COMPILER # pragma warning(push) @@ -51,80 +50,82 @@ void ares_free_data(void *dataptr) /* 1684: conversion from pointer to same-sized integral type */ #endif - ptr = (void *)((char *)dataptr - offsetof(struct ares_data, data)); + ptr = (void *)((char *)dataptr - offsetof(struct ares_data, data)); #ifdef __INTEL_COMPILER # pragma warning(pop) #endif - if (ptr->mark != ARES_DATATYPE_MARK) - return; - - switch (ptr->type) - { - case ARES_DATATYPE_MX_REPLY: - - if (ptr->data.mx_reply.next) - ares_free_data(ptr->data.mx_reply.next); - if (ptr->data.mx_reply.host) - ares_free(ptr->data.mx_reply.host); - break; - - case ARES_DATATYPE_SRV_REPLY: - - if (ptr->data.srv_reply.next) - ares_free_data(ptr->data.srv_reply.next); - if (ptr->data.srv_reply.host) - ares_free(ptr->data.srv_reply.host); - break; - - case ARES_DATATYPE_TXT_REPLY: - case ARES_DATATYPE_TXT_EXT: - - if (ptr->data.txt_reply.next) - ares_free_data(ptr->data.txt_reply.next); - if (ptr->data.txt_reply.txt) - ares_free(ptr->data.txt_reply.txt); - break; - - case ARES_DATATYPE_ADDR_NODE: - - if (ptr->data.addr_node.next) - ares_free_data(ptr->data.addr_node.next); - break; - - case ARES_DATATYPE_ADDR_PORT_NODE: - - if (ptr->data.addr_port_node.next) - ares_free_data(ptr->data.addr_port_node.next); - break; - - case ARES_DATATYPE_NAPTR_REPLY: - - if (ptr->data.naptr_reply.next) - ares_free_data(ptr->data.naptr_reply.next); - if (ptr->data.naptr_reply.flags) - ares_free(ptr->data.naptr_reply.flags); - if (ptr->data.naptr_reply.service) - ares_free(ptr->data.naptr_reply.service); - if (ptr->data.naptr_reply.regexp) - ares_free(ptr->data.naptr_reply.regexp); - if (ptr->data.naptr_reply.replacement) - ares_free(ptr->data.naptr_reply.replacement); - break; - - case ARES_DATATYPE_SOA_REPLY: - if (ptr->data.soa_reply.nsname) - ares_free(ptr->data.soa_reply.nsname); - if (ptr->data.soa_reply.hostmaster) - ares_free(ptr->data.soa_reply.hostmaster); - break; - - default: - return; - } - - ares_free(ptr); + if (ptr->mark != ARES_DATATYPE_MARK) + return; + + switch (ptr->type) + { + case ARES_DATATYPE_MX_REPLY: + + if (ptr->data.mx_reply.next) + next_data = ptr->data.mx_reply.next; + if (ptr->data.mx_reply.host) + ares_free(ptr->data.mx_reply.host); + break; + + case ARES_DATATYPE_SRV_REPLY: + + if (ptr->data.srv_reply.next) + next_data = ptr->data.srv_reply.next; + if (ptr->data.srv_reply.host) + ares_free(ptr->data.srv_reply.host); + break; + + case ARES_DATATYPE_TXT_REPLY: + case ARES_DATATYPE_TXT_EXT: + + if (ptr->data.txt_reply.next) + next_data = ptr->data.txt_reply.next; + if (ptr->data.txt_reply.txt) + ares_free(ptr->data.txt_reply.txt); + break; + + case ARES_DATATYPE_ADDR_NODE: + + if (ptr->data.addr_node.next) + next_data = ptr->data.addr_node.next; + break; + + case ARES_DATATYPE_ADDR_PORT_NODE: + + if (ptr->data.addr_port_node.next) + next_data = ptr->data.addr_port_node.next; + break; + + case ARES_DATATYPE_NAPTR_REPLY: + + if (ptr->data.naptr_reply.next) + next_data = ptr->data.naptr_reply.next; + if (ptr->data.naptr_reply.flags) + ares_free(ptr->data.naptr_reply.flags); + if (ptr->data.naptr_reply.service) + ares_free(ptr->data.naptr_reply.service); + if (ptr->data.naptr_reply.regexp) + ares_free(ptr->data.naptr_reply.regexp); + if (ptr->data.naptr_reply.replacement) + ares_free(ptr->data.naptr_reply.replacement); + break; + + case ARES_DATATYPE_SOA_REPLY: + if (ptr->data.soa_reply.nsname) + ares_free(ptr->data.soa_reply.nsname); + if (ptr->data.soa_reply.hostmaster) + ares_free(ptr->data.soa_reply.hostmaster); + break; + + default: + return; + } + + ares_free(ptr); + dataptr = next_data; + } } diff --git a/deps/cares/src/ares_gethostbyaddr.c b/deps/cares/src/ares_gethostbyaddr.c index a0a90f6bb1712b..a8ca0f5744d376 100644 --- a/deps/cares/src/ares_gethostbyaddr.c +++ b/deps/cares/src/ares_gethostbyaddr.c @@ -190,18 +190,18 @@ static int file_lookup(struct ares_addr *addr, struct hostent **host) char tmp[MAX_PATH]; HKEY hkeyHosts; - if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ, + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ, &hkeyHosts) == ERROR_SUCCESS) { DWORD dwLength = MAX_PATH; - RegQueryValueEx(hkeyHosts, DATABASEPATH, NULL, NULL, (LPBYTE)tmp, + RegQueryValueExA(hkeyHosts, DATABASEPATH, NULL, NULL, (LPBYTE)tmp, &dwLength); - ExpandEnvironmentStrings(tmp, PATH_HOSTS, MAX_PATH); + ExpandEnvironmentStringsA(tmp, PATH_HOSTS, MAX_PATH); RegCloseKey(hkeyHosts); } } else if (platform == WIN_9X) - GetWindowsDirectory(PATH_HOSTS, MAX_PATH); + GetWindowsDirectoryA(PATH_HOSTS, MAX_PATH); else return ARES_ENOTFOUND; diff --git a/deps/cares/src/ares_gethostbyname.c b/deps/cares/src/ares_gethostbyname.c index caab6ae49566d6..7c46d96ceaf214 100644 --- a/deps/cares/src/ares_gethostbyname.c +++ b/deps/cares/src/ares_gethostbyname.c @@ -351,18 +351,18 @@ static int file_lookup(const char *name, int family, struct hostent **host) char tmp[MAX_PATH]; HKEY hkeyHosts; - if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ, + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ, &hkeyHosts) == ERROR_SUCCESS) { DWORD dwLength = MAX_PATH; - RegQueryValueEx(hkeyHosts, DATABASEPATH, NULL, NULL, (LPBYTE)tmp, + RegQueryValueExA(hkeyHosts, DATABASEPATH, NULL, NULL, (LPBYTE)tmp, &dwLength); - ExpandEnvironmentStrings(tmp, PATH_HOSTS, MAX_PATH); + ExpandEnvironmentStringsA(tmp, PATH_HOSTS, MAX_PATH); RegCloseKey(hkeyHosts); } } else if (platform == WIN_9X) - GetWindowsDirectory(PATH_HOSTS, MAX_PATH); + GetWindowsDirectoryA(PATH_HOSTS, MAX_PATH); else return ARES_ENOTFOUND; diff --git a/deps/cares/src/ares_init.c b/deps/cares/src/ares_init.c index 3ba8cb3570c67d..90cfcabdd33a9e 100644 --- a/deps/cares/src/ares_init.c +++ b/deps/cares/src/ares_init.c @@ -44,6 +44,7 @@ #if defined(ANDROID) || defined(__ANDROID__) #include +#include "ares_android.h" /* From the Bionic sources */ #define DNS_PROP_NAME_PREFIX "net.dns" #define MAX_DNS_PROPERTIES 8 @@ -585,7 +586,7 @@ static int get_REG_SZ(HKEY hKey, const char *leafKeyName, char **outptr) *outptr = NULL; /* Find out size of string stored in registry */ - res = RegQueryValueEx(hKey, leafKeyName, 0, NULL, NULL, &size); + res = RegQueryValueExA(hKey, leafKeyName, 0, NULL, NULL, &size); if ((res != ERROR_SUCCESS && res != ERROR_MORE_DATA) || !size) return 0; @@ -596,7 +597,7 @@ static int get_REG_SZ(HKEY hKey, const char *leafKeyName, char **outptr) return 0; /* Get the value for real */ - res = RegQueryValueEx(hKey, leafKeyName, 0, NULL, + res = RegQueryValueExA(hKey, leafKeyName, 0, NULL, (unsigned char *)*outptr, &size); if ((res != ERROR_SUCCESS) || (size == 1)) { @@ -627,7 +628,7 @@ static int get_REG_SZ_9X(HKEY hKey, const char *leafKeyName, char **outptr) *outptr = NULL; /* Find out size of string stored in registry */ - res = RegQueryValueEx(hKey, leafKeyName, 0, &dataType, NULL, &size); + res = RegQueryValueExA(hKey, leafKeyName, 0, &dataType, NULL, &size); if ((res != ERROR_SUCCESS && res != ERROR_MORE_DATA) || !size) return 0; @@ -638,7 +639,7 @@ static int get_REG_SZ_9X(HKEY hKey, const char *leafKeyName, char **outptr) return 0; /* Get the value for real */ - res = RegQueryValueEx(hKey, leafKeyName, 0, &dataType, + res = RegQueryValueExA(hKey, leafKeyName, 0, &dataType, (unsigned char *)*outptr, &size); if ((res != ERROR_SUCCESS) || (size == 1)) { @@ -683,11 +684,11 @@ static int get_enum_REG_SZ(HKEY hKeyParent, const char *leafKeyName, for(;;) { enumKeyNameBuffSize = sizeof(enumKeyName); - res = RegEnumKeyEx(hKeyParent, enumKeyIdx++, enumKeyName, + res = RegEnumKeyExA(hKeyParent, enumKeyIdx++, enumKeyName, &enumKeyNameBuffSize, 0, NULL, NULL, NULL); if (res != ERROR_SUCCESS) break; - res = RegOpenKeyEx(hKeyParent, enumKeyName, 0, KEY_QUERY_VALUE, + res = RegOpenKeyExA(hKeyParent, enumKeyName, 0, KEY_QUERY_VALUE, &hKeyEnum); if (res != ERROR_SUCCESS) continue; @@ -718,7 +719,7 @@ static int get_DNS_Registry_9X(char **outptr) *outptr = NULL; - res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_9X, 0, KEY_READ, + res = RegOpenKeyExA(HKEY_LOCAL_MACHINE, WIN_NS_9X, 0, KEY_READ, &hKey_VxD_MStcp); if (res != ERROR_SUCCESS) return 0; @@ -750,7 +751,7 @@ static int get_DNS_Registry_NT(char **outptr) *outptr = NULL; - res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ, + res = RegOpenKeyExA(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ, &hKey_Tcpip_Parameters); if (res != ERROR_SUCCESS) return 0; @@ -772,7 +773,7 @@ static int get_DNS_Registry_NT(char **outptr) goto done; /* Try adapter specific parameters */ - res = RegOpenKeyEx(hKey_Tcpip_Parameters, "Interfaces", 0, + res = RegOpenKeyExA(hKey_Tcpip_Parameters, "Interfaces", 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hKey_Interfaces); if (res != ERROR_SUCCESS) @@ -949,9 +950,16 @@ static BOOL ares_IsWindowsVistaOrGreater(void) OSVERSIONINFO vinfo; memset(&vinfo, 0, sizeof(vinfo)); vinfo.dwOSVersionInfoSize = sizeof(vinfo); +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4996) /* warning C4996: 'GetVersionExW': was declared deprecated */ +#endif if (!GetVersionEx(&vinfo) || vinfo.dwMajorVersion < 6) return FALSE; return TRUE; +#ifdef _MSC_VER +#pragma warning(pop) +#endif } /* A structure to hold the string form of IPv4 and IPv6 addresses so we can @@ -962,6 +970,10 @@ typedef struct /* The metric we sort them by. */ ULONG metric; + /* Original index of the item, used as a secondary sort parameter to make + * qsort() stable if the metrics are equal */ + size_t orig_idx; + /* Room enough for the string form of any IPv4 or IPv6 address that * ares_inet_ntop() will create. Based on the existing c-ares practice. */ @@ -976,8 +988,69 @@ static int compareAddresses(const void *arg1, { const Address * const left = arg1; const Address * const right = arg2; + /* Lower metric the more preferred */ if(left->metric < right->metric) return -1; if(left->metric > right->metric) return 1; + /* If metrics are equal, lower original index more preferred */ + if(left->orig_idx < right->orig_idx) return -1; + if(left->orig_idx > right->orig_idx) return 1; + return 0; +} + +/* Validate that the ip address matches the subnet (network base and network + * mask) specified. Addresses are specified in standard Network Byte Order as + * 16 bytes, and the netmask is 0 to 128 (bits). + */ +static int ares_ipv6_subnet_matches(const unsigned char netbase[16], + unsigned char netmask, + const unsigned char ipaddr[16]) +{ + unsigned char mask[16] = { 0 }; + unsigned char i; + + /* Misuse */ + if (netmask > 128) + return 0; + + /* Quickly set whole bytes */ + memset(mask, 0xFF, netmask / 8); + + /* Set remaining bits */ + if(netmask % 8) { + mask[netmask / 8] = (unsigned char)(0xff << (8 - (netmask % 8))); + } + + for (i=0; i<16; i++) { + if ((netbase[i] & mask[i]) != (ipaddr[i] & mask[i])) + return 0; + } + + return 1; +} + +static int ares_ipv6_server_blacklisted(const unsigned char ipaddr[16]) +{ + const struct { + const char *netbase; + unsigned char netmask; + } blacklist[] = { + /* Deprecated by [RFC3879] in September 2004. Formerly a Site-Local scoped + * address prefix. Causes known issues on Windows as these are not valid DNS + * servers. */ + { "fec0::", 10 }, + { NULL, 0 } + }; + size_t i; + + for (i=0; blacklist[i].netbase != NULL; i++) { + unsigned char netbase[16]; + + if (ares_inet_pton(AF_INET6, blacklist[i].netbase, netbase) != 1) + continue; + + if (ares_ipv6_subnet_matches(netbase, blacklist[i].netmask, ipaddr)) + return 1; + } return 0; } @@ -1187,6 +1260,9 @@ static int get_DNS_AdaptersAddresses(char **outptr) addresses[addressesIndex].metric = -1; } + /* Record insertion index to make qsort stable */ + addresses[addressesIndex].orig_idx = addressesIndex; + if (! ares_inet_ntop(AF_INET, &namesrvr.sa4->sin_addr, addresses[addressesIndex].text, sizeof(addresses[0].text))) { @@ -1205,6 +1281,11 @@ static int get_DNS_AdaptersAddresses(char **outptr) sizeof(namesrvr.sa6->sin6_addr)) == 0) continue; + if (ares_ipv6_server_blacklisted( + (const unsigned char *)&namesrvr.sa6->sin6_addr) + ) + continue; + /* Allocate room for another address, if necessary, else skip. */ if(addressesIndex == addressesSize) { const size_t newSize = addressesSize + 4; @@ -1231,6 +1312,9 @@ static int get_DNS_AdaptersAddresses(char **outptr) addresses[addressesIndex].metric = -1; } + /* Record insertion index to make qsort stable */ + addresses[addressesIndex].orig_idx = addressesIndex; + if (! ares_inet_ntop(AF_INET6, &namesrvr.sa6->sin6_addr, addresses[addressesIndex].text, sizeof(addresses[0].text))) { @@ -1245,7 +1329,8 @@ static int get_DNS_AdaptersAddresses(char **outptr) } } - /* Sort all of the textual addresses by their metric. */ + /* Sort all of the textual addresses by their metric (and original index if + * metrics are equal). */ qsort(addresses, addressesIndex, sizeof(*addresses), compareAddresses); /* Join them all into a single string, removing duplicates. */ @@ -1382,7 +1467,7 @@ static int get_SuffixList_Windows(char **outptr) DWORD keyNameBuffSize; DWORD keyIdx = 0; char *p = NULL; - char *pp; + const char *pp; size_t len = 0; *outptr = NULL; @@ -1391,7 +1476,7 @@ static int get_SuffixList_Windows(char **outptr) return 0; /* 1. Global DNS Suffix Search List */ - if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { if (get_REG_SZ(hKey, SEARCHLIST_KEY, outptr)) @@ -1403,7 +1488,7 @@ static int get_SuffixList_Windows(char **outptr) /* 2. Connection Specific Search List composed of: * a. Primary DNS Suffix */ - if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_DNSCLIENT, 0, + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, WIN_DNSCLIENT, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { get_REG_SZ(hKey, PRIMARYDNSSUFFIX_KEY, outptr); @@ -1413,17 +1498,17 @@ static int get_SuffixList_Windows(char **outptr) return 0; /* b. Interface SearchList, Domain, DhcpDomain */ - if (!RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY "\\" INTERFACES_KEY, 0, + if (!RegOpenKeyExA(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY "\\" INTERFACES_KEY, 0, KEY_READ, &hKey) == ERROR_SUCCESS) return 0; for(;;) { keyNameBuffSize = sizeof(keyName); - if (RegEnumKeyEx(hKey, keyIdx++, keyName, &keyNameBuffSize, + if (RegEnumKeyExA(hKey, keyIdx++, keyName, &keyNameBuffSize, 0, NULL, NULL, NULL) != ERROR_SUCCESS) break; - if (RegOpenKeyEx(hKey, keyName, 0, KEY_QUERY_VALUE, &hKeyEnum) + if (RegOpenKeyExA(hKey, keyName, 0, KEY_QUERY_VALUE, &hKeyEnum) != ERROR_SUCCESS) continue; if (get_REG_SZ(hKeyEnum, SEARCHLIST_KEY, &p) || @@ -1432,7 +1517,7 @@ static int get_SuffixList_Windows(char **outptr) { /* p can be comma separated (SearchList) */ pp = p; - while (len = next_suffix(&pp, len)) + while ((len = next_suffix(&pp, len)) != 0) { if (!contains_suffix(*outptr, pp, len)) commanjoin(outptr, pp, len); @@ -1542,18 +1627,55 @@ static int init_by_resolv_conf(ares_channel channel) unsigned int i; char propname[PROP_NAME_MAX]; char propvalue[PROP_VALUE_MAX]=""; + char **dns_servers; + size_t num_servers; + + /* Use the Android connectivity manager to get a list + * of DNS servers. As of Android 8 (Oreo) net.dns# + * system properties are no longer available. Google claims this + * improves privacy. Apps now need the ACCESS_NETWORK_STATE + * permission and must use the ConnectivityManager which + * is Java only. */ + dns_servers = ares_get_android_server_list(MAX_DNS_PROPERTIES, &num_servers); + if (dns_servers != NULL) + { + for (i = 0; i < num_servers; i++) + { + status = config_nameserver(&servers, &nservers, dns_servers[i]); + if (status != ARES_SUCCESS) + break; + status = ARES_EOF; + } + for (i = 0; i < num_servers; i++) + { + ares_free(dns_servers[i]); + } + ares_free(dns_servers); + } - for (i = 1; i <= MAX_DNS_PROPERTIES; i++) { - snprintf(propname, sizeof(propname), "%s%u", DNS_PROP_NAME_PREFIX, i); - if (__system_property_get(propname, propvalue) < 1) { +# ifdef HAVE___SYSTEM_PROPERTY_GET + /* Old way using the system property still in place as + * a fallback. Older android versions can still use this. + * it's possible for older apps not not have added the new + * permission and we want to try to avoid breaking those. + * + * We'll only run this if we don't have any dns servers + * because this will get the same ones (if it works). */ + if (status != ARES_EOF) { + for (i = 1; i <= MAX_DNS_PROPERTIES; i++) { + snprintf(propname, sizeof(propname), "%s%u", DNS_PROP_NAME_PREFIX, i); + if (__system_property_get(propname, propvalue) < 1) { + status = ARES_EOF; + break; + } + + status = config_nameserver(&servers, &nservers, propvalue); + if (status != ARES_SUCCESS) + break; status = ARES_EOF; - break; } - status = config_nameserver(&servers, &nservers, propvalue); - if (status != ARES_SUCCESS) - break; - status = ARES_EOF; } +# endif /* HAVE___SYSTEM_PROPERTY_GET */ #elif defined(CARES_USE_LIBRESOLV) struct __res_state res; memset(&res, 0, sizeof(res)); @@ -1873,8 +1995,10 @@ static int init_by_defaults(ares_channel channel) continue; } else if(res) { - rc = ARES_EBADNAME; - goto error; + /* Lets not treat a gethostname failure as critical, since we + * are ok if gethostname doesn't even exist */ + *hostname = '\0'; + break; } } while (res != 0); diff --git a/deps/cares/src/ares_library_init.c b/deps/cares/src/ares_library_init.c index 0a853d72cfa4e7..88e7a537480813 100644 --- a/deps/cares/src/ares_library_init.c +++ b/deps/cares/src/ares_library_init.c @@ -30,6 +30,10 @@ fpGetAdaptersAddresses_t ares_fpGetAdaptersAddresses = ZERO_NULL; fpGetBestRoute2_t ares_fpGetBestRoute2 = ZERO_NULL; #endif +#if defined(ANDROID) || defined(__ANDROID__) +#include "ares_android.h" +#endif + /* library-private global vars with source visibility restricted to this file */ static unsigned int ares_initialized; @@ -160,6 +164,10 @@ void ares_library_cleanup(void) if (ares_init_flags & ARES_LIB_INIT_WIN32) ares_win32_cleanup(); +#if defined(ANDROID) || defined(__ANDROID__) + ares_library_cleanup_android(); +#endif + ares_init_flags = ARES_LIB_INIT_NONE; ares_malloc = malloc; ares_realloc = realloc; diff --git a/deps/cares/src/ares_platform.c b/deps/cares/src/ares_platform.c index c20061583f42d6..6c749dccb24a12 100644 --- a/deps/cares/src/ares_platform.c +++ b/deps/cares/src/ares_platform.c @@ -36,6 +36,10 @@ win_platform ares__getplatform(void) memset(&OsvEx, 0, sizeof(OsvEx)); OsvEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4996) /* warning C4996: 'GetVersionExW': was declared deprecated */ +#endif if (!GetVersionEx((void *)&OsvEx)) { memset(&OsvEx, 0, sizeof(OsvEx)); @@ -43,6 +47,9 @@ win_platform ares__getplatform(void) if (!GetVersionEx((void *)&OsvEx)) return WIN_UNKNOWN; } +#ifdef _MSC_VER +#pragma warning(pop) +#endif switch(OsvEx.dwPlatformId) { diff --git a/deps/cares/src/inet_ntop.c b/deps/cares/src/inet_ntop.c index 9420f6cb0d22a8..ce3ce588d3c0bd 100644 --- a/deps/cares/src/inet_ntop.c +++ b/deps/cares/src/inet_ntop.c @@ -54,7 +54,7 @@ static const char *inet_ntop6(const unsigned char *src, char *dst, size_t size); * On Windows we store the error in the thread errno, not * in the winsock error code. This is to avoid loosing the * actual last winsock error. So use macro ERRNO to fetch the - * errno this funtion sets when returning NULL, not SOCKERRNO. + * errno this function sets when returning NULL, not SOCKERRNO. * author: * Paul Vixie, 1996. */ diff --git a/deps/v8/AUTHORS b/deps/v8/AUTHORS index c54cad02b2d15e..214db64903909b 100644 --- a/deps/v8/AUTHORS +++ b/deps/v8/AUTHORS @@ -66,6 +66,7 @@ Felix Geisendörfer Filipe David Manana Franziska Hinkelmann Geoffrey Garside +Gus Caplan Gwang Yoon Hwang Henrique Ferreiro Hirofumi Mako diff --git a/deps/v8/include/v8.h b/deps/v8/include/v8.h index 7393dc6cbcc13a..7760cc87d9fd46 100644 --- a/deps/v8/include/v8.h +++ b/deps/v8/include/v8.h @@ -2345,6 +2345,11 @@ class V8_EXPORT Value : public Data { bool IsWebAssemblyCompiledModule() const; + /** + * Returns true if the value is a Module Namespace Object. + */ + bool IsModuleNamespaceObject() const; + V8_WARN_UNUSED_RESULT MaybeLocal ToBoolean( Local context) const; V8_WARN_UNUSED_RESULT MaybeLocal ToNumber( diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc index 152040d8713fbe..f8bace70a745bd 100644 --- a/deps/v8/src/api.cc +++ b/deps/v8/src/api.cc @@ -3753,6 +3753,10 @@ bool Value::IsSetIterator() const { bool Value::IsPromise() const { return Utils::OpenHandle(this)->IsJSPromise(); } +bool Value::IsModuleNamespaceObject() const { + return Utils::OpenHandle(this)->IsJSModuleNamespace(); +} + MaybeLocal Value::ToString(Local context) const { auto obj = Utils::OpenHandle(this); if (obj->IsString()) return ToApiHandle(obj); diff --git a/deps/v8/test/cctest/test-api.cc b/deps/v8/test/cctest/test-api.cc index 4e3f22a3b35a76..51a4385d924ddb 100644 --- a/deps/v8/test/cctest/test-api.cc +++ b/deps/v8/test/cctest/test-api.cc @@ -27131,6 +27131,35 @@ TEST(DynamicImport) { CHECK(result->Equals(i::String::cast(promise->result()))); } +TEST(GetModuleNamespace) { + LocalContext context; + v8::Isolate* isolate = context->GetIsolate(); + v8::HandleScope scope(isolate); + + Local url = v8_str("www.google.com"); + Local source_text = v8_str("export default 5; export const a = 10;"); + v8::ScriptOrigin origin(url, Local(), Local(), + Local(), Local(), + Local(), Local(), + Local(), True(isolate)); + v8::ScriptCompiler::Source source(source_text, origin); + Local module = + v8::ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked(); + module->InstantiateModule(context.local(), UnexpectedModuleResolveCallback) + .ToChecked(); + module->Evaluate(context.local()).ToLocalChecked(); + + Local ns_val = module->GetModuleNamespace(); + CHECK(ns_val->IsModuleNamespaceObject()); + Local ns = ns_val.As(); + CHECK(ns->Get(context.local(), v8_str("default")) + .ToLocalChecked() + ->StrictEquals(v8::Number::New(isolate, 5))); + CHECK(ns->Get(context.local(), v8_str("a")) + .ToLocalChecked() + ->StrictEquals(v8::Number::New(isolate, 10))); +} + TEST(GlobalTemplateWithDoubleProperty) { v8::Isolate* isolate = CcTest::isolate(); v8::HandleScope handle_scope(isolate); diff --git a/doc/api/addons.md b/doc/api/addons.md index e52e43153e6982..3c4c7b39bedfeb 100644 --- a/doc/api/addons.md +++ b/doc/api/addons.md @@ -1,6 +1,7 @@ # C++ Addons + Node.js Addons are dynamically-linked shared objects, written in C++, that can be loaded into Node.js using the [`require()`][require] function, and used @@ -1096,8 +1097,10 @@ has ended but before the JavaScript VM is terminated and Node.js shuts down. #### void AtExit(callback, args) -* `callback` {void (\*)(void\*)} A pointer to the function to call at exit. -* `args` {void\*} A pointer to pass to the callback at exit. +* `callback` <void (\*)(void\*)> + A pointer to the function to call at exit. +* `args` <void\*> + A pointer to pass to the callback at exit. Registers exit hooks that run after the event loop has ended but before the VM is killed. diff --git a/doc/api/assert.md b/doc/api/assert.md index 38f1ffbb217aab..8935983cf418b3 100644 --- a/doc/api/assert.md +++ b/doc/api/assert.md @@ -374,6 +374,51 @@ parameter is undefined, a default error message is assigned. If the `message` parameter is an instance of an [`Error`][] then it will be thrown instead of the `AssertionError`. +## assert.doesNotReject(block[, error][, message]) + +* `block` {Function|Promise} +* `error` {RegExp|Function} +* `message` {any} + +Awaits the `block` promise or, if `block` is a function, immediately calls the +function and awaits the returned promise to complete. It will then check that +the promise is not rejected. + +If `block` is a function and it throws an error synchronously, +`assert.doesNotReject()` will return a rejected Promise with that error without +checking the error handler. + +Please note: Using `assert.doesNotReject()` is actually not useful because there +is little benefit by catching a rejection and then rejecting it again. Instead, +consider adding a comment next to the specific code path that should not reject +and keep error messages as expressive as possible. + +If specified, `error` can be a [`Class`][], [`RegExp`][] or a validation +function. See [`assert.throws()`][] for more details. + +Besides the async nature to await the completion behaves identically to +[`assert.doesNotThrow()`][]. + +```js +(async () => { + await assert.doesNotReject( + async () => { + throw new TypeError('Wrong value'); + }, + SyntaxError + ); +})(); +``` + +```js +assert.doesNotReject(Promise.reject(new TypeError('Wrong value'))) + .then(() => { + // ... + }); +``` + ## assert.doesNotThrow(block[, error][, message]) +* `block` {Function|Promise} +* `error` {RegExp|Function|Object|Error} +* `message` {any} + +Awaits the `block` promise or, if `block` is a function, immediately calls the +function and awaits the returned promise to complete. It will then check that +the promise is rejected. + +If `block` is a function and it throws an error synchronously, +`assert.rejects()` will return a rejected Promise with that error without +checking the error handler. + +Besides the async nature to await the completion behaves identical to +[`assert.throws()`][]. + +If specified, `error` can be a [`Class`][], [`RegExp`][], a validation function, +an object where each property will be tested for, or an instance of error where +each property will be tested for including the non-enumerable `message` and +`name` properties. + +If specified, `message` will be the message provided by the `AssertionError` if +the block fails to reject. + +```js +(async () => { + await assert.rejects( + async () => { + throw new TypeError('Wrong value'); + }, + { + name: 'TypeError', + message: 'Wrong value' + } + ); +})(); +``` + +```js +assert.rejects( + Promise.reject(new Error('Wrong value')), + Error +).then(() => { + // ... +}); +``` + +Note that `error` cannot be a string. If a string is provided as the second +argument, then `error` is assumed to be omitted and the string will be used for +`message` instead. This can lead to easy-to-miss mistakes. Please read the +example in [`assert.throws()`][] carefully if using a string as the second +argument gets considered. + ## assert.throws(block[, error][, message]) * `block` {Function} -* `error` {RegExp|Function|object} +* `error` {RegExp|Function|Object|Error} * `message` {any} Expects the function `block` to throw an error. -If specified, `error` can be a constructor, [`RegExp`][], a validation -function, or an object where each property will be tested for. +If specified, `error` can be a [`Class`][], [`RegExp`][], a validation function, +an object where each property will be tested for, or an instance of error where +each property will be tested for including the non-enumerable `message` and +`name` properties. If specified, `message` will be the message provided by the `AssertionError` if the block fails to throw. @@ -890,10 +995,11 @@ assert.throws( Custom error object / error instance: ```js +const err = new TypeError('Wrong value'); +err.code = 404; + assert.throws( () => { - const err = new TypeError('Wrong value'); - err.code = 404; throw err; }, { @@ -902,9 +1008,19 @@ assert.throws( // Note that only properties on the error object will be tested! } ); + +// Fails due to the different `message` and `name` properties: +assert.throws( + () => { + const otherErr = new Error('Not found'); + otherErr.code = 404; + throw otherErr; + }, + err // This tests for `message`, `name` and `code`. +); ``` -Note that `error` can not be a string. If a string is provided as the second +Note that `error` cannot be a string. If a string is provided as the second argument, then `error` is assumed to be omitted and the string will be used for `message` instead. This can lead to easy-to-miss mistakes. Please read the example below carefully if using a string as the second argument gets @@ -984,6 +1100,7 @@ For more information, see [`assert.ok()`]: #assert_assert_ok_value_message [`assert.strictEqual()`]: #assert_assert_strictequal_actual_expected_message [`assert.throws()`]: #assert_assert_throws_block_error_message +[`assert.rejects()`]: #assert_assert_rejects_block_error_message [`strict mode`]: #assert_strict_mode [Abstract Equality Comparison]: https://tc39.github.io/ecma262/#sec-abstract-equality-comparison [Object.prototype.toString()]: https://tc39.github.io/ecma262/#sec-object.prototype.tostring diff --git a/doc/api/async_hooks.md b/doc/api/async_hooks.md index 9501daa9919435..eda15f5e3ec01e 100644 --- a/doc/api/async_hooks.md +++ b/doc/api/async_hooks.md @@ -15,9 +15,9 @@ const async_hooks = require('async_hooks'); ## Terminology An asynchronous resource represents an object with an associated callback. -This callback may be called multiple times, for example, the `connection` event -in `net.createServer`, or just a single time like in `fs.open`. A resource -can also be closed before the callback is called. AsyncHook does not +This callback may be called multiple times, for example, the `'connection'` +event in `net.createServer()`, or just a single time like in `fs.open()`. +A resource can also be closed before the callback is called. AsyncHook does not explicitly distinguish between these different cases but will represent them as the abstract concept that is a resource. @@ -75,7 +75,7 @@ function destroy(asyncId) { } function promiseResolve(asyncId) { } ``` -#### `async_hooks.createHook(callbacks)` +#### async_hooks.createHook(callbacks) @@ -679,7 +679,7 @@ 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()` +#### asyncResource.emitBefore() @@ -695,7 +695,7 @@ 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()` +#### asyncResource.emitAfter() @@ -714,18 +714,18 @@ 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()` +#### asyncResource.emitDestroy() Call all `destroy` hooks. This should only ever be called once. An error will be thrown if it is called more than once. This **must** be manually called. If the resource is left to be collected by the GC then the `destroy` hooks will never be called. -#### `asyncResource.asyncId()` +#### asyncResource.asyncId() * Returns: {number} The unique `asyncId` assigned to the resource. -#### `asyncResource.triggerAsyncId()` +#### asyncResource.triggerAsyncId() * Returns: {number} The same `triggerAsyncId` that is passed to the `AsyncResource` constructor. diff --git a/doc/api/buffer.md b/doc/api/buffer.md index 6c5ed3dc95e046..33a0755247ab2a 100644 --- a/doc/api/buffer.md +++ b/doc/api/buffer.md @@ -426,9 +426,9 @@ changes: * `size` {integer} The desired length of the new `Buffer`. -Allocates a new `Buffer` of `size` bytes. If the `size` is larger than -[`buffer.constants.MAX_LENGTH`] or smaller than 0, a [`RangeError`] will be -thrown. A zero-length `Buffer` will be created if `size` is 0. +Allocates a new `Buffer` of `size` bytes. If `size` is larger than +[`buffer.constants.MAX_LENGTH`] or smaller than 0, [`ERR_INVALID_OPT_VALUE`] is +thrown. A zero-length `Buffer` is created if `size` is 0. Prior to Node.js 8.0.0, the underlying memory for `Buffer` instances created in this way is *not initialized*. The contents of a newly created @@ -502,9 +502,9 @@ console.log(buf); // Prints: ``` -Allocates a new `Buffer` of `size` bytes. If the `size` is larger than -[`buffer.constants.MAX_LENGTH`] or smaller than 0, a [`RangeError`] will be -thrown. A zero-length `Buffer` will be created if `size` is 0. +Allocates a new `Buffer` of `size` bytes. If `size` is larger than +[`buffer.constants.MAX_LENGTH`] or smaller than 0, [`ERR_INVALID_OPT_VALUE`] is +thrown. A zero-length `Buffer` is created if `size` is 0. If `fill` is specified, the allocated `Buffer` will be initialized by calling [`buf.fill(fill)`][`buf.fill()`]. @@ -543,9 +543,9 @@ changes: * `size` {integer} The desired length of the new `Buffer`. -Allocates a new `Buffer` of `size` bytes. If the `size` is larger than -[`buffer.constants.MAX_LENGTH`] or smaller than 0, a [`RangeError`] will be -thrown. A zero-length `Buffer` will be created if `size` is 0. +Allocates a new `Buffer` of `size` bytes. If `size` is larger than +[`buffer.constants.MAX_LENGTH`] or smaller than 0, [`ERR_INVALID_OPT_VALUE`] is +thrown. A zero-length `Buffer` is created if `size` is 0. The underlying memory for `Buffer` instances created in this way is *not initialized*. The contents of the newly created `Buffer` are unknown and @@ -587,9 +587,9 @@ added: v5.12.0 * `size` {integer} The desired length of the new `Buffer`. -Allocates a new `Buffer` of `size` bytes. If the `size` is larger than -[`buffer.constants.MAX_LENGTH`] or smaller than 0, a [`RangeError`] will be -thrown. A zero-length `Buffer` will be created if `size` is 0. +Allocates a new `Buffer` of `size` bytes. If `size` is larger than +[`buffer.constants.MAX_LENGTH`] or smaller than 0, [`ERR_INVALID_OPT_VALUE`] is +thrown. A zero-length `Buffer` is created if `size` is 0. The underlying memory for `Buffer` instances created in this way is *not initialized*. The contents of the newly created `Buffer` are unknown and @@ -1030,8 +1030,8 @@ console.log(buf1.compare(buf2, 5, 6, 5)); // Prints: 1 ``` -A `RangeError` will be thrown if: `targetStart < 0`, `sourceStart < 0`, -`targetEnd > target.byteLength` or `sourceEnd > source.byteLength`. +[`ERR_INDEX_OUT_OF_RANGE`] is thrown if `targetStart < 0`, `sourceStart < 0`, +`targetEnd > target.byteLength`, or `sourceEnd > source.byteLength`. ### buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]]) -* `offset` {integer} Number of bytes to skip before starting to read. Must satisfy: `0 <= offset <= buf.length - 8`. -* `noAssert` {boolean} Skip `offset` validation? **Default:** `false` +* `offset` {integer} Number of bytes to skip before starting to read. Must + satisfy `0 <= offset <= buf.length - 8`. +* `noAssert` {boolean} Skip `offset` validation? **Default:** `false`. * Returns: {number} Reads a 64-bit double from `buf` at the specified `offset` with specified @@ -1496,8 +1497,9 @@ console.log(buf.readDoubleLE(1, true)); added: v0.11.15 --> -* `offset` {integer} Number of bytes to skip before starting to read. Must satisfy: `0 <= offset <= buf.length - 4`. -* `noAssert` {boolean} Skip `offset` validation? **Default:** `false` +* `offset` {integer} Number of bytes to skip before starting to read. Must + satisfy `0 <= offset <= buf.length - 4`. +* `noAssert` {boolean} Skip `offset` validation? **Default:** `false`. * Returns: {number} Reads a 32-bit float from `buf` at the specified `offset` with specified @@ -1526,8 +1528,9 @@ console.log(buf.readFloatLE(1, true)); added: v0.5.0 --> -* `offset` {integer} Number of bytes to skip before starting to read. Must satisfy: `0 <= offset <= buf.length - 1`. -* `noAssert` {boolean} Skip `offset` validation? **Default:** `false` +* `offset` {integer} Number of bytes to skip before starting to read. Must + satisfy `0 <= offset <= buf.length - 1`. +* `noAssert` {boolean} Skip `offset` validation? **Default:** `false`. * Returns: {integer} Reads a signed 8-bit integer from `buf` at the specified `offset`. @@ -1554,8 +1557,9 @@ console.log(buf.readInt8(2)); added: v0.5.5 --> -* `offset` {integer} Number of bytes to skip before starting to read. Must satisfy: `0 <= offset <= buf.length - 2`. -* `noAssert` {boolean} Skip `offset` validation? **Default:** `false` +* `offset` {integer} Number of bytes to skip before starting to read. Must + satisfy `0 <= offset <= buf.length - 2`. +* `noAssert` {boolean} Skip `offset` validation? **Default:** `false`. * Returns: {integer} Reads a signed 16-bit integer from `buf` at the specified `offset` with @@ -1584,8 +1588,9 @@ console.log(buf.readInt16LE(1)); added: v0.5.5 --> -* `offset` {integer} Number of bytes to skip before starting to read. Must satisfy: `0 <= offset <= buf.length - 4`. -* `noAssert` {boolean} Skip `offset` validation? **Default:** `false` +* `offset` {integer} Number of bytes to skip before starting to read. Must + satisfy `0 <= offset <= buf.length - 4`. +* `noAssert` {boolean} Skip `offset` validation? **Default:** `false`. * Returns: {integer} Reads a signed 32-bit integer from `buf` at the specified `offset` with @@ -1614,9 +1619,11 @@ console.log(buf.readInt32LE(1)); added: v0.11.15 --> -* `offset` {integer} Number of bytes to skip before starting to read. Must satisfy: `0 <= offset <= buf.length - byteLength`. -* `byteLength` {integer} Number of bytes to read. Must satisfy: `0 < byteLength <= 6`. -* `noAssert` {boolean} Skip `offset` and `byteLength` validation? **Default:** `false`. +* `offset` {integer} Number of bytes to skip before starting to read. Must + satisfy `0 <= offset <= buf.length - byteLength`. +* `byteLength` {integer} Number of bytes to read. Must satisfy + `0 < byteLength <= 6`. +* `noAssert` {boolean} Skip `offset` validation? **Default:** `false`. * Returns: {integer} Reads `byteLength` number of bytes from `buf` at the specified `offset` @@ -1642,8 +1649,9 @@ console.log(buf.readIntBE(1, 6).toString(16)); added: v0.5.0 --> -* `offset` {integer} Number of bytes to skip before starting to read. Must satisfy: `0 <= offset <= buf.length - 1`. -* `noAssert` {boolean} Skip `offset` validation? **Default:** `false` +* `offset` {integer} Number of bytes to skip before starting to read. Must + satisfy `0 <= offset <= buf.length - 1`. +* `noAssert` {boolean} Skip `offset` validation? **Default:** `false`. * Returns: {integer} Reads an unsigned 8-bit integer from `buf` at the specified `offset`. @@ -1668,8 +1676,9 @@ console.log(buf.readUInt8(2)); added: v0.5.5 --> -* `offset` {integer} Number of bytes to skip before starting to read. Must satisfy: `0 <= offset <= buf.length - 2`. -* `noAssert` {boolean} Skip `offset` validation? **Default:** `false` +* `offset` {integer} Number of bytes to skip before starting to read. Must + satisfy `0 <= offset <= buf.length - 2`. +* `noAssert` {boolean} Skip `offset` validation? **Default:** `false`. * Returns: {integer} Reads an unsigned 16-bit integer from `buf` at the specified `offset` with @@ -1700,8 +1709,9 @@ console.log(buf.readUInt16LE(2).toString(16)); added: v0.5.5 --> -* `offset` {integer} Number of bytes to skip before starting to read. Must satisfy: `0 <= offset <= buf.length - 4`. -* `noAssert` {boolean} Skip `offset` validation? **Default:** `false` +* `offset` {integer} Number of bytes to skip before starting to read. Must + satisfy `0 <= offset <= buf.length - 4`. +* `noAssert` {boolean} Skip `offset` validation? **Default:** `false`. * Returns: {integer} Reads an unsigned 32-bit integer from `buf` at the specified `offset` with @@ -1728,9 +1738,11 @@ console.log(buf.readUInt32LE(1).toString(16)); added: v0.11.15 --> -* `offset` {integer} Number of bytes to skip before starting to read. Must satisfy: `0 <= offset <= buf.length - byteLength`. -* `byteLength` {integer} Number of bytes to read. Must satisfy: `0 < byteLength <= 6`. -* `noAssert` {boolean} Skip `offset` and `byteLength` validation? **Default:** `false` +* `offset` {integer} Number of bytes to skip before starting to read. Must + satisfy `0 <= offset <= buf.length - byteLength`. +* `byteLength` {integer} Number of bytes to read. Must satisfy + `0 < byteLength <= 6`. +* `noAssert` {boolean} Skip `offset` validation? **Default:** `false`. * Returns: {integer} Reads `byteLength` number of bytes from `buf` at the specified `offset` @@ -1827,8 +1839,9 @@ added: v5.10.0 * Returns: {Buffer} A reference to `buf`. -Interprets `buf` as an array of unsigned 16-bit integers and swaps the byte-order -*in-place*. Throws a `RangeError` if [`buf.length`] is not a multiple of 2. +Interprets `buf` as an array of unsigned 16-bit integers and swaps the +byte-order *in-place*. Throws [`ERR_INVALID_BUFFER_SIZE`] if [`buf.length`] is +not a multiple of 2. ```js const buf1 = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]); @@ -1854,8 +1867,9 @@ added: v5.10.0 * Returns: {Buffer} A reference to `buf`. -Interprets `buf` as an array of unsigned 32-bit integers and swaps the byte-order -*in-place*. Throws a `RangeError` if [`buf.length`] is not a multiple of 4. +Interprets `buf` as an array of unsigned 32-bit integers and swaps the +byte-order *in-place*. Throws [`ERR_INVALID_BUFFER_SIZE`] if [`buf.length`] is +not a multiple of 4. ```js const buf1 = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]); @@ -1881,8 +1895,9 @@ added: v6.3.0 * Returns: {Buffer} A reference to `buf`. -Interprets `buf` as an array of 64-bit numbers and swaps the byte-order *in-place*. -Throws a `RangeError` if [`buf.length`] is not a multiple of 8. +Interprets `buf` as an array of 64-bit numbers and swaps the byte-order +*in-place*. Throws [`ERR_INVALID_BUFFER_SIZE`] if [`buf.length`] is not a +multiple of 8. ```js const buf1 = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]); @@ -2039,8 +2054,9 @@ added: v0.11.15 --> * `value` {number} Number to be written to `buf`. -* `offset` {integer} Number of bytes to skip before starting to write. Must satisfy: `0 <= offset <= buf.length - 8`. -* `noAssert` {boolean} Skip `value` and `offset` validation? **Default:** `false` +* `offset` {integer} Number of bytes to skip before starting to write. Must + satisfy `0 <= offset <= buf.length - 8`. +* `noAssert` {boolean} Skip `offset` validation? **Default:** `false`. * Returns: {integer} `offset` plus the number of bytes written. Writes `value` to `buf` at the specified `offset` with specified endian @@ -2072,8 +2088,9 @@ added: v0.11.15 --> * `value` {number} Number to be written to `buf`. -* `offset` {integer} Number of bytes to skip before starting to write. Must satisfy: `0 <= offset <= buf.length - 4`. -* `noAssert` {boolean} Skip `value` and `offset` validation? **Default:** `false` +* `offset` {integer} Number of bytes to skip before starting to write. Must + satisfy `0 <= offset <= buf.length - 4`. +* `noAssert` {boolean} Skip `offset` validation? **Default:** `false`. * Returns: {integer} `offset` plus the number of bytes written. Writes `value` to `buf` at the specified `offset` with specified endian @@ -2104,8 +2121,9 @@ added: v0.5.0 --> * `value` {integer} Number to be written to `buf`. -* `offset` {integer} Number of bytes to skip before starting to write. Must satisfy: `0 <= offset <= buf.length - 1`. -* `noAssert` {boolean} Skip `value` and `offset` validation? **Default:** `false` +* `offset` {integer} Number of bytes to skip before starting to write. Must + satisfy `0 <= offset <= buf.length - 1`. +* `noAssert` {boolean} Skip `offset` validation? **Default:** `false`. * Returns: {integer} `offset` plus the number of bytes written. Writes `value` to `buf` at the specified `offset`. `value` *should* be a valid @@ -2134,8 +2152,9 @@ added: v0.5.5 --> * `value` {integer} Number to be written to `buf`. -* `offset` {integer} Number of bytes to skip before starting to write. Must satisfy: `0 <= offset <= buf.length - 2`. -* `noAssert` {boolean} Skip `value` and `offset` validation? **Default:** `false` +* `offset` {integer} Number of bytes to skip before starting to write. Must + satisfy `0 <= offset <= buf.length - 2`. +* `noAssert` {boolean} Skip `offset` validation? **Default:** `false`. * Returns: {integer} `offset` plus the number of bytes written. Writes `value` to `buf` at the specified `offset` with specified endian @@ -2165,8 +2184,9 @@ added: v0.5.5 --> * `value` {integer} Number to be written to `buf`. -* `offset` {integer} Number of bytes to skip before starting to write. Must satisfy: `0 <= offset <= buf.length - 4`. -* `noAssert` {boolean} Skip `value` and `offset` validation? **Default:** `false` +* `offset` {integer} Number of bytes to skip before starting to write. Must + satisfy `0 <= offset <= buf.length - 4`. +* `noAssert` {boolean} Skip `offset` validation? **Default:** `false`. * Returns: {integer} `offset` plus the number of bytes written. Writes `value` to `buf` at the specified `offset` with specified endian @@ -2196,10 +2216,11 @@ added: v0.11.15 --> * `value` {integer} Number to be written to `buf`. -* `offset` {integer} Number of bytes to skip before starting to write. Must satisfy: `0 <= offset <= buf.length - byteLength`. -* `byteLength` {integer} Number of bytes to write. Must satisfy: `0 < byteLength <= 6`. -* `noAssert` {boolean} Skip `value`, `offset`, and `byteLength` validation? - **Default:** `false` +* `offset` {integer} Number of bytes to skip before starting to write. Must + satisfy `0 <= offset <= buf.length - byteLength`. +* `byteLength` {integer} Number of bytes to write. Must satisfy + `0 < byteLength <= 6`. +* `noAssert` {boolean} Skip `offset` validation? **Default:** `false`. * Returns: {integer} `offset` plus the number of bytes written. Writes `byteLength` bytes of `value` to `buf` at the specified `offset`. @@ -2229,8 +2250,9 @@ added: v0.5.0 --> * `value` {integer} Number to be written to `buf`. -* `offset` {integer} Number of bytes to skip before starting to write. Must satisfy: `0 <= offset <= buf.length - 1`. -* `noAssert` {boolean} Skip `value` and `offset` validation? **Default:** `false` +* `offset` {integer} Number of bytes to skip before starting to write. Must + satisfy `0 <= offset <= buf.length - 1`. +* `noAssert` {boolean} Skip `offset` validation? **Default:** `false`. * Returns: {integer} `offset` plus the number of bytes written. Writes `value` to `buf` at the specified `offset`. `value` *should* be a @@ -2259,8 +2281,9 @@ added: v0.5.5 --> * `value` {integer} Number to be written to `buf`. -* `offset` {integer} Number of bytes to skip before starting to write. Must satisfy: `0 <= offset <= buf.length - 2`. -* `noAssert` {boolean} Skip `value` and `offset` validation? **Default:** `false` +* `offset` {integer} Number of bytes to skip before starting to write. Must + satisfy `0 <= offset <= buf.length - 2`. +* `noAssert` {boolean} Skip `offset` validation? **Default:** `false`. * Returns: {integer} `offset` plus the number of bytes written. Writes `value` to `buf` at the specified `offset` with specified endian @@ -2294,8 +2317,9 @@ added: v0.5.5 --> * `value` {integer} Number to be written to `buf`. -* `offset` {integer} Number of bytes to skip before starting to write. Must satisfy: `0 <= offset <= buf.length - 4`. -* `noAssert` {boolean} Skip `value` and `offset` validation? **Default:** `false` +* `offset` {integer} Number of bytes to skip before starting to write. Must + satisfy `0 <= offset <= buf.length - 4`. +* `noAssert` {boolean} Skip `offset` validation? **Default:** `false`. * Returns: {integer} `offset` plus the number of bytes written. Writes `value` to `buf` at the specified `offset` with specified endian @@ -2327,10 +2351,11 @@ added: v0.5.5 --> * `value` {integer} Number to be written to `buf`. -* `offset` {integer} Number of bytes to skip before starting to write. Must satisfy: `0 <= offset <= buf.length - byteLength`. -* `byteLength` {integer} Number of bytes to write. Must satisfy: `0 < byteLength <= 6`. -* `noAssert` {boolean} Skip `value`, `offset`, and `byteLength` validation? - **Default:** `false`. +* `offset` {integer} Number of bytes to skip before starting to write. Must + satisfy `0 <= offset <= buf.length - byteLength`. +* `byteLength` {integer} Number of bytes to write. Must satisfy + `0 < byteLength <= 6`. +* `noAssert` {boolean} Skip `offset` validation? **Default:** `false`. * Returns: {integer} `offset` plus the number of bytes written. Writes `byteLength` bytes of `value` to `buf` at the specified `offset`. @@ -2462,9 +2487,9 @@ deprecated: v6.0.0 * `size` {integer} The desired length of the new `SlowBuffer`. -Allocates a new `Buffer` of `size` bytes. If the `size` is larger than -[`buffer.constants.MAX_LENGTH`] or smaller than 0, a [`RangeError`] will be -thrown. A zero-length `Buffer` will be created if `size` is 0. +Allocates a new `Buffer` of `size` bytes. If `size` is larger than +[`buffer.constants.MAX_LENGTH`] or smaller than 0, [`ERR_INVALID_OPT_VALUE`] is +thrown. A zero-length `Buffer` is created if `size` is 0. The underlying memory for `SlowBuffer` instances is *not initialized*. The contents of a newly created `SlowBuffer` are unknown and may contain sensitive @@ -2528,8 +2553,10 @@ This value may depend on the JS engine that is being used. [`Buffer.from(string)`]: #buffer_class_method_buffer_from_string_encoding [`Buffer.poolSize`]: #buffer_class_property_buffer_poolsize [`DataView`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView +[`ERR_INDEX_OUT_OF_RANGE`]: errors.html#ERR_INDEX_OUT_OF_RANGE +[`ERR_INVALID_BUFFER_SIZE`]: errors.html#ERR_INVALID_BUFFER_SIZE +[`ERR_INVALID_OPT_VALUE`]: errors.html#ERR_INVALID_OPT_VALUE [`JSON.stringify()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify -[`RangeError`]: errors.html#errors_class_rangeerror [`SharedArrayBuffer`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer [`String#indexOf()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf [`String#lastIndexOf()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/lastIndexOf diff --git a/doc/api/child_process.md b/doc/api/child_process.md index cd31719cd57e19..e4a45171145981 100644 --- a/doc/api/child_process.md +++ b/doc/api/child_process.md @@ -167,11 +167,11 @@ directly by the shell and special characters (vary based on need to be dealt with accordingly: ```js exec('"/path/to/test file/test.sh" arg1 arg2'); -//Double quotes are used so that the space in the path is not interpreted as -//multiple arguments +// Double quotes are used so that the space in the path is not interpreted as +// multiple arguments exec('echo "The \\$HOME variable is $HOME"'); -//The $HOME variable is escaped in the first instance, but not in the second +// The $HOME variable is escaped in the first instance, but not in the second ``` **Never pass unsanitized user input to this function. Any input containing shell @@ -606,9 +606,8 @@ pipes between the parent and child. The value is one of the following: between parent and child. A [`ChildProcess`][] may have at most *one* IPC stdio file descriptor. Setting this option enables the [`subprocess.send()`][] method. If the child is a Node.js process, the presence of an IPC channel - will enable [`process.send()`][], [`process.disconnect()`][], - [`process.on('disconnect')`][], and [`process.on('message')`] within the - child. + will enable [`process.send()`][] and [`process.disconnect()`][] methods, + as well as [`'disconnect'`][] and [`'message'`][] events 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 @@ -649,8 +648,8 @@ spawn('prg', [], { stdio: ['pipe', null, null, null, 'pipe'] }); *It is worth noting that when an IPC channel is established between the parent and child processes, and the child is a Node.js process, the child is launched with the IPC channel unreferenced (using `unref()`) until the -child registers an event handler for the [`process.on('disconnect')`][] event -or the [`process.on('message')`][] event. This allows the child to exit +child registers an event handler for the [`'disconnect'`][] event +or the [`'message'`][] event. This allows the child to exit normally without the process being held open by the open IPC channel.* See also: [`child_process.exec()`][] and [`child_process.fork()`][] @@ -1092,7 +1091,7 @@ changes: * `options` {Object} The `options` argument, if present, is an object used to parameterize the sending of certain types of handles. `options` supports the following properties: - * `keepOpen` - A Boolean value that can be used when passing instances of + * `keepOpen` {boolean} A value that can be used when passing instances of `net.Socket`. When `true`, the socket is kept open in the sending process. **Default:** `false`. * `callback` {Function} @@ -1101,8 +1100,7 @@ changes: When an IPC channel has been established between the parent and child ( i.e. when using [`child_process.fork()`][]), the `subprocess.send()` method can be used to send messages to the child process. When the child process is a -Node.js instance, these messages can be received via the -[`process.on('message')`][] event. +Node.js instance, these messages can be received via the [`'message'`][] event. The message goes through serialization and parsing. The resulting message might not be the same as what is originally sent. @@ -1137,16 +1135,16 @@ allows the child to send messages back to the parent. There is a special case when sending a `{cmd: 'NODE_foo'}` message. Messages containing a `NODE_` prefix in the `cmd` property are reserved for use within -Node.js core and will not be emitted in the child's [`process.on('message')`][] +Node.js core and will not be emitted in the child's [`'message'`][] event. Rather, such messages are emitted using the -`process.on('internalMessage')` event and are consumed internally by Node.js. +`'internalMessage'` event and are consumed internally by Node.js. Applications should avoid using such messages or listening for `'internalMessage'` events as it is subject to change without notice. The optional `sendHandle` argument that may be passed to `subprocess.send()` is for passing a TCP server or socket object to the child process. The child will receive the object as the second argument passed to the callback function -registered on the [`process.on('message')`][] event. Any data that is received +registered on the [`'message'`][] event. Any data that is received and buffered in the socket will not be sent to the child. The optional `callback` is a function that is invoked after the message is @@ -1361,8 +1359,10 @@ the same requirement. Thus, in `child_process` functions where a shell can be spawned, `'cmd.exe'` is used as a fallback if `process.env.ComSpec` is unavailable. +[`'disconnect'`]: process.html#process_event_disconnect [`'error'`]: #child_process_event_error [`'exit'`]: #child_process_event_exit +[`'message'`]: process.html#process_event_message [`ChildProcess`]: #child_process_child_process [`Error`]: errors.html#errors_class_error [`EventEmitter`]: events.html#events_class_eventemitter @@ -1387,8 +1387,6 @@ unavailable. [`process.disconnect()`]: process.html#process_process_disconnect [`process.env`]: process.html#process_process_env [`process.execPath`]: process.html#process_process_execpath -[`process.on('disconnect')`]: process.html#process_event_disconnect -[`process.on('message')`]: process.html#process_event_message [`process.send()`]: process.html#process_process_send_message_sendhandle_options_callback [`stdio`]: #child_process_options_stdio [`util.promisify()`]: util.html#util_util_promisify_original diff --git a/doc/api/cli.md b/doc/api/cli.md index 83dc4459352ca7..076b1238904228 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -24,91 +24,80 @@ _For more info about `node debug`, please see the [debugger][] documentation._ ## Options -### `-v`, `--version` +### `-` -Print node's version. +Alias for stdin, analogous to the use of - in other command line utilities, +meaning that the script will be read from stdin, and the rest of the options +are passed to that script. -### `-h`, `--help` +### `--` -Print node command line options. -The output of this option is less detailed than this document. +Indicate the end of node options. Pass the rest of the arguments to the script. +If no script filename or eval/print script is supplied prior to this, then +the next argument will be used as a script filename. -### `-e`, `--eval "script"` +### `--abort-on-uncaught-exception` -Evaluate the following argument as JavaScript. The modules which are -predefined in the REPL can also be used in `script`. +Aborting instead of exiting causes a core file to be generated for post-mortem +analysis using a debugger (such as `lldb`, `gdb`, and `mdb`). -On Windows, using `cmd.exe` a single quote will not work correctly because it -only recognizes double `"` for quoting. In Powershell or Git bash, both `'` -and `"` are usable. +If this flag is passed, the behavior can still be set to not abort through +[`process.setUncaughtExceptionCaptureCallback()`][] (and through usage of the +`domain` module that uses it). -### `-p`, `--print "script"` +### `--enable-fips` -Identical to `-e` but prints the result. +Enable FIPS-compliant crypto at startup. (Requires Node.js to be built with +`./configure --openssl-fips`) -### `-c`, `--check` +### `--experimental-modules` -Syntax check the script without executing. +Enable experimental ES module support and caching modules. -### `-i`, `--interactive` +### `--experimental-vm-modules` -Opens the REPL even if stdin does not appear to be a terminal. +Enable experimental ES Module support in the `vm` module. -### `-r`, `--require module` +### `--force-fips` -Preload the specified module at startup. - -Follows `require()`'s module resolution -rules. `module` may be either a path to a file, or a node module name. +Force FIPS-compliant crypto on startup. (Cannot be disabled from script code.) +(Same requirements as `--enable-fips`) -### `--inspect[=[host:]port]` +### `--icu-data-dir=file` -Activate inspector on host:port. Default is 127.0.0.1:9229. - -V8 inspector integration allows tools such as Chrome DevTools and IDEs to debug -and profile Node.js instances. The tools attach to Node.js instances via a -tcp port and communicate using the [Chrome Debugging Protocol][]. +Specify ICU data load path. (overrides `NODE_ICU_DATA`) ### `--inspect-brk[=[host:]port]` @@ -131,6 +120,27 @@ Useful when activating the inspector by sending the `SIGUSR1` signal. Default host is 127.0.0.1. +### `--inspect[=[host:]port]` + + +Activate inspector on host:port. Default is 127.0.0.1:9229. + +V8 inspector integration allows tools such as Chrome DevTools and IDEs to debug +and profile Node.js instances. The tools attach to Node.js instances via a +tcp port and communicate using the [Chrome Debugging Protocol][]. + + +### `--napi-modules` + + +Enable loading native modules compiled with the ABI-stable Node.js API (N-API) +(experimental). + + ### `--no-deprecation` -Print stack traces for deprecations. +Disables runtime checks for `async_hooks`. These will still be enabled +dynamically when `async_hooks` is enabled. -### `--throw-deprecation` +### `--no-warnings` -Throw errors for deprecations. +Silence all process warnings (including deprecations). + + +### `--openssl-config=file` + + +Load an OpenSSL configuration file on startup. Among other uses, this can be +used to enable FIPS-compliant crypto if Node.js is built with +`./configure --openssl-fips`. + ### `--pending-deprecation` -Silence all process warnings (including deprecations). +Instructs the module loader to preserve symbolic links when resolving and +caching modules. -### `--abort-on-uncaught-exception` - +By default, when Node.js loads a module from a path that is symbolically linked +to a different on-disk location, Node.js will dereference the link and use the +actual on-disk "real path" of the module as both an identifier and as a root +path to locate other dependency modules. In most cases, this default behavior +is acceptable. However, when using symbolically linked peer dependencies, as +illustrated in the example below, the default behavior causes an exception to +be thrown if `moduleA` attempts to require `moduleB` as a peer dependency: -Aborting instead of exiting causes a core file to be generated for post-mortem -analysis using a debugger (such as `lldb`, `gdb`, and `mdb`). +```text +{appDir} + ├── app + │ ├── index.js + │ └── node_modules + │ ├── moduleA -> {appDir}/moduleA + │ └── moduleB + │ ├── index.js + │ └── package.json + └── moduleA + ├── index.js + └── package.json +``` -If this flag is passed, the behavior can still be set to not abort through -[`process.setUncaughtExceptionCaptureCallback()`][] (and through usage of the -`domain` module that uses it). +The `--preserve-symlinks` command line flag instructs Node.js to use the +symlink path for modules as opposed to the real path, allowing symbolically +linked peer dependencies to be found. -### `--trace-warnings` +Note, however, that using `--preserve-symlinks` can have other side effects. +Specifically, symbolically linked *native* modules can fail to load if those +are linked from more than one location in the dependency tree (Node.js would +see those as two separate modules and would attempt to load the module multiple +times, causing an exception to be thrown). + + +### `--prof-process` -Print stack traces for process warnings (including deprecations). +Process V8 profiler output generated using the V8 option `--prof`. + ### `--redirect-warnings=file` -Prints a stack trace whenever synchronous I/O is detected after the first turn -of the event loop. +Throw errors for deprecations. -### `--no-force-async-hooks-checks` + +### `--tls-cipher-list=list` -Disables runtime checks for `async_hooks`. These will still be enabled -dynamically when `async_hooks` is enabled. +Specify an alternative default TLS cipher list. (Requires Node.js to be built +with crypto support. (Default)) -### `--trace-events-enabled` + +### `--trace-deprecation` -Enables the collection of trace event tracing information. +Print stack traces for deprecations. + ### `--trace-event-categories` -Automatically zero-fills all newly allocated [Buffer][] and [SlowBuffer][] -instances. +Enables the collection of trace event tracing information. -### `--preserve-symlinks` +### `--trace-sync-io` -Instructs the module loader to preserve symbolic links when resolving and -caching modules. +Prints a stack trace whenever synchronous I/O is detected after the first turn +of the event loop. -By default, when Node.js loads a module from a path that is symbolically linked -to a different on-disk location, Node.js will dereference the link and use the -actual on-disk "real path" of the module as both an identifier and as a root -path to locate other dependency modules. In most cases, this default behavior -is acceptable. However, when using symbolically linked peer dependencies, as -illustrated in the example below, the default behavior causes an exception to -be thrown if `moduleA` attempts to require `moduleB` as a peer dependency: -```text -{appDir} - ├── app - │ ├── index.js - │ └── node_modules - │ ├── moduleA -> {appDir}/moduleA - │ └── moduleB - │ ├── index.js - │ └── package.json - └── moduleA - ├── index.js - └── package.json -``` +### `--trace-warnings` + -The `--preserve-symlinks` command line flag instructs Node.js to use the -symlink path for modules as opposed to the real path, allowing symbolically -linked peer dependencies to be found. +Print stack traces for process warnings (including deprecations). -Note, however, that using `--preserve-symlinks` can have other side effects. -Specifically, symbolically linked *native* modules can fail to load if those -are linked from more than one location in the dependency tree (Node.js would -see those as two separate modules and would attempt to load the module multiple -times, causing an exception to be thrown). ### `--track-heap-objects` -Process V8 profiler output generated using the V8 option `--prof`. +Use bundled Mozilla CA store as supplied by current Node.js version +or use OpenSSL's default CA store. The default store is selectable +at build-time. + +The bundled CA store, as supplied by Node.js, is a snapshot of Mozilla CA store +that is fixed at release time. It is identical on all supported platforms. + +Using OpenSSL store allows for external modifications of the store. For most +Linux and BSD distributions, this store is maintained by the distribution +maintainers and system administrators. OpenSSL CA store location is dependent on +configuration of the OpenSSL library but this can be altered at runtime using +environment variables. + +See `SSL_CERT_DIR` and `SSL_CERT_FILE`. ### `--v8-options` @@ -320,87 +360,107 @@ underscores (`_`). For example, `--stack-trace-limit` is equivalent to `--stack_trace_limit`. -### `--tls-cipher-list=list` + +### `--v8-pool-size=num` -Specify an alternative default TLS cipher list. (Requires Node.js to be built -with crypto support. (Default)) +Set V8's thread pool size which will be used to allocate background jobs. +If set to `0` then V8 will choose an appropriate size of the thread pool based +on the number of online processors. -### `--enable-fips` +If the value provided is larger than V8's maximum, then the largest value +will be chosen. + + +### `--zero-fill-buffers` -Enable FIPS-compliant crypto at startup. (Requires Node.js to be built with -`./configure --openssl-fips`) +Automatically zero-fills all newly allocated [Buffer][] and [SlowBuffer][] +instances. -### `--force-fips` +### `-c`, `--check` -Force FIPS-compliant crypto on startup. (Cannot be disabled from script code.) -(Same requirements as `--enable-fips`) +Syntax check the script without executing. -### `--openssl-config=file` +### `-e`, `--eval "script"` -Load an OpenSSL configuration file on startup. Among other uses, this can be -used to enable FIPS-compliant crypto if Node.js is built with -`./configure --openssl-fips`. +Evaluate the following argument as JavaScript. The modules which are +predefined in the REPL can also be used in `script`. -### `--use-openssl-ca`, `--use-bundled-ca` +On Windows, using `cmd.exe` a single quote will not work correctly because it +only recognizes double `"` for quoting. In Powershell or Git bash, both `'` +and `"` are usable. + + +### `-h`, `--help` -Use OpenSSL's default CA store or use bundled Mozilla CA store as supplied by -current Node.js version. The default store is selectable at build-time. +Print node command line options. +The output of this option is less detailed than this document. -Using OpenSSL store allows for external modifications of the store. For most -Linux and BSD distributions, this store is maintained by the distribution -maintainers and system administrators. OpenSSL CA store location is dependent on -configuration of the OpenSSL library but this can be altered at runtime using -environment variables. -The bundled CA store, as supplied by Node.js, is a snapshot of Mozilla CA store -that is fixed at release time. It is identical on all supported platforms. +### `-i`, `--interactive` + -See `SSL_CERT_DIR` and `SSL_CERT_FILE`. +Opens the REPL even if stdin does not appear to be a terminal. -### `--icu-data-dir=file` + +### `-p`, `--print "script"` -Specify ICU data load path. (overrides `NODE_ICU_DATA`) +Identical to `-e` but prints the result. -### `-` +### `-r`, `--require module` -Alias for stdin, analogous to the use of - in other command line utilities, -meaning that the script will be read from stdin, and the rest of the options -are passed to that script. +Preload the specified module at startup. +Follows `require()`'s module resolution +rules. `module` may be either a path to a file, or a node module name. -### `--` + +### `-v`, `--version` -Indicate the end of node options. Pass the rest of the arguments to the script. -If no script filename or eval/print script is supplied prior to this, then -the next argument will be used as a script filename. +Print node's version. ## Environment Variables @@ -412,22 +472,27 @@ added: v0.1.32 `','`-separated list of core modules that should print debug information. -### `NODE_PATH=path[:…]` +### `NODE_DISABLE_COLORS=1` -`':'`-separated list of directories prefixed to the module search path. - -On Windows, this is a `';'`-separated list instead. +When set to `1` colors will not be used in the REPL. -### `NODE_DISABLE_COLORS=1` +### `NODE_EXTRA_CA_CERTS=file` -When set to `1` colors will not be used in the REPL. +When set, the well known "root" CAs (like VeriSign) will be extended with the +extra certificates in `file`. The file should consist of one or more trusted +certificates in PEM format. A message will be emitted (once) with +[`process.emitWarning()`][emit_warning] if the file is missing or +malformed, but any errors are otherwise ignored. + +Note that neither the well known nor extra certificates are used when the `ca` +options property is explicitly specified for a TLS or HTTPS client or server. ### `NODE_ICU_DATA=file` @@ -438,6 +503,7 @@ added: v0.11.15 Data path for ICU (Intl object) data. Will extend linked-in data when compiled with small-icu support. + ### `NODE_NO_WARNINGS=1` + +`':'`-separated list of directories prefixed to the module search path. + +On Windows, this is a `';'`-separated list instead. + + ### `NODE_PENDING_DEPRECATION=1` -Path to the file used to store the persistent REPL history. The default path is -`~/.node_repl_history`, which is overridden by this variable. Setting the value -to an empty string (`''` or `' '`) disables persistent REPL history. +When set, process warnings will be emitted to the given file instead of +printing to stderr. The file will be created if it does not exist, and will be +appended to if it does. If an error occurs while attempting to write the +warning to the file, the warning will be written to stderr instead. This is +equivalent to using the `--redirect-warnings=file` command-line flag. -### `NODE_EXTRA_CA_CERTS=file` +### `NODE_REPL_HISTORY=file` -When set, the well known "root" CAs (like VeriSign) will be extended with the -extra certificates in `file`. The file should consist of one or more trusted -certificates in PEM format. A message will be emitted (once) with -[`process.emitWarning()`][emit_warning] if the file is missing or -malformed, but any errors are otherwise ignored. +Path to the file used to store the persistent REPL history. The default path is +`~/.node_repl_history`, which is overridden by this variable. Setting the value +to an empty string (`''` or `' '`) disables persistent REPL history. -Note that neither the well known nor extra certificates are used when the `ca` -options property is explicitly specified for a TLS or HTTPS client or server. ### `OPENSSL_CONF=file` - -When set, process warnings will be emitted to the given file instead of -printing to stderr. The file will be created if it does not exist, and will be -appended to if it does. If an error occurs while attempting to write the -warning to the file, the warning will be written to stderr instead. This is -equivalent to using the `--redirect-warnings=file` command-line flag. ### `UV_THREADPOOL_SIZE=size` diff --git a/doc/api/cluster.md b/doc/api/cluster.md index a9456feedf7a4f..04b9eac46c8a92 100644 --- a/doc/api/cluster.md +++ b/doc/api/cluster.md @@ -192,7 +192,7 @@ added: v0.7.0 * `message` {Object} * `handle` {undefined|Object} -Similar to the `cluster.on('message')` event, but specific to this worker. +Similar to the `'message'` event of `cluster`, but specific to this worker. Within a worker, `process.on('message')` may also be used. @@ -269,7 +269,7 @@ changes: * Returns: {cluster.Worker} A reference to `worker`. -In a worker, this function will close all servers, wait for the `'close'` event +In a worker, this function will close all servers, wait for the `'close'` event on those servers, and then disconnect the IPC channel. In the master, an internal message is sent to the worker causing it to call @@ -280,7 +280,7 @@ Causes `.exitedAfterDisconnect` to be set. Note that after a server is closed, it will no longer accept new connections, but connections may be accepted by any other listening worker. Existing connections will be allowed to close as usual. When no more connections exist, -see [`server.close()`][], the IPC channel to the worker will close allowing it +see [`server.close()`][], the IPC channel to the worker will close allowing it to die gracefully. The above applies *only* to server connections, client connections are not @@ -463,7 +463,7 @@ added: v0.7.9 Emitted after the worker IPC channel has disconnected. This can occur when a worker exits gracefully, is killed, or is disconnected manually (such as with -worker.disconnect()). +`worker.disconnect()`). There may be a delay between the `'disconnect'` and `'exit'` events. These events can be used to detect if the process is stuck in a cleanup or if there @@ -497,7 +497,7 @@ cluster.on('exit', (worker, code, signal) => { }); ``` -See [child_process event: 'exit'][]. +See [child_process event: `'exit'`][]. ## Event: 'fork' + The `Console` class can be used to create a simple logger with configurable @@ -77,9 +78,29 @@ const { Console } = require('console'); const { Console } = console; ``` -### new Console(stdout[, stderr]) -* `stdout` {stream.Writable} -* `stderr` {stream.Writable} +### new Console(stdout[, stderr][, ignoreErrors]) +### new Console(options) + + +* `options` {Object} + * `stdout` {stream.Writable} + * `stderr` {stream.Writable} + * `ignoreErrors` {boolean} Ignore errors when writing to the underlying + streams. **Default:** `true`. + * `colorMode` {boolean|string} Set color support for this `Console` instance. + Setting to `true` enables coloring while inspecting values, setting to + `'auto'` will make color support depend on the value of the `isTTY` property + and the value returned by `getColorDepth()` on the respective stream. + **Default:** `'auto'` Creates a new `Console` with one or two writable stream instances. `stdout` is a writable stream to print log or info output. `stderr` is used for warning or @@ -89,7 +110,7 @@ error output. If `stderr` is not provided, `stdout` is used for `stderr`. const output = fs.createWriteStream('./stdout.log'); const errorOutput = fs.createWriteStream('./stderr.log'); // custom simple logger -const logger = new Console(output, errorOutput); +const logger = new Console({ stdout: output, stderr: errorOutput }); // use it like console const count = 5; logger.log('count: %d', count); @@ -100,7 +121,7 @@ The global `console` is a special `Console` whose output is sent to [`process.stdout`][] and [`process.stderr`][]. It is equivalent to calling: ```js -new Console(process.stdout, process.stderr); +new Console({ stdout: process.stdout, stderr: process.stderr }); ``` ### console.assert(value[, message][, ...args]) @@ -362,6 +383,47 @@ console.log('count:', count); See [`util.format()`][] for more information. +### console.table(tabularData[, properties]) + + +* `tabularData` {any} +* `properties` {string[]} Alternate properties for constructing the table. + +Try to construct a table with the columns of the properties of `tabularData` +(or use `properties`) and rows of `tabularData` and logit. Falls back to just +logging the argument if it can’t be parsed as tabular. + +```js +// These can't be parsed as tabular data +console.table(Symbol()); +// Symbol() + +console.table(undefined); +// undefined +``` + +```js +console.table([{ a: 1, b: 'Y' }, { a: 'Z', b: 2 }]); +// ┌─────────┬─────┬─────┐ +// │ (index) │ a │ b │ +// ├─────────┼─────┼─────┤ +// │ 0 │ 1 │ 'Y' │ +// │ 1 │ 'Z' │ 2 │ +// └─────────┴─────┴─────┘ +``` + +```js +console.table([{ a: 1, b: 'Y' }, { a: 'Z', b: 2 }], ['a']); +// ┌─────────┬─────┐ +// │ (index) │ a │ +// ├─────────┼─────┤ +// │ 0 │ 1 │ +// │ 1 │ 'Z' │ +// └─────────┴─────┘ +``` + ### console.time(label) - `outputEncoding` {string} - Returns: {Buffer | string} Any remaining enciphered contents. - If `outputEncoding` parameter is one of `'latin1'`, `'base64'` or `'hex'`, - a string is returned. If an `outputEncoding` is not provided, a [`Buffer`][] - is returned. + If `outputEncoding` is one of `'latin1'`, `'base64'` or `'hex'`, a string is + returned. If an `outputEncoding` is not provided, a [`Buffer`][] is returned. Once the `cipher.final()` method has been called, the `Cipher` object can no longer be used to encrypt data. Attempts to call `cipher.final()` more than @@ -387,9 +386,8 @@ added: v0.1.94 --> - `outputEncoding` {string} - Returns: {Buffer | string} Any remaining deciphered contents. - If `outputEncoding` parameter is one of `'latin1'`, `'ascii'` or `'utf8'`, - a string is returned. If an `outputEncoding` is not provided, a [`Buffer`][] - is returned. + If `outputEncoding` is one of `'latin1'`, `'ascii'` or `'utf8'`, a string is + returned. If an `outputEncoding` is not provided, a [`Buffer`][] is returned. Once the `decipher.final()` method has been called, the `Decipher` object can no longer be used to decrypt data. Attempts to call `decipher.final()` more @@ -1288,11 +1286,15 @@ become deprecated in a future Node.js release. ### crypto.fips Property for checking and controlling whether a FIPS compliant crypto provider is currently in use. Setting to true requires a FIPS build of Node.js. +This property is deprecated. Please use `crypto.setFips()` and +`crypto.getFips()` instead. + ### crypto.createCipher(algorithm, password[, options]) +- Returns: {boolean} `true` if and only if a FIPS compliant crypto provider is + currently in use. + ### crypto.getHashes() +* `bool` {boolean} `true` to enable FIPS mode. + +Enables the FIPS compliant crypto provider in a FIPS-enabled Node.js build. +Throws an error if FIPS mode is not available. + ### crypto.timingSafeEqual(a, b) + Node.js may deprecate APIs when either: (a) use of the API is considered to be unsafe, (b) an improved alternative API has been made available, or (c) @@ -82,15 +83,15 @@ is strongly recommended: * [`Buffer.alloc(size[, fill[, encoding]])`][alloc] - Create a `Buffer` with *initialized* memory. -* [`Buffer.allocUnsafe(size)`][alloc_unsafe_size] - Create a `Buffer` with +* [`Buffer.allocUnsafe(size)`][alloc_unsafe_size] - Create a `Buffer` with *uninitialized* memory. * [`Buffer.allocUnsafeSlow(size)`][] - Create a `Buffer` with *uninitialized* memory. * [`Buffer.from(array)`][] - Create a `Buffer` with a copy of `array` -* [`Buffer.from(arrayBuffer[, byteOffset[, length]])`][from_arraybuffer] - +* [`Buffer.from(arrayBuffer[, byteOffset[, length]])`][from_arraybuffer] - Create a `Buffer` that wraps the given `arrayBuffer`. * [`Buffer.from(buffer)`][] - Create a `Buffer` that copies `buffer`. -* [`Buffer.from(string[, encoding])`][from_string_encoding] - Create a `Buffer` +* [`Buffer.from(string[, encoding])`][from_string_encoding] - Create a `Buffer` that copies `string`. @@ -699,8 +700,8 @@ Type: Runtime `Module._debug()` has been deprecated. -The `Module._debug()` function was never documented as an officially -supported API. +The `Module._debug()` function was never documented as an officially supported +API. ### DEP0078: REPLServer.turnOffEditorMode() @@ -771,7 +772,7 @@ API instead. Type: Runtime -`runInAsyncIdScope` doesn't emit the `before` or `after` event and can thus +`runInAsyncIdScope` doesn't emit the `'before'` or `'after'` event and can thus cause a lot of issues. See https://github.com/nodejs/node/issues/14328 for more details. @@ -784,13 +785,22 @@ Importing assert directly is not recommended as the exposed functions will use loose equality checks. Use `require('assert').strict` instead. The API is the same as the legacy assert but it will always use strict equality checks. + +### DEP0093: crypto.fips is deprecated and replaced. + +Type: Documentation-only + +The [`crypto.fips`][] property is deprecated. Please use `crypto.setFips()` +and `crypto.getFips()` instead. + -### DEP0098: AsyncHooks Embedder AsyncResource.emit{Before,After} APIs +### DEP0098: AsyncHooks Embedder AsyncResource.emitBefore and AsyncResource.emitAfter APIs Type: Runtime -The embedded API provided by AsyncHooks exposes emit{Before,After} methods -which are very easy to use incorrectly which can lead to unrecoverable errors. +The embedded API provided by AsyncHooks exposes `.emitBefore()` and +`.emitAfter()` methods which are very easy to use incorrectly which can lead +to unrecoverable errors. Use [`asyncResource.runInAsyncScope()`][] API instead which provides a much safer, and more convenient, alternative. See @@ -806,6 +816,25 @@ because it also made sense to interpret the value as the number of bytes read by the engine, but is inconsistent with other streams in Node.js that expose values under these names. + +### DEP0103: process.binding('util').is[...] typechecks + +Type: Documentation-only (supports [`--pending-deprecation`][]) + +Using `process.binding()` in general should be avoided. The type checking +methods in particular can be replaced by using [`util.types`][]. + + +### DEP0108: zlib.bytesRead + +Type: Documentation-only + +Deprecated alias for [`zlib.bytesWritten`][]. This original name was chosen +because it also made sense to interpret the value as the number of bytes +read by the engine, but is inconsistent with other streams in Node.js that +expose values under these names. + +[`--pending-deprecation`]: cli.html#cli_pending_deprecation [`Buffer.allocUnsafeSlow(size)`]: buffer.html#buffer_class_method_buffer_allocunsafeslow_size [`Buffer.from(array)`]: buffer.html#buffer_class_method_buffer_from_array [`Buffer.from(buffer)`]: buffer.html#buffer_class_method_buffer_from_buffer @@ -820,6 +849,7 @@ expose values under these names. [`console.error()`]: console.html#console_console_error_data_args [`console.log()`]: console.html#console_console_log_data_args [`crypto.createCredentials()`]: crypto.html#crypto_crypto_createcredentials_details +[`crypto.fips`]: crypto.html#crypto_crypto_fips [`crypto.pbkdf2()`]: crypto.html#crypto_crypto_pbkdf2_password_salt_iterations_keylen_digest_callback [`domain`]: domain.html [`ecdh.setPublicKey()`]: crypto.html#crypto_ecdh_setpublickey_publickey_encoding @@ -865,6 +895,7 @@ expose values under these names. [`util.log()`]: util.html#util_util_log_string [`util.print()`]: util.html#util_util_print_strings [`util.puts()`]: util.html#util_util_puts_strings +[`util.types`]: util.html#util_util_types [`util`]: util.html [`worker.exitedAfterDisconnect`]: cluster.html#cluster_worker_exitedafterdisconnect [`zlib.bytesWritten`]: zlib.html#zlib_zlib_byteswritten diff --git a/doc/api/dgram.md b/doc/api/dgram.md index b30a489ed2fae7..06c15ffa25c879 100644 --- a/doc/api/dgram.md +++ b/doc/api/dgram.md @@ -100,6 +100,8 @@ available interface, call `addMembership` multiple times, once per interface. added: v0.1.99 --> +* Returns: {Object} + Returns an object containing the address information for a socket. For UDP sockets, this object will contain `address`, `family` and `port` properties. @@ -394,7 +396,7 @@ added: v8.6.0 *Note: All references to scope in this section are referring to [IPv6 Zone Indices][], which are defined by [RFC 4007][]. In string form, an IP -with a scope index is written as `'IP%scope'` where scope is an interface name +with a scope index is written as `'IP%scope'` where scope is an interface name or interface number.* Sets the default outgoing multicast interface of the socket to a chosen @@ -603,13 +605,13 @@ and port can be retrieved using [`socket.address().address`][] and added: v0.1.99 --> -* `type` {string} - Either 'udp4' or 'udp6'. +* `type` {string} - Either `'udp4'` or `'udp6'`. * `callback` {Function} - Attached as a listener to `'message'` events. * Returns: {dgram.Socket} Creates a `dgram.Socket` object of the specified `type`. The `type` argument -can be either `udp4` or `udp6`. An optional `callback` function can be passed -which is added as a listener for `'message'` events. +can be either `'udp4'` or `'udp6'`. An optional `callback` function can be +passed which is added as a listener for `'message'` events. Once the socket is created, calling [`socket.bind()`][] will instruct the socket to begin listening for datagram messages. When `address` and `port` are diff --git a/doc/api/dns.md b/doc/api/dns.md index 5fd6b28e05a20a..a878ad27177b7d 100644 --- a/doc/api/dns.md +++ b/doc/api/dns.md @@ -56,7 +56,7 @@ dns.resolve4('archive.org', (err, addresses) => { There are subtle consequences in choosing one over the other, please consult the [Implementation considerations section][] for more information. -## Class dns.Resolver +## Class: dns.Resolver @@ -110,6 +110,8 @@ callbacks will be called with an error with code `ECANCELLED`. added: v0.11.3 --> +* Returns: {string[]} + Returns an array of IP address strings, formatted according to [rfc5952][], that are currently configured for DNS resolution. A string will include a port section if a custom port is used. @@ -143,7 +145,7 @@ changes: - `verbatim` {boolean} When `true`, the callback receives IPv4 and IPv6 addresses in the order the DNS resolver returned them. When `false`, IPv4 addresses are placed before IPv6 addresses. - **Default:** currently `false` (addresses are reordered) but this is + **Default:** currently `false` (addresses are reordered) but this is expected to change in the not too distant future. New code should use `{ verbatim: true }`. - `callback` {Function} diff --git a/doc/api/documentation.md b/doc/api/documentation.md index 922935f4c98a01..bc5e27e6afeefa 100644 --- a/doc/api/documentation.md +++ b/doc/api/documentation.md @@ -63,7 +63,7 @@ failures or behavior changes when API modifications occur. To help avoid such surprises, `Experimental` features may require a command-line flag to explicitly enable them, or may cause a process warning to be emitted. By default, such warnings are printed to [`stderr`][] and may be handled by -attaching a listener to the [`process.on('warning')`][] event. +attaching a listener to the [`'warning'`][] event. ## JSON Output + > Stability: 1 - Experimental diff --git a/doc/api/events.md b/doc/api/events.md index 470d9ba997fbe7..e380a71366701f 100644 --- a/doc/api/events.md +++ b/doc/api/events.md @@ -298,6 +298,7 @@ added: v0.1.26 --> - `eventName` {string|symbol} - `...args` {any} +- Returns: {boolean} Synchronously calls each of the listeners registered for the event named `eventName`, in the order they were registered, passing the supplied arguments @@ -310,6 +311,8 @@ Returns `true` if the event had listeners, `false` otherwise. added: v6.0.0 --> +- Returns: {Array} + Returns an array listing the events for which the emitter has registered listeners. The values in the array will be strings or Symbols. @@ -331,6 +334,8 @@ console.log(myEE.eventNames()); added: v1.0.0 --> +- Returns: {integer} + Returns the current max listener value for the `EventEmitter` which is either set by [`emitter.setMaxListeners(n)`][] or defaults to [`EventEmitter.defaultMaxListeners`][]. @@ -341,6 +346,7 @@ added: v3.2.0 --> * `eventName` {string|symbol} The name of the event being listened for +* Returns: {integer} Returns the number of listeners listening to the event named `eventName`. @@ -354,6 +360,7 @@ changes: original listeners instead of wrapper functions now. --> - `eventName` {string|symbol} +- Returns: {Function[]} Returns a copy of the array of listeners for the event named `eventName`. @@ -372,6 +379,7 @@ added: v0.1.101 * `eventName` {string|symbol} The name of the event. * `listener` {Function} The callback function +* Returns: {EventEmitter} Adds the `listener` function to the end of the listeners array for the event named `eventName`. No checks are made to see if the `listener` has @@ -408,6 +416,7 @@ added: v0.3.0 * `eventName` {string|symbol} The name of the event. * `listener` {Function} The callback function +* Returns: {EventEmitter} Adds a **one-time** `listener` function for the event named `eventName`. The next time `eventName` is triggered, this listener is removed and then invoked. @@ -441,6 +450,7 @@ added: v6.0.0 * `eventName` {string|symbol} The name of the event. * `listener` {Function} The callback function +* Returns: {EventEmitter} Adds the `listener` function to the *beginning* of the listeners array for the event named `eventName`. No checks are made to see if the `listener` has @@ -463,6 +473,7 @@ added: v6.0.0 * `eventName` {string|symbol} The name of the event. * `listener` {Function} The callback function +* Returns: {EventEmitter} Adds a **one-time** `listener` function for the event named `eventName` to the *beginning* of the listeners array. The next time `eventName` is triggered, this @@ -481,6 +492,7 @@ Returns a reference to the `EventEmitter`, so that calls can be chained. added: v0.1.26 --> - `eventName` {string|symbol} +- Returns: {EventEmitter} Removes all listeners, or those of the specified `eventName`. @@ -496,6 +508,7 @@ added: v0.1.26 --> - `eventName` {string|symbol} - `listener` {Function} +- Returns: {EventEmitter} Removes the specified `listener` from the listener array for the event named `eventName`. @@ -509,15 +522,15 @@ server.on('connection', callback); server.removeListener('connection', callback); ``` -`removeListener` will remove, at most, one instance of a listener from the +`removeListener()` will remove, at most, one instance of a listener from the listener array. If any single listener has been added multiple times to the -listener array for the specified `eventName`, then `removeListener` must be +listener array for the specified `eventName`, then `removeListener()` must be called multiple times to remove each instance. Note that once an event has been emitted, all listeners attached to it at the -time of emitting will be called in order. This implies that any +time of emitting will be called in order. This implies that any `removeListener()` or `removeAllListeners()` calls *after* emitting and *before* -the last listener finishes execution will not remove them from `emit()` +the last listener finishes execution will not remove them from `emit()` in progress. Subsequent events will behave as expected. ```js @@ -564,6 +577,7 @@ Returns a reference to the `EventEmitter`, so that calls can be chained. added: v0.3.5 --> - `n` {integer} +- Returns: {EventEmitter} By default EventEmitters will print a warning if more than `10` listeners are added for a particular event. This is a useful default that helps finding @@ -579,9 +593,10 @@ Returns a reference to the `EventEmitter`, so that calls can be chained. added: v9.4.0 --> - `eventName` {string|symbol} +- Returns: {Function[]} Returns a copy of the array of listeners for the event named `eventName`, -including any wrappers (such as those created by `.once`). +including any wrappers (such as those created by `.once()`). ```js const emitter = new EventEmitter(); @@ -599,7 +614,7 @@ logFnWrapper.listener(); logFnWrapper(); emitter.on('log', () => console.log('log persistently')); -// will return a new Array with a single function bound by `on` above +// will return a new Array with a single function bound by `.on()` above const newListeners = emitter.rawListeners('log'); // logs "log persistently" twice diff --git a/doc/api/fs.md b/doc/api/fs.md index 913a34603c9153..238f998a9e327b 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -312,7 +312,7 @@ support. If `filename` is provided, it will be provided as a `Buffer` if `filename` will be a UTF-8 string. ```js -// Example when handled through fs.watch listener +// Example when handled through fs.watch() listener fs.watch('./tmp', { encoding: 'buffer' }, (eventType, filename) => { if (filename) { console.log(filename); @@ -330,6 +330,13 @@ added: v0.5.8 Emitted when an error occurs while watching the file. +### Event: 'close' + + +Emitted when the watcher stops watching for changes. + ### watcher.close() -* `fd` {integer} Integer file descriptor used by the ReadStream. +* `fd` {integer} Integer file descriptor used by the `ReadStream`. Emitted when the `fs.ReadStream`'s file descriptor has been opened. +### Event: 'ready' + + +Emitted when the `fs.ReadStream` is ready to be used. + +Fires immediately after `'open'`. + ### readStream.bytesRead -* `fd` {integer} Integer file descriptor used by the WriteStream. +* `fd` {integer} Integer file descriptor used by the `WriteStream`. + +Emitted when the `WriteStream`'s file is opened. + +### Event: 'ready' + + +Emitted when the `fs.WriteStream` is ready to be used. -Emitted when the WriteStream's file is opened. +Fires immediately after `'open'`. ### writeStream.bytesWritten * `name` {string} -* Returns: {string} +* Returns: {any} Reads out a header on the request. Note that the name is case insensitive. +The type of the return value depends on the arguments provided to +[`request.setHeader()`][]. Example: ```js +request.setHeader('content-type', 'text/html'); +request.setHeader('Content-Length', Buffer.byteLength(body)); +request.setHeader('Set-Cookie', ['type=ninja', 'language=javascript']); const contentType = request.getHeader('Content-Type'); +// contentType is 'text/html' +const contentLength = request.getHeader('Content-Length'); +// contentLength is of type number +const setCookie = request.getHeader('set-cookie'); +// setCookie is of type string[] + ``` ### request.removeHeader(name) @@ -580,11 +591,14 @@ added: v1.6.0 --> * `name` {string} -* `value` {string} +* `value` {any} Sets a single header value for headers object. If this header already exists in the to-be-sent headers, its value will be replaced. Use an array of strings -here to send multiple headers with the same name. +here to send multiple headers with the same name. Non-string values will be +stored without modification. Therefore, [`request.getHeader()`][] may return +non-string values. However, the non-string values will be converted to strings +for network transmission. Example: ```js @@ -625,13 +639,12 @@ added: v0.5.9 * `timeout` {number} Milliseconds before a request times out. * `callback` {Function} Optional function to be called when a timeout occurs. - Same as binding to the `timeout` event. + Same as binding to the `'timeout'` event. +* Returns: {http.ClientRequest} Once a socket is assigned to this request and is connected [`socket.setTimeout()`][] will be called. -Returns `request`. - ### request.socket * `exception` {Error} @@ -892,6 +905,7 @@ added: v0.9.12 * `msecs` {number} **Default:** `120000` (2 minutes) * `callback` {Function} +* Returns: {http.Server} Sets the timeout value for sockets, and emits a `'timeout'` event on the Server object, passing the socket as an argument, if a timeout @@ -904,8 +918,6 @@ By default, the Server's timeout value is 2 minutes, and sockets are destroyed automatically if they time out. However, if a callback is assigned to the Server's `'timeout'` event, timeouts must be handled explicitly. -Returns `server`. - ### server.timeout * `name` {string} -* Returns: {string} +* Returns: {any} Reads out a header that's already been queued but not sent to the client. -Note that the name is case insensitive. +Note that the name is case insensitive. The type of the return value depends +on the arguments provided to [`response.setHeader()`][]. Example: ```js +response.setHeader('Content-Type', 'text/html'); +response.setHeader('Content-Length', Buffer.byteLength(body)); +response.setHeader('Set-Cookie', ['type=ninja', 'language=javascript']); const contentType = response.getHeader('content-type'); +// contentType is 'text/html' +const contentLength = response.getHeader('Content-Length'); +// contentLength is of type number +const setCookie = response.getHeader('set-cookie'); +// setCookie is of type string[] ``` ### response.getHeaderNames() @@ -1165,11 +1186,14 @@ added: v0.4.0 --> * `name` {string} -* `value` {string | string[]} +* `value` {any} Sets a single header value for implicit headers. If this header already exists in the to-be-sent headers, its value will be replaced. Use an array of strings -here to send multiple headers with the same name. +here to send multiple headers with the same name. Non-string values will be +stored without modification. Therefore, [`response.getHeader()`][] may return +non-string values. However, the non-string values will be converted to strings +for network transmission. Example: @@ -1207,6 +1231,7 @@ added: v0.9.12 * `msecs` {number} * `callback` {Function} +* Returns: {http.ServerResponse} Sets the Socket's timeout value to `msecs`. If a callback is provided, then it is added as a listener on the `'timeout'` event on @@ -1217,8 +1242,6 @@ the server, then sockets are destroyed when they time out. If a handler is assigned to the request, the response, or the server's `'timeout'` events, timed out sockets must be handled explicitly. -Returns `response`. - ### response.socket -The `'close'` event is emitted once the `Http2Session` has been destroyed. +The `'close'` event is emitted once the `Http2Session` has been destroyed. Its +listener does not expect any arguments. #### Event: 'connect' -Calls [`ref()`][`net.Socket.prototype.ref`] on this `Http2Session` +Calls [`ref()`][`net.Socket.prototype.ref()`] on this `Http2Session` instance's underlying [`net.Socket`]. #### http2session.remoteSettings @@ -571,7 +572,7 @@ client. added: v9.4.0 --> -Calls [`unref()`][`net.Socket.prototype.unref`] on this `Http2Session` +Calls [`unref()`][`net.Socket.prototype.unref()`] on this `Http2Session` instance's underlying [`net.Socket`]. ### Class: ServerHttp2Session @@ -717,8 +718,8 @@ const { const req = clientSession.request({ [HTTP2_HEADER_PATH]: '/' }); req.on('response', (headers) => { console.log(headers[HTTP2_HEADER_STATUS]); - req.on('data', (chunk) => { /** .. **/ }); - req.on('end', () => { /** .. **/ }); + req.on('data', (chunk) => { /* .. */ }); + req.on('end', () => { /* .. */ }); }); ``` @@ -856,7 +857,7 @@ added: v8.4.0 --> The `'timeout'` event is emitted after no activity is received for this -`'Http2Stream'` within the number of milliseconds set using +`Http2Stream` within the number of milliseconds set using `http2stream.setTimeout()`. #### Event: 'trailers' @@ -1457,9 +1458,9 @@ a request with an HTTP `Expect: 100-continue` is received. If this event is not listened for, the server will automatically respond with a status `100 Continue` as appropriate. -Handling this event involves calling [`response.writeContinue()`][] if the -client should continue to send the request body, or generating an appropriate -HTTP response (e.g. 400 Bad Request) if the client should not continue to send +Handling this event involves calling [`response.writeContinue()`][] if the +client should continue to send the request body, or generating an appropriate +HTTP response (e.g. 400 Bad Request) if the client should not continue to send the request body. Note that when this event is emitted and handled, the [`'request'`][] event will @@ -1572,9 +1573,9 @@ time a request with an HTTP `Expect: 100-continue` is received. If this event is not listened for, the server will automatically respond with a status `100 Continue` as appropriate. -Handling this event involves calling [`response.writeContinue()`][] if the -client should continue to send the request body, or generating an appropriate -HTTP response (e.g. 400 Bad Request) if the client should not continue to send +Handling this event involves calling [`response.writeContinue()`][] if the +client should continue to send the request body, or generating an appropriate +HTTP response (e.g. 400 Bad Request) if the client should not continue to send the request body. Note that when this event is emitted and handled, the [`'request'`][] event will @@ -1954,7 +1955,7 @@ Returns a `ClientHttp2Session` instance. const http2 = require('http2'); const client = http2.connect('https://localhost:1234'); -/** use the client **/ +/* Use the client */ client.close(); ``` @@ -2480,6 +2481,7 @@ added: v8.4.0 * `msecs` {number} * `callback` {Function} +* Returns: {http2.Http2ServerRequest} Sets the [`Http2Stream`]()'s timeout value to `msecs`. If a callback is provided, then it is added as a listener on the `'timeout'` event on @@ -2490,8 +2492,6 @@ the server, then [`Http2Stream`]()s are destroyed when they time out. If a handler is assigned to the request, the response, or the server's `'timeout'` events, timed out sockets must be handled explicitly. -Returns `request`. - #### request.socket -* {http2.Http2Stream} +* {Http2Stream} The [`Http2Stream`][] object backing the request. @@ -2852,6 +2852,7 @@ added: v8.4.0 * `msecs` {number} * `callback` {Function} +* Returns: {http2.Http2ServerResponse} Sets the [`Http2Stream`]()'s timeout value to `msecs`. If a callback is provided, then it is added as a listener on the `'timeout'` event on @@ -2862,8 +2863,6 @@ the server, then [`Http2Stream`]()s are destroyed when they time out. If a handler is assigned to the request, the response, or the server's `'timeout'` events, timed out sockets must be handled explicitly. -Returns `response`. - #### response.socket -* {http2.Http2Stream} +* {Http2Stream} The [`Http2Stream`][] object backing the response. @@ -3146,8 +3145,8 @@ following additional properties: [`http2stream.pushStream()`]: #http2_http2stream_pushstream_headers_options_callback [`net.Server.close()`]: net.html#net_server_close_callback [`net.Socket`]: net.html#net_class_net_socket -[`net.Socket.prototype.ref`]: net.html#net_socket_ref -[`net.Socket.prototype.unref`]: net.html#net_socket_unref +[`net.Socket.prototype.ref()`]: net.html#net_socket_ref +[`net.Socket.prototype.unref()`]: net.html#net_socket_unref [`net.connect()`]: net.html#net_net_connect [`request.socket.getPeerCertificate()`]: tls.html#tls_tlssocket_getpeercertificate_detailed [`response.end()`]: #http2_response_end_data_encoding_callback diff --git a/doc/api/https.md b/doc/api/https.md index 75d76da42b10d3..20da8fd1f26567 100644 --- a/doc/api/https.md +++ b/doc/api/https.md @@ -67,7 +67,7 @@ added: v0.3.4 --> - `options` {Object} Accepts `options` from [`tls.createServer()`][], [`tls.createSecureContext()`][] and [`http.createServer()`][]. -- `requestListener` {Function} A listener to be added to the `request` event. +- `requestListener` {Function} A listener to be added to the `'request'` event. Example: @@ -158,7 +158,7 @@ changes: pr-url: https://github.com/nodejs/node/pull/10638 description: The `options` parameter can be a WHATWG `URL` object. --> -- `options` {Object | string | URL} Accepts all `options` from +- `options` {Object | string | URL} Accepts all `options` from [`http.request()`][], with some differences in default values: - `protocol` **Default:** `'https:'` - `port` **Default:** `443` diff --git a/doc/api/inspector.md b/doc/api/inspector.md index 3f461d0ecfc1d0..3883d7c8583c1d 100644 --- a/doc/api/inspector.md +++ b/doc/api/inspector.md @@ -34,6 +34,8 @@ Deactivate the inspector. Blocks until there are no active connections. ### inspector.url() +* Returns: {string|undefined} + Return the URL of the active inspector, or `undefined` if there is none. ## Class: inspector.Session @@ -79,7 +81,7 @@ added: v8.0.0 Emitted when an inspector notification is received that has its method field set to the `` value. -The following snippet installs a listener on the [`Debugger.paused`][] +The following snippet installs a listener on the [`'Debugger.paused'`][] event, and prints the reason for program suspension whenever program execution is suspended (through breakpoints, for example): @@ -165,8 +167,8 @@ session.post('Profiler.enable', () => { ``` -[`session.connect()`]: #inspector_session_connect -[`Debugger.paused`]: https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#event-paused +[`'Debugger.paused'`]: https://chromedevtools.github.io/devtools-protocol/v8/Debugger#event-paused [`EventEmitter`]: events.html#events_class_eventemitter +[`session.connect()`]: #inspector_session_connect [Chrome DevTools Protocol Viewer]: https://chromedevtools.github.io/devtools-protocol/v8/ [CPU Profiler]: https://chromedevtools.github.io/devtools-protocol/v8/Profiler diff --git a/doc/api/intl.md b/doc/api/intl.md index 60a108b4733ca1..9fa4d438faa4c9 100644 --- a/doc/api/intl.md +++ b/doc/api/intl.md @@ -1,6 +1,7 @@ # Internationalization Support + Node.js has many features that make it easier to write internationalized programs. Some of them are: diff --git a/doc/api/modules.md b/doc/api/modules.md index b1656fcd272ea7..20fb5fab02360f 100644 --- a/doc/api/modules.md +++ b/doc/api/modules.md @@ -666,7 +666,7 @@ added: v8.9.0 * Returns: {string[]|null} Returns an array containing the paths searched during resolution of `request` or -null if the `request` string references a core module, for example `http` or +`null` if the `request` string references a core module, for example `http` or `fs`. ## The `module` Object diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 77dfda133695c5..8700e1e500e2c7 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -1,6 +1,7 @@ # N-API + > Stability: 1 - Experimental @@ -139,8 +140,8 @@ create a scope before invoking any functions that can result in the creation of JavaScript values. Handle scopes are created using [`napi_open_handle_scope`][] and are destroyed -using [`napi_close_handle_scope`][]. Closing the scope can indicate to the GC -that all `napi_value`s created during the lifetime of the handle scope are no +using [`napi_close_handle_scope`][]. Closing the scope can indicate to the GC +that all `napi_value`s created during the lifetime of the handle scope are no longer referenced from the current stack frame. For more details, review the [Object Lifetime Management][]. @@ -550,9 +551,9 @@ napi_status napi_fatal_exception(napi_env env, napi_value err); ``` - `[in] env`: The environment that the API is invoked under. -- `[in] err`: The error you want to pass to `uncaughtException`. +- `[in] err`: The error you want to pass to `'uncaughtException'`. -Trigger an `uncaughtException` in JavaScript. Useful if an async +Trigger an `'uncaughtException'` in JavaScript. Useful if an async callback throws an exception with no way to recover. ### Fatal Errors @@ -667,7 +668,7 @@ current scope and the lifespan of the handle changes from the current scope to that of the outer scope. The methods available to open/close escapable scopes are -[`napi_open_escapable_handle_scope`][] and +[`napi_open_escapable_handle_scope`][] and [`napi_close_escapable_handle_scope`][]. The request to promote a handle is made through [`napi_escape_handle`][] which @@ -1371,7 +1372,7 @@ TypedArray objects provide an array-like view over an underlying data buffer where each element has the same underlying binary scalar datatype. It's required that (length * size_of_element) + byte_offset should -be <= the size in bytes of the array passed in. If not, a RangeError exception +be <= the size in bytes of the array passed in. If not, a RangeError exception is raised. JavaScript TypedArray Objects are described in @@ -1617,10 +1618,10 @@ its length. *WARNING*: Use caution while using this API. The lifetime of the underlying data buffer is managed by the ArrayBuffer even after it's returned. A -possible safe way to use this API is in conjunction with -[`napi_create_reference`][], which can be used to guarantee control over the -lifetime of the ArrayBuffer. It's also safe to use the returned data buffer -within the same callback as long as there are no calls to other APIs that might +possible safe way to use this API is in conjunction with +[`napi_create_reference`][], which can be used to guarantee control over the +lifetime of the ArrayBuffer. It's also safe to use the returned data buffer +within the same callback as long as there are no calls to other APIs that might trigger a GC. #### napi_get_buffer_info @@ -1909,7 +1910,7 @@ napi_status napi_get_value_string_utf16(napi_env env, passed in, the length of the string (in 2-byte code units) is returned. - `[in] bufsize`: Size of the destination buffer. When this value is insufficient, the returned string will be truncated. -- `[out] result`: Number of 2-byte code units copied into the buffer, excluding +- `[out] result`: Number of 2-byte code units copied into the buffer, excluding the null terminator. Returns `napi_ok` if the API succeeded. If a non-String `napi_value` @@ -2770,7 +2771,7 @@ Returns `napi_ok` if the API succeeded. This method allows the efficient definition of multiple properties on a given object. The properties are defined using property descriptors (See -[`napi_property_descriptor`][]). Given an array of such property descriptors, +[`napi_property_descriptor`][]). Given an array of such property descriptors, this API will set the properties on the object one at a time, as defined by DefineOwnProperty (described in [Section 9.1.6][] of the ECMA262 specification). @@ -3182,7 +3183,7 @@ This API may modify the prototype chain of the wrapper object. Afterward, additional manipulation of the wrapper's prototype chain may cause `napi_unwrap()` to fail. -Calling napi_wrap() a second time on an object will return an error. To +Calling napi_wrap() a second time on an object will return an error. To associate another native instance with the object, use napi_remove_wrap() first. ### napi_unwrap diff --git a/doc/api/net.md b/doc/api/net.md index 2cdb107fa5f17e..0575bb5f8f2491 100644 --- a/doc/api/net.md +++ b/doc/api/net.md @@ -112,6 +112,8 @@ Emitted when the server has been bound after calling [`server.listen()`][]. added: v0.1.90 --> +* Returns: {Object} + Returns the bound address, the address family name, and port of the server as reported by the operating system if listening on an IP socket. Useful to find which port was assigned when getting an OS-assigned address. @@ -147,14 +149,12 @@ added: v0.1.90 * Returns: {net.Server} Stops the server from accepting new connections and keeps existing -connections. This function is asynchronous, the server is finally -closed when all connections are ended and the server emits a [`'close'`][] event. +connections. This function is asynchronous, the server is finally closed +when all connections are ended and the server emits a [`'close'`][] event. The optional `callback` will be called once the `'close'` event occurs. Unlike that event, it will be called with an Error as its only argument if the server was not open when it was closed. -Returns `server`. - ### server.connections -* `had_error` {boolean} `true` if the socket had a transmission error. +* `hadError` {boolean} `true` if the socket had a transmission error. -Emitted once the socket is fully closed. The argument `had_error` is a boolean +Emitted once the socket is fully closed. The argument `hadError` is a boolean which says if the socket was closed due to a transmission error. ### Event: 'connect' @@ -425,7 +425,7 @@ See [`net.createConnection()`][]. added: v0.1.90 --> -* {Buffer} +* {Buffer|string} Emitted when data is received. The argument `data` will be a `Buffer` or `String`. Encoding of data is set by [`socket.setEncoding()`][]. @@ -485,6 +485,15 @@ Not applicable to UNIX sockets. * `family` {string|null} The address type. See [`dns.lookup()`][]. * `host` {string} The hostname. +### Event: 'ready' + + +Emitted when a socket is ready to be used. + +Triggered immediately after `'connect'`. + ### Event: 'timeout' +* Returns: {Object} + Returns the bound address, the address family name and port of the socket as reported by the operating system. Returns an object with three properties, e.g. @@ -601,8 +612,6 @@ For [IPC][] connections, available `options` are: See [Identifying paths for IPC connections][]. If provided, the TCP-specific options above are ignored. -Returns `socket`. - #### socket.connect(path[, connectListener]) * `path` {string} Path the client should connect to. See @@ -617,8 +626,6 @@ Alias to [`socket.connect(options[, connectListener])`][`socket.connect(options)`] called with `{ path: path }` as `options`. -Returns `socket`. - #### socket.connect(port[, host][, connectListener]) +* `data` {string|Buffer|Uint8Array} +* `encoding` {string} Only used when data is `string`. **Default:** `utf8`. +* `callback` {Function} +* Returns: {boolean} + Sends data on the socket. The second parameter specifies the encoding in the case of a string — it defaults to UTF8 encoding. @@ -839,6 +849,9 @@ buffer. Returns `false` if all or part of the data was queued in user memory. The optional `callback` parameter will be executed when the data is finally written out - this may not be immediately. +See Writable stream [`write()`][stream_writable_write] method for more +information. + ## net.connect() Aliases to @@ -925,7 +938,7 @@ in the [`net.createServer()`][] section: ```js const net = require('net'); const client = net.createConnection({ port: 8124 }, () => { - //'connect' listener + // 'connect' listener console.log('connected to server!'); client.write('world!\r\n'); }); @@ -1068,24 +1081,29 @@ $ nc -U /tmp/echo.sock added: v0.3.0 --> -Tests if input is an IP address. Returns 0 for invalid strings, -returns 4 for IP version 4 addresses, and returns 6 for IP version 6 addresses. +* Returns: {integer} +Tests if input is an IP address. Returns `0` for invalid strings, +returns `4` for IP version 4 addresses, and returns `6` for IP version 6 +addresses. ## net.isIPv4(input) -Returns true if input is a version 4 IP address, otherwise returns false. +* Returns: {boolean} +Returns `true` if input is a version 4 IP address, otherwise returns `false`. ## net.isIPv6(input) -Returns true if input is a version 6 IP address, otherwise returns false. +* Returns: {boolean} + +Returns `true` if input is a version 6 IP address, otherwise returns `false`. [`'close'`]: #net_event_close [`'connect'`]: #net_event_connect @@ -1137,5 +1155,6 @@ Returns true if input is a version 6 IP address, otherwise returns false. [duplex stream]: stream.html#stream_class_stream_duplex [half-closed]: https://tools.ietf.org/html/rfc1122 [socket(7)]: http://man7.org/linux/man-pages/man7/socket.7.html +[stream_writable_write]: stream.html#stream_writable_write_chunk_encoding_callback [unspecified IPv4 address]: https://en.wikipedia.org/wiki/0.0.0.0 [unspecified IPv6 address]: https://en.wikipedia.org/wiki/IPv6_address#Unspecified_address diff --git a/doc/api/perf_hooks.md b/doc/api/perf_hooks.md index eeb4e55d71a43b..2c85a0043a6d98 100644 --- a/doc/api/perf_hooks.md +++ b/doc/api/perf_hooks.md @@ -427,12 +427,16 @@ The high resolution millisecond timestamp at which the V8 platform was initialized. -## Class: PerformanceObserver(callback) +## Class: PerformanceObserver + +### new PerformanceObserver(callback) -* `callback` {Function} A `PerformanceObserverCallback` callback function. +* `callback` {Function} + * `list` {PerformanceObserverEntryList} + * `observer` {PerformanceObserver} `PerformanceObserver` objects provide notifications when new `PerformanceEntry` instances have been added to the Performance Timeline. @@ -451,69 +455,16 @@ obs.observe({ entryTypes: ['mark'], buffered: true }); performance.mark('test'); ``` - Because `PerformanceObserver` instances introduce their own additional performance overhead, instances should not be left subscribed to notifications indefinitely. Users should disconnect observers as soon as they are no longer needed. -### Callback: PerformanceObserverCallback(list, observer) - - -* `list` {PerformanceObserverEntryList} -* `observer` {PerformanceObserver} - -The `PerformanceObserverCallback` is invoked when a `PerformanceObserver` is +The `callback` is invoked when a `PerformanceObserver` is notified about new `PerformanceEntry` instances. The callback receives a `PerformanceObserverEntryList` instance and a reference to the `PerformanceObserver`. -### Class: PerformanceObserverEntryList - - -The `PerformanceObserverEntryList` class is used to provide access to the -`PerformanceEntry` instances passed to a `PerformanceObserver`. - -#### performanceObserverEntryList.getEntries() - - -* Returns: {PerformanceEntry[]} - -Returns a list of `PerformanceEntry` objects in chronological order -with respect to `performanceEntry.startTime`. - -#### performanceObserverEntryList.getEntriesByName(name[, type]) - - -* `name` {string} -* `type` {string} -* Returns: {PerformanceEntry[]} - -Returns a list of `PerformanceEntry` objects in chronological order -with respect to `performanceEntry.startTime` whose `performanceEntry.name` is -equal to `name`, and optionally, whose `performanceEntry.entryType` is equal to -`type`. - -#### performanceObserverEntryList.getEntriesByType(type) - - -* `type` {string} -* Returns: {PerformanceEntry[]} - -Returns a list of `PerformanceEntry` objects in chronological order -with respect to `performanceEntry.startTime` whose `performanceEntry.entryType` -is equal to `type`. - ### performanceObserver.disconnect() + +The `PerformanceObserverEntryList` class is used to provide access to the +`PerformanceEntry` instances passed to a `PerformanceObserver`. + +### performanceObserverEntryList.getEntries() + + +* Returns: {PerformanceEntry[]} + +Returns a list of `PerformanceEntry` objects in chronological order +with respect to `performanceEntry.startTime`. + +### performanceObserverEntryList.getEntriesByName(name[, type]) + + +* `name` {string} +* `type` {string} +* Returns: {PerformanceEntry[]} + +Returns a list of `PerformanceEntry` objects in chronological order +with respect to `performanceEntry.startTime` whose `performanceEntry.name` is +equal to `name`, and optionally, whose `performanceEntry.entryType` is equal to +`type`. + +### performanceObserverEntryList.getEntriesByType(type) + + +* `type` {string} +* Returns: {PerformanceEntry[]} + +Returns a list of `PerformanceEntry` objects in chronological order +with respect to `performanceEntry.startTime` whose `performanceEntry.entryType` +is equal to `type`. + + ## Examples ### Measuring the duration of async operations diff --git a/doc/api/process.md b/doc/api/process.md index 3d66c047b8121d..859ad4eef0cb9e 100644 --- a/doc/api/process.md +++ b/doc/api/process.md @@ -89,8 +89,8 @@ the child process. The listener callback is invoked with the following arguments: * `message` {Object} a parsed JSON object or primitive value. -* `sendHandle` {Handle object} a [`net.Socket`][] or [`net.Server`][] object, or - undefined. +* `sendHandle` {net.Server|net.Socket} a [`net.Server`][] or [`net.Socket`][] + object, or undefined. The message goes through serialization and parsing. The resulting message might not be the same as what is originally sent. @@ -193,9 +193,9 @@ of allocated resources (e.g. file descriptors, handles, etc) before shutting down the process. **It is not safe to resume normal operation after `'uncaughtException'`.** -To restart a crashed application in a more reliable way, whether -`uncaughtException` is emitted or not, an external monitor should be employed -in a separate process to detect application failures and recover or restart as +To restart a crashed application in a more reliable way, whether +`'uncaughtException'` is emitted or not, an external monitor should be employed +in a separate process to detect application failures and recover or restart as needed. ### Event: 'unhandledRejection' @@ -272,7 +272,7 @@ lead to sub-optimal application performance, bugs, or security vulnerabilities. The listener function is called with a single `warning` argument whose value is an `Error` object. There are three key properties that describe the warning: -* `name` {string} The name of the warning (currently `Warning` by default). +* `name` {string} The name of the warning (currently `'Warning'` by default). * `message` {string} A system-provided description of the warning. * `stack` {string} A stack trace to the location in the code where the warning was issued. @@ -326,7 +326,7 @@ Using the `--no-deprecation` command line flag will suppress all reporting of the custom deprecation. The `*-deprecation` command line flags only affect warnings that use the name -`DeprecationWarning`. +`'DeprecationWarning'`. #### Emitting custom warnings @@ -340,7 +340,7 @@ custom or application-specific warnings. Signal events will be emitted when the Node.js process receives a signal. Please refer to signal(7) for a listing of standard POSIX signal names such as -`SIGINT`, `SIGHUP`, etc. +`'SIGINT'`, `'SIGHUP'`, etc. The signal handler will receive the signal's name (`'SIGINT'`, `'SIGTERM'`, etc.) as the first argument. @@ -365,37 +365,38 @@ process.on('SIGINT', handle); process.on('SIGTERM', handle); ``` -* `SIGUSR1` is reserved by Node.js to start the [debugger][]. It's possible to +* `'SIGUSR1'` is reserved by Node.js to start the [debugger][]. It's possible to install a listener but doing so might interfere with the debugger. -* `SIGTERM` and `SIGINT` have default handlers on non-Windows platforms that +* `'SIGTERM'` and `'SIGINT'` have default handlers on non-Windows platforms that reset the terminal mode before exiting with code `128 + signal number`. If one of these signals has a listener installed, its default behavior will be removed (Node.js will no longer exit). -* `SIGPIPE` is ignored by default. It can have a listener installed. -* `SIGHUP` is generated on Windows when the console window is closed, and on +* `'SIGPIPE'` is ignored by default. It can have a listener installed. +* `'SIGHUP'` is generated on Windows when the console window is closed, and on other platforms under various similar conditions, see signal(7). It can have a listener installed, however Node.js will be unconditionally terminated by Windows about 10 seconds later. On non-Windows platforms, the default behavior of `SIGHUP` is to terminate Node.js, but once a listener has been installed its default behavior will be removed. -* `SIGTERM` is not supported on Windows, it can be listened on. -* `SIGINT` from the terminal is supported on all platforms, and can usually be - generated with `+C` (though this may be configurable). It is not +* `'SIGTERM'` is not supported on Windows, it can be listened on. +* `'SIGINT'` from the terminal is supported on all platforms, and can usually be + generated with `+C` (though this may be configurable). It is not generated when terminal raw mode is enabled. -* `SIGBREAK` is delivered on Windows when `+` is pressed, on +* `'SIGBREAK'` is delivered on Windows when `+` is pressed, on non-Windows platforms it can be listened on, but there is no way to send or generate it. -* `SIGWINCH` is delivered when the console has been resized. On Windows, this +* `'SIGWINCH'` is delivered when the console has been resized. On Windows, this will only happen on write to the console when the cursor is being moved, or when a readable tty is used in raw mode. -* `SIGKILL` cannot have a listener installed, it will unconditionally terminate - Node.js on all platforms. -* `SIGSTOP` cannot have a listener installed. -* `SIGBUS`, `SIGFPE`, `SIGSEGV` and `SIGILL`, when not raised artificially - using kill(2), inherently leave the process in a state from which it is not - safe to attempt to call JS listeners. Doing so might lead to the process - hanging in an endless loop, since listeners attached using `process.on()` are - called asynchronously and therefore unable to correct the underlying problem. +* `'SIGKILL'` cannot have a listener installed, it will unconditionally + terminate Node.js on all platforms. +* `'SIGSTOP'` cannot have a listener installed. +* `'SIGBUS'`, `'SIGFPE'`, `'SIGSEGV'` and `'SIGILL'`, when not raised + artificially using kill(2), inherently leave the process in a state from + which it is not safe to attempt to call JS listeners. Doing so might lead to + the process hanging in an endless loop, since listeners attached using + `process.on()` are called asynchronously and therefore unable to correct the + underlying problem. Windows does not support sending signals, but Node.js offers some emulation with [`process.kill()`][], and [`subprocess.kill()`][]. Sending signal `0` can @@ -417,7 +418,7 @@ added: v0.5.0 * {string} -The `process.arch` property returns a string identifying the operating system +The `process.arch` property returns a string identifying the operating system CPU architecture for which the Node.js binary was compiled. The current possible values are: `'arm'`, `'arm64'`, `'ia32'`, `'mips'`, @@ -709,7 +710,7 @@ added: v8.0.0 The `process.emitWarning()` method can be used to emit custom or application specific process warnings. These can be listened for by adding a handler to the -[`process.on('warning')`][process_warning] event. +[`'warning'`][process_warning] event. ```js // Emit a warning with a code and additional detail. @@ -724,7 +725,7 @@ process.emitWarning('Something happened!', { In this example, an `Error` object is generated internally by `process.emitWarning()` and passed through to the -[`process.on('warning')`][process_warning] event. +[`'warning'`][process_warning] handler. ```js process.on('warning', (warning) => { @@ -753,7 +754,7 @@ added: v6.0.0 The `process.emitWarning()` method can be used to emit custom or application specific process warnings. These can be listened for by adding a handler to the -[`process.on('warning')`][process_warning] event. +[`'warning'`][process_warning] event. ```js // Emit a warning using a string. @@ -773,8 +774,8 @@ process.emitWarning('Something happened!', 'CustomWarning', 'WARN001'); ``` In each of the previous examples, an `Error` object is generated internally by -`process.emitWarning()` and passed through to the -[`process.on('warning')`][process_warning] event. +`process.emitWarning()` and passed through to the [`'warning'`][process_warning] +handler. ```js process.on('warning', (warning) => { @@ -786,7 +787,7 @@ process.on('warning', (warning) => { ``` If `warning` is passed as an `Error` object, it will be passed through to the -`process.on('warning')` event handler unmodified (and the optional `type`, +`'warning'` event handler unmodified (and the optional `type`, `code` and `ctor` arguments will be ignored): ```js @@ -807,7 +808,7 @@ Note that while process warnings use `Error` objects, the process warning mechanism is **not** a replacement for normal error handling mechanisms. The following additional handling is implemented if the warning `type` is -`DeprecationWarning`: +`'DeprecationWarning'`: * If the `--throw-deprecation` command-line flag is used, the deprecation warning is thrown as an exception rather than being emitted as an event. @@ -1411,8 +1412,8 @@ added: v0.8.0 The `process.noDeprecation` property indicates whether the `--no-deprecation` flag is set on the current Node.js process. See the documentation for -the [`warning` event][process_warning] and the -[`emitWarning` method][process_emit_warning] for more information about this +the [`'warning'` event][process_warning] and the +[`emitWarning()` method][process_emit_warning] for more information about this flag's behavior. ## process.pid @@ -1526,7 +1527,7 @@ added: v0.5.9 --> * `message` {Object} -* `sendHandle` {Handle object} +* `sendHandle` {net.Server|net.Socket} * `options` {Object} * `callback` {Function} * Returns: {boolean} @@ -1631,7 +1632,7 @@ added: v0.9.4 * `groups` {integer[]} The `process.setgroups()` method sets the supplementary group IDs for the -Node.js process. This is a privileged operation that requires the Node.js +Node.js process. This is a privileged operation that requires the Node.js process to have `root` or the `CAP_SETGID` capability. The `groups` array can contain numeric group IDs, group names or both. @@ -1675,7 +1676,7 @@ The `process.setUncaughtExceptionCapture` function sets a function that will be invoked when an uncaught exception occurs, which will receive the exception value itself as its first argument. -If such a function is set, the [`process.on('uncaughtException')`][] event will +If such a function is set, the [`'uncaughtException'`][] event will not be emitted. If `--abort-on-uncaught-exception` was passed from the command line or set through [`v8.setFlagsFromString()`][], the process will not abort. @@ -1807,8 +1808,8 @@ added: v0.9.12 The `process.throwDeprecation` property indicates whether the `--throw-deprecation` flag is set on the current Node.js process. See the -documentation for the [`warning` event][process_warning] and the -[`emitWarning` method][process_emit_warning] for more information about this +documentation for the [`'warning'` event][process_warning] and the +[`emitWarning()` method][process_emit_warning] for more information about this flag's behavior. ## process.title @@ -1840,8 +1841,8 @@ added: v0.8.0 The `process.traceDeprecation` property indicates whether the `--trace-deprecation` flag is set on the current Node.js process. See the -documentation for the [`warning` event][process_warning] and the -[`emitWarning` method][process_emit_warning] for more information about this +documentation for the [`'warning'` event][process_warning] and the +[`emitWarning()` method][process_emit_warning] for more information about this flag's behavior. ## process.umask([mask]) @@ -2005,7 +2006,6 @@ cases: [`process.exit()`]: #process_process_exit_code [`process.exitCode`]: #process_process_exitcode [`process.kill()`]: #process_process_kill_pid_signal -[`process.on('uncaughtException')`]: process.html#process_event_uncaughtexception [`process.setUncaughtExceptionCaptureCallback()`]: process.html#process_process_setuncaughtexceptioncapturecallback_fn [`promise.catch()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch [`require()`]: globals.html#globals_require diff --git a/doc/api/punycode.md b/doc/api/punycode.md index 3f5b9746c1995d..fc67d55f85cd94 100644 --- a/doc/api/punycode.md +++ b/doc/api/punycode.md @@ -145,6 +145,8 @@ punycode.ucs2.encode([0x1D306]); // '\uD834\uDF06' added: v0.6.1 --> +* {string} + Returns a string identifying the current [Punycode.js][] version number. [Punycode.js]: https://mths.be/punycode diff --git a/doc/api/readline.md b/doc/api/readline.md index 94151dfa62abc2..c1e50ef7eee350 100644 --- a/doc/api/readline.md +++ b/doc/api/readline.md @@ -56,7 +56,7 @@ The `'close'` event is emitted when one of the following occur: * The `input` stream receives its `'end'` event; * The `input` stream receives `-D` to signal end-of-transmission (EOT); * The `input` stream receives `-C` to signal `SIGINT` and there is no - `SIGINT` event listener registered on the `readline.Interface` instance. + `'SIGINT'` event listener registered on the `readline.Interface` instance. The listener function is called without passing any arguments. @@ -89,8 +89,8 @@ added: v0.7.5 The `'pause'` event is emitted when one of the following occur: * The `input` stream is paused. -* The `input` stream is not paused and receives the `SIGCONT` event. (See - events [`SIGTSTP`][] and [`SIGCONT`][]) +* The `input` stream is not paused and receives the `'SIGCONT'` event. (See + events [`'SIGTSTP'`][] and [`'SIGCONT'`][]) The listener function is called without passing any arguments. @@ -164,11 +164,11 @@ added: v0.7.5 --> The `'SIGTSTP'` event is emitted when the `input` stream receives a `-Z` -input, typically known as `SIGTSTP`. If there are no `SIGTSTP` event listeners +input, typically known as `SIGTSTP`. If there are no `'SIGTSTP'` event listeners registered when the `input` stream receives a `SIGTSTP`, the Node.js process will be sent to the background. -When the program is resumed using fg(1p), the `'pause'` and `SIGCONT` events +When the program is resumed using fg(1p), the `'pause'` and `'SIGCONT'` events will be emitted. These can be used to resume the `input` stream. The `'pause'` and `'SIGCONT'` events will not be emitted if the `input` was @@ -529,8 +529,8 @@ rl.on('line', (line) => { }); ``` -[`SIGCONT`]: readline.html#readline_event_sigcont -[`SIGTSTP`]: readline.html#readline_event_sigtstp +[`'SIGCONT'`]: readline.html#readline_event_sigcont +[`'SIGTSTP'`]: readline.html#readline_event_sigtstp [`process.stdin`]: process.html#process_process_stdin [`process.stdout`]: process.html#process_process_stdout [Readable]: stream.html#stream_readable_streams diff --git a/doc/api/repl.md b/doc/api/repl.md index 00d46612806e3a..4cc803a903fd3d 100644 --- a/doc/api/repl.md +++ b/doc/api/repl.md @@ -411,6 +411,7 @@ deprecated: v9.0.0 * `keyword` {string} the potential keyword to parse and execute * `rest` {any} any parameters to the keyword command +* Returns: {boolean} > Stability: 0 - Deprecated. diff --git a/doc/api/stream.md b/doc/api/stream.md index 1523e598624aed..b06ba57a910a78 100644 --- a/doc/api/stream.md +++ b/doc/api/stream.md @@ -46,6 +46,9 @@ There are four fundamental stream types within Node.js: * [Transform][] - Duplex streams that can modify or transform the data as it is written and read (for example [`zlib.createDeflate()`][]). +Additionally this module includes the utility functions [pipeline][] and +[finished][]. + ### Object Mode All streams created by Node.js APIs operate exclusively on strings and `Buffer` @@ -128,7 +131,7 @@ const server = http.createServer((req, res) => { body += chunk; }); - // the end event indicates that the entire body has been received + // the 'end' event indicates that the entire body has been received req.on('end', () => { try { const data = JSON.parse(body); @@ -354,6 +357,17 @@ buffered writes in a more optimized manner. See also: [`writable.uncork()`][]. +##### writable.destroy([error]) + + +* Returns: {this} + +Destroy the stream, and emit the passed `'error'`. After this call, the +writable stream has ended. Implementors should not override this method, +but instead implement [`writable._destroy`][writable-_destroy]. + ##### writable.end([chunk][, encoding][, callback]) + +* {number} + Return the value of `highWaterMark` passed when constructing this `Writable`. @@ -532,17 +549,6 @@ write('hello', () => { A Writable stream in object mode will always ignore the `encoding` argument. -##### writable.destroy([error]) - - -* Returns: {this} - -Destroy the stream, and emit the passed error. After this call, the -writable stream has ended. Implementors should not override this method, -but instead implement [`writable._destroy`][writable-_destroy]. - ### Readable Streams Readable streams are an abstraction for a *source* from which data is @@ -794,6 +800,19 @@ In general, the `readable.pipe()` and `'data'` event mechanisms are easier to understand than the `'readable'` event. However, handling `'readable'` might result in increased throughput. +##### readable.destroy([error]) + + +* `error` {Error} Error which will be passed as payload in `'error'` event +* Returns: {this} + +Destroy the stream, and emit `'error'`. After this call, the +readable stream will release any internal resources. +Implementors should not override this method, but instead implement +[`readable._destroy`][readable-_destroy]. + ##### readable.isPaused() - -Destroy the stream, and emit `'error'`. After this call, the -readable stream will release any internal resources. -Implementors should not override this method, but instead implement -[`readable._destroy`][readable-_destroy]. - ### Duplex and Transform Streams #### Class: stream.Duplex @@ -1205,8 +1214,109 @@ added: v8.0.0 Destroy the stream, and emit `'error'`. After this call, the transform stream would release any internal resources. implementors should not override this method, but instead implement -[`readable._destroy`][readable-_destroy]. -The default implementation of `_destroy` for `Transform` also emit `'close'`. +[`readable._destroy()`][readable-_destroy]. +The default implementation of `_destroy()` for `Transform` also emit `'close'`. + +### stream.finished(stream, callback) + + +* `stream` {Stream} A readable and/or writable stream. +* `callback` {Function} A callback function that takes an optional error + argument. + +A function to get notified when a stream is no longer readable, writable +or has experienced an error or a premature close event. + +```js +const { finished } = require('stream'); + +const rs = fs.createReadStream('archive.tar'); + +finished(rs, (err) => { + if (err) { + console.error('Stream failed', err); + } else { + console.log('Stream is done reading'); + } +}); + +rs.resume(); // drain the stream +``` + +Especially useful in error handling scenarios where a stream is destroyed +prematurely (like an aborted HTTP request), and will not emit `'end'` +or `'finish'`. + +The `finished` API is promisify'able as well; + +```js +const finished = util.promisify(stream.finished); + +const rs = fs.createReadStream('archive.tar'); + +async function run() { + await finished(rs); + console.log('Stream is done reading'); +} + +run().catch(console.error); +rs.resume(); // drain the stream +``` + +### stream.pipeline(...streams[, callback]) + + +* `...streams` {Stream} Two or more streams to pipe between. +* `callback` {Function} A callback function that takes an optional error + argument. + +A module method to pipe between streams forwarding errors and properly cleaning +up and provide a callback when the pipeline is complete. + +```js +const { pipeline } = require('stream'); +const fs = require('fs'); +const zlib = require('zlib'); + +// Use the pipeline API to easily pipe a series of streams +// together and get notified when the pipeline is fully done. + +// A pipeline to gzip a potentially huge tar file efficiently: + +pipeline( + fs.createReadStream('archive.tar'), + zlib.createGzip(), + fs.createWriteStream('archive.tar.gz'), + (err) => { + if (err) { + console.error('Pipeline failed', err); + } else { + console.log('Pipeline succeeded'); + } + } +); +``` + +The `pipeline` API is promisify'able as well: + +```js +const pipeline = util.promisify(stream.pipeline); + +async function run() { + await pipeline( + fs.createReadStream('archive.tar'), + zlib.createGzip(), + fs.createWriteStream('archive.tar.gz') + ); + console.log('Pipeline succeeded'); +} + +run().catch(console.error); +``` ## API for Stream Implementers @@ -1493,7 +1603,7 @@ by child classes, and if so, will be called by the internal Writable class methods only. This optional function will be called before the stream closes, delaying the -`finish` event until `callback` is called. This is useful to close resources +`'finish'` event until `callback` is called. This is useful to close resources or write buffered data before a stream ends. #### Errors While Writing @@ -1563,8 +1673,7 @@ const { StringDecoder } = require('string_decoder'); class StringWritable extends Writable { constructor(options) { super(options); - const state = this._writableState; - this._decoder = new StringDecoder(state.defaultEncoding); + this._decoder = new StringDecoder(options && options.defaultEncoding); this.data = ''; } _write(chunk, encoding, callback) { @@ -2189,7 +2298,7 @@ For example, consider the following code: // WARNING! BROKEN! net.createServer((socket) => { - // we add an 'end' method, but never consume the data + // we add an 'end' listener, but never consume the data socket.on('end', () => { // It will never get here. socket.end('The message was received but was not processed.\n'); @@ -2302,6 +2411,8 @@ contain multi-byte characters. [http-incoming-message]: http.html#http_class_http_incomingmessage [zlib]: zlib.html [hwm-gotcha]: #stream_highwatermark_discrepancy_after_calling_readable_setencoding +[pipeline]: #stream_stream_pipeline_streams_callback +[finished]: #stream_stream_finished_stream_callback [stream-_flush]: #stream_transform_flush_callback [stream-_read]: #stream_readable_read_size_1 [stream-_transform]: #stream_transform_transform_chunk_encoding_callback diff --git a/doc/api/string_decoder.md b/doc/api/string_decoder.md index 224d208e9cc889..1311f372551c9c 100644 --- a/doc/api/string_decoder.md +++ b/doc/api/string_decoder.md @@ -42,7 +42,9 @@ decoder.write(Buffer.from([0x82])); console.log(decoder.end(Buffer.from([0xAC]))); ``` -## Class: new StringDecoder([encoding]) +## Class: StringDecoder + +### new StringDecoder([encoding]) @@ -58,6 +60,7 @@ added: v0.9.3 --> * `buffer` {Buffer} A `Buffer` containing the bytes to decode. +* Returns: {string} Returns any remaining input stored in the internal buffer as a string. Bytes representing incomplete UTF-8 and UTF-16 characters will be replaced with @@ -77,6 +80,7 @@ changes: --> * `buffer` {Buffer} A `Buffer` containing the bytes to decode. +* Returns: {string} Returns a decoded string, ensuring that any incomplete multibyte characters at the end of the `Buffer` are omitted from the returned string and stored in an diff --git a/doc/api/timers.md b/doc/api/timers.md index 6a994c9c154383..9c455552b1454c 100644 --- a/doc/api/timers.md +++ b/doc/api/timers.md @@ -28,11 +28,13 @@ functions that can be used to control this default behavior. added: v9.7.0 --> +* Returns: {Immediate} + When called, requests that the Node.js event loop *not* exit so long as the `Immediate` is active. Calling `immediate.ref()` multiple times will have no effect. -By default, all `Immediate` objects are "ref'd", making it normally unnecessary +By default, all `Immediate` objects are "ref'ed", making it normally unnecessary to call `immediate.ref()` unless `immediate.unref()` had been called previously. Returns a reference to the `Immediate`. @@ -42,6 +44,8 @@ Returns a reference to the `Immediate`. added: v9.7.0 --> +* Returns: {Immediate} + When called, the active `Immediate` object will not require the Node.js event loop to remain active. If there is no other activity keeping the event loop running, the process may exit before the `Immediate` object's callback is @@ -66,10 +70,12 @@ control this default behavior. added: v0.9.1 --> +* Returns: {Timeout} + When called, requests that the Node.js event loop *not* exit so long as the `Timeout` is active. Calling `timeout.ref()` multiple times will have no effect. -By default, all `Timeout` objects are "ref'd", making it normally unnecessary +By default, all `Timeout` objects are "ref'ed", making it normally unnecessary to call `timeout.ref()` unless `timeout.unref()` had been called previously. Returns a reference to the `Timeout`. @@ -79,6 +85,8 @@ Returns a reference to the `Timeout`. added: v0.9.1 --> +* Returns: {Timeout} + When called, the active `Timeout` object will not require the Node.js event loop to remain active. If there is no other activity keeping the event loop running, the process may exit before the `Timeout` object's callback is invoked. Calling @@ -147,6 +155,7 @@ added: v0.0.1 * `delay` {number} The number of milliseconds to wait before calling the `callback`. * `...args` {any} Optional arguments to pass when the `callback` is called. +* Returns: {Timeout} Schedules repeated execution of `callback` every `delay` milliseconds. Returns a `Timeout` for use with [`clearInterval()`][]. @@ -165,6 +174,7 @@ added: v0.0.1 * `delay` {number} The number of milliseconds to wait before calling the `callback`. * `...args` {any} Optional arguments to pass when the `callback` is called. +* Returns: {Timeout} Schedules execution of a one-time `callback` after `delay` milliseconds. Returns a `Timeout` for use with [`clearTimeout()`][]. diff --git a/doc/api/tls.md b/doc/api/tls.md index e05193990c801d..13ff0b706c61d2 100644 --- a/doc/api/tls.md +++ b/doc/api/tls.md @@ -266,7 +266,7 @@ The typical flow of an OCSP Request is as follows: listener if registered. 3. Server extracts the OCSP URL from either the `certificate` or `issuer` and performs an [OCSP request] to the CA. -4. Server receives `OCSPResponse` from the CA and sends it back to the client +4. Server receives `'OCSPResponse'` from the CA and sends it back to the client via the `callback` argument 5. Client validates the response and either destroys the socket or performs a handshake. @@ -373,6 +373,8 @@ the client request's SNI hostname matches the supplied `hostname` (or wildcard). added: v0.6.0 --> +* Returns: {Object} + Returns the bound address, the address family name, and port of the server as reported by the operating system. See [`net.Server.address()`][] for more information. @@ -398,6 +400,8 @@ deprecated: v0.9.7 > Stability: 0 - Deprecated: Use [`server.getConnections()`][] instead. +* {number} + Returns the current number of concurrent connections on the server. ### server.getTicketKeys() @@ -405,6 +409,8 @@ Returns the current number of concurrent connections on the server. added: v3.0.0 --> +* Returns: {Buffer} + Returns a `Buffer` instance holding the keys currently used for encryption/decryption of the [TLS Session Tickets][] @@ -518,6 +524,8 @@ checked to determine the negotiated protocol. added: v0.11.4 --> +* Returns: {Object} + Returns the bound address, the address family name, and port of the underlying socket as reported by the operating system. Returns an object with three properties, e.g. @@ -536,6 +544,8 @@ property is set only when `tlsSocket.authorized === false`. added: v0.11.4 --> +* Returns: {boolean} + Returns `true` if the peer certificate was signed by one of the CAs specified when creating the `tls.TLSSocket` instance, otherwise `false`. @@ -560,6 +570,8 @@ Always returns `true`. This may be used to distinguish TLS sockets from regular added: v0.11.4 --> +* Returns: {Object} + Returns an object representing the cipher name. The `version` key is a legacy field which always contains the value `'TLSv1/SSLv3'`. @@ -574,6 +586,8 @@ information. added: v5.0.0 --> +* Returns: {Object} + Returns an object representing the type, name, and size of parameter of an ephemeral key exchange in [Perfect Forward Secrecy][] on a client connection. It returns an empty object when the key exchange is not @@ -607,6 +621,7 @@ added: v0.11.4 * `detailed` {boolean} Include the full certificate chain if `true`, otherwise include just the peer's certificate. +* Returns: {Object} Returns an object representing the peer's certificate. The returned object has some properties corresponding to the fields of the certificate. @@ -667,6 +682,8 @@ to implement the `tls-unique` channel binding from [RFC 5929][]. added: v5.7.0 --> +* Returns: {string} + Returns a string containing the negotiated SSL/TLS protocol version of the current connection. The value `'unknown'` will be returned for connected sockets that have not completed the handshaking process. The value `null` will @@ -707,6 +724,8 @@ reuse provide `session` option to [`tls.connect()`][]. added: v0.11.4 --> +* {string} + Returns the string representation of the local IP address. ### tlsSocket.localPort @@ -714,6 +733,8 @@ Returns the string representation of the local IP address. added: v0.11.4 --> +* {number} + Returns the numeric representation of the local port. ### tlsSocket.remoteAddress @@ -721,6 +742,8 @@ Returns the numeric representation of the local port. added: v0.11.4 --> +* {string} + Returns the string representation of the remote IP address. For example, `'74.125.127.100'` or `'2001:4860:a005::68'`. @@ -729,6 +752,8 @@ Returns the string representation of the remote IP address. For example, added: v0.11.4 --> +* {string} + Returns the string representation of the remote IP family. `'IPv4'` or `'IPv6'`. ### tlsSocket.remotePort @@ -736,6 +761,8 @@ Returns the string representation of the remote IP family. `'IPv4'` or `'IPv6'`. added: v0.11.4 --> +* {number} + Returns the numeric representation of the remote port. For example, `443`. ### tlsSocket.renegotiate(options, callback) @@ -769,6 +796,7 @@ added: v0.11.11 * `size` {number} The maximum TLS fragment size. The maximum value is `16384`. **Default:** `16384`. +* Returns: {boolean} The `tlsSocket.setMaxSendFragment()` method sets the maximum TLS fragment size. Returns `true` if setting the limit succeeded; `false` otherwise. @@ -788,6 +816,7 @@ added: v0.8.4 * `host` {string} The hostname to verify the certificate against * `cert` {Object} An object representing the peer's certificate. The returned object has some properties corresponding to the fields of the certificate. +* Returns: {Error|undefined} Verifies the certificate `cert` is issued to host `host`. @@ -1243,6 +1272,8 @@ openssl s_client -connect 127.0.0.1:8000 added: v0.10.2 --> +* Returns: {string[]} + Returns an array with the names of the supported SSL ciphers. For example: @@ -1303,7 +1334,8 @@ deprecated: v0.11.3 The `'secure'` event is emitted by the `SecurePair` object once a secure connection has been established. -As with checking for the server [`secureConnection`](#tls_event_secureconnection) +As with checking for the server +[`'secureConnection'`](#tls_event_secureconnection) event, `pair.cleartext.authorized` should be inspected to confirm whether the certificate used is properly authorized. diff --git a/doc/api/tracing.md b/doc/api/tracing.md index 0c79f029223a0f..b11454b3a5c043 100644 --- a/doc/api/tracing.md +++ b/doc/api/tracing.md @@ -5,12 +5,12 @@ Trace Event provides a mechanism to centralize tracing information generated by V8, Node core, and userspace code. -Tracing can be enabled by passing the `--trace-events-enabled` flag when +Tracing can be enabled by passing the `--trace-events-enabled` flag when starting a Node.js application. The set of categories for which traces are recorded can be specified using the -`--trace-event-categories` flag followed by a list of comma separated category -names. By default the `node`, `node.async_hooks`, and `v8` categories are +`--trace-event-categories` flag followed by a list of comma separated category +names. By default the `node`, `node.async_hooks`, and `v8` categories are enabled. ```txt diff --git a/doc/api/url.md b/doc/api/url.md index 9e189991b05a3e..de8809e664551f 100644 --- a/doc/api/url.md +++ b/doc/api/url.md @@ -93,7 +93,9 @@ return `true`. #### Constructor: new URL(input[, base]) -* `input` {string} The input URL to parse +* `input` {string} The absolute or relative input URL to parse. If `input` + is relative, then `base` is required. If `input` is absolute, the `base` + is ignored. * `base` {string|URL} The base URL to resolve against if the `input` is not absolute. @@ -128,6 +130,32 @@ const myURL = new URL('https://你好你好'); This feature is only available if the `node` executable was compiled with [ICU][] enabled. If not, the domain names are passed through unchanged. +In cases where it is not known in advance if `input` is an absolute URL +and a `base` is provided, it is advised to validate that the `origin` of +the `URL` object is what is expected. + +```js +const { URL } = require('url'); + +let myURL = new URL('http://anotherExample.org/', 'https://example.org/'); +// http://anotherexample.org/ + +myURL = new URL('https://anotherExample.org/', 'https://example.org/'); +// https://anotherexample.org/ + +myURL = new URL('foo://anotherExample.org/', 'https://example.org/'); +// foo://anotherExample.org/ + +myURL = new URL('http:anotherExample.org/', 'https://example.org/'); +// http://anotherexample.org/ + +myURL = new URL('https:anotherExample.org/', 'https://example.org/'); +// https://example.org/anotherExample.org/ + +myURL = new URL('foo:anotherExample.org/', 'https://example.org/'); +// foo:anotherExample.org/ +``` + #### url.hash * {string} @@ -757,7 +785,7 @@ percent-encoded where necessary. Returns an ES6 Iterator over the values of each name-value pair. -#### urlSearchParams\[@@iterator\]() +#### urlSearchParams\[Symbol.iterator\]() * Returns: {Iterator} @@ -840,6 +868,7 @@ added: v7.6.0 * `unicode` {boolean} `true` if Unicode characters appearing in the host component of the URL string should be encoded directly as opposed to being Punycode encoded. **Default:** `false`. +* Returns: {string} Returns a customizable serialization of a URL String representation of a [WHATWG URL][] object. @@ -1197,7 +1226,7 @@ console.log(myURL.origin); [`url.toJSON()`]: #url_url_tojson [`url.toString()`]: #url_url_tostring [`urlSearchParams.entries()`]: #url_urlsearchparams_entries -[`urlSearchParams@@iterator()`]: #url_urlsearchparams_iterator +[`urlSearchParams@@iterator()`]: #url_urlsearchparams_symbol_iterator [ICU]: intl.html#intl_options_for_building_node_js [Punycode]: https://tools.ietf.org/html/rfc5891#section-4.4 [WHATWG URL Standard]: https://url.spec.whatwg.org/ diff --git a/doc/api/util.md b/doc/api/util.md index 13bfda7dfd3e9f..d91d6108fcdc52 100644 --- a/doc/api/util.md +++ b/doc/api/util.md @@ -116,13 +116,24 @@ FOO-BAR 3257: hi there, it's foo-bar [2333] Multiple comma-separated `section` names may be specified in the `NODE_DEBUG` environment variable: `NODE_DEBUG=fs,net,tls`. -## util.deprecate(function, string) +## util.deprecate(function, string[, code]) -The `util.deprecate()` method wraps the given `function` or class in such a way -that it is marked as deprecated. +* `fn` {Function} The function that is being deprecated. +* `msg` {string} A warning message to display when the deprecated function is + invoked. +* `code` {string} A deprecation code. See the [list of deprecated APIs][] for a + list of codes. +* Returns: {Function} The deprecated function wrapped to emit a warning. + +The `util.deprecate()` method wraps `fn` (which may be a function or class) in +such a way that it is marked as deprecated. ```js @@ -134,10 +145,22 @@ exports.obsoleteFunction = util.deprecate(() => { ``` When called, `util.deprecate()` will return a function that will emit a -`DeprecationWarning` using the `process.on('warning')` event. By default, -this warning will be emitted and printed to `stderr` exactly once, the first -time it is called. After the warning is emitted, the wrapped `function` -is called. +`DeprecationWarning` using the [`'warning'`][] event. The warning will +be emitted and printed to `stderr` the first time the returned function is +called. After the warning is emitted, the wrapped function is called without +emitting a warning. + +If the same optional `code` is supplied in multiple calls to `util.deprecate()`, +the warning will be emitted only once for that `code`. + +```js +const util = require('util'); + +const fn1 = util.deprecate(someFunction, someMessage, 'DEP0001'); +const fn2 = util.deprecate(someOtherFunction, someOtherMessage, 'DEP0001'); +fn1(); // emits a deprecation warning with code DEP0001 +fn2(); // does not emit a deprecation warning because it has the same code +``` If either the `--no-deprecation` or `--no-warnings` command line flags are used, or if the `process.noDeprecation` property is set to `true` *prior* to @@ -231,6 +254,24 @@ intended as a debugging tool. Some input values can have a significant performance overhead that can block the event loop. Use this function with care and never in a hot code path. +## util.formatWithOptions(inspectOptions, format[, ...args]) + + +* `inspectOptions` {Object} +* `format` {string} + +This function is identical to [`util.format()`][], except in that it takes +an `inspectOptions` argument which specifies options that are passed along to +[`util.inspect()`][]. + +```js +util.formatWithOptions({ colors: true }, 'See object %O', { foo: 42 }); +// Returns 'See object { foo: 42 }', where `42` is colored as a number +// when printed to a terminal. +``` + ## util.getSystemErrorName(err) + +`util.types` provides a number of type checks for different kinds of built-in +objects. Unlike `instanceof` or `Object.prototype.toString.call(value)`, +these checks do not inspect properties of the object that are accessible from +JavaScript (like their prototype), and usually have the overhead of +calling into C++. + +The result generally does not make any guarantees about what kinds of +properties or behavior a value exposes in JavaScript. They are primarily +useful for addon developers who prefer to do type checking in JavaScript. + +### util.types.isAnyArrayBuffer(value) + + +* Returns: {boolean} + +Returns `true` if the value is a built-in [`ArrayBuffer`][] or +[`SharedArrayBuffer`][] instance. + +See also [`util.types.isArrayBuffer()`][] and +[`util.types.isSharedArrayBuffer()`][]. + +For example: + +```js +util.types.isAnyArrayBuffer(new ArrayBuffer()); // Returns true +util.types.isAnyArrayBuffer(new SharedArrayBuffer()); // Returns true +``` + +### util.types.isArgumentsObject(value) + + +* Returns: {boolean} + +Returns `true` if the value is an `arguments` object. + +For example: + + +```js +function foo() { + util.types.isArgumentsObject(arguments); // Returns true +} +``` + +### util.types.isArrayBuffer(value) + + +* Returns: {boolean} + +Returns `true` if the value is a built-in [`ArrayBuffer`][] instance. +This does *not* include [`SharedArrayBuffer`][] instances. Usually, it is +desirable to test for both; See [`util.types.isAnyArrayBuffer()`][] for that. + +For example: + +```js +util.types.isArrayBuffer(new ArrayBuffer()); // Returns true +util.types.isArrayBuffer(new SharedArrayBuffer()); // Returns false +``` + +### util.types.isAsyncFunction(value) + + +* Returns: {boolean} + +Returns `true` if the value is an [async function][]. +Note that this only reports back what the JavaScript engine is seeing; +in particular, the return value may not match the original source code if +a transpilation tool was used. + +For example: + +```js +util.types.isAsyncFunction(function foo() {}); // Returns false +util.types.isAsyncFunction(async function foo() {}); // Returns true +``` + +### util.types.isBooleanObject(value) + + +* Returns: {boolean} + +Returns `true` if the value is a boolean object, e.g. created +by `new Boolean()`. + +For example: + +```js +util.types.isBooleanObject(false); // Returns false +util.types.isBooleanObject(true); // Returns false +util.types.isBooleanObject(new Boolean(false)); // Returns true +util.types.isBooleanObject(new Boolean(true)); // Returns true +util.types.isBooleanObject(Boolean(false)); // Returns false +util.types.isBooleanObject(Boolean(true)); // Returns false +``` + +### util.types.isDataView(value) + + +* Returns: {boolean} + +Returns `true` if the value is a built-in [`DataView`][] instance. + +For example: + +```js +const ab = new ArrayBuffer(20); +util.types.isDataView(new DataView(ab)); // Returns true +util.types.isDataView(new Float64Array()); // Returns false +``` + +See also [`ArrayBuffer.isView()`][]. + +### util.types.isDate(value) + + +* Returns: {boolean} + +Returns `true` if the value is a built-in [`Date`][] instance. + +For example: + +```js +util.types.isDate(new Date()); // Returns true +``` + +### util.types.isExternal(value) + + +* Returns: {boolean} + +Returns `true` if the value is a native `External` value. + +### util.types.isFloat32Array(value) + + +* Returns: {boolean} + +Returns `true` if the value is a built-in [`Float32Array`][] instance. + +For example: + +```js +util.types.isFloat32Array(new ArrayBuffer()); // Returns false +util.types.isFloat32Array(new Float32Array()); // Returns true +util.types.isFloat32Array(new Float64Array()); // Returns false +``` + +### util.types.isFloat64Array(value) + + +* Returns: {boolean} + +Returns `true` if the value is a built-in [`Float64Array`][] instance. + +For example: + +```js +util.types.isFloat64Array(new ArrayBuffer()); // Returns false +util.types.isFloat64Array(new Uint8Array()); // Returns false +util.types.isFloat64Array(new Float64Array()); // Returns true +``` + +### util.types.isGeneratorFunction(value) + + +* Returns: {boolean} + +Returns `true` if the value is a generator function. +Note that this only reports back what the JavaScript engine is seeing; +in particular, the return value may not match the original source code if +a transpilation tool was used. + +For example: + +```js +util.types.isGeneratorFunction(function foo() {}); // Returns false +util.types.isGeneratorFunction(function* foo() {}); // Returns true +``` + +### util.types.isGeneratorObject(value) + + +* Returns: {boolean} + +Returns `true` if the value is a generator object as returned from a +built-in generator function. +Note that this only reports back what the JavaScript engine is seeing; +in particular, the return value may not match the original source code if +a transpilation tool was used. + +For example: + +```js +function* foo() {} +const generator = foo(); +util.types.isGeneratorObject(generator); // Returns true +``` + +### util.types.isInt8Array(value) + + +* Returns: {boolean} + +Returns `true` if the value is a built-in [`Int8Array`][] instance. + +For example: + +```js +util.types.isInt8Array(new ArrayBuffer()); // Returns false +util.types.isInt8Array(new Int8Array()); // Returns true +util.types.isInt8Array(new Float64Array()); // Returns false +``` + +### util.types.isInt16Array(value) + + +* Returns: {boolean} + +Returns `true` if the value is a built-in [`Int16Array`][] instance. + +For example: + +```js +util.types.isInt16Array(new ArrayBuffer()); // Returns false +util.types.isInt16Array(new Int16Array()); // Returns true +util.types.isInt16Array(new Float64Array()); // Returns false +``` + +### util.types.isInt32Array(value) + + +* Returns: {boolean} + +Returns `true` if the value is a built-in [`Int32Array`][] instance. + +For example: + +```js +util.types.isInt32Array(new ArrayBuffer()); // Returns false +util.types.isInt32Array(new Int32Array()); // Returns true +util.types.isInt32Array(new Float64Array()); // Returns false +``` + +### util.types.isMap(value) + + +* Returns: {boolean} + +Returns `true` if the value is a built-in [`Map`][] instance. + +For example: + +```js +util.types.isMap(new Map()); // Returns true +``` + +### util.types.isMapIterator(value) + + +* Returns: {boolean} + +Returns `true` if the value is an iterator returned for a built-in +[`Map`][] instance. + +For example: + +```js +const map = new Map(); +util.types.isMapIterator(map.keys()); // Returns true +util.types.isMapIterator(map.values()); // Returns true +util.types.isMapIterator(map.entries()); // Returns true +util.types.isMapIterator(map[Symbol.iterator]()); // Returns true +``` + +### util.types.isNativeError(value) + + +* Returns: {boolean} + +Returns `true` if the value is an instance of a built-in [`Error`][] type. + +For example: + +```js +util.types.isNativeError(new Error()); // Returns true +util.types.isNativeError(new TypeError()); // Returns true +util.types.isNativeError(new RangeError()); // Returns true +``` + +### util.types.isNumberObject(value) + + +* Returns: {boolean} + +Returns `true` if the value is a number object, e.g. created +by `new Number()`. + +For example: + +```js +util.types.isNumberObject(0); // Returns false +util.types.isNumberObject(new Number(0)); // Returns true +``` + +### util.types.isPromise(value) + + +* Returns: {boolean} + +Returns `true` if the value is a built-in [`Promise`][]. + +For example: + +```js +util.types.isPromise(Promise.resolve(42)); // Returns true +``` + +### util.types.isProxy(value) + + +* Returns: {boolean} + +Returns `true` if the value is a [`Proxy`][] instance. + +For example: + +```js +const target = {}; +const proxy = new Proxy(target, {}); +util.types.isProxy(target); // Returns false +util.types.isProxy(proxy); // Returns true +``` + +### util.types.isRegExp(value) + + +* Returns: {boolean} + +Returns `true` if the value is a regular expression object. + +For example: + +```js +util.types.isRegExp(/abc/); // Returns true +util.types.isRegExp(new RegExp('abc')); // Returns true +``` + +### util.types.isSet(value) + + +* Returns: {boolean} + +Returns `true` if the value is a built-in [`Set`][] instance. + +For example: + +```js +util.types.isSet(new Set()); // Returns true +``` + +### util.types.isSetIterator(value) + + +* Returns: {boolean} + +Returns `true` if the value is an iterator returned for a built-in +[`Set`][] instance. + +For example: + +```js +const set = new Set(); +util.types.isSetIterator(set.keys()); // Returns true +util.types.isSetIterator(set.values()); // Returns true +util.types.isSetIterator(set.entries()); // Returns true +util.types.isSetIterator(set[Symbol.iterator]()); // Returns true +``` + +### util.types.isSharedArrayBuffer(value) + + +* Returns: {boolean} + +Returns `true` if the value is a built-in [`SharedArrayBuffer`][] instance. +This does *not* include [`ArrayBuffer`][] instances. Usually, it is +desirable to test for both; See [`util.types.isAnyArrayBuffer()`][] for that. + +For example: + +```js +util.types.isSharedArrayBuffer(new ArrayBuffer()); // Returns false +util.types.isSharedArrayBuffer(new SharedArrayBuffer()); // Returns true +``` + +### util.types.isStringObject(value) + + +* Returns: {boolean} + +Returns `true` if the value is a string object, e.g. created +by `new String()`. + +For example: + +```js +util.types.isStringObject('foo'); // Returns false +util.types.isStringObject(new String('foo')); // Returns true +``` + +### util.types.isSymbolObject(value) + + +* Returns: {boolean} + +Returns `true` if the value is a symbol object, created +by calling `Object()` on a `Symbol` primitive. + +For example: + +```js +const symbol = Symbol('foo'); +util.types.isSymbolObject(symbol); // Returns false +util.types.isSymbolObject(Object(symbol)); // Returns true +``` + +### util.types.isTypedArray(value) + + +* Returns: {boolean} + +Returns `true` if the value is a built-in [`TypedArray`][] instance. + +For example: + +```js +util.types.isTypedArray(new ArrayBuffer()); // Returns false +util.types.isTypedArray(new Uint8Array()); // Returns true +util.types.isTypedArray(new Float64Array()); // Returns true +``` + +See also [`ArrayBuffer.isView()`][]. + +### util.types.isUint8Array(value) + + +* Returns: {boolean} + +Returns `true` if the value is a built-in [`Uint8Array`][] instance. + +For example: + +```js +util.types.isUint8Array(new ArrayBuffer()); // Returns false +util.types.isUint8Array(new Uint8Array()); // Returns true +util.types.isUint8Array(new Float64Array()); // Returns false +``` + +### util.types.isUint8ClampedArray(value) + + +* Returns: {boolean} + +Returns `true` if the value is a built-in [`Uint8ClampedArray`][] instance. + +For example: + +```js +util.types.isUint8ClampedArray(new ArrayBuffer()); // Returns false +util.types.isUint8ClampedArray(new Uint8ClampedArray()); // Returns true +util.types.isUint8ClampedArray(new Float64Array()); // Returns false +``` + +### util.types.isUint16Array(value) + + +* Returns: {boolean} + +Returns `true` if the value is a built-in [`Uint16Array`][] instance. + +For example: + +```js +util.types.isUint16Array(new ArrayBuffer()); // Returns false +util.types.isUint16Array(new Uint16Array()); // Returns true +util.types.isUint16Array(new Float64Array()); // Returns false +``` + +### util.types.isUint32Array(value) + + +* Returns: {boolean} + +Returns `true` if the value is a built-in [`Uint32Array`][] instance. + +For example: + +```js +util.types.isUint32Array(new ArrayBuffer()); // Returns false +util.types.isUint32Array(new Uint32Array()); // Returns true +util.types.isUint32Array(new Float64Array()); // Returns false +``` + +### util.types.isWeakMap(value) + + +* Returns: {boolean} + +Returns `true` if the value is a built-in [`WeakMap`][] instance. + +For example: + +```js +util.types.isWeakMap(new WeakMap()); // Returns true +``` + +### util.types.isWeakSet(value) + + +* Returns: {boolean} + +Returns `true` if the value is a built-in [`WeakSet`][] instance. + +For example: + +```js +util.types.isWeakSet(new WeakSet()); // Returns true +``` + +### util.types.isWebAssemblyCompiledModule(value) + + +* Returns: {boolean} + +Returns `true` if the value is a built-in [`WebAssembly.Module`][] instance. + +For example: + +```js +const module = new WebAssembly.Module(wasmBuffer); +util.types.isWebAssemblyCompiledModule(module); // Returns true +``` + + ## Deprecated APIs The following APIs have been deprecated and should no longer be used. Existing @@ -882,12 +1542,12 @@ added: v0.6.0 deprecated: v4.0.0 --> -> Stability: 0 - Deprecated +> Stability: 0 - Deprecated: Use [`Array.isArray()`][] instead. * `object` {any} * Returns: {boolean} -Internal alias for [`Array.isArray`][]. +Alias for [`Array.isArray()`][]. Returns `true` if the given `object` is an `Array`. Otherwise, returns `false`. @@ -908,7 +1568,7 @@ added: v0.11.5 deprecated: v4.0.0 --> -> Stability: 0 - Deprecated +> Stability: 0 - Deprecated: Use `typeof value === 'boolean'` instead. * `object` {any} * Returns: {boolean} @@ -956,7 +1616,7 @@ added: v0.6.0 deprecated: v4.0.0 --> -> Stability: 0 - Deprecated +> Stability: 0 - Deprecated: Use [`util.types.isDate()`][] instead. * `object` {any} * Returns: {boolean} @@ -980,7 +1640,7 @@ added: v0.6.0 deprecated: v4.0.0 --> -> Stability: 0 - Deprecated +> Stability: 0 - Deprecated: Use [`util.types.isNativeError()`][] instead. * `object` {any} * Returns: {boolean} @@ -1020,7 +1680,7 @@ added: v0.11.5 deprecated: v4.0.0 --> -> Stability: 0 - Deprecated +> Stability: 0 - Deprecated: Use `typeof value === 'function'` instead. * `object` {any} * Returns: {boolean} @@ -1048,7 +1708,7 @@ added: v0.11.5 deprecated: v4.0.0 --> -> Stability: 0 - Deprecated +> Stability: 0 - Deprecated: Use `value === null` instead. * `object` {any} * Returns: {boolean} @@ -1073,7 +1733,8 @@ added: v0.11.5 deprecated: v4.0.0 --> -> Stability: 0 - Deprecated +> Stability: 0 - Deprecated: Use +> `value === undefined || value === null` instead. * `object` {any} * Returns: {boolean} @@ -1098,7 +1759,7 @@ added: v0.11.5 deprecated: v4.0.0 --> -> Stability: 0 - Deprecated +> Stability: 0 - Deprecated: Use `typeof value === 'number'` instead. * `object` {any} * Returns: {boolean} @@ -1124,13 +1785,15 @@ added: v0.11.5 deprecated: v4.0.0 --> -> Stability: 0 - Deprecated +> Stability: 0 - Deprecated: +> Use `value !== null && typeof value === 'object'` instead. * `object` {any} * Returns: {boolean} Returns `true` if the given `object` is strictly an `Object` **and** not a -`Function`. Otherwise, returns `false`. +`Function` (even though functions are objects in JavaScript). +Otherwise, returns `false`. ```js const util = require('util'); @@ -1151,7 +1814,9 @@ added: v0.11.5 deprecated: v4.0.0 --> -> Stability: 0 - Deprecated +> Stability: 0 - Deprecated: Use +> `(typeof value !== 'object' && typeof value !== 'function') || value === null` +> instead. * `object` {any} * Returns: {boolean} @@ -1212,7 +1877,7 @@ added: v0.11.5 deprecated: v4.0.0 --> -> Stability: 0 - Deprecated +> Stability: 0 - Deprecated: Use `typeof value === 'string'` instead. * `object` {any} * Returns: {boolean} @@ -1238,7 +1903,7 @@ added: v0.11.5 deprecated: v4.0.0 --> -> Stability: 0 - Deprecated +> Stability: 0 - Deprecated: Use `typeof value === 'symbol'` instead. * `object` {any} * Returns: {boolean} @@ -1262,7 +1927,7 @@ added: v0.11.5 deprecated: v4.0.0 --> -> Stability: 0 - Deprecated +> Stability: 0 - Deprecated: Use `value === undefined` instead. * `object` {any} * Returns: {boolean} @@ -1321,15 +1986,45 @@ deprecated: v0.11.3 Deprecated predecessor of `console.log`. [`'uncaughtException'`]: process.html#process_event_uncaughtexception -[`Array.isArray`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray -[`Buffer.isBuffer()`]: buffer.html#buffer_class_method_buffer_isbuffer_obj -[`Error`]: errors.html#errors_class_error -[`Object.assign()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign +[`'warning'`]: process.html#process_event_warning +[`Array.isArray()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray +[`ArrayBuffer`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer +[`ArrayBuffer.isView()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer/isView +[async function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function [`assert.deepStrictEqual()`]: assert.html#assert_assert_deepstrictequal_actual_expected_message +[`Buffer.isBuffer()`]: buffer.html#buffer_class_method_buffer_isbuffer_obj [`console.error()`]: console.html#console_console_error_data_args [`console.log()`]: console.html#console_console_log_data_args +[`DataView`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView +[`Date`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date +[`Error`]: errors.html#errors_class_error +[`Float32Array`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array +[`Float64Array`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float64Array +[`Int8Array`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int8Array +[`Int16Array`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int16Array +[`Int32Array`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int32Array +[`Map`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map +[`Object.assign()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign +[`Promise`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise +[`Proxy`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy +[`Set`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set +[`SharedArrayBuffer`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer +[`TypedArray`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray +[`util.format()`]: #util_util_format_format_args [`util.inspect()`]: #util_util_inspect_object_options [`util.promisify()`]: #util_util_promisify_original +[`util.types.isAnyArrayBuffer()`]: #util_util_types_isanyarraybuffer_value +[`util.types.isArrayBuffer()`]: #util_util_types_isarraybuffer_value +[`util.types.isDate()`]: #util_util_types_isdate_value +[`util.types.isNativeError()`]: #util_util_types_isnativeerror_value +[`util.types.isSharedArrayBuffer()`]: #util_util_types_issharedarraybuffer_value +[`Uint8Array`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array +[`Uint8ClampedArray`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8ClampedArray +[`Uint16Array`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint16Array +[`Uint32Array`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint32Array +[`WeakMap`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap +[`WeakSet`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet +[`WebAssembly.Module`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Module [Custom inspection functions on Objects]: #util_custom_inspection_functions_on_objects [Custom promisified functions]: #util_custom_promisified_functions [Customizing `util.inspect` colors]: #util_customizing_util_inspect_colors diff --git a/doc/api/v8.md b/doc/api/v8.md index 0a7d0b615fbf45..c5d23af7a3154a 100644 --- a/doc/api/v8.md +++ b/doc/api/v8.md @@ -16,6 +16,8 @@ The APIs and implementation are subject to change at any time. added: v8.0.0 --> +* Returns: {integer} + Returns an integer representing a "version tag" derived from the V8 version, command line flags and detected CPU features. This is useful for determining whether a [`vm.Script`][] `cachedData` buffer is compatible with this instance @@ -30,6 +32,8 @@ changes: description: Support values exceeding the 32-bit unsigned integer range. --> +* Returns: {Object[]} + Returns statistics about the V8 heap spaces, i.e. the segments which make up the V8 heap. Neither the ordering of heap spaces, nor the availability of a heap space can be guaranteed as the statistics are provided via the V8 @@ -96,6 +100,8 @@ changes: description: Support values exceeding the 32-bit unsigned integer range. --> +* Returns: {Object} + Returns an object with the following properties: * `total_heap_size` {number} @@ -108,10 +114,10 @@ Returns an object with the following properties: * `peak_malloced_memory` {number} * `does_zap_garbage` {number} -`does_zap_garbage` is a 0/1 boolean, which signifies whether the -`--zap_code_space` option is enabled or not. This makes V8 overwrite heap -garbage with a bit pattern. The RSS footprint (resident memory set) gets bigger -because it continuously touches all heap pages and that makes them less likely +`does_zap_garbage` is a 0/1 boolean, which signifies whether the +`--zap_code_space` option is enabled or not. This makes V8 overwrite heap +garbage with a bit pattern. The RSS footprint (resident memory set) gets bigger +because it continuously touches all heap pages and that makes them less likely to get swapped out by the operating system. @@ -165,7 +171,7 @@ This API is under development, and changes (including incompatible changes to the API or wire format) may occur until this warning is removed. ### v8.serialize(value) - @@ -174,7 +180,7 @@ added: v8.0.0 Uses a [`DefaultSerializer`][] to serialize `value` into a buffer. ### v8.deserialize(buffer) - @@ -184,7 +190,7 @@ Uses a [`DefaultDeserializer`][] with default options to read a JS value from a buffer. ### class: v8.Serializer - @@ -204,6 +210,8 @@ This throws an error if `value` cannot be serialized. #### serializer.releaseBuffer() +* Returns: {Buffer} + Returns the stored internal buffer. This serializer should not be used once the buffer is released. Calling this method results in undefined behavior if a previous write has failed. @@ -291,13 +299,13 @@ Indicate whether to treat `TypedArray` and `DataView` objects as host objects, i.e. pass them to [`serializer._writeHostObject()`][]. ### class: v8.Deserializer - #### new Deserializer(buffer) -* `buffer` {Buffer|Uint8Array} A buffer returned by +* `buffer` {Buffer|Uint8Array} A buffer returned by [`serializer.releaseBuffer()`][]. Creates a new `Deserializer` object. @@ -371,7 +379,7 @@ This method is not present on the `Deserializer` class itself but can be provided by subclasses. ### class: v8.DefaultSerializer - @@ -380,7 +388,7 @@ A subclass of [`Serializer`][] that serializes `TypedArray` stores the part of their underlying `ArrayBuffer`s that they are referring to. ### class: v8.DefaultDeserializer - diff --git a/doc/api/vm.md b/doc/api/vm.md index aa52d7c02e54ad..03a85df732f67b 100644 --- a/doc/api/vm.md +++ b/doc/api/vm.md @@ -621,6 +621,7 @@ added: v0.11.7 --> * `sandbox` {Object} +* Returns: {boolean} Returns `true` if the given `sandbox` object has been [contextified][] using [`vm.createContext()`][]. diff --git a/doc/api/zlib.md b/doc/api/zlib.md index ebfbc6f1a6a8d2..3ed588b48000a1 100644 --- a/doc/api/zlib.md +++ b/doc/api/zlib.md @@ -281,7 +281,7 @@ Compression strategy. * `zlib.constants.Z_FIXED` * `zlib.constants.Z_DEFAULT_STRATEGY` -## Class Options +## Class: Options Decompress a gzip stream. @@ -362,7 +362,7 @@ added: v0.5.8 changes: - version: v5.0.0 pr-url: https://github.com/nodejs/node/pull/2595 - description: A truncated input stream will now result in an `error` event. + description: A truncated input stream will now result in an `'error'` event. --> Decompress a deflate stream. @@ -376,7 +376,7 @@ changes: description: Custom dictionaries are now supported by `InflateRaw`. - version: v5.0.0 pr-url: https://github.com/nodejs/node/pull/2595 - description: A truncated input stream will now result in an `error` event. + description: A truncated input stream will now result in an `'error'` event. --> Decompress a raw deflate stream. @@ -473,14 +473,14 @@ Provides an object enumerating Zlib-related constants. added: v0.5.8 --> -Creates and returns a new [Deflate][] object with the given [options][]. +Creates and returns a new [Deflate][] object with the given [`options`][]. ## zlib.createDeflateRaw([options]) -Creates and returns a new [DeflateRaw][] object with the given [options][]. +Creates and returns a new [DeflateRaw][] object with the given [`options`][]. An upgrade of zlib from 1.2.8 to 1.2.11 changed behavior when windowBits is set to 8 for raw deflate streams. zlib would automatically set windowBits @@ -494,35 +494,35 @@ that effectively uses an 8-bit window only. added: v0.5.8 --> -Creates and returns a new [Gunzip][] object with the given [options][]. +Creates and returns a new [Gunzip][] object with the given [`options`][]. ## zlib.createGzip([options]) -Creates and returns a new [Gzip][] object with the given [options][]. +Creates and returns a new [Gzip][] object with the given [`options`][]. ## zlib.createInflate([options]) -Creates and returns a new [Inflate][] object with the given [options][]. +Creates and returns a new [Inflate][] object with the given [`options`][]. ## zlib.createInflateRaw([options]) -Creates and returns a new [InflateRaw][] object with the given [options][]. +Creates and returns a new [InflateRaw][] object with the given [`options`][]. ## zlib.createUnzip([options]) -Creates and returns a new [Unzip][] object with the given [options][]. +Creates and returns a new [Unzip][] object with the given [`options`][]. ## Convenience Methods @@ -771,6 +771,7 @@ Decompress a chunk of data with [Unzip][]. [`Content-Encoding`]: https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11 [`DataView`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView [`TypedArray`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray +[`options`]: #zlib_class_options [DeflateRaw]: #zlib_class_zlib_deflateraw [Deflate]: #zlib_class_zlib_deflate [Gunzip]: #zlib_class_zlib_gunzip @@ -778,7 +779,6 @@ Decompress a chunk of data with [Unzip][]. [InflateRaw]: #zlib_class_zlib_inflateraw [Inflate]: #zlib_class_zlib_inflate [Memory Usage Tuning]: #zlib_memory_usage_tuning -[options]: #zlib_class_options [Unzip]: #zlib_class_zlib_unzip [`UV_THREADPOOL_SIZE`]: cli.html#cli_uv_threadpool_size_size [`zlib.bytesWritten`]: #zlib_zlib_byteswritten diff --git a/doc/guides/building-node-with-ninja.md b/doc/guides/building-node-with-ninja.md index d2ff7900722bcd..a70d78fd7aac49 100644 --- a/doc/guides/building-node-with-ninja.md +++ b/doc/guides/building-node-with-ninja.md @@ -35,14 +35,14 @@ runner directly, like so: ## Alias -`alias nnode='./configure --ninja && ninja -C out/Release && ln -fs +`alias nnode='./configure --ninja && ninja -C out/Release && ln -fs out/Release/node node'` ## Producing a debug build The above alias can be modified slightly to produce a debug build, rather than a release build as shown below: -`alias nnodedebug='./configure --ninja && ninja -C out/Debug && ln -fs +`alias nnodedebug='./configure --ninja && ninja -C out/Debug && ln -fs out/Debug/node node_g'` diff --git a/doc/guides/maintaining-V8.md b/doc/guides/maintaining-V8.md index 0d04855684300d..728fb1feb46530 100644 --- a/doc/guides/maintaining-V8.md +++ b/doc/guides/maintaining-V8.md @@ -242,12 +242,12 @@ V8 5.1 (since it was already abandoned). Since Node.js `v6.x` uses V8 5.1, the fix needed to be cherry-picked. To cherry-pick, here's an example workflow: * Download and apply the commit linked-to in the issue (in this case a51f429). - `curl -L https://github.com/v8/v8/commit/a51f429.patch | git am -3 - --directory=deps/v8`. If the branches have diverged significantly, this may - not apply cleanly. It may help to try to cherry-pick the merge to the oldest - branch that was done upstream in V8. In this example, this would be the patch - from the merge to 5.2. The hope is that this would be closer to the V8 5.1, - and has a better chance of applying cleanly. If you're stuck, feel free to + `curl -L https://github.com/v8/v8/commit/a51f429.patch | git am -3 + --directory=deps/v8`. If the branches have diverged significantly, this may + not apply cleanly. It may help to try to cherry-pick the merge to the oldest + branch that was done upstream in V8. In this example, this would be the patch + from the merge to 5.2. The hope is that this would be closer to the V8 5.1, + and has a better chance of applying cleanly. If you're stuck, feel free to ping @ofrobots for help. * Modify the commit message to match the format we use for V8 backports and replace yourself as the author. `git commit --amend --reset-author`. You may diff --git a/doc/guides/maintaining-the-build-files.md b/doc/guides/maintaining-the-build-files.md index 311860aa0535d1..d6e4b0be249c24 100644 --- a/doc/guides/maintaining-the-build-files.md +++ b/doc/guides/maintaining-the-build-files.md @@ -15,7 +15,7 @@ There are three main build files that may be directly run when building Node.js: Makefile mentioned below is maintained separately by humans). For a detailed guide on this script, see [configure](#configure). - `vcbuild.bat`: A Windows Batch Script that locates build tools, provides a - subset of the targets available in the [Makefile](#makefile), and a few + subset of the targets available in the [Makefile](#makefile), and a few targets of its own. For a detailed guide on this script, see [vcbuild.bat](#vcbuild.bat). - `Makefile`: A Makefile that can be run with GNU Make. It provides a set of diff --git a/doc/guides/writing-and-running-benchmarks.md b/doc/guides/writing-and-running-benchmarks.md index 1110f61befdbef..9f3c209df3237b 100644 --- a/doc/guides/writing-and-running-benchmarks.md +++ b/doc/guides/writing-and-running-benchmarks.md @@ -31,7 +31,7 @@ either [`wrk`][wrk] or [`autocannon`][autocannon]. path. In order to compare two HTTP benchmark runs, make sure that the Node.js version in the path is not altered. -`wrk` may be available through one of the available package managers. If not, +`wrk` may be available through one of the available package managers. If not, it can be easily built [from source][wrk] via `make`. By default, `wrk` will be used as the benchmarker. If it is not available, @@ -184,8 +184,8 @@ The `compare.js` tool will then produce a csv file with the benchmark results. $ node benchmark/compare.js --old ./node-master --new ./node-pr-5134 string_decoder > compare-pr-5134.csv ``` -*Tips: there are some useful options of `benchmark/compare.js`. For example, if -you want to compare the benchmark of a single script instead of a whole module, +*Tips: there are some useful options of `benchmark/compare.js`. For example, if +you want to compare the benchmark of a single script instead of a whole module, you can use the `--filter` option:* ```console @@ -236,8 +236,8 @@ is less than `0.05`._ The `compare.R` tool can also produce a box plot by using the `--plot filename` option. In this case there are 48 different benchmark combinations, and there may be a need to filter the csv file. This can be done while benchmarking -using the `--set` parameter (e.g. `--set encoding=ascii`) or by filtering -results afterwards using tools such as `sed` or `grep`. In the `sed` case be +using the `--set` parameter (e.g. `--set encoding=ascii`) or by filtering +results afterwards using tools such as `sed` or `grep`. In the `sed` case be sure to keep the first line since that contains the header information. ```console diff --git a/doc/guides/writing-tests.md b/doc/guides/writing-tests.md index 90e0d7234a0379..4c97f3be92f046 100644 --- a/doc/guides/writing-tests.md +++ b/doc/guides/writing-tests.md @@ -145,14 +145,14 @@ platforms. ### The *common* API -Make use of the helpers from the `common` module as much as possible. Please +Make use of the helpers from the `common` module as much as possible. Please refer to the [common file documentation](https://github.com/nodejs/node/tree/master/test/common) for the full details of the helpers. #### common.mustCall -One interesting case is `common.mustCall`. The use of `common.mustCall` may -avoid the use of extra variables and the corresponding assertions. Let's +One interesting case is `common.mustCall`. The use of `common.mustCall` may +avoid the use of extra variables and the corresponding assertions. Let's explain this with a real test from the test suite. ```javascript @@ -208,8 +208,8 @@ const server = http.createServer(common.mustCall(function(req, res) { #### Countdown Module The common [Countdown module](https://github.com/nodejs/node/tree/master/test/common#countdown-module) -provides a simple countdown mechanism for tests that require a particular -action to be taken after a given number of completed tasks (for instance, +provides a simple countdown mechanism for tests that require a particular +action to be taken after a given number of completed tasks (for instance, shutting down an HTTP server after a specific number of requests). ```javascript diff --git a/doc/node.1 b/doc/node.1 index d543d9fa0355f2..e1b0bf03265ed9 100644 --- a/doc/node.1 +++ b/doc/node.1 @@ -61,46 +61,41 @@ without arguments to start a REPL. . .Sh OPTIONS .Bl -tag -width 6n -.It Fl v , Fl -version -Print node's version. +.It Sy \- +Alias for stdin, analogous to the use of - in other command-line utilities. +The executed script is read from stdin, and remaining arguments are passed to the script. . -.It Fl h , Fl -help -Print node command line options. -The output of this option is less detailed than this document. +.It Fl \- +Indicate the end of node options. +Pass the rest of the arguments to the script. +.Pp +If no script filename or eval/print script is supplied prior to this, then +the next argument will be used as a script filename. . -.It Fl e , Fl -eval Ar string -Evaluate -.Ar string -as JavaScript. +.It Fl -abort-on-uncaught-exception +Aborting instead of exiting causes a core file to be generated for analysis. . -.It Fl p , Fl -print Ar string -Identical to -.Fl e , -but prints the result. +.It Fl -enable-fips +Enable FIPS-compliant crypto at startup. +Requires Node.js to be built with +.Sy ./configure --openssl-fips . . -.It Fl c , Fl -check -Check the script's syntax without executing it. -Exits with an error code if script is invalid. +.It Fl \-experimental-modules +Enable experimental ES module support and caching modules. . -.It Fl i , Fl -interactive -Open the REPL even if stdin does not appear to be a terminal. +.It Fl \-experimental-vm-modules +Enable experimental ES module support in VM module. . -.It Fl r , Fl -require Ar module -Preload the specified -.Ar module -at startup. -Follows `require()`'s module resolution rules. -.Ar module -may be either a path to a file, or a node module name. +.It Fl -force-fips +Force FIPS-compliant crypto on startup +(Cannot be disabled from script code). +Same requirements as +.Fl -enable-fips . . -.It Fl -inspect Ns = Ns Ar [host:]port -Activate inspector on -.Ar host:port . -Default is -.Sy 127.0.0.1:9229 . -.Pp -V8 Inspector integration allows attaching Chrome DevTools and IDEs to Node.js instances for debugging and profiling. -It uses the Chrome Debugging Protocol. +.It Fl -icu-data-dir Ns = Ns Ar file +Specify ICU data load path. +Overrides +.Ev NODE_ICU_DATA . . .It Fl -inspect-brk Ns = Ns Ar [host:]port Activate inspector on @@ -112,45 +107,58 @@ Set the .Ar host:port to be used when the inspector is activated. . -.It Fl -no-deprecation -Silence deprecation warnings. +.It Fl -inspect Ns = Ns Ar [host:]port +Activate inspector on +.Ar host:port . +Default is +.Sy 127.0.0.1:9229 . +.Pp +V8 Inspector integration allows attaching Chrome DevTools and IDEs to Node.js instances for debugging and profiling. +It uses the Chrome Debugging Protocol. . -.It Fl -trace-deprecation -Print stack traces for deprecations. +.It Fl -napi-modules +Enable loading native modules compiled with the ABI-stable Node.js API (N-API) +(experimental). . -.It Fl -throw-deprecation -Throw errors for deprecations. +.It Fl -no-deprecation +Silence deprecation warnings. . -.It Fl -pending-deprecation -Emit pending deprecation warnings. +.It Fl -no-force-async-hooks-checks +Disable runtime checks for `async_hooks`. +These will still be enabled dynamically when `async_hooks` is enabled. . .It Fl -no-warnings Silence all process warnings (including deprecations). . -.It Fl -napi-modules -Enable loading native modules compiled with the ABI-stable Node.js API (N-API) -(experimental). +.It Fl -openssl-config Ns = Ns Ar file +Load an OpenSSL configuration file on startup. +Among other uses, this can be used to enable FIPS-compliant crypto if Node.js is built with +.Sy ./configure --openssl-fips . . -.It Fl -abort-on-uncaught-exception -Aborting instead of exiting causes a core file to be generated for analysis. +.It Fl -pending-deprecation +Emit pending deprecation warnings. . -.It Fl -trace-warnings -Print stack traces for process warnings (including deprecations). +.It Fl -preserve-symlinks +Instructs the module loader to preserve symbolic links when resolving and caching modules. +. +.It Fl -prof-process +Process V8 profiler output generated using the V8 option +.Fl -prof . . .It Fl -redirect-warnings Ns = Ns Ar file Write process warnings to the given .Ar file instead of printing to stderr. . -.It Fl -trace-sync-io -Print a stack trace whenever synchronous I/O is detected after the first turn of the event loop. +.It Fl -throw-deprecation +Throw errors for deprecations. . -.It Fl -no-force-async-hooks-checks -Disable runtime checks for `async_hooks`. -These will still be enabled dynamically when `async_hooks` is enabled. +.It Fl -tls-cipher-list Ns = Ns Ar list +Specify an alternative default TLS cipher list. +Requires Node.js to be built with crypto support. (Default) . -.It Fl -trace-events-enabled -Enable the collection of trace event tracing information. +.It Fl -trace-deprecation +Print stack traces for deprecations. . .It Fl -trace-event-categories Ar categories A comma-separated list of categories that should be traced when trace event tracing is enabled using @@ -160,18 +168,33 @@ A comma-separated list of categories that should be traced when trace event trac Template string specifying the filepath for the trace event data, it supports \fB${rotation}\fR and \fB${pid}\fR. . -.It Fl -zero-fill-buffers -Automatically zero-fills all newly allocated Buffer and SlowBuffer instances. +.It Fl -trace-events-enabled +Enable the collection of trace event tracing information. . -.It Fl -preserve-symlinks -Instructs the module loader to preserve symbolic links when resolving and caching modules. +.It Fl -trace-sync-io +Print a stack trace whenever synchronous I/O is detected after the first turn of the event loop. +. +.It Fl -trace-warnings +Print stack traces for process warnings (including deprecations). . .It Fl -track-heap-objects Track heap object allocations for heap snapshots. . -.It Fl -prof-process -Process V8 profiler output generated using the V8 option -.Fl -prof . +.It Fl -use-bundled\-ca, Fl -use-openssl-ca +Use bundled Mozilla CA store as supplied by current Node.js version or use OpenSSL's default CA store. +The default store is selectable at build-time. +.Pp +The bundled CA store, as supplied by Node.js, is a snapshot of Mozilla CA store that is fixed at release time. +It is identical on all supported platforms. +.Pp +Using OpenSSL store allows for external modifications of the store. +For most Linux and BSD distributions, this store is maintained by the distribution maintainers and system administrators. +OpenSSL CA store location is dependent on configuration of the OpenSSL library but this can be altered at runtime using environment variables. +.Pp +See +.Ev SSL_CERT_DIR +and +.Ev SSL_CERT_FILE . . .It Fl -v8-options Print V8 command-line options. @@ -188,63 +211,40 @@ Set V8's thread pool size which will be used to allocate background jobs. If set to 0 then V8 will choose an appropriate size of the thread pool based on the number of online processors. If the value provided is larger than V8's maximum, then the largest value will be chosen. . -.It Fl -tls-cipher-list Ns = Ns Ar list -Specify an alternative default TLS cipher list. -Requires Node.js to be built with crypto support. (Default) -. -.It Fl -enable-fips -Enable FIPS-compliant crypto at startup. -Requires Node.js to be built with -.Sy ./configure --openssl-fips . -. -.It Fl -force-fips -Force FIPS-compliant crypto on startup -(Cannot be disabled from script code). -Same requirements as -.Fl -enable-fips . +.It Fl -zero-fill-buffers +Automatically zero-fills all newly allocated Buffer and SlowBuffer instances. . -.It Fl -openssl-config Ns = Ns Ar file -Load an OpenSSL configuration file on startup. -Among other uses, this can be used to enable FIPS-compliant crypto if Node.js is built with -.Sy ./configure --openssl-fips . +.It Fl c , Fl -check +Check the script's syntax without executing it. +Exits with an error code if script is invalid. . -.It Fl -use-openssl-ca , Fl -use-bundled\-ca -Use OpenSSL's default CA store or use bundled Mozilla CA store as supplied by current Node.js version. -The default store is selectable at build-time. -.Pp -Using OpenSSL store allows for external modifications of the store. -For most Linux and BSD distributions, this store is maintained by the distribution maintainers and system administrators. -OpenSSL CA store location is dependent on configuration of the OpenSSL library but this can be altered at runtime using environment variables. -.Pp -The bundled CA store, as supplied by Node.js, is a snapshot of Mozilla CA store that is fixed at release time. -It is identical on all supported platforms. -.Pp -See -.Ev SSL_CERT_DIR -and -.Ev SSL_CERT_FILE . +.It Fl e , Fl -eval Ar string +Evaluate +.Ar string +as JavaScript. . -.It Fl -icu-data-dir Ns = Ns Ar file -Specify ICU data load path. -Overrides -.Ev NODE_ICU_DATA . +.It Fl h , Fl -help +Print node command line options. +The output of this option is less detailed than this document. . -.It Fl \-experimental-modules -Enable experimental ES module support and caching modules. +.It Fl i , Fl -interactive +Open the REPL even if stdin does not appear to be a terminal. . -.It Fl \-experimental-vm-modules -Enable experimental ES module support in VM module. +.It Fl p , Fl -print Ar string +Identical to +.Fl e , +but prints the result. . -.It Sy \- -Alias for stdin, analogous to the use of - in other command-line utilities. -The executed script is read from stdin, and remaining arguments are passed to the script. +.It Fl r , Fl -require Ar module +Preload the specified +.Ar module +at startup. +Follows `require()`'s module resolution rules. +.Ar module +may be either a path to a file, or a node module name. . -.It Fl \- -Indicate the end of node options. -Pass the rest of the arguments to the script. -.Pp -If no script filename or eval/print script is supplied prior to this, then -the next argument will be used as a script filename. +.It Fl v , Fl -version +Print node's version. .El . .\" ===================================================================== @@ -298,6 +298,13 @@ When set to .Ar 1 , emit pending deprecation warnings. . +.It Ev NODE_REDIRECT_WARNINGS Ar file +Write process warnings to the given +.Ar file +instead of printing to stderr. +Equivalent to passing +.Fl -redirect-warnings Ar file +on command-line. .It Ev NODE_REPL_HISTORY Ar file Path to the .Ar file @@ -325,14 +332,6 @@ is enabled, this overrides and sets OpenSSL's directory containing trusted certi If .Fl -use-openssl-ca is enabled, this overrides and sets OpenSSL's file containing trusted certificates. -. -.It Ev NODE_REDIRECT_WARNINGS Ar file -Write process warnings to the given -.Ar file -instead of printing to stderr. -Equivalent to passing -.Fl -redirect-warnings Ar file -on command-line. .El . .\"===================================================================== diff --git a/lib/.eslintrc.yaml b/lib/.eslintrc.yaml index 4eebdb6617c101..80cada0b8c9ecc 100644 --- a/lib/.eslintrc.yaml +++ b/lib/.eslintrc.yaml @@ -1,10 +1,30 @@ rules: + no-restricted-syntax: + # Config copied from .eslintrc.js + - error + - selector: "CallExpression[callee.object.name='assert'][callee.property.name='doesNotThrow']" + message: "Please replace `assert.doesNotThrow()` and add a comment next to the code instead." + - selector: "CallExpression[callee.object.name='assert'][callee.property.name='rejects'][arguments.length<2]" + message: "assert.rejects() must be invoked with at least two arguments." + - selector: "CallExpression[callee.object.name='assert'][callee.property.name='throws'][arguments.1.type='Literal']:not([arguments.1.regex])" + message: "Use an object as second argument of assert.throws()" + - selector: "CallExpression[callee.object.name='assert'][callee.property.name='throws'][arguments.length<2]" + message: "assert.throws() must be invoked with at least two arguments." + - selector: "CallExpression[callee.name='setTimeout'][arguments.length<2]" + message: "setTimeout() must be invoked with at least two arguments." + - selector: "CallExpression[callee.name='setInterval'][arguments.length<2]" + message: "setInterval() must be invoked with at least 2 arguments." + - selector: "ThrowStatement > CallExpression[callee.name=/Error$/]" + message: "Use new keyword when throwing an Error." + # Config specific to lib + - selector: "NewExpression[callee.name=/Error$/]:not([callee.name=/^(AssertionError|NghttpError)$/])" + message: "Use an error exported by the internal/errors module." # Custom rules in tools/eslint-rules - require-buffer: error - buffer-constructor: error - no-let-in-for-declaration: error - lowercase-name-for-primitive: error - non-ascii-character: error + node-core/require-buffer: error + node-core/buffer-constructor: error + node-core/no-let-in-for-declaration: error + node-core/lowercase-name-for-primitive: error + node-core/non-ascii-character: error globals: CHECK: false CHECK_EQ: false diff --git a/lib/_http_agent.js b/lib/_http_agent.js index 7586a48680bb6a..30b72775c283de 100644 --- a/lib/_http_agent.js +++ b/lib/_http_agent.js @@ -135,8 +135,8 @@ Agent.prototype.getName = function getName(options) { return name; }; -Agent.prototype.addRequest = function addRequest(req, options, port/*legacy*/, - localAddress/*legacy*/) { +Agent.prototype.addRequest = function addRequest(req, options, port/* legacy */, + localAddress/* legacy */) { // Legacy API: addRequest(req, host, port, localAddress) if (typeof options === 'string') { options = { diff --git a/lib/_http_client.js b/lib/_http_client.js index 59f7c93acb72c7..cd8a7564e705bf 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -218,7 +218,7 @@ function ClientRequest(options, cb) { var posColon = hostHeader.indexOf(':'); if (posColon !== -1 && hostHeader.indexOf(':', posColon + 1) !== -1 && - hostHeader.charCodeAt(0) !== 91/*'['*/) { + hostHeader.charCodeAt(0) !== 91/* '[' */) { hostHeader = `[${hostHeader}]`; } diff --git a/lib/_http_common.js b/lib/_http_common.js index ffb90407c62175..b101c11911fa1e 100644 --- a/lib/_http_common.js +++ b/lib/_http_common.js @@ -237,7 +237,7 @@ const tokenRegExp = /^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/; * Verifies that the given val is a valid HTTP token * per the rules defined in RFC 7230 * See https://tools.ietf.org/html/rfc7230#section-3.2.6 -**/ + */ function checkIsHttpToken(val) { return tokenRegExp.test(val); } @@ -248,7 +248,7 @@ const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/; * field-value = *( field-content / obs-fold ) * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] * field-vchar = VCHAR / obs-text - **/ + */ function checkInvalidHeaderChar(val) { return headerCharRegex.test(val); } diff --git a/lib/_http_server.js b/lib/_http_server.js index 111a8525c47d62..d4dd2b15de90aa 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -553,7 +553,7 @@ function resOnFinish(req, res, socket, state, server) { // if the user never called req.read(), and didn't pipe() or // .resume() or .on('data'), then we call req._dump() so that the // bytes will be pulled off the wire. - if (!req._consuming && !req._readableState.resumeScheduled) + if (!req._readableState.resumeScheduled) req._dump(); res.detachSocket(socket); diff --git a/lib/_stream_readable.js b/lib/_stream_readable.js index 725cc2651873db..098fe3a15366b2 100644 --- a/lib/_stream_readable.js +++ b/lib/_stream_readable.js @@ -29,7 +29,7 @@ const Stream = require('stream'); const { Buffer } = require('buffer'); const util = require('util'); const debug = util.debuglog('stream'); -const BufferList = require('internal/streams/BufferList'); +const BufferList = require('internal/streams/buffer_list'); const destroyImpl = require('internal/streams/destroy'); const errors = require('internal/errors'); var StringDecoder; @@ -300,7 +300,7 @@ function chunkInvalid(state, chunk) { } -// if it's past the high water mark, we can push in some more. +// We can push more data if we are below the highWaterMark. // Also, if we have no data yet, we can stand some // more bytes. This is to work around cases where hwm=0, // such as the repl. Also, if the push() triggered a @@ -989,106 +989,18 @@ function fromList(n, state) { if (state.decoder) ret = state.buffer.join(''); else if (state.buffer.length === 1) - ret = state.buffer.head.data; + ret = state.buffer.first(); else ret = state.buffer.concat(state.length); state.buffer.clear(); } else { // read part of list - ret = fromListPartial(n, state.buffer, state.decoder); + ret = state.buffer.consume(n, state.decoder); } return ret; } -// Extracts only enough buffered data to satisfy the amount requested. -// This function is designed to be inlinable, so please take care when making -// changes to the function body. -function fromListPartial(n, list, hasStrings) { - var ret; - if (n < list.head.data.length) { - // slice is the same for buffers and strings - ret = list.head.data.slice(0, n); - list.head.data = list.head.data.slice(n); - } else if (n === list.head.data.length) { - // first chunk is a perfect match - ret = list.shift(); - } else { - // result spans more than one buffer - ret = hasStrings ? copyFromBufferString(n, list) : copyFromBuffer(n, list); - } - return ret; -} - -// Copies a specified amount of characters from the list of buffered data -// chunks. -// This function is designed to be inlinable, so please take care when making -// changes to the function body. -function copyFromBufferString(n, list) { - var p = list.head; - var c = 1; - var ret = p.data; - n -= ret.length; - while (p = p.next) { - const str = p.data; - const nb = (n > str.length ? str.length : n); - if (nb === str.length) - ret += str; - else - ret += str.slice(0, n); - n -= nb; - if (n === 0) { - if (nb === str.length) { - ++c; - if (p.next) - list.head = p.next; - else - list.head = list.tail = null; - } else { - list.head = p; - p.data = str.slice(nb); - } - break; - } - ++c; - } - list.length -= c; - return ret; -} - -// Copies a specified amount of bytes from the list of buffered data chunks. -// This function is designed to be inlinable, so please take care when making -// changes to the function body. -function copyFromBuffer(n, list) { - const ret = Buffer.allocUnsafe(n); - var p = list.head; - var c = 1; - p.data.copy(ret); - n -= p.data.length; - while (p = p.next) { - const buf = p.data; - const nb = (n > buf.length ? buf.length : n); - buf.copy(ret, ret.length - n, 0, nb); - n -= nb; - if (n === 0) { - if (nb === buf.length) { - ++c; - if (p.next) - list.head = p.next; - else - list.head = list.tail = null; - } else { - list.head = p; - p.data = buf.slice(nb); - } - break; - } - ++c; - } - list.length -= c; - return ret; -} - function endReadable(stream) { var state = stream._readableState; diff --git a/lib/assert.js b/lib/assert.js index 6c33af3fd4b2ca..faf950c2d767f5 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -22,8 +22,11 @@ const { isDeepEqual, isDeepStrictEqual } = require('internal/util/comparisons'); -const { AssertionError, TypeError } = require('internal/errors'); -const { inspect } = require('util'); +const { AssertionError, TypeError, codes } = require('internal/errors'); +const { + ERR_INVALID_ARG_TYPE +} = codes; +const { inspect, types: { isPromise } } = require('util'); const ERR_DIFF_DEACTIVATED = 0; const ERR_DIFF_NOT_EQUAL = 1; @@ -254,8 +257,7 @@ function expectedException(actual, expected, msg) { function getActual(block) { if (typeof block !== 'function') { - throw new TypeError('ERR_INVALID_ARG_TYPE', 'block', 'Function', - block); + throw new TypeError('ERR_INVALID_ARG_TYPE', 'block', 'Function', block); } try { block(); @@ -265,19 +267,44 @@ function getActual(block) { return NO_EXCEPTION_SENTINEL; } -// Expected to throw an error. -assert.throws = function throws(block, error, message) { - const actual = getActual(block); +function checkIsPromise(obj) { + // Accept native ES6 promises and promises that are implemented in a similar + // way. Do not accept thenables that use a function as `obj` and that have no + // `catch` handler. + return isPromise(obj) || + obj !== null && typeof obj === 'object' && + typeof obj.then === 'function' && + typeof obj.catch === 'function'; +} - if (typeof error === 'string') { - if (arguments.length === 3) - throw new TypeError('ERR_INVALID_ARG_TYPE', - 'error', - ['Function', 'RegExp'], - error); +async function waitForActual(block) { + let resultPromise; + if (typeof block === 'function') { + // Return a rejected promise if `block` throws synchronously. + resultPromise = block(); + } else if (checkIsPromise(block)) { + resultPromise = block; + } else { + throw new ERR_INVALID_ARG_TYPE('block', ['Function', 'Promise'], block); + } + + try { + await resultPromise; + } catch (e) { + return e; + } + return NO_EXCEPTION_SENTINEL; +} +function expectsError(stackStartFn, actual, error, message) { + if (typeof error === 'string') { + if (arguments.length === 4) { + throw new ERR_INVALID_ARG_TYPE('error', + ['Object', 'Error', 'Function', 'RegExp'], + error); + } message = error; - error = null; + error = undefined; } if (actual === NO_EXCEPTION_SENTINEL) { @@ -286,40 +313,58 @@ assert.throws = function throws(block, error, message) { details += ` (${error.name})`; } details += message ? `: ${message}` : '.'; + const fnType = stackStartFn.name === 'rejects' ? 'rejection' : 'exception'; innerFail({ - actual, + actual: undefined, expected: error, - operator: 'throws', - message: `Missing expected exception${details}`, - stackStartFn: throws + operator: stackStartFn.name, + message: `Missing expected ${fnType}${details}`, + stackStartFn }); } if (error && expectedException(actual, error, message) === false) { throw actual; } -}; +} -assert.doesNotThrow = function doesNotThrow(block, error, message) { - const actual = getActual(block); +function expectsNoError(stackStartFn, actual, error, message) { if (actual === NO_EXCEPTION_SENTINEL) return; if (typeof error === 'string') { message = error; - error = null; + error = undefined; } if (!error || expectedException(actual, error)) { const details = message ? `: ${message}` : '.'; + const fnType = stackStartFn.name === 'doesNotReject' ? + 'rejection' : 'exception'; innerFail({ actual, expected: error, - operator: 'doesNotThrow', - message: `Got unwanted exception${details}\n${actual && actual.message}`, - stackStartFn: doesNotThrow + operator: stackStartFn.name, + message: `Got unwanted ${fnType}${details}\n${actual && actual.message}`, + stackStartFn }); } throw actual; +} + +assert.throws = function throws(block, ...args) { + expectsError(throws, getActual(block), ...args); +}; + +assert.rejects = async function rejects(block, ...args) { + expectsError(rejects, await waitForActual(block), ...args); +}; + +assert.doesNotThrow = function doesNotThrow(block, ...args) { + expectsNoError(doesNotThrow, getActual(block), ...args); +}; + +assert.doesNotReject = async function doesNotReject(block, ...args) { + expectsNoError(doesNotReject, await waitForActual(block), ...args); }; assert.ifError = function ifError(err) { if (err) throw err; }; diff --git a/lib/buffer.js b/lib/buffer.js index 1ea5beba815994..58717fe47f3d20 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -41,7 +41,15 @@ const { kMaxLength, kStringMaxLength } = process.binding('buffer'); -const { isAnyArrayBuffer } = process.binding('util'); +const { internalBinding } = require('internal/bootstrap/loaders'); +// We cannot use internalBinding unconditionally here because of the way +// that test/parallel/test-buffer-bindingobj-no-zerofill.js is written. +let isAnyArrayBuffer; +try { + isAnyArrayBuffer = internalBinding('types').isAnyArrayBuffer; +} catch (e) { + isAnyArrayBuffer = require('util').types.isAnyArrayBuffer; +} const { customInspectSymbol, normalizeEncoding, @@ -155,7 +163,7 @@ const doFlaggedDeprecation = * runtime deprecation would introduce too much breakage at this time. It's not * likely that the Buffer constructors would ever actually be removed. * Deprecation Code: DEP0005 - **/ + */ function Buffer(arg, encodingOrOffset, length) { doFlaggedDeprecation(); // Common case. @@ -183,7 +191,7 @@ Object.defineProperty(Buffer, Symbol.species, { * Buffer.from(array) * Buffer.from(buffer) * Buffer.from(arrayBuffer[, byteOffset[, length]]) - **/ + */ Buffer.from = function from(value, encodingOrOffset, length) { if (typeof value === 'string') return fromString(value, encodingOrOffset); @@ -264,7 +272,7 @@ function assertSize(size) { /** * Creates a new filled Buffer instance. * alloc(size[, fill[, encoding]]) - **/ + */ Buffer.alloc = function alloc(size, fill, encoding) { assertSize(size); if (size > 0 && fill !== undefined) { @@ -284,7 +292,7 @@ Buffer.alloc = function alloc(size, fill, encoding) { /** * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer * instance. If `--zero-fill-buffers` is set, will zero-fill the buffer. - **/ + */ Buffer.allocUnsafe = function allocUnsafe(size) { assertSize(size); return allocate(size); @@ -294,7 +302,7 @@ Buffer.allocUnsafe = function allocUnsafe(size) { * Equivalent to SlowBuffer(num), by default creates a non-zero-filled * Buffer instance that is not allocated off the pre-initialized pool. * If `--zero-fill-buffers` is set, will zero-fill the buffer. - **/ + */ Buffer.allocUnsafeSlow = function allocUnsafeSlow(size) { assertSize(size); return createUnsafeBuffer(size); diff --git a/lib/child_process.js b/lib/child_process.js index c929b47ede295f..e14f99dc313fb3 100644 --- a/lib/child_process.js +++ b/lib/child_process.js @@ -51,7 +51,7 @@ function stdioStringToArray(option) { } } -exports.fork = function(modulePath /*, args, options*/) { +exports.fork = function(modulePath /* , args, options */) { // Get options and args arguments. var execArgv; @@ -135,7 +135,7 @@ function normalizeExecArgs(command, options, callback) { } -exports.exec = function(command /*, options, callback*/) { +exports.exec = function(command /* , options, callback */) { var opts = normalizeExecArgs.apply(null, arguments); return exports.execFile(opts.file, opts.options, @@ -164,7 +164,7 @@ Object.defineProperty(exports.exec, util.promisify.custom, { value: customPromiseExecFunction(exports.exec) }); -exports.execFile = function(file /*, args, options, callback*/) { +exports.execFile = function(file /* , args, options, callback */) { var args = []; var callback; var options = { @@ -490,7 +490,7 @@ function normalizeSpawnArguments(file, args, options) { } -var spawn = exports.spawn = function(/*file, args, options*/) { +var spawn = exports.spawn = function(/* file, args, options */) { var opts = normalizeSpawnArguments.apply(null, arguments); var options = opts.options; var child = new ChildProcess(); @@ -513,7 +513,7 @@ var spawn = exports.spawn = function(/*file, args, options*/) { return child; }; -function spawnSync(/*file, args, options*/) { +function spawnSync(/* file, args, options */) { var opts = normalizeSpawnArguments.apply(null, arguments); var options = opts.options; @@ -581,7 +581,7 @@ function checkExecSyncError(ret, args, cmd) { } -function execFileSync(/*command, args, options*/) { +function execFileSync(/* command, args, options */) { var opts = normalizeSpawnArguments.apply(null, arguments); var inheritStderr = !opts.options.stdio; @@ -600,7 +600,7 @@ function execFileSync(/*command, args, options*/) { exports.execFileSync = execFileSync; -function execSync(command /*, options*/) { +function execSync(command /* , options */) { var opts = normalizeExecArgs.apply(null, arguments); var inheritStderr = !opts.options.stdio; diff --git a/lib/console.js b/lib/console.js index c89ab098d40a5e..badc2c9a5c8788 100644 --- a/lib/console.js +++ b/lib/console.js @@ -23,24 +23,64 @@ const { isStackOverflowError, - codes: { ERR_CONSOLE_WRITABLE_STREAM }, + codes: { + ERR_CONSOLE_WRITABLE_STREAM, + ERR_INVALID_ARG_TYPE, + ERR_INVALID_ARG_VALUE, + }, } = require('internal/errors'); +const { Buffer: { isBuffer } } = require('buffer'); +const cliTable = require('internal/cli_table'); const util = require('util'); +const { + isTypedArray, isSet, isMap, +} = util.types; const kCounts = Symbol('counts'); +const { + keys: ObjectKeys, + values: ObjectValues, +} = Object; +const hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty); + +const { + isArray: ArrayIsArray, + from: ArrayFrom, +} = Array; + // Track amount of indentation required via `console.group()`. -const kGroupIndent = Symbol('groupIndent'); +const kGroupIndent = Symbol('kGroupIndent'); + +const kFormatForStderr = Symbol('kFormatForStderr'); +const kFormatForStdout = Symbol('kFormatForStdout'); +const kGetInspectOptions = Symbol('kGetInspectOptions'); +const kColorMode = Symbol('kColorMode'); -function Console(stdout, stderr, ignoreErrors = true) { +function Console(options /* or: stdout, stderr, ignoreErrors = true */) { if (!(this instanceof Console)) { - return new Console(stdout, stderr, ignoreErrors); + return new Console(...arguments); } + + let stdout, stderr, ignoreErrors, colorMode; + if (options && typeof options.write !== 'function') { + ({ + stdout, + stderr = stdout, + ignoreErrors = true, + colorMode = 'auto' + } = options); + } else { + return new Console({ + stdout: options, + stderr: arguments[1], + ignoreErrors: arguments[2] + }); + } + if (!stdout || typeof stdout.write !== 'function') { throw new ERR_CONSOLE_WRITABLE_STREAM('stdout'); } - if (!stderr) { - stderr = stdout; - } else if (typeof stderr.write !== 'function') { + if (!stderr || typeof stderr.write !== 'function') { throw new ERR_CONSOLE_WRITABLE_STREAM('stderr'); } @@ -62,7 +102,11 @@ function Console(stdout, stderr, ignoreErrors = true) { prop.value = createWriteErrorHandler(stderr); Object.defineProperty(this, '_stderrErrorHandler', prop); + if (typeof colorMode !== 'boolean' && colorMode !== 'auto') + throw new ERR_INVALID_ARG_VALUE('colorMode', colorMode); + this[kCounts] = new Map(); + this[kColorMode] = colorMode; Object.defineProperty(this, kGroupIndent, { writable: true }); this[kGroupIndent] = ''; @@ -124,13 +168,33 @@ function write(ignoreErrors, stream, string, errorhandler, groupIndent) { } } +const kColorInspectOptions = { colors: true }; +const kNoColorInspectOptions = {}; +Console.prototype[kGetInspectOptions] = function(stream) { + let color = this[kColorMode]; + if (color === 'auto') { + color = stream.isTTY && ( + typeof stream.getColorDepth === 'function' ? + stream.getColorDepth() > 2 : true); + } + + return color ? kColorInspectOptions : kNoColorInspectOptions; +}; + +Console.prototype[kFormatForStdout] = function(args) { + const opts = this[kGetInspectOptions](this._stdout); + return util.formatWithOptions(opts, ...args); +}; + +Console.prototype[kFormatForStderr] = function(args) { + const opts = this[kGetInspectOptions](this._stderr); + return util.formatWithOptions(opts, ...args); +}; + Console.prototype.log = function log(...args) { write(this._ignoreErrors, this._stdout, - // The performance of .apply and the spread operator seems on par in V8 - // 6.3 but the spread operator, unlike .apply(), pushes the elements - // onto the stack. That is, it makes stack overflows more likely. - util.format.apply(null, args), + this[kFormatForStdout](args), this._stdoutErrorHandler, this[kGroupIndent]); }; @@ -141,14 +205,16 @@ Console.prototype.dirxml = Console.prototype.log; Console.prototype.warn = function warn(...args) { write(this._ignoreErrors, this._stderr, - util.format.apply(null, args), + this[kFormatForStderr](args), this._stderrErrorHandler, this[kGroupIndent]); }; Console.prototype.error = Console.prototype.warn; Console.prototype.dir = function dir(object, options) { - options = Object.assign({ customInspect: false }, options); + options = Object.assign({ + customInspect: false + }, this[kGetInspectOptions](this._stdout), options); write(this._ignoreErrors, this._stdout, util.inspect(object, options), @@ -179,7 +245,7 @@ Console.prototype.timeEnd = function timeEnd(label = 'default') { Console.prototype.trace = function trace(...args) { const err = { name: 'Trace', - message: util.format.apply(null, args) + message: this[kFormatForStderr](args) }; Error.captureStackTrace(err, trace); this.error(err.stack); @@ -241,7 +307,109 @@ Console.prototype.groupEnd = function groupEnd() { this[kGroupIndent].slice(0, this[kGroupIndent].length - 2); }; -module.exports = new Console(process.stdout, process.stderr); +const keyKey = 'Key'; +const valuesKey = 'Values'; +const indexKey = '(index)'; +const iterKey = '(iteration index)'; + + +const isArray = (v) => ArrayIsArray(v) || isTypedArray(v) || isBuffer(v); +const inspect = (v) => { + const opt = { depth: 0, maxArrayLength: 3 }; + if (v !== null && typeof v === 'object' && + !isArray(v) && ObjectKeys(v).length > 2) + opt.depth = -1; + return util.inspect(v, opt); +}; + +const getIndexArray = (length) => ArrayFrom({ length }, (_, i) => inspect(i)); + +// https://console.spec.whatwg.org/#table +Console.prototype.table = function(tabularData, properties) { + if (properties !== undefined && !ArrayIsArray(properties)) + throw new ERR_INVALID_ARG_TYPE('properties', 'Array', properties); + + if (tabularData == null || + (typeof tabularData !== 'object' && typeof tabularData !== 'function')) + return this.log(tabularData); + + const final = (k, v) => this.log(cliTable(k, v)); + + if (isMap(tabularData)) { + const keys = []; + const values = []; + let length = 0; + for (const [k, v] of tabularData) { + keys.push(inspect(k)); + values.push(inspect(v)); + length++; + } + return final([ + iterKey, keyKey, valuesKey + ], [ + getIndexArray(length), + keys, + values, + ]); + } + + const setlike = isSet(tabularData); + if (setlike || + (properties === undefined && + (isArray(tabularData) || isTypedArray(tabularData)))) { + const values = []; + let length = 0; + for (const v of tabularData) { + values.push(inspect(v)); + length++; + } + return final([setlike ? iterKey : indexKey, valuesKey], [ + getIndexArray(length), + values, + ]); + } + + const map = {}; + let hasPrimitives = false; + const valuesKeyArray = []; + const indexKeyArray = ObjectKeys(tabularData); + + for (var i = 0; i < indexKeyArray.length; i++) { + const item = tabularData[indexKeyArray[i]]; + const primitive = item === null || + (typeof item !== 'function' && typeof item !== 'object'); + if (properties === undefined && primitive) { + hasPrimitives = true; + valuesKeyArray[i] = inspect(item); + } else { + const keys = properties || ObjectKeys(item); + for (const key of keys) { + if (map[key] === undefined) + map[key] = []; + if ((primitive && properties) || !hasOwnProperty(item, key)) + map[key][i] = ''; + else + map[key][i] = item == null ? item : inspect(item[key]); + } + } + } + + const keys = ObjectKeys(map); + const values = ObjectValues(map); + if (hasPrimitives) { + keys.push(valuesKey); + values.push(valuesKeyArray); + } + keys.unshift(indexKey); + values.unshift(indexKeyArray); + + return final(keys, values); +}; + +module.exports = new Console({ + stdout: process.stdout, + stderr: process.stderr +}); module.exports.Console = Console; function noop() {} diff --git a/lib/crypto.js b/lib/crypto.js index d7c59f553edd2a..b4be60db8125f1 100644 --- a/lib/crypto.js +++ b/lib/crypto.js @@ -162,6 +162,10 @@ module.exports = exports = { rng: randomBytes, setEngine, timingSafeEqual, + getFips: !fipsMode ? getFipsDisabled : + fipsForced ? getFipsForced : getFipsCrypto, + setFips: !fipsMode ? setFipsDisabled : + fipsForced ? setFipsForced : setFipsCrypto, // Classes Certificate, @@ -196,6 +200,7 @@ function getFipsForced() { } Object.defineProperties(exports, { + // crypto.fips is deprecated. DEP00XX. Use crypto.getFips()/crypto.setFips() fips: { get: !fipsMode ? getFipsDisabled : fipsForced ? getFipsForced : getFipsCrypto, diff --git a/lib/dgram.js b/lib/dgram.js index 56eb0ea29b0356..789b60708efd6a 100644 --- a/lib/dgram.js +++ b/lib/dgram.js @@ -180,7 +180,7 @@ function bufferSize(self, size, buffer) { } } -Socket.prototype.bind = function(port_, address_ /*, callback*/) { +Socket.prototype.bind = function(port_, address_ /* , callback */) { let port = port_; this._healthCheck(); diff --git a/lib/dns.js b/lib/dns.js index c2fa1169076b2e..7b279c924ce790 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -48,16 +48,16 @@ const digits = [ function isIPv4(str) { if (!digits[str.charCodeAt(0)]) return false; if (str.length === 1) return false; - if (str.charCodeAt(1) === 46/*'.'*/) + if (str.charCodeAt(1) === 46/* '.' */) return true; else if (!digits[str.charCodeAt(1)]) return false; if (str.length === 2) return false; - if (str.charCodeAt(2) === 46/*'.'*/) + if (str.charCodeAt(2) === 46/* '.' */) return true; else if (!digits[str.charCodeAt(2)]) return false; - return (str.length > 3 && str.charCodeAt(3) === 46/*'.'*/); + return (str.length > 3 && str.charCodeAt(3) === 46/* '.' */); } const dnsException = errors.dnsException; diff --git a/lib/fs.js b/lib/fs.js index 8fa61b10f03107..74be1e387884cb 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -35,6 +35,9 @@ const binding = process.binding('fs'); const fs = exports; const { Buffer } = require('buffer'); const errors = require('internal/errors'); +const { + ERR_INVALID_ARG_TYPE +} = errors.codes; const { Readable, Writable } = require('stream'); const EventEmitter = require('events'); const { FSReqWrap } = binding; @@ -191,6 +194,12 @@ function isFd(path) { return (path >>> 0) === path; } +function isFileType(fileType) { + // Use stats array directly to avoid creating an fs.Stats instance just for + // our internal use. + return (statValues[1/* mode */] & S_IFMT) === fileType; +} + // Constructor for file stats. function Stats( dev, @@ -449,10 +458,8 @@ function readFileAfterStat(err) { if (err) return context.close(err); - // Use stats array directly to avoid creating an fs.Stats instance just for - // our internal use. var size; - if ((statValues[1/*mode*/] & S_IFMT) === S_IFREG) + if (isFileType(S_IFREG)) size = context.size = statValues[8]; else size = context.size = 0; @@ -565,10 +572,8 @@ fs.readFileSync = function(path, options) { var fd = isUserFd ? path : fs.openSync(path, options.flag || 'r', 0o666); tryStatSync(fd, isUserFd); - // Use stats array directly to avoid creating an fs.Stats instance just for - // our internal use. var size; - if ((statValues[1/*mode*/] & S_IFMT) === S_IFREG) + if (isFileType(S_IFREG)) size = statValues[8]; else size = 0; @@ -1407,8 +1412,13 @@ FSWatcher.prototype.start = function(filename, FSWatcher.prototype.close = function() { this._handle.close(); + process.nextTick(emitCloseNT, this); }; +function emitCloseNT(self) { + self.emit('close'); +} + fs.watch = function(filename, options, listener) { handleError((filename = getPathFromURL(filename))); nullCheck(filename); @@ -1466,7 +1476,7 @@ function StatWatcher() { this._handle.onchange = function(newStatus) { if (oldStatus === -1 && newStatus === -1 && - statValues[2/*new nlink*/] === statValues[16/*old nlink*/]) return; + statValues[2/* new nlink */] === statValues[16/* old nlink */]) return; oldStatus = newStatus; self.emit('change', statsFromValues(), statsFromPrevValues()); @@ -1664,8 +1674,7 @@ fs.realpathSync = function realpathSync(p, options) { // continue if not a symlink, break if a pipe/socket if (knownHard[base] || (cache && cache.get(base) === base)) { - if ((statValues[1/*mode*/] & S_IFMT) === S_IFIFO || - (statValues[1/*mode*/] & S_IFMT) === S_IFSOCK) { + if (isFileType(S_IFIFO) || isFileType(S_IFSOCK)) { break; } continue; @@ -1682,7 +1691,7 @@ fs.realpathSync = function realpathSync(p, options) { var baseLong = pathModule.toNamespacedPath(base); binding.lstat(baseLong); - if ((statValues[1/*mode*/] & S_IFMT) !== S_IFLNK) { + if (!isFileType(S_IFLNK)) { knownHard[base] = true; if (cache) cache.set(base, base); continue; @@ -1803,8 +1812,7 @@ fs.realpath = function realpath(p, options, callback) { // continue if not a symlink, break if a pipe/socket if (knownHard[base]) { - if ((statValues[1/*mode*/] & S_IFMT) === S_IFIFO || - (statValues[1/*mode*/] & S_IFMT) === S_IFSOCK) { + if (isFileType(S_IFIFO) || isFileType(S_IFSOCK)) { return callback(null, encodeRealpathResult(p, options)); } return process.nextTick(LOOP); @@ -1820,7 +1828,7 @@ fs.realpath = function realpath(p, options, callback) { // our internal use. // if not a symlink, skip to the next path part - if ((statValues[1/*mode*/] & S_IFMT) !== S_IFLNK) { + if (!isFileType(S_IFLNK)) { knownHard[base] = true; return process.nextTick(LOOP); } @@ -2020,19 +2028,13 @@ function ReadStream(path, options) { this.closed = false; if (this.start !== undefined) { - if (typeof this.start !== 'number') { - throw new errors.TypeError('ERR_INVALID_ARG_TYPE', - 'start', - 'number', - this.start); + if (typeof this.start !== 'number' || Number.isNaN(this.start)) { + throw new ERR_INVALID_ARG_TYPE('start', 'number', this.start); } if (this.end === undefined) { this.end = Infinity; - } else if (typeof this.end !== 'number') { - throw new errors.TypeError('ERR_INVALID_ARG_TYPE', - 'end', - 'number', - this.end); + } else if (typeof this.end !== 'number' || Number.isNaN(this.end)) { + throw new ERR_INVALID_ARG_TYPE('end', 'number', this.end); } if (this.start > this.end) { @@ -2051,6 +2053,8 @@ function ReadStream(path, options) { // (That is a semver-major change). if (typeof this.end !== 'number') this.end = Infinity; + else if (Number.isNaN(this.end)) + throw new ERR_INVALID_ARG_TYPE('end', 'number', this.end); if (typeof this.fd !== 'number') this.open(); diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js index 8a87712613de7c..03e06d5b23d937 100644 --- a/lib/internal/bootstrap/node.js +++ b/lib/internal/bootstrap/node.js @@ -113,6 +113,28 @@ NativeModule.require('internal/process/esm_loader').setup(); } + { + // Install legacy getters on the `util` binding for typechecking. + // TODO(addaleax): Turn into a full runtime deprecation. + const { pendingDeprecation } = process.binding('config'); + const { deprecate } = NativeModule.require('internal/util'); + const utilBinding = process.binding('util'); + const types = internalBinding('types'); + for (const name of [ + 'isArrayBuffer', 'isArrayBufferView', 'isAsyncFunction', + 'isDataView', 'isDate', 'isExternal', 'isMap', 'isMapIterator', + 'isNativeError', 'isPromise', 'isRegExp', 'isSet', 'isSetIterator', + 'isTypedArray', 'isUint8Array', 'isAnyArrayBuffer' + ]) { + utilBinding[name] = pendingDeprecation ? + deprecate(types[name], + 'Accessing native typechecking bindings of Node ' + + 'directly is deprecated. ' + + `Please use \`util.types.${name}\` instead.`, + 'DEP0103') : + types[name]; + } + } // There are various modes that Node can run in. The most common two // are running from a script and running the REPL - but there are a few diff --git a/lib/internal/cli_table.js b/lib/internal/cli_table.js new file mode 100644 index 00000000000000..4c07d92eebdaa7 --- /dev/null +++ b/lib/internal/cli_table.js @@ -0,0 +1,83 @@ +'use strict'; + +const { Buffer } = require('buffer'); +const { removeColors } = require('internal/util'); +const HasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty); + +const tableChars = { + /* eslint-disable node-core/non-ascii-character */ + middleMiddle: '─', + rowMiddle: '┼', + topRight: '┐', + topLeft: '┌', + leftMiddle: '├', + topMiddle: '┬', + bottomRight: '┘', + bottomLeft: '└', + bottomMiddle: '┴', + rightMiddle: '┤', + left: '│ ', + right: ' │', + middle: ' │ ', + /* eslint-enable node-core/non-ascii-character */ +}; + +const countSymbols = (string) => { + const normalized = removeColors(string).normalize('NFC'); + return Buffer.from(normalized, 'UCS-2').byteLength / 2; +}; + +const renderRow = (row, columnWidths) => { + let out = tableChars.left; + for (var i = 0; i < row.length; i++) { + const cell = row[i]; + const len = countSymbols(cell); + const needed = (columnWidths[i] - len) / 2; + // round(needed) + ceil(needed) will always add up to the amount + // of spaces we need while also left justifying the output. + out += `${' '.repeat(needed)}${cell}${' '.repeat(Math.ceil(needed))}`; + if (i !== row.length - 1) + out += tableChars.middle; + } + out += tableChars.right; + return out; +}; + +const table = (head, columns) => { + const rows = []; + const columnWidths = head.map((h) => countSymbols(h)); + const longestColumn = columns.reduce((n, a) => Math.max(n, a.length), 0); + + for (var i = 0; i < head.length; i++) { + const column = columns[i]; + for (var j = 0; j < longestColumn; j++) { + if (!rows[j]) + rows[j] = []; + const v = rows[j][i] = HasOwnProperty(column, j) ? column[j] : ''; + const width = columnWidths[i] || 0; + const counted = countSymbols(v); + columnWidths[i] = Math.max(width, counted); + } + } + + const divider = columnWidths.map((i) => + tableChars.middleMiddle.repeat(i + 2)); + + const tl = tableChars.topLeft; + const tr = tableChars.topRight; + const lm = tableChars.leftMiddle; + let result = `${tl}${divider.join(tableChars.topMiddle)}${tr} +${renderRow(head, columnWidths)} +${lm}${divider.join(tableChars.rowMiddle)}${tableChars.rightMiddle} +`; + + for (const row of rows) + result += `${renderRow(row, columnWidths)}\n`; + + result += `${tableChars.bottomLeft}${ + divider.join(tableChars.bottomMiddle)}${tableChars.bottomRight}`; + + return result; +}; + +module.exports = table; diff --git a/lib/internal/constants.js b/lib/internal/constants.js index c60387dbd2f9ff..5c884ae6bc4a7f 100644 --- a/lib/internal/constants.js +++ b/lib/internal/constants.js @@ -2,10 +2,10 @@ module.exports = { // Alphabet chars. - CHAR_UPPERCASE_A: 65, /*A*/ - CHAR_LOWERCASE_A: 97, /*a*/ - CHAR_UPPERCASE_Z: 90, /*Z*/ - CHAR_LOWERCASE_Z: 122, /*z*/ + CHAR_UPPERCASE_A: 65, /* A */ + CHAR_LOWERCASE_A: 97, /* a */ + CHAR_UPPERCASE_Z: 90, /* Z */ + CHAR_LOWERCASE_Z: 122, /* z */ // Non-alphabetic chars. CHAR_DOT: 46, /* . */ diff --git a/lib/internal/encoding.js b/lib/internal/encoding.js index fa178f3a8c7caa..a18978d4b7d904 100644 --- a/lib/internal/encoding.js +++ b/lib/internal/encoding.js @@ -16,10 +16,11 @@ const { } = require('internal/util'); const { isArrayBufferView } = require('internal/util/types'); +const { internalBinding } = require('internal/bootstrap/loaders'); const { isArrayBuffer -} = process.binding('util'); +} = internalBinding('types'); const { encodeUtf8String diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 4b2aed31537ce7..7b7811efef9d8e 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -1,6 +1,6 @@ -/* eslint documented-errors: "error" */ -/* eslint alphabetize-errors: "error" */ -/* eslint prefer-util-format-errors: "error" */ +/* eslint node-core/documented-errors: "error" */ +/* eslint node-core/alphabetize-errors: "error" */ +/* eslint node-core/prefer-util-format-errors: "error" */ 'use strict'; @@ -14,9 +14,9 @@ const kCode = Symbol('code'); const messages = new Map(); const codes = {}; -var green = ''; -var red = ''; -var white = ''; +let green = ''; +let red = ''; +let white = ''; const { UV_EAI_MEMORY, @@ -27,13 +27,7 @@ const { kMaxLength } = process.binding('buffer'); const { defineProperty } = Object; // Lazily loaded -var util_ = null; -function lazyUtil() { - if (!util_) { - util_ = require('util'); - } - return util_; -} +var util; var internalUtil = null; function lazyInternalUtil() { @@ -43,6 +37,13 @@ function lazyInternalUtil() { return internalUtil; } +function inspectValue(val) { + return util.inspect( + val, + { compact: false, customInspect: false } + ).split('\n'); +} + function makeNodeError(Base) { return class NodeError extends Base { constructor(key, ...args) { @@ -89,11 +90,9 @@ function createErrDiff(actual, expected, operator) { var lastPos = 0; var end = ''; var skipped = false; - const util = lazyUtil(); - const actualLines = util - .inspect(actual, { compact: false, customInspect: false }).split('\n'); - const expectedLines = util - .inspect(expected, { compact: false, customInspect: false }).split('\n'); + if (util === undefined) util = require('util'); + const actualLines = inspectValue(actual); + const expectedLines = inspectValue(expected); const msg = `Input A expected to ${operator} input B:\n` + `${green}+ expected${white} ${red}- actual${white}`; const skippedMsg = ' ... Lines skipped'; @@ -260,14 +259,20 @@ class AssertionError extends Error { if (message != null) { super(message); } else { - if (util_ === null && - process.stdout.isTTY && - process.stdout.getColorDepth() !== 1) { - green = '\u001b[32m'; - white = '\u001b[39m'; - red = '\u001b[31m'; + if (process.stdout.isTTY) { + // Reset on each call to make sure we handle dynamically set environment + // variables correct. + if (process.stdout.getColorDepth() !== 1) { + green = '\u001b[32m'; + white = '\u001b[39m'; + red = '\u001b[31m'; + } else { + green = ''; + white = ''; + red = ''; + } } - const util = lazyUtil(); + if (util === undefined) util = require('util'); if (typeof actual === 'object' && actual !== null && 'stack' in actual && actual instanceof Error) { actual = `${actual.name}: ${actual.message}`; @@ -288,10 +293,7 @@ class AssertionError extends Error { } else if (errorDiff === 1) { // In case the objects are equal but the operator requires unequal, show // the first object and say A equals B - const res = util.inspect( - actual, - { compact: false, customInspect: false } - ).split('\n'); + const res = inspectValue(actual); if (res.length > 20) { res[19] = '...'; @@ -333,10 +335,10 @@ function message(key, args) { const msg = messages.get(key); internalAssert(msg, `An invalid error message key was used: ${key}.`); let fmt; + if (util === undefined) util = require('util'); if (typeof msg === 'function') { fmt = msg; } else { - const util = lazyUtil(); fmt = util.format; if (args === undefined || args.length === 0) return msg; @@ -358,7 +360,8 @@ function errnoException(err, syscall, original) { // getSystemErrorName(err) to guard against invalid arguments from users. // This can be replaced with [ code ] = errmap.get(err) when this method // is no longer exposed to user land. - const code = lazyUtil().getSystemErrorName(err); + if (util === undefined) util = require('util'); + const code = util.getSystemErrorName(err); const message = original ? `${syscall} ${code} ${original}` : `${syscall} ${code}`; @@ -386,7 +389,8 @@ function exceptionWithHostPort(err, syscall, address, port, additional) { // getSystemErrorName(err) to guard against invalid arguments from users. // This can be replaced with [ code ] = errmap.get(err) when this method // is no longer exposed to user land. - const code = lazyUtil().getSystemErrorName(err); + if (util === undefined) util = require('util'); + const code = util.getSystemErrorName(err); let details = ''; if (port && port > 0) { details = ` ${address}:${port}`; @@ -411,33 +415,33 @@ function exceptionWithHostPort(err, syscall, address, port, additional) { } /** - * @param {number|string} err - A libuv error number or a c-ares error code + * @param {number|string} code - A libuv error number or a c-ares error code * @param {string} syscall * @param {string} [hostname] * @returns {Error} */ -function dnsException(err, syscall, hostname) { - const ex = new Error(); +function dnsException(code, syscall, hostname) { + let message; // FIXME(bnoordhuis) Remove this backwards compatibility nonsense and pass // the true error to the user. ENOTFOUND is not even a proper POSIX error! - if (err === UV_EAI_MEMORY || - err === UV_EAI_NODATA || - err === UV_EAI_NONAME) { - err = 'ENOTFOUND'; // Fabricated error name. + if (code === UV_EAI_MEMORY || + code === UV_EAI_NODATA || + code === UV_EAI_NONAME) { + code = 'ENOTFOUND'; // Fabricated error name. } - if (typeof err === 'string') { // c-ares error code. - const errHost = hostname ? ` ${hostname}` : ''; - ex.message = `${syscall} ${err}${errHost}`; - // TODO(joyeecheung): errno is supposed to be a number, like in uvException - ex.code = ex.errno = err; - ex.syscall = syscall; + if (typeof code === 'string') { // c-ares error code. + message = `${syscall} ${code}${hostname ? ` ${hostname}` : ''}`; } else { // libuv error number - const code = lazyInternalUtil().getSystemErrorName(err); - ex.message = `${syscall} ${code}`; - // TODO(joyeecheung): errno is supposed to be err, like in uvException - ex.code = ex.errno = code; - ex.syscall = syscall; + code = lazyInternalUtil().getSystemErrorName(code); + message = `${syscall} ${code}`; } + // eslint-disable-next-line no-restricted-syntax + const ex = new Error(message); + // TODO(joyeecheung): errno is supposed to be a number / err, like in + // uvException. + ex.errno = code; + ex.code = code; + ex.syscall = syscall; if (hostname) { ex.hostname = hostname; } @@ -562,15 +566,12 @@ E('ERR_HTTP2_GOAWAY_SESSION', E('ERR_HTTP2_HEADERS_AFTER_RESPOND', 'Cannot specify additional headers after response initiated', Error); E('ERR_HTTP2_HEADERS_SENT', 'Response has already been initiated.', Error); - E('ERR_HTTP2_HEADER_SINGLE_VALUE', 'Header field "%s" must only have a single value', TypeError); E('ERR_HTTP2_INFO_STATUS_NOT_ALLOWED', 'Informational status codes cannot be used', RangeError); - -// This should probably be a `TypeError`. E('ERR_HTTP2_INVALID_CONNECTION_HEADERS', - 'HTTP/1 Connection specific headers are forbidden: "%s"', Error); + 'HTTP/1 Connection specific headers are forbidden: "%s"', TypeError); E('ERR_HTTP2_INVALID_HEADER_VALUE', 'Invalid value "%s" for header "%s"', TypeError); E('ERR_HTTP2_INVALID_INFO_STATUS', @@ -595,10 +596,8 @@ E('ERR_HTTP2_PAYLOAD_FORBIDDEN', 'Responses with %s status must not have a payload', Error); E('ERR_HTTP2_PING_CANCEL', 'HTTP2 ping cancelled', Error); E('ERR_HTTP2_PING_LENGTH', 'HTTP2 ping payload must be 8 bytes', RangeError); - -// This should probably be a `TypeError`. E('ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED', - 'Cannot set HTTP/2 pseudo-headers', Error); + 'Cannot set HTTP/2 pseudo-headers', TypeError); E('ERR_HTTP2_PUSH_DISABLED', 'HTTP/2 client has disabled push streams', Error); E('ERR_HTTP2_SEND_FILE', 'Only regular files can be sent', Error); E('ERR_HTTP2_SESSION_ERROR', 'Session closed with error code %s', Error); @@ -623,12 +622,12 @@ E('ERR_INSPECTOR_ALREADY_CONNECTED', E('ERR_INSPECTOR_CLOSED', 'Session was closed', Error); E('ERR_INSPECTOR_NOT_AVAILABLE', 'Inspector is not available', Error); E('ERR_INSPECTOR_NOT_CONNECTED', 'Session is not connected', Error); +E('ERR_INVALID_ADDRESS_FAMILY', 'Invalid address family: %s', RangeError); E('ERR_INVALID_ARG_TYPE', invalidArgType, TypeError); E('ERR_INVALID_ARG_VALUE', (name, value, reason = 'is invalid') => { - const util = lazyUtil(); let inspected = util.inspect(value); if (inspected.length > 128) { - inspected = inspected.slice(0, 128) + '...'; + inspected = `${inspected.slice(0, 128)}...`; } return `The argument '${name}' ${reason}. Received ${inspected}`; }, TypeError, RangeError); // Some are currently falsy implemented as "Error" @@ -641,7 +640,7 @@ E('ERR_INVALID_ASYNC_ID', 'Invalid %s value: %s', RangeError); E('ERR_INVALID_BUFFER_SIZE', 'Buffer size must be a multiple of %s', RangeError); E('ERR_INVALID_CALLBACK', 'Callback must be a function', TypeError); -E('ERR_INVALID_CHAR', invalidChar, TypeError); //Check falsy "Error" entries. +E('ERR_INVALID_CHAR', invalidChar, TypeError); // Check falsy "Error" entries. // This should probably be a `TypeError`. E('ERR_INVALID_CURSOR_POS', @@ -728,6 +727,7 @@ E('ERR_STDERR_CLOSE', 'process.stderr cannot be closed', Error); E('ERR_STDOUT_CLOSE', 'process.stdout cannot be closed', Error); E('ERR_STREAM_CANNOT_PIPE', 'Cannot pipe, not readable', Error); E('ERR_STREAM_NULL_VALUES', 'May not write null values to stream', TypeError); +E('ERR_STREAM_PREMATURE_CLOSE', 'Premature close', Error); E('ERR_STREAM_PUSH_AFTER_EOF', 'stream.push() after EOF', Error); E('ERR_STREAM_READ_NOT_IMPLEMENTED', '_read() is not implemented', Error); E('ERR_STREAM_UNSHIFT_AFTER_END_EVENT', diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index 295cc05a72e7c5..2d69cf3f4e01ff 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -8,7 +8,6 @@ const { async_id_symbol } = process.binding('async_wrap'); const http = require('http'); const binding = process.binding('http2'); const assert = require('assert'); -const { Buffer } = require('buffer'); const EventEmitter = require('events'); const net = require('net'); const tls = require('tls'); @@ -60,8 +59,13 @@ const { enroll, unenroll } = require('timers'); +const { + createWriteWrap, + writeGeneric, + writevGeneric +} = require('internal/stream_base_commons'); -const { ShutdownWrap, WriteWrap } = process.binding('stream_wrap'); +const { ShutdownWrap } = process.binding('stream_wrap'); const { constants, nameForErrorCode } = binding; const NETServer = net.Server; @@ -1395,28 +1399,6 @@ class ClientHttp2Session extends Http2Session { } } -function createWriteReq(req, handle, data, encoding) { - switch (encoding) { - case 'utf8': - case 'utf-8': - return handle.writeUtf8String(req, data); - case 'ascii': - return handle.writeAsciiString(req, data); - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return handle.writeUcs2String(req, data); - case 'latin1': - case 'binary': - return handle.writeLatin1String(req, data); - case 'buffer': - return handle.writeBuffer(req, data); - default: - return handle.writeBuffer(req, Buffer.from(data, encoding)); - } -} - function trackWriteState(stream, bytes) { const session = stream[kSession]; stream[kState].writeQueueSize += bytes; @@ -1637,16 +1619,11 @@ class Http2Stream extends Duplex { if (!this.headersSent) this[kProceed](); - const handle = this[kHandle]; - const req = new WriteWrap(); + const req = createWriteWrap(this[kHandle], afterDoStreamWrite); req.stream = this[kID]; - req.handle = handle; - req.callback = cb; - req.oncomplete = afterDoStreamWrite; - req.async = false; - const err = createWriteReq(req, handle, data, encoding); - if (err) - return this.destroy(errors.errnoException(err, 'write', req.error), cb); + + writeGeneric(this, req, data, encoding, cb); + trackWriteState(this, req.bytes); } @@ -1674,22 +1651,11 @@ class Http2Stream extends Duplex { if (!this.headersSent) this[kProceed](); - const handle = this[kHandle]; - const req = new WriteWrap(); + var req = createWriteWrap(this[kHandle], afterDoStreamWrite); req.stream = this[kID]; - req.handle = handle; - req.callback = cb; - req.oncomplete = afterDoStreamWrite; - req.async = false; - const chunks = new Array(data.length << 1); - for (var i = 0; i < data.length; i++) { - const entry = data[i]; - chunks[i * 2] = entry.chunk; - chunks[i * 2 + 1] = entry.encoding; - } - const err = handle.writev(req, chunks); - if (err) - return this.destroy(errors.errnoException(err, 'write', req.error), cb); + + writevGeneric(this, req, data, cb); + trackWriteState(this, req.bytes); } diff --git a/lib/internal/modules/cjs/helpers.js b/lib/internal/modules/cjs/helpers.js index 0bb1cea4050e16..000110d03ad5f3 100644 --- a/lib/internal/modules/cjs/helpers.js +++ b/lib/internal/modules/cjs/helpers.js @@ -1,5 +1,12 @@ 'use strict'; +const { + CHAR_LINE_FEED, + CHAR_CARRIAGE_RETURN, + CHAR_EXCLAMATION_MARK, + CHAR_HASH, +} = require('internal/constants'); + // Invoke with makeRequireFunction(module) where |module| is the Module object // to use as the context for the require() function. function makeRequireFunction(mod) { @@ -55,8 +62,8 @@ function stripShebang(content) { // Remove shebang var contLen = content.length; if (contLen >= 2) { - if (content.charCodeAt(0) === 35/*#*/ && - content.charCodeAt(1) === 33/*!*/) { + if (content.charCodeAt(0) === CHAR_HASH && + content.charCodeAt(1) === CHAR_EXCLAMATION_MARK) { if (contLen === 2) { // Exact match content = ''; @@ -65,7 +72,7 @@ function stripShebang(content) { var i = 2; for (; i < contLen; ++i) { var code = content.charCodeAt(i); - if (code === 10/*\n*/ || code === 13/*\r*/) + if (code === CHAR_LINE_FEED || code === CHAR_CARRIAGE_RETURN) break; } if (i === contLen) diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index 39333f049f6c1a..335525636c7a2e 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -40,6 +40,7 @@ const { stripBOM, stripShebang } = require('internal/modules/cjs/helpers'); +const { safeGetenv } = process.binding('util'); const preserveSymlinks = !!process.binding('config').preserveSymlinks; const experimentalModules = !!process.binding('config').experimentalModules; @@ -49,8 +50,22 @@ module.exports = Module; // these are below module.exports for the circular reference const asyncESM = require('internal/process/esm_loader'); -const ModuleJob = require('internal/modules/esm/ModuleJob'); -const createDynamicModule = require('internal/modules/esm/CreateDynamicModule'); +const ModuleJob = require('internal/modules/esm/module_job'); +const createDynamicModule = require( + 'internal/modules/esm/create_dynamic_module'); +const { + CHAR_UPPERCASE_A, + CHAR_LOWERCASE_A, + CHAR_UPPERCASE_Z, + CHAR_LOWERCASE_Z, + CHAR_FORWARD_SLASH, + CHAR_BACKWARD_SLASH, + CHAR_COLON, + CHAR_DOT, + CHAR_UNDERSCORE, + CHAR_0, + CHAR_9, +} = require('internal/constants'); function stat(filename) { filename = path.toNamespacedPath(filename); @@ -205,7 +220,7 @@ Module._findPath = function(request, paths, isMain) { var exts; var trailingSlash = request.length > 0 && - request.charCodeAt(request.length - 1) === 47/*/*/; + request.charCodeAt(request.length - 1) === CHAR_FORWARD_SLASH; // For each path for (var i = 0; i < paths.length; i++) { @@ -280,8 +295,8 @@ if (process.platform === 'win32') { // return root node_modules when path is 'D:\\'. // path.resolve will make sure from.length >=3 in Windows. - if (from.charCodeAt(from.length - 1) === 92/*\*/ && - from.charCodeAt(from.length - 2) === 58/*:*/) + if (from.charCodeAt(from.length - 1) === CHAR_BACKWARD_SLASH && + from.charCodeAt(from.length - 2) === CHAR_COLON) return [from + 'node_modules']; const paths = []; @@ -294,7 +309,9 @@ if (process.platform === 'win32') { // Use colon as an extra condition since we can get node_modules // path for drive root like 'C:\node_modules' and don't need to // parse drive name. - if (code === 92/*\*/ || code === 47/*/*/ || code === 58/*:*/) { + if (code === CHAR_BACKWARD_SLASH || + code === CHAR_FORWARD_SLASH || + code === CHAR_COLON) { if (p !== nmLen) paths.push(from.slice(0, last) + '\\node_modules'); last = i; @@ -328,7 +345,7 @@ if (process.platform === 'win32') { var last = from.length; for (var i = from.length - 1; i >= 0; --i) { const code = from.charCodeAt(i); - if (code === 47/*/*/) { + if (code === CHAR_FORWARD_SLASH) { if (p !== nmLen) paths.push(from.slice(0, last) + '/node_modules'); last = i; @@ -361,9 +378,9 @@ Module._resolveLookupPaths = function(request, parent, newReturn) { // Check for relative path if (request.length < 2 || - request.charCodeAt(0) !== 46/*.*/ || - (request.charCodeAt(1) !== 46/*.*/ && - request.charCodeAt(1) !== 47/*/*/)) { + request.charCodeAt(0) !== CHAR_DOT || + (request.charCodeAt(1) !== CHAR_DOT && + request.charCodeAt(1) !== CHAR_FORWARD_SLASH)) { var paths = modulePaths; if (parent) { if (!parent.paths) @@ -411,10 +428,10 @@ Module._resolveLookupPaths = function(request, parent, newReturn) { // We matched 'index.', let's validate the rest for (; i < base.length; ++i) { const code = base.charCodeAt(i); - if (code !== 95/*_*/ && - (code < 48/*0*/ || code > 57/*9*/) && - (code < 65/*A*/ || code > 90/*Z*/) && - (code < 97/*a*/ || code > 122/*z*/)) + if (code !== CHAR_UNDERSCORE && + (code < CHAR_0 || code > CHAR_9) && + (code < CHAR_UPPERCASE_A || code > CHAR_UPPERCASE_Z) && + (code < CHAR_LOWERCASE_A || code > CHAR_LOWERCASE_Z)) break; } if (i === base.length) { @@ -678,7 +695,7 @@ Module._extensions['.json'] = function(module, filename) { }; -//Native extension for .node +// Native extension for .node Module._extensions['.node'] = function(module, filename) { return process.dlopen(module, path.toNamespacedPath(filename)); }; @@ -701,10 +718,13 @@ Module._initPaths = function() { const isWindows = process.platform === 'win32'; var homeDir; + var nodePath; if (isWindows) { homeDir = process.env.USERPROFILE; + nodePath = process.env.NODE_PATH; } else { - homeDir = process.env.HOME; + homeDir = safeGetenv('HOME'); + nodePath = safeGetenv('NODE_PATH'); } // $PREFIX/lib/node, where $PREFIX is the root of the Node.js installation. @@ -723,7 +743,6 @@ Module._initPaths = function() { paths.unshift(path.resolve(homeDir, '.node_modules')); } - var nodePath = process.env.NODE_PATH; if (nodePath) { paths = nodePath.split(path.delimiter).filter(function(path) { return !!path; diff --git a/lib/internal/modules/esm/ModuleWrap.js b/lib/internal/modules/esm/ModuleWrap.js deleted file mode 100644 index e589960193c94c..00000000000000 --- a/lib/internal/modules/esm/ModuleWrap.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict'; - -// exposes ModuleWrap for testing - -const { internalBinding } = require('internal/bootstrap/loaders'); -module.exports = internalBinding('module_wrap').ModuleWrap; diff --git a/lib/internal/modules/esm/CreateDynamicModule.js b/lib/internal/modules/esm/create_dynamic_module.js similarity index 100% rename from lib/internal/modules/esm/CreateDynamicModule.js rename to lib/internal/modules/esm/create_dynamic_module.js diff --git a/lib/internal/modules/esm/DefaultResolve.js b/lib/internal/modules/esm/default_resolve.js similarity index 100% rename from lib/internal/modules/esm/DefaultResolve.js rename to lib/internal/modules/esm/default_resolve.js diff --git a/lib/internal/modules/esm/Loader.js b/lib/internal/modules/esm/loader.js similarity index 92% rename from lib/internal/modules/esm/Loader.js rename to lib/internal/modules/esm/loader.js index 170fa23ec798ff..08acb603c2c712 100644 --- a/lib/internal/modules/esm/Loader.js +++ b/lib/internal/modules/esm/loader.js @@ -1,11 +1,12 @@ 'use strict'; const errors = require('internal/errors'); -const ModuleMap = require('internal/modules/esm/ModuleMap'); -const ModuleJob = require('internal/modules/esm/ModuleJob'); -const defaultResolve = require('internal/modules/esm/DefaultResolve'); -const createDynamicModule = require('internal/modules/esm/CreateDynamicModule'); -const translators = require('internal/modules/esm/Translators'); +const ModuleMap = require('internal/modules/esm/module_map'); +const ModuleJob = require('internal/modules/esm/module_job'); +const defaultResolve = require('internal/modules/esm/default_resolve'); +const createDynamicModule = require( + 'internal/modules/esm/create_dynamic_module'); +const translators = require('internal/modules/esm/translators'); const FunctionBind = Function.call.bind(Function.prototype.bind); diff --git a/lib/internal/modules/esm/ModuleJob.js b/lib/internal/modules/esm/module_job.js similarity index 99% rename from lib/internal/modules/esm/ModuleJob.js rename to lib/internal/modules/esm/module_job.js index d948252829ddbf..8be284b3bf6e18 100644 --- a/lib/internal/modules/esm/ModuleJob.js +++ b/lib/internal/modules/esm/module_job.js @@ -105,7 +105,6 @@ class ModuleJob { try { module.evaluate(); } catch (e) { - e.stack; this.hadError = true; this.error = e; throw e; diff --git a/lib/internal/modules/esm/ModuleMap.js b/lib/internal/modules/esm/module_map.js similarity index 93% rename from lib/internal/modules/esm/ModuleMap.js rename to lib/internal/modules/esm/module_map.js index 7f5ee8bf9e7762..04cd3227c32652 100644 --- a/lib/internal/modules/esm/ModuleMap.js +++ b/lib/internal/modules/esm/module_map.js @@ -1,6 +1,6 @@ 'use strict'; -const ModuleJob = require('internal/modules/esm/ModuleJob'); +const ModuleJob = require('internal/modules/esm/module_job'); const { SafeMap } = require('internal/safe_globals'); const debug = require('util').debuglog('esm'); const errors = require('internal/errors'); diff --git a/lib/internal/modules/esm/Translators.js b/lib/internal/modules/esm/translators.js similarity index 97% rename from lib/internal/modules/esm/Translators.js rename to lib/internal/modules/esm/translators.js index 2928115be515e0..d181647c4505e8 100644 --- a/lib/internal/modules/esm/Translators.js +++ b/lib/internal/modules/esm/translators.js @@ -8,7 +8,8 @@ const { } = require('internal/modules/cjs/helpers'); const CJSModule = require('internal/modules/cjs/loader'); const internalURLModule = require('internal/url'); -const createDynamicModule = require('internal/modules/esm/CreateDynamicModule'); +const createDynamicModule = require( + 'internal/modules/esm/create_dynamic_module'); const fs = require('fs'); const { _makeLong } = require('path'); const { SafeMap } = require('internal/safe_globals'); diff --git a/lib/internal/net.js b/lib/internal/net.js index 847539d576906d..c0fed31a12b599 100644 --- a/lib/internal/net.js +++ b/lib/internal/net.js @@ -17,7 +17,7 @@ function makeSyncWrite(fd) { if (enc !== 'buffer') chunk = Buffer.from(chunk, enc); - this._bytesDispatched += chunk.length; + this._handle.bytesWritten += chunk.length; try { writeBuffer(fd, chunk, 0, chunk.length, null); diff --git a/lib/internal/process/esm_loader.js b/lib/internal/process/esm_loader.js index ca2ce57feb54d8..36a3f4b342aafe 100644 --- a/lib/internal/process/esm_loader.js +++ b/lib/internal/process/esm_loader.js @@ -6,7 +6,7 @@ const { } = internalBinding('module_wrap'); const { getURLFromFilePath } = require('internal/url'); -const Loader = require('internal/modules/esm/Loader'); +const Loader = require('internal/modules/esm/loader'); const path = require('path'); const { URL } = require('url'); diff --git a/lib/internal/stream_base_commons.js b/lib/internal/stream_base_commons.js new file mode 100644 index 00000000000000..b252b1d8ff7670 --- /dev/null +++ b/lib/internal/stream_base_commons.js @@ -0,0 +1,88 @@ +'use strict'; + +const { Buffer } = require('buffer'); +const errors = require('internal/errors'); +const { WriteWrap } = process.binding('stream_wrap'); + +const errnoException = errors.errnoException; + +function handleWriteReq(req, data, encoding) { + const { handle } = req; + + switch (encoding) { + case 'buffer': + return handle.writeBuffer(req, data); + case 'latin1': + case 'binary': + return handle.writeLatin1String(req, data); + case 'utf8': + case 'utf-8': + return handle.writeUtf8String(req, data); + case 'ascii': + return handle.writeAsciiString(req, data); + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return handle.writeUcs2String(req, data); + default: + return handle.writeBuffer(req, Buffer.from(data, encoding)); + } +} + +function createWriteWrap(handle, oncomplete) { + var req = new WriteWrap(); + + req.handle = handle; + req.oncomplete = oncomplete; + req.async = false; + + return req; +} + +function writevGeneric(self, req, data, cb) { + var allBuffers = data.allBuffers; + var chunks; + var i; + if (allBuffers) { + chunks = data; + for (i = 0; i < data.length; i++) + data[i] = data[i].chunk; + } else { + chunks = new Array(data.length << 1); + for (i = 0; i < data.length; i++) { + var entry = data[i]; + chunks[i * 2] = entry.chunk; + chunks[i * 2 + 1] = entry.encoding; + } + } + var err = req.handle.writev(req, chunks, allBuffers); + + // Retain chunks + if (err === 0) req._chunks = chunks; + + afterWriteDispatched(self, req, err, cb); +} + +function writeGeneric(self, req, data, encoding, cb) { + var err = handleWriteReq(req, data, encoding); + + afterWriteDispatched(self, req, err, cb); +} + +function afterWriteDispatched(self, req, err, cb) { + if (err !== 0) + return self.destroy(errnoException(err, 'write', req.error), cb); + + if (!req.async) { + cb(); + } else { + req.callback = cb; + } +} + +module.exports = { + createWriteWrap, + writevGeneric, + writeGeneric +}; diff --git a/lib/internal/streams/BufferList.js b/lib/internal/streams/BufferList.js deleted file mode 100644 index b2daf82e74190b..00000000000000 --- a/lib/internal/streams/BufferList.js +++ /dev/null @@ -1,74 +0,0 @@ -'use strict'; - -const { Buffer } = require('buffer'); - -function copyBuffer(src, target, offset) { - Buffer.prototype.copy.call(src, target, offset); -} - -module.exports = class BufferList { - constructor() { - this.head = null; - this.tail = null; - this.length = 0; - } - - push(v) { - const entry = { data: v, next: null }; - if (this.length > 0) - this.tail.next = entry; - else - this.head = entry; - this.tail = entry; - ++this.length; - } - - unshift(v) { - const entry = { data: v, next: this.head }; - if (this.length === 0) - this.tail = entry; - this.head = entry; - ++this.length; - } - - shift() { - if (this.length === 0) - return; - const ret = this.head.data; - if (this.length === 1) - this.head = this.tail = null; - else - this.head = this.head.next; - --this.length; - return ret; - } - - clear() { - this.head = this.tail = null; - this.length = 0; - } - - join(s) { - if (this.length === 0) - return ''; - var p = this.head; - var ret = '' + p.data; - while (p = p.next) - ret += s + p.data; - return ret; - } - - concat(n) { - if (this.length === 0) - return Buffer.alloc(0); - const ret = Buffer.allocUnsafe(n >>> 0); - var p = this.head; - var i = 0; - while (p) { - copyBuffer(p.data, ret, i); - i += p.data.length; - p = p.next; - } - return ret; - } -}; diff --git a/lib/internal/streams/buffer_list.js b/lib/internal/streams/buffer_list.js new file mode 100644 index 00000000000000..1732d3c58c89d6 --- /dev/null +++ b/lib/internal/streams/buffer_list.js @@ -0,0 +1,159 @@ +'use strict'; + +const { Buffer } = require('buffer'); + +function copyBuffer(src, target, offset) { + Buffer.prototype.copy.call(src, target, offset); +} + +module.exports = class BufferList { + constructor() { + this.head = null; + this.tail = null; + this.length = 0; + } + + push(v) { + const entry = { data: v, next: null }; + if (this.length > 0) + this.tail.next = entry; + else + this.head = entry; + this.tail = entry; + ++this.length; + } + + unshift(v) { + const entry = { data: v, next: this.head }; + if (this.length === 0) + this.tail = entry; + this.head = entry; + ++this.length; + } + + shift() { + if (this.length === 0) + return; + const ret = this.head.data; + if (this.length === 1) + this.head = this.tail = null; + else + this.head = this.head.next; + --this.length; + return ret; + } + + clear() { + this.head = this.tail = null; + this.length = 0; + } + + join(s) { + if (this.length === 0) + return ''; + var p = this.head; + var ret = '' + p.data; + while (p = p.next) + ret += s + p.data; + return ret; + } + + concat(n) { + if (this.length === 0) + return Buffer.alloc(0); + const ret = Buffer.allocUnsafe(n >>> 0); + var p = this.head; + var i = 0; + while (p) { + copyBuffer(p.data, ret, i); + i += p.data.length; + p = p.next; + } + return ret; + } + + // Consumes a specified amount of bytes or characters from the buffered data. + consume(n, hasStrings) { + var ret; + if (n < this.head.data.length) { + // `slice` is the same for buffers and strings. + ret = this.head.data.slice(0, n); + this.head.data = this.head.data.slice(n); + } else if (n === this.head.data.length) { + // First chunk is a perfect match. + ret = this.shift(); + } else { + // Result spans more than one buffer. + ret = hasStrings ? this._getString(n) : this._getBuffer(n); + } + return ret; + } + + first() { + return this.head.data; + } + + // Consumes a specified amount of characters from the buffered data. + _getString(n) { + var p = this.head; + var c = 1; + var ret = p.data; + n -= ret.length; + while (p = p.next) { + const str = p.data; + const nb = (n > str.length ? str.length : n); + if (nb === str.length) + ret += str; + else + ret += str.slice(0, n); + n -= nb; + if (n === 0) { + if (nb === str.length) { + ++c; + if (p.next) + this.head = p.next; + else + this.head = this.tail = null; + } else { + this.head = p; + p.data = str.slice(nb); + } + break; + } + ++c; + } + this.length -= c; + return ret; + } + + // Consumes a specified amount of bytes from the buffered data. + _getBuffer(n) { + const ret = Buffer.allocUnsafe(n); + var p = this.head; + var c = 1; + p.data.copy(ret); + n -= p.data.length; + while (p = p.next) { + const buf = p.data; + const nb = (n > buf.length ? buf.length : n); + buf.copy(ret, ret.length - n, 0, nb); + n -= nb; + if (n === 0) { + if (nb === buf.length) { + ++c; + if (p.next) + this.head = p.next; + else + this.head = this.tail = null; + } else { + this.head = p; + p.data = buf.slice(nb); + } + break; + } + ++c; + } + this.length -= c; + return ret; + } +}; diff --git a/lib/internal/streams/destroy.js b/lib/internal/streams/destroy.js index 86a22633f2fe78..985332ac4607a8 100644 --- a/lib/internal/streams/destroy.js +++ b/lib/internal/streams/destroy.js @@ -55,6 +55,8 @@ function undestroy() { this._writableState.destroyed = false; this._writableState.ended = false; this._writableState.ending = false; + this._writableState.finalCalled = false; + this._writableState.prefinished = false; this._writableState.finished = false; this._writableState.errorEmitted = false; } diff --git a/lib/internal/streams/end-of-stream.js b/lib/internal/streams/end-of-stream.js new file mode 100644 index 00000000000000..eeb8a61456a730 --- /dev/null +++ b/lib/internal/streams/end-of-stream.js @@ -0,0 +1,96 @@ +// Ported from https://github.com/mafintosh/end-of-stream with +// permission from the author, Mathias Buus (@mafintosh). + +'use strict'; + +const { + ERR_STREAM_PREMATURE_CLOSE +} = require('internal/errors').codes; + +function noop() {} + +function isRequest(stream) { + return stream.setHeader && typeof stream.abort === 'function'; +} + +function once(callback) { + let called = false; + return function(err) { + if (called) return; + called = true; + callback.call(this, err); + }; +} + +function eos(stream, opts, callback) { + if (typeof opts === 'function') return eos(stream, null, opts); + if (!opts) opts = {}; + + callback = once(callback || noop); + + const ws = stream._writableState; + const rs = stream._readableState; + let readable = opts.readable || (opts.readable !== false && stream.readable); + let writable = opts.writable || (opts.writable !== false && stream.writable); + + const onlegacyfinish = () => { + if (!stream.writable) onfinish(); + }; + + const onfinish = () => { + writable = false; + if (!readable) callback.call(stream); + }; + + const onend = () => { + readable = false; + if (!writable) callback.call(stream); + }; + + const onerror = (err) => { + callback.call(stream, err); + }; + + const onclose = () => { + if (readable && !(rs && rs.ended)) { + return callback.call(stream, new ERR_STREAM_PREMATURE_CLOSE()); + } + if (writable && !(ws && ws.ended)) { + return callback.call(stream, new ERR_STREAM_PREMATURE_CLOSE()); + } + }; + + const onrequest = () => { + stream.req.on('finish', onfinish); + }; + + if (isRequest(stream)) { + stream.on('complete', onfinish); + stream.on('abort', onclose); + if (stream.req) onrequest(); + else stream.on('request', onrequest); + } else if (writable && !ws) { // legacy streams + stream.on('end', onlegacyfinish); + stream.on('close', onlegacyfinish); + } + + stream.on('end', onend); + stream.on('finish', onfinish); + if (opts.error !== false) stream.on('error', onerror); + stream.on('close', onclose); + + return function() { + stream.removeListener('complete', onfinish); + stream.removeListener('abort', onclose); + stream.removeListener('request', onrequest); + if (stream.req) stream.req.removeListener('finish', onfinish); + stream.removeListener('end', onlegacyfinish); + stream.removeListener('close', onlegacyfinish); + stream.removeListener('finish', onfinish); + stream.removeListener('end', onend); + stream.removeListener('error', onerror); + stream.removeListener('close', onclose); + }; +} + +module.exports = eos; diff --git a/lib/internal/streams/pipeline.js b/lib/internal/streams/pipeline.js new file mode 100644 index 00000000000000..7e87210a774c5f --- /dev/null +++ b/lib/internal/streams/pipeline.js @@ -0,0 +1,95 @@ +// Ported from https://github.com/mafintosh/pump with +// permission from the author, Mathias Buus (@mafintosh). + +'use strict'; + +const eos = require('internal/streams/end-of-stream'); + +const { + ERR_MISSING_ARGS, + ERR_STREAM_DESTROYED +} = require('internal/errors').codes; + +function once(callback) { + let called = false; + return function(err) { + if (called) return; + called = true; + callback(err); + }; +} + +function noop() {} + +function isRequest(stream) { + return stream.setHeader && typeof stream.abort === 'function'; +} + +function destroyer(stream, reading, writing, callback) { + callback = once(callback); + + let closed = false; + stream.on('close', () => { + closed = true; + }); + + eos(stream, { readable: reading, writable: writing }, (err) => { + if (err) return callback(err); + closed = true; + callback(); + }); + + let destroyed = false; + return (err) => { + if (closed) return; + if (destroyed) return; + destroyed = true; + + // request.destroy just do .end - .abort is what we want + if (isRequest(stream)) return stream.abort(); + if (typeof stream.destroy === 'function') return stream.destroy(); + + callback(err || new ERR_STREAM_DESTROYED('pipe')); + }; +} + +function call(fn) { + fn(); +} + +function pipe(from, to) { + return from.pipe(to); +} + +function popCallback(streams) { + if (!streams.length) return noop; + if (typeof streams[streams.length - 1] !== 'function') return noop; + return streams.pop(); +} + +function pipeline(...streams) { + const callback = popCallback(streams); + + if (Array.isArray(streams[0])) streams = streams[0]; + + if (streams.length < 2) { + throw new ERR_MISSING_ARGS('streams'); + } + + let error; + const destroys = streams.map(function(stream, i) { + const reading = i < streams.length - 1; + const writing = i > 0; + return destroyer(stream, reading, writing, function(err) { + if (!error) error = err; + if (err) destroys.forEach(call); + if (reading) return; + destroys.forEach(call); + callback(error); + }); + }); + + return streams.reduce(pipe); +} + +module.exports = pipeline; diff --git a/lib/internal/test/binding.js b/lib/internal/test/binding.js new file mode 100644 index 00000000000000..7e1badf4c777ae --- /dev/null +++ b/lib/internal/test/binding.js @@ -0,0 +1,15 @@ +'use strict'; + +process.emitWarning( + 'These APIs are exposed only for testing and are not ' + + 'tracked by any versioning system or deprecation process.', + 'internal/test/binding'); + +// These exports should be scoped as specifically as possible +// to avoid exposing APIs because even with that warning and +// this file being internal people will still try to abuse it. + +const { internalBinding } = require('internal/bootstrap/loaders'); +module.exports = { + ModuleWrap: internalBinding('module_wrap').ModuleWrap, +}; diff --git a/lib/internal/test/unicode.js b/lib/internal/test/unicode.js index 7172a43ec20a8a..451c3c4737898a 100644 --- a/lib/internal/test/unicode.js +++ b/lib/internal/test/unicode.js @@ -3,6 +3,6 @@ // This module exists entirely for regression testing purposes. // See `test/parallel/test-internal-unicode.js`. -/* eslint-disable non-ascii-character */ +/* eslint-disable node-core/non-ascii-character */ module.exports = '✓'; -/* eslint-enable non-ascii-character */ +/* eslint-enable node-core/non-ascii-character */ diff --git a/lib/internal/url.js b/lib/internal/url.js index 34fa4688fa32e9..44d76b6eeaf381 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -790,7 +790,9 @@ function parseParams(qs) { // Adapted from querystring's implementation. // Ref: https://url.spec.whatwg.org/#concept-urlencoded-byte-serializer const noEscape = [ -//0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F +/* + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F +*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00 - 0x0F 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x10 - 0x1F 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, // 0x20 - 0x2F diff --git a/lib/internal/util/comparisons.js b/lib/internal/util/comparisons.js index 1145bc7d9971ba..119fb66611a7c6 100644 --- a/lib/internal/util/comparisons.js +++ b/lib/internal/util/comparisons.js @@ -2,7 +2,8 @@ const { compare } = process.binding('buffer'); const { isArrayBufferView } = require('internal/util/types'); -const { isDate, isMap, isRegExp, isSet } = process.binding('util'); +const { internalBinding } = require('internal/bootstrap/loaders'); +const { isDate, isMap, isRegExp, isSet } = internalBinding('types'); function objectToString(o) { return Object.prototype.toString.call(o); diff --git a/lib/internal/util/types.js b/lib/internal/util/types.js index c990bea6db4605..4df62554d134ed 100644 --- a/lib/internal/util/types.js +++ b/lib/internal/util/types.js @@ -29,8 +29,48 @@ function isUint8Array(value) { return TypedArrayProto_toStringTag(value) === 'Uint8Array'; } +function isUint8ClampedArray(value) { + return TypedArrayProto_toStringTag(value) === 'Uint8ClampedArray'; +} + +function isUint16Array(value) { + return TypedArrayProto_toStringTag(value) === 'Uint16Array'; +} + +function isUint32Array(value) { + return TypedArrayProto_toStringTag(value) === 'Uint32Array'; +} + +function isInt8Array(value) { + return TypedArrayProto_toStringTag(value) === 'Int8Array'; +} + +function isInt16Array(value) { + return TypedArrayProto_toStringTag(value) === 'Int16Array'; +} + +function isInt32Array(value) { + return TypedArrayProto_toStringTag(value) === 'Int32Array'; +} + +function isFloat32Array(value) { + return TypedArrayProto_toStringTag(value) === 'Float32Array'; +} + +function isFloat64Array(value) { + return TypedArrayProto_toStringTag(value) === 'Float64Array'; +} + module.exports = { isArrayBufferView, isTypedArray, - isUint8Array + isUint8Array, + isUint8ClampedArray, + isUint16Array, + isUint32Array, + isInt8Array, + isInt16Array, + isInt32Array, + isFloat32Array, + isFloat64Array }; diff --git a/lib/internal/vm/Module.js b/lib/internal/vm/module.js similarity index 100% rename from lib/internal/vm/Module.js rename to lib/internal/vm/module.js diff --git a/lib/net.js b/lib/net.js index 62d065f9969edc..7d4f189db71e33 100644 --- a/lib/net.js +++ b/lib/net.js @@ -45,11 +45,19 @@ const { TCP, constants: TCPConstants } = process.binding('tcp_wrap'); const { Pipe, constants: PipeConstants } = process.binding('pipe_wrap'); const { TCPConnectWrap } = process.binding('tcp_wrap'); const { PipeConnectWrap } = process.binding('pipe_wrap'); -const { ShutdownWrap, WriteWrap } = process.binding('stream_wrap'); +const { ShutdownWrap } = process.binding('stream_wrap'); const { async_id_symbol } = process.binding('async_wrap'); const { newUid, defaultTriggerAsyncIdScope } = require('internal/async_hooks'); const { nextTick } = require('internal/process/next_tick'); +const { + createWriteWrap, + writevGeneric, + writeGeneric +} = require('internal/stream_base_commons'); const errors = require('internal/errors'); +const { + ERR_INVALID_ADDRESS_FAMILY +} = errors.codes; const dns = require('dns'); const kLastWriteQueueSize = Symbol('lastWriteQueueSize'); @@ -173,7 +181,6 @@ function normalizeArgs(args) { // called when creating new Socket, or when re-using a closed Socket function initSocketHandle(self) { self._undestroy(); - self._bytesDispatched = 0; self._sockname = null; // Handle creation may be deferred to bind() or connect() time. @@ -189,7 +196,8 @@ function initSocketHandle(self) { } -const BYTES_READ = Symbol('bytesRead'); +const kBytesRead = Symbol('kBytesRead'); +const kBytesWritten = Symbol('kBytesWritten'); function Socket(options) { @@ -244,6 +252,11 @@ function Socket(options) { this._writev = null; this._write = makeSyncWrite(fd); + // makeSyncWrite adjusts this value like the original handle would, so + // we need to let it do that by turning it into a writable, own property. + Object.defineProperty(this._handle, 'bytesWritten', { + value: 0, writable: true + }); } } else { // these will be set once there is a connection @@ -251,7 +264,6 @@ function Socket(options) { } // shut down the socket when we're finished with it. - this.on('finish', onSocketFinish); this.on('end', onReadableStreamEnd); initSocketHandle(this); @@ -280,7 +292,8 @@ function Socket(options) { this._server = null; // Used after `.destroy()` - this[BYTES_READ] = 0; + this[kBytesRead] = 0; + this[kBytesWritten] = 0; } util.inherits(Socket, stream.Duplex); @@ -292,39 +305,42 @@ Socket.prototype._unrefTimer = function _unrefTimer() { function shutdownSocket(self, callback) { var req = new ShutdownWrap(); - req.oncomplete = callback; + req.oncomplete = afterShutdown; req.handle = self._handle; + req.callback = callback; return self._handle.shutdown(req); } // the user has called .end(), and all the bytes have been // sent out to the other side. -function onSocketFinish() { - // If still connecting - defer handling 'finish' until 'connect' will happen +Socket.prototype._final = function(cb) { + // If still connecting - defer handling `_final` until 'connect' will happen if (this.connecting) { - debug('osF: not yet connected'); - return this.once('connect', onSocketFinish); + debug('_final: not yet connected'); + return this.once('connect', () => this._final(cb)); } - debug('onSocketFinish'); if (!this.readable || this._readableState.ended) { - debug('oSF: ended, destroy', this._readableState); + debug('_final: ended, destroy', this._readableState); + cb(); return this.destroy(); } - debug('oSF: not ended, call shutdown()'); + debug('_final: not ended, call shutdown()'); // otherwise, just shutdown, or destroy() if not possible - if (!this._handle || !this._handle.shutdown) + if (!this._handle || !this._handle.shutdown) { + cb(); return this.destroy(); + } var err = defaultTriggerAsyncIdScope( - this[async_id_symbol], shutdownSocket, this, afterShutdown + this[async_id_symbol], shutdownSocket, this, cb ); if (err) return this.destroy(errnoException(err, 'shutdown')); -} +}; function afterShutdown(status, handle) { @@ -333,6 +349,8 @@ function afterShutdown(status, handle) { debug('afterShutdown destroyed=%j', self.destroyed, self._readableState); + this.callback(); + // callback may come after call to destroy. if (self.destroyed) return; @@ -534,8 +552,9 @@ Socket.prototype._destroy = function(exception, cb) { if (this !== process.stderr) debug('close handle'); var isException = exception ? true : false; - // `bytesRead` should be accessible after `.destroy()` - this[BYTES_READ] = this._handle.bytesRead; + // `bytesRead` and `kBytesWritten` should be accessible after `.destroy()` + this[kBytesRead] = this._handle.bytesRead; + this[kBytesWritten] = this._handle.bytesWritten; this._handle.close(() => { debug('emit close'); @@ -645,7 +664,7 @@ function protoGetter(name, callback) { } protoGetter('bytesRead', function bytesRead() { - return this._handle ? this._handle.bytesRead : this[BYTES_READ]; + return this._handle ? this._handle.bytesRead : this[kBytesRead]; }); protoGetter('remoteAddress', function remoteAddress() { @@ -707,48 +726,13 @@ Socket.prototype._writeGeneric = function(writev, data, encoding, cb) { return false; } - var req = new WriteWrap(); - req.handle = this._handle; - req.oncomplete = afterWrite; - req.async = false; - var err; - - if (writev) { - var allBuffers = data.allBuffers; - var chunks; - var i; - if (allBuffers) { - chunks = data; - for (i = 0; i < data.length; i++) - data[i] = data[i].chunk; - } else { - chunks = new Array(data.length << 1); - for (i = 0; i < data.length; i++) { - var entry = data[i]; - chunks[i * 2] = entry.chunk; - chunks[i * 2 + 1] = entry.encoding; - } - } - err = this._handle.writev(req, chunks, allBuffers); - - // Retain chunks - if (err === 0) req._chunks = chunks; - } else { - err = createWriteReq(req, this._handle, data, encoding); - } - - if (err) - return this.destroy(errnoException(err, 'write', req.error), cb); - - this._bytesDispatched += req.bytes; - - if (!req.async) { - cb(); - return; - } - - req.cb = cb; - this[kLastWriteQueueSize] = req.bytes; + var req = createWriteWrap(this._handle, afterWrite); + if (writev) + writevGeneric(this, req, data, cb); + else + writeGeneric(this, req, data, encoding, cb); + if (req.async) + this[kLastWriteQueueSize] = req.bytes; }; @@ -761,33 +745,12 @@ Socket.prototype._write = function(data, encoding, cb) { this._writeGeneric(false, data, encoding, cb); }; -function createWriteReq(req, handle, data, encoding) { - switch (encoding) { - case 'latin1': - case 'binary': - return handle.writeLatin1String(req, data); - - case 'buffer': - return handle.writeBuffer(req, data); - - case 'utf8': - case 'utf-8': - return handle.writeUtf8String(req, data); - - case 'ascii': - return handle.writeAsciiString(req, data); - - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return handle.writeUcs2String(req, data); - - default: - return handle.writeBuffer(req, Buffer.from(data, encoding)); - } -} +// Legacy alias. Having this is probably being overly cautious, but it doesn't +// really hurt anyone either. This can probably be removed safely if desired. +protoGetter('_bytesDispatched', function _bytesDispatched() { + return this._handle ? this._handle.bytesWritten : this[kBytesWritten]; +}); protoGetter('bytesWritten', function bytesWritten() { var bytes = this._bytesDispatched; @@ -844,7 +807,7 @@ function afterWrite(status, handle, err) { if (status < 0) { var ex = errnoException(status, 'write', this.error); debug('write failure', ex); - self.destroy(ex, this.cb); + self.destroy(ex, this.callback); return; } @@ -853,8 +816,8 @@ function afterWrite(status, handle, err) { if (self !== process.stderr && self !== process.stdout) debug('afterWrite call cb'); - if (this.cb) - this.cb.call(undefined); + if (this.callback) + this.callback.call(undefined); } @@ -1089,6 +1052,12 @@ function lookupAndConnect(self, options) { err.port = options.port; err.message = err.message + ' ' + options.host + ':' + options.port; process.nextTick(connectErrorNT, self, err); + } else if (addressType !== 4 && addressType !== 6) { + err = new ERR_INVALID_ADDRESS_FAMILY(addressType); + err.host = options.host; + err.port = options.port; + err.message = err.message + ' ' + options.host + ':' + options.port; + process.nextTick(connectErrorNT, self, err); } else { self._unrefTimer(); defaultTriggerAsyncIdScope( diff --git a/lib/os.js b/lib/os.js index 6af5d15cbc43cf..48785de3cb1ad2 100644 --- a/lib/os.js +++ b/lib/os.js @@ -21,7 +21,7 @@ 'use strict'; -const { pushValToArrayMax } = process.binding('util'); +const { pushValToArrayMax, safeGetenv } = process.binding('util'); const constants = process.binding('constants').os; const { deprecate } = require('internal/util'); const { getCIDRSuffix } = require('internal/os'); @@ -105,9 +105,9 @@ function tmpdir() { if (path.length > 1 && path.endsWith('\\') && !path.endsWith(':\\')) path = path.slice(0, -1); } else { - path = process.env.TMPDIR || - process.env.TMP || - process.env.TEMP || + path = safeGetenv('TMPDIR') || + safeGetenv('TMP') || + safeGetenv('TEMP') || '/tmp'; if (path.length > 1 && path.endsWith('/')) path = path.slice(0, -1); diff --git a/lib/querystring.js b/lib/querystring.js index ec6ad51a89177e..3a601467b38c4e 100644 --- a/lib/querystring.js +++ b/lib/querystring.js @@ -76,12 +76,12 @@ function unescapeBuffer(s, decodeSpaces) { var hasHex = false; while (index < s.length) { currentChar = s.charCodeAt(index); - if (currentChar === 43 /*'+'*/ && decodeSpaces) { + if (currentChar === 43 /* '+' */ && decodeSpaces) { out[outIndex++] = 32; // ' ' index++; continue; } - if (currentChar === 37 /*'%'*/ && index < maxLength) { + if (currentChar === 37 /* '%' */ && index < maxLength) { currentChar = s.charCodeAt(++index); hexHigh = unhexTable[currentChar]; if (!(hexHigh >= 0)) { @@ -365,7 +365,7 @@ function parse(qs, sep, eq, options) { if (!keyEncoded) { // Try to match an (valid) encoded byte once to minimize unnecessary // calls to string decoding functions - if (code === 37/*%*/) { + if (code === 37/* % */) { encodeCheck = 1; continue; } else if (encodeCheck > 0) { @@ -380,7 +380,7 @@ function parse(qs, sep, eq, options) { } } } - if (code === 43/*+*/) { + if (code === 43/* + */) { if (lastPos < i) key += qs.slice(lastPos, i); key += plusChar; @@ -388,7 +388,7 @@ function parse(qs, sep, eq, options) { continue; } } - if (code === 43/*+*/) { + if (code === 43/* + */) { if (lastPos < i) value += qs.slice(lastPos, i); value += plusChar; @@ -396,7 +396,7 @@ function parse(qs, sep, eq, options) { } else if (!valEncoded) { // Try to match an (valid) encoded byte (once) to minimize unnecessary // calls to string decoding functions - if (code === 37/*%*/) { + if (code === 37/* % */) { encodeCheck = 1; } else if (encodeCheck > 0) { // eslint-disable-next-line no-extra-boolean-cast diff --git a/lib/stream.js b/lib/stream.js index 9a816600a05e5a..7c235108c07256 100644 --- a/lib/stream.js +++ b/lib/stream.js @@ -22,6 +22,8 @@ 'use strict'; const { Buffer } = require('buffer'); +const pipeline = require('internal/streams/pipeline'); +const eos = require('internal/streams/end-of-stream'); // Note: export Stream before Readable/Writable/Duplex/... // to avoid a cross-reference(require) issues @@ -33,21 +35,23 @@ Stream.Duplex = require('_stream_duplex'); Stream.Transform = require('_stream_transform'); Stream.PassThrough = require('_stream_passthrough'); +Stream.pipeline = pipeline; +Stream.finished = eos; + // Backwards-compat with node 0.4.x Stream.Stream = Stream; // Internal utilities try { - Stream._isUint8Array = require('internal/util/types').isUint8Array; -} catch (e) { - // Throws for code outside of Node.js core. - - try { - Stream._isUint8Array = process.binding('util').isUint8Array; - } catch (e) { + const types = require('util').types; + if (types && typeof types.isUint8Array === 'function') { + Stream._isUint8Array = types.isUint8Array; + } else { // This throws for Node < 4.2.0 because there's no util binding and // returns undefined for Node < 7.4.0. + Stream._isUint8Array = process.binding('util').isUint8Array; } +} catch (e) { } if (!Stream._isUint8Array) { diff --git a/lib/string_decoder.js b/lib/string_decoder.js index 1e569ba6b26a4c..e883ea6690229a 100644 --- a/lib/string_decoder.js +++ b/lib/string_decoder.js @@ -22,10 +22,24 @@ 'use strict'; const { Buffer } = require('buffer'); +const { internalBinding } = require('internal/bootstrap/loaders'); +const { + kIncompleteCharactersStart, + kIncompleteCharactersEnd, + kMissingBytes, + kBufferedBytes, + kEncodingField, + kSize, + decode, + flush, + encodings +} = internalBinding('string_decoder'); const internalUtil = require('internal/util'); const errors = require('internal/errors'); const isEncoding = Buffer[internalUtil.kIsEncodingSymbol]; +const kNativeDecoder = Symbol('kNativeDecoder'); + // Do not cache `Buffer.isEncoding` when checking encoding names as some // modules monkey-patch it to support additional encodings function normalizeEncoding(enc) { @@ -36,258 +50,68 @@ function normalizeEncoding(enc) { return nenc || enc; } +const encodingsMap = {}; +for (var i = 0; i < encodings.length; ++i) + encodingsMap[encodings[i]] = i; + // StringDecoder provides an interface for efficiently splitting a series of // buffers into a series of JS strings without breaking apart multi-byte // characters. -exports.StringDecoder = StringDecoder; function StringDecoder(encoding) { this.encoding = normalizeEncoding(encoding); - var nb; - switch (this.encoding) { - case 'utf16le': - this.text = utf16Text; - this.end = utf16End; - nb = 4; - break; - case 'utf8': - this.fillLast = utf8FillLast; - nb = 4; - break; - case 'base64': - this.text = base64Text; - this.end = base64End; - nb = 3; - break; - default: - this.write = simpleWrite; - this.end = simpleEnd; - return; - } - this.lastNeed = 0; - this.lastTotal = 0; - this.lastChar = Buffer.allocUnsafe(nb); + this[kNativeDecoder] = Buffer.alloc(kSize); + this[kNativeDecoder][kEncodingField] = encodingsMap[this.encoding]; } -StringDecoder.prototype.write = function(buf) { - if (buf.length === 0) - return ''; - var r; - var i; - if (this.lastNeed) { - r = this.fillLast(buf); - if (r === undefined) - return ''; - i = this.lastNeed; - this.lastNeed = 0; - } else { - i = 0; - } - if (i < buf.length) - return (r ? r + this.text(buf, i) : this.text(buf, i)); - return r || ''; +StringDecoder.prototype.write = function write(buf) { + if (typeof buf === 'string') + return buf; + if (!ArrayBuffer.isView(buf)) + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'buf', + ['Buffer', 'Uint8Array', 'ArrayBufferView']); + return decode(this[kNativeDecoder], buf); }; -StringDecoder.prototype.end = utf8End; - -// Returns only complete characters in a Buffer -StringDecoder.prototype.text = utf8Text; - -// Attempts to complete a partial non-UTF-8 character using bytes from a Buffer -StringDecoder.prototype.fillLast = function(buf) { - if (this.lastNeed <= buf.length) { - buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, this.lastNeed); - return this.lastChar.toString(this.encoding, 0, this.lastTotal); - } - buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, buf.length); - this.lastNeed -= buf.length; +StringDecoder.prototype.end = function end(buf) { + let ret = ''; + if (buf !== undefined) + ret = this.write(buf); + if (this[kNativeDecoder][kBufferedBytes] > 0) + ret += flush(this[kNativeDecoder]); + return ret; }; -// Checks the type of a UTF-8 byte, whether it's ASCII, a leading byte, or a -// continuation byte. If an invalid byte is detected, -2 is returned. -function utf8CheckByte(byte) { - if (byte <= 0x7F) - return 0; - else if (byte >> 5 === 0x06) - return 2; - else if (byte >> 4 === 0x0E) - return 3; - else if (byte >> 3 === 0x1E) - return 4; - return (byte >> 6 === 0x02 ? -1 : -2); -} +/* Everything below this line is undocumented legacy stuff. */ +StringDecoder.prototype.text = function text(buf, offset) { + this[kNativeDecoder][kMissingBytes] = 0; + this[kNativeDecoder][kBufferedBytes] = 0; + return this.write(buf.slice(offset)); +}; -// Checks at most 3 bytes at the end of a Buffer in order to detect an -// incomplete multi-byte UTF-8 character. The total number of bytes (2, 3, or 4) -// needed to complete the UTF-8 character (if applicable) are returned. -function utf8CheckIncomplete(self, buf, i) { - var j = buf.length - 1; - if (j < i) - return 0; - var nb = utf8CheckByte(buf[j]); - if (nb >= 0) { - if (nb > 0) - self.lastNeed = nb - 1; - return nb; - } - if (--j < i || nb === -2) - return 0; - nb = utf8CheckByte(buf[j]); - if (nb >= 0) { - if (nb > 0) - self.lastNeed = nb - 2; - return nb; - } - if (--j < i || nb === -2) - return 0; - nb = utf8CheckByte(buf[j]); - if (nb >= 0) { - if (nb > 0) { - if (nb === 2) - nb = 0; - else - self.lastNeed = nb - 3; +Object.defineProperties(StringDecoder.prototype, { + lastChar: { + configurable: true, + enumerable: true, + get() { + return this[kNativeDecoder].subarray(kIncompleteCharactersStart, + kIncompleteCharactersEnd); } - return nb; - } - return 0; -} - -// Validates as many continuation bytes for a multi-byte UTF-8 character as -// needed or are available. If we see a non-continuation byte where we expect -// one, we "replace" the validated continuation bytes we've seen so far with -// a single UTF-8 replacement character ('\ufffd'), to match v8's UTF-8 decoding -// behavior. The continuation byte check is included three times in the case -// where all of the continuation bytes for a character exist in the same buffer. -// It is also done this way as a slight performance increase instead of using a -// loop. -function utf8CheckExtraBytes(self, buf, p) { - if ((buf[0] & 0xC0) !== 0x80) { - self.lastNeed = 0; - return '\ufffd'; - } - if (self.lastNeed > 1 && buf.length > 1) { - if ((buf[1] & 0xC0) !== 0x80) { - self.lastNeed = 1; - return '\ufffd'; + }, + lastNeed: { + configurable: true, + enumerable: true, + get() { + return this[kNativeDecoder][kMissingBytes]; } - if (self.lastNeed > 2 && buf.length > 2) { - if ((buf[2] & 0xC0) !== 0x80) { - self.lastNeed = 2; - return '\ufffd'; - } + }, + lastTotal: { + configurable: true, + enumerable: true, + get() { + return this[kNativeDecoder][kBufferedBytes] + + this[kNativeDecoder][kMissingBytes]; } } -} - -// Attempts to complete a multi-byte UTF-8 character using bytes from a Buffer. -function utf8FillLast(buf) { - const p = this.lastTotal - this.lastNeed; - var r = utf8CheckExtraBytes(this, buf, p); - if (r !== undefined) - return r; - if (this.lastNeed <= buf.length) { - buf.copy(this.lastChar, p, 0, this.lastNeed); - return this.lastChar.toString(this.encoding, 0, this.lastTotal); - } - buf.copy(this.lastChar, p, 0, buf.length); - this.lastNeed -= buf.length; -} - -// Returns all complete UTF-8 characters in a Buffer. If the Buffer ended on a -// partial character, the character's bytes are buffered until the required -// number of bytes are available. -function utf8Text(buf, i) { - const total = utf8CheckIncomplete(this, buf, i); - if (!this.lastNeed) - return buf.toString('utf8', i); - this.lastTotal = total; - const end = buf.length - (total - this.lastNeed); - buf.copy(this.lastChar, 0, end); - return buf.toString('utf8', i, end); -} +}); -// For UTF-8, a replacement character is added when ending on a partial -// character. -function utf8End(buf) { - const r = (buf && buf.length ? this.write(buf) : ''); - if (this.lastNeed) { - this.lastNeed = 0; - this.lastTotal = 0; - return r + '\ufffd'; - } - return r; -} - -// UTF-16LE typically needs two bytes per character, but even if we have an even -// number of bytes available, we need to check if we end on a leading/high -// surrogate. In that case, we need to wait for the next two bytes in order to -// decode the last character properly. -function utf16Text(buf, i) { - if ((buf.length - i) % 2 === 0) { - const r = buf.toString('utf16le', i); - if (r) { - const c = r.charCodeAt(r.length - 1); - if (c >= 0xD800 && c <= 0xDBFF) { - this.lastNeed = 2; - this.lastTotal = 4; - this.lastChar[0] = buf[buf.length - 2]; - this.lastChar[1] = buf[buf.length - 1]; - return r.slice(0, -1); - } - } - return r; - } - this.lastNeed = 1; - this.lastTotal = 2; - this.lastChar[0] = buf[buf.length - 1]; - return buf.toString('utf16le', i, buf.length - 1); -} - -// For UTF-16LE we do not explicitly append special replacement characters if we -// end on a partial character, we simply let v8 handle that. -function utf16End(buf) { - const r = (buf && buf.length ? this.write(buf) : ''); - if (this.lastNeed) { - const end = this.lastTotal - this.lastNeed; - this.lastNeed = 0; - this.lastTotal = 0; - return r + this.lastChar.toString('utf16le', 0, end); - } - return r; -} - -function base64Text(buf, i) { - const n = (buf.length - i) % 3; - if (n === 0) - return buf.toString('base64', i); - this.lastNeed = 3 - n; - this.lastTotal = 3; - if (n === 1) { - this.lastChar[0] = buf[buf.length - 1]; - } else { - this.lastChar[0] = buf[buf.length - 2]; - this.lastChar[1] = buf[buf.length - 1]; - } - return buf.toString('base64', i, buf.length - n); -} - - -function base64End(buf) { - const r = (buf && buf.length ? this.write(buf) : ''); - if (this.lastNeed) { - const end = 3 - this.lastNeed; - this.lastNeed = 0; - this.lastTotal = 0; - return r + this.lastChar.toString('base64', 0, end); - } - return r; -} - -// Pass bytes on through for single-byte encodings (e.g. ascii, latin1, hex) -function simpleWrite(buf) { - return buf.toString(this.encoding); -} - -function simpleEnd(buf) { - return (buf && buf.length ? this.write(buf) : ''); -} +exports.StringDecoder = StringDecoder; diff --git a/lib/timers.js b/lib/timers.js index 4d3f655a1a1271..97eb3f213a637d 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -96,7 +96,7 @@ const TIMEOUT_MAX = 2 ** 31 - 1; // TimerWrap C++ handle, which makes the call after the duration to process the // list it is attached to. // -/* eslint-disable non-ascii-character */ +/* eslint-disable node-core/non-ascii-character */ // // ╔════ > Object Map // ║ @@ -118,7 +118,7 @@ const TIMEOUT_MAX = 2 ** 31 - 1; // ║ // ╚════ > Linked List // -/* eslint-enable non-ascii-character */ +/* eslint-enable node-core/non-ascii-character */ // // With this, virtually constant-time insertion (append), removal, and timeout // is possible in the JavaScript layer. Any one list of timers is able to be diff --git a/lib/url.js b/lib/url.js index 9ada211cae7b9f..fa685801e0373d 100644 --- a/lib/url.js +++ b/lib/url.js @@ -480,19 +480,19 @@ function validateHostname(self, rest, hostname) { // Escaped characters. Use empty strings to fill up unused entries. // Using Array is faster than Object/Map const escapedCodes = [ - /*0 - 9*/ '', '', '', '', '', '', '', '', '', '%09', - /*10 - 19*/ '%0A', '', '', '%0D', '', '', '', '', '', '', - /*20 - 29*/ '', '', '', '', '', '', '', '', '', '', - /*30 - 39*/ '', '', '%20', '', '%22', '', '', '', '', '%27', - /*40 - 49*/ '', '', '', '', '', '', '', '', '', '', - /*50 - 59*/ '', '', '', '', '', '', '', '', '', '', - /*60 - 69*/ '%3C', '', '%3E', '', '', '', '', '', '', '', - /*70 - 79*/ '', '', '', '', '', '', '', '', '', '', - /*80 - 89*/ '', '', '', '', '', '', '', '', '', '', - /*90 - 99*/ '', '', '%5C', '', '%5E', '', '%60', '', '', '', - /*100 - 109*/ '', '', '', '', '', '', '', '', '', '', - /*110 - 119*/ '', '', '', '', '', '', '', '', '', '', - /*120 - 125*/ '', '', '', '%7B', '%7C', '%7D' + /* 0 - 9 */ '', '', '', '', '', '', '', '', '', '%09', + /* 10 - 19 */ '%0A', '', '', '%0D', '', '', '', '', '', '', + /* 20 - 29 */ '', '', '', '', '', '', '', '', '', '', + /* 30 - 39 */ '', '', '%20', '', '%22', '', '', '', '', '%27', + /* 40 - 49 */ '', '', '', '', '', '', '', '', '', '', + /* 50 - 59 */ '', '', '', '', '', '', '', '', '', '', + /* 60 - 69 */ '%3C', '', '%3E', '', '', '', '', '', '', '', + /* 70 - 79 */ '', '', '', '', '', '', '', '', '', '', + /* 80 - 89 */ '', '', '', '', '', '', '', '', '', '', + /* 90 - 99 */ '', '', '%5C', '', '%5E', '', '%60', '', '', '', + /* 100 - 109 */ '', '', '', '', '', '', '', '', '', '', + /* 110 - 119 */ '', '', '', '', '', '', '', '', '', '', + /* 120 - 125 */ '', '', '', '%7B', '%7C', '%7D' ]; // Automatically escape all delimiters and unwise characters from RFC 2396. @@ -590,7 +590,7 @@ Url.prototype.format = function format() { var search = this.search || (query && ('?' + query)) || ''; - if (protocol && protocol.charCodeAt(protocol.length - 1) !== 58/*:*/) + if (protocol && protocol.charCodeAt(protocol.length - 1) !== 58/* : */) protocol += ':'; var newPathname = ''; @@ -626,10 +626,10 @@ Url.prototype.format = function format() { pathname = '/' + pathname; host = '//' + host; } else if (protocol.length >= 4 && - protocol.charCodeAt(0) === 102/*f*/ && - protocol.charCodeAt(1) === 105/*i*/ && - protocol.charCodeAt(2) === 108/*l*/ && - protocol.charCodeAt(3) === 101/*e*/) { + protocol.charCodeAt(0) === 102/* f */ && + protocol.charCodeAt(1) === 105/* i */ && + protocol.charCodeAt(2) === 108/* l */ && + protocol.charCodeAt(3) === 101/* e */) { host = '//'; } } @@ -691,7 +691,7 @@ Url.prototype.resolveObject = function resolveObject(relative) { result[rkey] = relative[rkey]; } - //urlParse appends trailing / to urls like http://www.example.com + // urlParse appends trailing / to urls like http://www.example.com if (slashedProtocol[result.protocol] && result.hostname && !result.pathname) { result.path = result.pathname = '/'; @@ -817,9 +817,9 @@ Url.prototype.resolveObject = function resolveObject(relative) { // Put this after the other two cases because it simplifies the booleans if (noLeadingSlashes) { result.hostname = result.host = srcPath.shift(); - //occasionally the auth can get stuck only in host - //this especially happens in cases like - //url.resolveObject('mailto:local1@domain1', 'local2@domain2') + // Occasionally the auth can get stuck only in host. + // This especially happens in cases like + // url.resolveObject('mailto:local1@domain1', 'local2@domain2') const authInHost = result.host && result.host.indexOf('@') > 0 && result.host.split('@'); if (authInHost) { @@ -829,7 +829,7 @@ Url.prototype.resolveObject = function resolveObject(relative) { } result.search = relative.search; result.query = relative.query; - //to support http.request + // To support http.request if (result.pathname !== null || result.search !== null) { result.path = (result.pathname ? result.pathname : '') + (result.search ? result.search : ''); @@ -842,7 +842,7 @@ Url.prototype.resolveObject = function resolveObject(relative) { // no path at all. easy. // we've already handled the other stuff above. result.pathname = null; - //to support http.request + // To support http.request if (result.search) { result.path = '/' + result.search; } else { @@ -899,9 +899,9 @@ Url.prototype.resolveObject = function resolveObject(relative) { if (noLeadingSlashes) { result.hostname = result.host = isAbsolute ? '' : srcPath.length ? srcPath.shift() : ''; - //occasionally the auth can get stuck only in host - //this especially happens in cases like - //url.resolveObject('mailto:local1@domain1', 'local2@domain2') + // Occasionally the auth can get stuck only in host. + // This especially happens in cases like + // url.resolveObject('mailto:local1@domain1', 'local2@domain2') const authInHost = result.host && result.host.indexOf('@') > 0 ? result.host.split('@') : false; if (authInHost) { @@ -923,7 +923,7 @@ Url.prototype.resolveObject = function resolveObject(relative) { result.pathname = srcPath.join('/'); } - //to support request.http + // To support request.http if (result.pathname !== null || result.search !== null) { result.path = (result.pathname ? result.pathname : '') + (result.search ? result.search : ''); diff --git a/lib/util.js b/lib/util.js index 70fd1a05564389..ba6b51df195519 100644 --- a/lib/util.js +++ b/lib/util.js @@ -28,6 +28,14 @@ const { isBuffer } = require('buffer').Buffer; const { getPromiseDetails, getProxyDetails, + kPending, + kRejected, +} = process.binding('util'); + +const { internalBinding } = require('internal/bootstrap/loaders'); +const types = internalBinding('types'); +Object.assign(types, require('internal/util/types')); +const { isAnyArrayBuffer, isDataView, isExternal, @@ -38,13 +46,8 @@ const { isSetIterator, isRegExp, isDate, - kPending, - kRejected, -} = process.binding('util'); - -const { isTypedArray -} = require('internal/util/types'); +} = types; const { isDeepStrictEqual @@ -157,24 +160,29 @@ function tryStringify(arg) { } } -function format(f) { - var i, tempStr; +const emptyOptions = {}; +function format(...args) { + return formatWithOptions(emptyOptions, ...args); +} + +function formatWithOptions(inspectOptions, f) { + let i, tempStr; if (typeof f !== 'string') { - if (arguments.length === 0) return ''; - var res = ''; - for (i = 0; i < arguments.length - 1; i++) { - res += inspect(arguments[i]); + if (arguments.length === 1) return ''; + let res = ''; + for (i = 1; i < arguments.length - 1; i++) { + res += inspect(arguments[i], inspectOptions); res += ' '; } - res += inspect(arguments[i]); + res += inspect(arguments[i], inspectOptions); return res; } - if (arguments.length === 1) return f; + if (arguments.length === 2) return f; - var str = ''; - var a = 1; - var lastPos = 0; + let str = ''; + let a = 2; + let lastPos = 0; for (i = 0; i < f.length - 1; i++) { if (f.charCodeAt(i) === 37) { // '%' const nextChar = f.charCodeAt(++i); @@ -190,12 +198,18 @@ function format(f) { tempStr = `${Number(arguments[a++])}`; break; case 79: // 'O' - tempStr = inspect(arguments[a++]); + tempStr = inspect(arguments[a++], inspectOptions); break; case 111: // 'o' - tempStr = inspect(arguments[a++], - { showHidden: true, depth: 4, showProxy: true }); + { + const opts = Object.assign({}, inspectOptions, { + showHidden: true, + showProxy: true, + depth: 4 + }); + tempStr = inspect(arguments[a++], opts); break; + } case 105: // 'i' tempStr = `${parseInt(arguments[a++])}`; break; @@ -228,7 +242,7 @@ function format(f) { if ((typeof x !== 'object' && typeof x !== 'symbol') || x === null) { str += ` ${x}`; } else { - str += ` ${inspect(x)}`; + str += ` ${inspect(x, inspectOptions)}`; } } return str; @@ -268,7 +282,7 @@ function debuglog(set) { * @param {any} value The value to print out. * @param {Object} opts Optional options object that alters the output. */ -/* Legacy: value, showHidden, depth, colors*/ +/* Legacy: value, showHidden, depth, colors */ function inspect(value, opts) { // Default options const ctx = { @@ -629,7 +643,7 @@ function formatPrimitive(fn, value, ctx) { // 2. If none matches, non-greedy match any text up to a whitespace or // the end of the string. // - // eslint-disable-next-line max-len, no-unescaped-regexp-dot + // eslint-disable-next-line max-len, node-core/no-unescaped-regexp-dot readableRegExps[divisor] = new RegExp(`(.|\\n){1,${divisor}}(\\s|$)|(\\n|.)+?(\\s|$)`, 'gm'); } const indent = ' '.repeat(ctx.indentationLvl); @@ -1105,6 +1119,7 @@ module.exports = exports = { debuglog, deprecate, format, + formatWithOptions, getSystemErrorName, inherits, inspect, @@ -1128,6 +1143,7 @@ module.exports = exports = { promisify, TextDecoder, TextEncoder, + types, // Deprecated Old Stuff debug: deprecate(debug, diff --git a/lib/vm.js b/lib/vm.js index 4da0d7c6c3c528..d79357413b3512 100644 --- a/lib/vm.js +++ b/lib/vm.js @@ -209,4 +209,4 @@ module.exports = { }; if (process.binding('config').experimentalVMModules) - module.exports.Module = require('internal/vm/Module').Module; + module.exports.Module = require('internal/vm/module').Module; diff --git a/lib/zlib.js b/lib/zlib.js index fe725f6e06f15c..82474f210eadb0 100644 --- a/lib/zlib.js +++ b/lib/zlib.js @@ -23,9 +23,13 @@ const errors = require('internal/errors'); const Transform = require('_stream_transform'); -const { _extend } = require('util'); -const { isAnyArrayBuffer } = process.binding('util'); -const { isArrayBufferView } = require('internal/util/types'); +const { + _extend, + types: { + isAnyArrayBuffer, + isArrayBufferView + } +} = require('util'); const binding = process.binding('zlib'); const assert = require('assert').ok; const { @@ -206,8 +210,16 @@ function Zlib(opts, mode) { finishFlush = Z_FINISH; } + // windowBits is special. On the compression side, 0 is an invalid value. + // But on the decompression side, a value of 0 for windowBits tells zlib + // to use the window size in the zlib header of the compressed stream. windowBits = opts.windowBits; - if (windowBits !== undefined && !Number.isNaN(windowBits)) { + if ((windowBits == null || windowBits === 0) && + (mode === INFLATE || + mode === GUNZIP || + mode === UNZIP)) { + windowBits = 0; + } else if (windowBits !== undefined && !Number.isNaN(windowBits)) { if (windowBits < Z_MIN_WINDOWBITS || windowBits > Z_MAX_WINDOWBITS || !Number.isFinite(windowBits)) { throw new errors.RangeError('ERR_INVALID_OPT_VALUE', diff --git a/node.gyp b/node.gyp index b35aec31dbde82..cd2d086c4bbb91 100644 --- a/node.gyp +++ b/node.gyp @@ -82,6 +82,7 @@ 'lib/zlib.js', 'lib/internal/async_hooks.js', 'lib/internal/buffer.js', + 'lib/internal/cli_table.js', 'lib/internal/child_process.js', 'lib/internal/cluster/child.js', 'lib/internal/cluster/master.js', @@ -107,13 +108,12 @@ 'lib/internal/linkedlist.js', 'lib/internal/modules/cjs/helpers.js', 'lib/internal/modules/cjs/loader.js', - 'lib/internal/modules/esm/Loader.js', - 'lib/internal/modules/esm/CreateDynamicModule.js', - 'lib/internal/modules/esm/DefaultResolve.js', - 'lib/internal/modules/esm/ModuleJob.js', - 'lib/internal/modules/esm/ModuleMap.js', - 'lib/internal/modules/esm/ModuleWrap.js', - 'lib/internal/modules/esm/Translators.js', + 'lib/internal/modules/esm/loader.js', + 'lib/internal/modules/esm/create_dynamic_module.js', + 'lib/internal/modules/esm/default_resolve.js', + 'lib/internal/modules/esm/module_job.js', + 'lib/internal/modules/esm/module_map.js', + 'lib/internal/modules/esm/translators.js', 'lib/internal/safe_globals.js', 'lib/internal/net.js', 'lib/internal/os.js', @@ -128,6 +128,7 @@ 'lib/internal/readline.js', 'lib/internal/repl.js', 'lib/internal/socket_list.js', + 'lib/internal/test/binding.js', 'lib/internal/test/unicode.js', 'lib/internal/tls.js', 'lib/internal/trace_events_async_hooks.js', @@ -142,11 +143,14 @@ 'lib/internal/http2/util.js', 'lib/internal/v8_prof_polyfill.js', 'lib/internal/v8_prof_processor.js', - 'lib/internal/vm/Module.js', + 'lib/internal/stream_base_commons.js', + 'lib/internal/vm/module.js', 'lib/internal/streams/lazy_transform.js', - 'lib/internal/streams/BufferList.js', + 'lib/internal/streams/buffer_list.js', 'lib/internal/streams/legacy.js', 'lib/internal/streams/destroy.js', + 'lib/internal/streams/pipeline.js', + 'lib/internal/streams/end-of-stream.js', 'lib/internal/wrap_js_stream.js', 'deps/v8/tools/splaytree.js', 'deps/v8/tools/codemap.js', @@ -315,6 +319,7 @@ 'src/node_postmortem_metadata.cc', 'src/node_serdes.cc', 'src/node_trace_events.cc', + 'src/node_types.cc', 'src/node_url.cc', 'src/node_util.cc', 'src/node_v8.cc', @@ -327,6 +332,7 @@ 'src/signal_wrap.cc', 'src/spawn_sync.cc', 'src/string_bytes.cc', + 'src/string_decoder.cc', 'src/string_search.cc', 'src/stream_base.cc', 'src/stream_wrap.cc', @@ -380,6 +386,8 @@ 'src/req_wrap.h', 'src/req_wrap-inl.h', 'src/string_bytes.h', + 'src/string_decoder.h', + 'src/string_decoder-inl.h', 'src/stream_base.h', 'src/stream_base-inl.h', 'src/stream_wrap.h', diff --git a/src/async_wrap.cc b/src/async_wrap.cc index f85c8c7be6692f..dc181d5392d5cc 100644 --- a/src/async_wrap.cc +++ b/src/async_wrap.cc @@ -21,6 +21,7 @@ #include "async_wrap-inl.h" #include "env-inl.h" +#include "node_internals.h" #include "util-inl.h" #include "v8.h" diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc index da3083a9ce3673..49ef72c4a6ad10 100644 --- a/src/cares_wrap.cc +++ b/src/cares_wrap.cc @@ -24,6 +24,7 @@ #include "async_wrap-inl.h" #include "env-inl.h" #include "node.h" +#include "node_internals.h" #include "req_wrap-inl.h" #include "util-inl.h" #include "uv.h" diff --git a/src/connection_wrap.cc b/src/connection_wrap.cc index a6cf67ceee2477..e0052a643b8734 100644 --- a/src/connection_wrap.cc +++ b/src/connection_wrap.cc @@ -3,6 +3,7 @@ #include "connect_wrap.h" #include "env-inl.h" #include "pipe_wrap.h" +#include "node_internals.h" #include "stream_base-inl.h" #include "stream_wrap.h" #include "tcp_wrap.h" diff --git a/src/env-inl.h b/src/env-inl.h index 6d7520dab0acc8..526ddefd447ce8 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -305,6 +305,10 @@ inline Environment* Environment::GetCurrent( info.Data().template As()->Value()); } +inline Environment* Environment::GetThreadLocalEnv() { + return static_cast(uv_key_get(&thread_local_env)); +} + inline Environment::Environment(IsolateData* isolate_data, v8::Local context) : isolate_(context->GetIsolate()), diff --git a/src/env.cc b/src/env.cc index 960cc4d5f88a69..7d91c243aefcca 100644 --- a/src/env.cc +++ b/src/env.cc @@ -75,6 +75,11 @@ v8::CpuProfiler* IsolateData::GetCpuProfiler() { return cpu_profiler_; } + +void InitThreadLocalOnce() { + CHECK_EQ(0, uv_key_create(&Environment::thread_local_env)); +} + void Environment::Start(int argc, const char* const* argv, int exec_argc, @@ -142,6 +147,10 @@ void Environment::Start(int argc, SetupProcessObject(this, argc, argv, exec_argc, exec_argv); LoadAsyncWrapperInfo(this); + + static uv_once_t init_once = UV_ONCE_INIT; + uv_once(&init_once, InitThreadLocalOnce); + uv_key_set(&thread_local_env, this); } void Environment::CleanupHandles() { @@ -373,4 +382,6 @@ void Environment::AsyncHooks::grow_async_ids_stack() { async_ids_stack_.GetJSArray()).FromJust(); } +uv_key_t Environment::thread_local_env = {}; + } // namespace node diff --git a/src/env.h b/src/env.h index 8e0cf61a4a57ab..24d5948a699432 100644 --- a/src/env.h +++ b/src/env.h @@ -123,6 +123,7 @@ struct PackageConfig { V(bytes_string, "bytes") \ V(bytes_parsed_string, "bytesParsed") \ V(bytes_read_string, "bytesRead") \ + V(bytes_written_string, "bytesWritten") \ V(cached_data_string, "cachedData") \ V(cached_data_produced_string, "cachedDataProduced") \ V(cached_data_rejected_string, "cachedDataRejected") \ @@ -563,6 +564,9 @@ class Environment { static inline Environment* GetCurrent( const v8::PropertyCallbackInfo& info); + static uv_key_t thread_local_env; + static inline Environment* GetThreadLocalEnv(); + inline Environment(IsolateData* isolate_data, v8::Local context); inline ~Environment(); diff --git a/src/fs_event_wrap.cc b/src/fs_event_wrap.cc index e37c82516f194a..be5327eb955d2d 100644 --- a/src/fs_event_wrap.cc +++ b/src/fs_event_wrap.cc @@ -23,6 +23,7 @@ #include "env-inl.h" #include "util-inl.h" #include "node.h" +#include "node_internals.h" #include "handle_wrap.h" #include "string_bytes.h" diff --git a/src/js_stream.cc b/src/js_stream.cc index 3ba6a254cfc03e..ed6c6ee738e568 100644 --- a/src/js_stream.cc +++ b/src/js_stream.cc @@ -3,6 +3,7 @@ #include "async_wrap.h" #include "env-inl.h" #include "node_buffer.h" +#include "node_internals.h" #include "stream_base-inl.h" #include "v8.h" diff --git a/src/node.cc b/src/node.cc index 737f589eaad01c..e0854ee942f66b 100644 --- a/src/node.cc +++ b/src/node.cc @@ -3780,62 +3780,79 @@ static void PrintHelp() { " node inspect script.js [arguments]\n" "\n" "Options:\n" - " -v, --version print Node.js version\n" - " -e, --eval script evaluate script\n" - " -p, --print evaluate script and print result\n" - " -c, --check syntax check script without executing\n" - " -i, --interactive always enter the REPL even if stdin\n" - " does not appear to be a terminal\n" - " -r, --require module to preload (option can be " - "repeated)\n" - " - script read from stdin (default; " - "interactive mode if a tty)\n" + " - script read from stdin (default; \n" + " interactive mode if a tty)\n" + " -- indicate the end of node options\n" + " --abort-on-uncaught-exception\n" + " aborting instead of exiting causes a\n" + " core file to be generated for analysis\n" +#if HAVE_OPENSSL && NODE_FIPS_MODE + " --enable-fips enable FIPS crypto at startup\n" +#endif // NODE_FIPS_MODE && NODE_FIPS_MODE +#if defined(NODE_HAVE_I18N_SUPPORT) + " --experimental-modules experimental ES Module support\n" + " and caching modules\n" + " --experimental-vm-modules experimental ES Module support\n" + " in vm module\n" +#endif // defined(NODE_HAVE_I18N_SUPPORT) +#if HAVE_OPENSSL && NODE_FIPS_MODE + " --force-fips force FIPS crypto (cannot be disabled)\n" +#endif // HAVE_OPENSSL && NODE_FIPS_MODE +#if defined(NODE_HAVE_I18N_SUPPORT) + " --icu-data-dir=dir set ICU data load path to dir\n" + " (overrides NODE_ICU_DATA)\n" +#if !defined(NODE_HAVE_SMALL_ICU) + " note: linked-in ICU data is present\n" +#endif +#endif // defined(NODE_HAVE_I18N_SUPPORT) #if HAVE_INSPECTOR - " --inspect[=[host:]port] activate inspector on host:port\n" - " (default: 127.0.0.1:9229)\n" " --inspect-brk[=[host:]port]\n" " activate inspector on host:port\n" " and break at start of user script\n" " --inspect-port=[host:]port\n" " set host:port for inspector\n" -#endif - " --no-deprecation silence deprecation warnings\n" - " --trace-deprecation show stack traces on deprecations\n" - " --throw-deprecation throw an exception on deprecations\n" - " --pending-deprecation emit pending deprecation warnings\n" - " --no-warnings silence all process warnings\n" + " --inspect[=[host:]port] activate inspector on host:port\n" + " (default: 127.0.0.1:9229)\n" +#endif // HAVE_INSPECTOR " --napi-modules load N-API modules (no-op - option\n" " kept for compatibility)\n" - " --abort-on-uncaught-exception\n" - " aborting instead of exiting causes a\n" - " core file to be generated for analysis\n" - " --trace-warnings show stack traces on process warnings\n" + " --no-deprecation silence deprecation warnings\n" + " --no-force-async-hooks-checks\n" + " disable checks for async_hooks\n" + " --no-warnings silence all process warnings\n" +#if HAVE_OPENSSL + " --openssl-config=file load OpenSSL configuration from the\n" + " specified file (overrides\n" + " OPENSSL_CONF)\n" +#endif // HAVE_OPENSSL + " --pending-deprecation emit pending deprecation warnings\n" +#if defined(NODE_HAVE_I18N_SUPPORT) + " --preserve-symlinks preserve symbolic links when resolving\n" +#endif + " --prof-process process v8 profiler output generated\n" + " using --prof\n" " --redirect-warnings=file\n" " write warnings to file instead of\n" " stderr\n" - " --trace-sync-io show stack trace when use of sync IO\n" - " is detected after the first tick\n" - " --no-force-async-hooks-checks\n" - " disable checks for async_hooks\n" - " --trace-events-enabled track trace events\n" + " --throw-deprecation throw an exception on deprecations\n" +#if HAVE_OPENSSL + " --tls-cipher-list=val use an alternative default TLS cipher " + "list\n" +#endif // HAVE_OPENSSL + " --trace-deprecation show stack traces on deprecations\n" " --trace-event-categories comma separated list of trace event\n" " categories to record\n" " --trace-event-file-pattern Template string specifying the\n" " filepath for the trace-events data, it\n" " supports ${rotation} and ${pid}\n" " log-rotation id. %%2$u is the pid.\n" + " --trace-events-enabled track trace events\n" + " --trace-sync-io show stack trace when use of sync IO\n" + " is detected after the first tick\n" + " --trace-warnings show stack traces on process warnings\n" " --track-heap-objects track heap object allocations for heap " "snapshots\n" - " --prof-process process v8 profiler output generated\n" - " using --prof\n" - " --zero-fill-buffers automatically zero-fill all newly " - "allocated\n" - " Buffer and SlowBuffer instances\n" - " --v8-options print v8 command line options\n" - " --v8-pool-size=num set v8's thread pool size\n" #if HAVE_OPENSSL - " --tls-cipher-list=val use an alternative default TLS cipher " - "list\n" " --use-bundled-ca use bundled CA store" #if !defined(NODE_OPENSSL_CERT_STORE) " (default)" @@ -3845,27 +3862,23 @@ static void PrintHelp() { #if defined(NODE_OPENSSL_CERT_STORE) " (default)" #endif +#endif // HAVE_OPENSSL "\n" -#if NODE_FIPS_MODE - " --enable-fips enable FIPS crypto at startup\n" - " --force-fips force FIPS crypto (cannot be disabled)\n" -#endif /* NODE_FIPS_MODE */ - " --openssl-config=file load OpenSSL configuration from the\n" - " specified file (overrides\n" - " OPENSSL_CONF)\n" -#endif /* HAVE_OPENSSL */ -#if defined(NODE_HAVE_I18N_SUPPORT) - " --icu-data-dir=dir set ICU data load path to dir\n" - " (overrides NODE_ICU_DATA)\n" -#if !defined(NODE_HAVE_SMALL_ICU) - " note: linked-in ICU data is present\n" -#endif - " --preserve-symlinks preserve symbolic links when resolving\n" - " --experimental-modules experimental ES Module support\n" - " and caching modules\n" - " --experimental-vm-modules experimental ES Module support\n" - " in vm module\n" -#endif + " --v8-options print v8 command line options\n" + " --v8-pool-size=num set v8's thread pool size\n" + " --zero-fill-buffers automatically zero-fill all newly " + "allocated\n" + " Buffer and SlowBuffer instances\n" + " -c, --check syntax check script without executing\n" + " -e, --eval script evaluate script\n" + " -h, --help print node command line options\n" + " -i, --interactive always enter the REPL even if stdin\n" + " does not appear to be a terminal\n" + " -p, --print evaluate script and print result\n" + " -r, --require module to preload (option can be " + "repeated)\n" + " -v, --version print Node.js version\n" + "\n" "Environment variables:\n" "NODE_DEBUG ','-separated list of core modules\n" @@ -3878,12 +3891,12 @@ static void PrintHelp() { #if !defined(NODE_HAVE_SMALL_ICU) " (will extend linked-in data)\n" #endif -#endif +#endif // defined(NODE_HAVE_I18N_SUPPORT) "NODE_NO_WARNINGS set to 1 to silence process warnings\n" #if !defined(NODE_WITHOUT_NODE_OPTIONS) "NODE_OPTIONS set CLI options in the environment\n" " via a space-separated list\n" -#endif +#endif // !defined(NODE_WITHOUT_NODE_OPTIONS) #ifdef _WIN32 "NODE_PATH ';'-separated list of directories\n" #else @@ -3892,10 +3905,14 @@ static void PrintHelp() { " prefixed to the module search path\n" "NODE_PENDING_DEPRECATION set to 1 to emit pending deprecation\n" " warnings\n" - "NODE_REPL_HISTORY path to the persistent REPL history\n" - " file\n" +#if defined(NODE_HAVE_I18N_SUPPORT) + "NODE_PRESERVE_SYMLINKS set to 1 to preserve symbolic links\n" + " when resolving and caching modules\n" +#endif "NODE_REDIRECT_WARNINGS write warnings to path instead of\n" " stderr\n" + "NODE_REPL_HISTORY path to the persistent REPL history\n" + " file\n" "OPENSSL_CONF load OpenSSL configuration from file\n" "\n" "Documentation can be found at https://nodejs.org/\n"); @@ -3932,43 +3949,44 @@ static void CheckIfAllowedInEnv(const char* exe, bool is_env, static const char* whitelist[] = { // Node options, sorted in `node --help` order for ease of comparison. - "--require", "-r", + "--enable-fips", + "--experimental-modules", + "--experimental-vm-modules", + "--expose-http2", // keep as a non-op through v9.x + "--force-fips", + "--icu-data-dir", "--inspect", "--inspect-brk", "--inspect-port", + "--loader", + "--napi-modules", "--no-deprecation", - "--trace-deprecation", - "--throw-deprecation", - "--pending-deprecation", + "--no-force-async-hooks-checks", "--no-warnings", - "--napi-modules", - "--expose-http2", // keep as a non-op through v9.x - "--experimental-modules", - "--experimental-vm-modules", - "--loader", - "--trace-warnings", + "--openssl-config", + "--pending-deprecation", "--redirect-warnings", - "--trace-sync-io", - "--no-force-async-hooks-checks", - "--trace-events-enabled", + "--require", + "--throw-deprecation", + "--tls-cipher-list", + "--trace-deprecation", "--trace-event-categories", "--trace-event-file-pattern", + "--trace-events-enabled", + "--trace-sync-io", + "--trace-warnings", "--track-heap-objects", - "--zero-fill-buffers", - "--v8-pool-size", - "--tls-cipher-list", "--use-bundled-ca", "--use-openssl-ca", - "--enable-fips", - "--force-fips", - "--openssl-config", - "--icu-data-dir", + "--v8-pool-size", + "--zero-fill-buffers", + "-r", // V8 options (define with '_', which allows '-' or '_') - "--perf_prof", - "--perf_basic_prof", "--abort_on_uncaught_exception", "--max_old_space_size", + "--perf_basic_prof", + "--perf_prof", "--stack_trace_limit", }; @@ -4646,11 +4664,8 @@ uv_loop_t* GetCurrentEventLoop(v8::Isolate* isolate) { } -static uv_key_t thread_local_env; - - void AtExit(void (*cb)(void* arg), void* arg) { - auto env = static_cast(uv_key_get(&thread_local_env)); + auto env = Environment::GetThreadLocalEnv(); AtExit(env, cb, arg); } @@ -4781,8 +4796,6 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data, Local context = NewContext(isolate); Context::Scope context_scope(context); Environment env(isolate_data, context); - CHECK_EQ(0, uv_key_create(&thread_local_env)); - uv_key_set(&thread_local_env, &env); env.Start(argc, argv, exec_argc, exec_argv, v8_is_profiling); const char* path = argc > 1 ? argv[1] : nullptr; @@ -4832,7 +4845,6 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data, const int exit_code = EmitExit(&env); RunAtExit(&env); - uv_key_delete(&thread_local_env); v8_platform.DrainVMTasks(isolate); v8_platform.CancelVMTasks(isolate); diff --git a/src/node_counters.cc b/src/node_counters.cc index 06d2b9f68f103e..99b3b8fd8ba1ac 100644 --- a/src/node_counters.cc +++ b/src/node_counters.cc @@ -20,6 +20,7 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. #include "node_counters.h" +#include "node_internals.h" #include "uv.h" #include "env-inl.h" diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 9e3e7da63e180c..c11792351eeb7a 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -27,6 +27,7 @@ #include "node_crypto_groups.h" #include "node_crypto_clienthello-inl.h" #include "node_mutex.h" +#include "node_internals.h" #include "tls_wrap.h" // TLSWrap #include "async_wrap-inl.h" diff --git a/src/node_file.cc b/src/node_file.cc index 10655e54e54e90..159877decdd066 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -487,6 +487,12 @@ static void InternalModuleReadFile(const FunctionCallbackInfo& args) { return; } + std::shared_ptr defer_close(nullptr, [fd, loop] (...) { + uv_fs_t close_req; + CHECK_EQ(0, uv_fs_close(loop, &close_req, fd, nullptr)); + uv_fs_req_cleanup(&close_req); + }); + const size_t kBlockSize = 32 << 10; std::vector chars; int64_t offset = 0; @@ -503,14 +509,12 @@ static void InternalModuleReadFile(const FunctionCallbackInfo& args) { numchars = uv_fs_read(loop, &read_req, fd, &buf, 1, offset, nullptr); uv_fs_req_cleanup(&read_req); - CHECK_GE(numchars, 0); + if (numchars < 0) + return; + offset += numchars; } while (static_cast(numchars) == kBlockSize); - uv_fs_t close_req; - CHECK_EQ(0, uv_fs_close(loop, &close_req, fd, nullptr)); - uv_fs_req_cleanup(&close_req); - size_t start = 0; if (offset >= 3 && 0 == memcmp(&chars[0], "\xEF\xBB\xBF", 3)) { start = 3; // Skip UTF-8 BOM. diff --git a/src/node_http2.cc b/src/node_http2.cc index 67c5c67982a178..d0460fcd73665b 100644 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -3,6 +3,7 @@ #include "node_buffer.h" #include "node_http2.h" #include "node_http2_state.h" +#include "node_internals.h" #include "node_perf.h" #include @@ -2727,8 +2728,8 @@ void Http2Session::Http2Ping::Send(uint8_t* payload) { } void Http2Session::Http2Ping::Done(bool ack, const uint8_t* payload) { - session_->statistics_.ping_rtt = (uv_hrtime() - startTime_); - double duration = (session_->statistics_.ping_rtt - startTime_) / 1e6; + session_->statistics_.ping_rtt = uv_hrtime() - startTime_; + double duration = session_->statistics_.ping_rtt / 1e6; Local buf = Undefined(env()->isolate()); if (payload != nullptr) { diff --git a/src/node_http_parser.cc b/src/node_http_parser.cc index 19a4a6fba487c1..de1dfead98dd00 100644 --- a/src/node_http_parser.cc +++ b/src/node_http_parser.cc @@ -21,6 +21,7 @@ #include "node.h" #include "node_buffer.h" +#include "node_internals.h" #include "async_wrap-inl.h" #include "env-inl.h" diff --git a/src/node_internals.h b/src/node_internals.h index 27ab40a39f5998..3aefdc84397013 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -121,10 +121,12 @@ struct sockaddr; V(signal_wrap) \ V(spawn_sync) \ V(stream_wrap) \ + V(string_decoder) \ V(tcp_wrap) \ V(timer_wrap) \ V(trace_events) \ V(tty_wrap) \ + V(types) \ V(udp_wrap) \ V(url) \ V(util) \ diff --git a/src/node_stat_watcher.cc b/src/node_stat_watcher.cc index 9aa0c950591d16..e6fcbd2c5fda77 100644 --- a/src/node_stat_watcher.cc +++ b/src/node_stat_watcher.cc @@ -20,6 +20,7 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. #include "node_stat_watcher.h" +#include "node_internals.h" #include "async_wrap-inl.h" #include "env-inl.h" #include "util-inl.h" diff --git a/src/node_types.cc b/src/node_types.cc new file mode 100644 index 00000000000000..bd7ea9cf23fff7 --- /dev/null +++ b/src/node_types.cc @@ -0,0 +1,70 @@ +#include "node_internals.h" + +using v8::Context; +using v8::FunctionCallbackInfo; +using v8::Local; +using v8::Object; +using v8::Value; + +namespace node { +namespace { + +#define VALUE_METHOD_MAP(V) \ + V(External) \ + V(Date) \ + V(ArgumentsObject) \ + V(BooleanObject) \ + V(NumberObject) \ + V(StringObject) \ + V(SymbolObject) \ + V(NativeError) \ + V(RegExp) \ + V(AsyncFunction) \ + V(GeneratorFunction) \ + V(GeneratorObject) \ + V(Promise) \ + V(Map) \ + V(Set) \ + V(MapIterator) \ + V(SetIterator) \ + V(WeakMap) \ + V(WeakSet) \ + V(ArrayBuffer) \ + V(DataView) \ + V(SharedArrayBuffer) \ + V(Proxy) \ + V(WebAssemblyCompiledModule) \ + V(ModuleNamespaceObject) \ + + +#define V(type) \ + static void Is##type(const FunctionCallbackInfo& args) { \ + args.GetReturnValue().Set(args[0]->Is##type()); \ + } + + VALUE_METHOD_MAP(V) +#undef V + +static void IsAnyArrayBuffer(const FunctionCallbackInfo& args) { + args.GetReturnValue().Set( + args[0]->IsArrayBuffer() || args[0]->IsSharedArrayBuffer()); +} + +void InitializeTypes(Local target, + Local unused, + Local context) { + Environment* env = Environment::GetCurrent(context); + +#define V(type) env->SetMethod(target, \ + "is" #type, \ + Is##type); + VALUE_METHOD_MAP(V) +#undef V + + env->SetMethod(target, "isAnyArrayBuffer", IsAnyArrayBuffer); +} + +} // anonymous namespace +} // namespace node + +NODE_MODULE_CONTEXT_AWARE_INTERNAL(types, node::InitializeTypes) diff --git a/src/node_util.cc b/src/node_util.cc index 852902d7cc8132..b9979efac608c2 100644 --- a/src/node_util.cc +++ b/src/node_util.cc @@ -14,40 +14,9 @@ using v8::Object; using v8::Private; using v8::Promise; using v8::Proxy; +using v8::String; using v8::Value; - -#define VALUE_METHOD_MAP(V) \ - V(isArrayBuffer, IsArrayBuffer) \ - V(isArrayBufferView, IsArrayBufferView) \ - V(isAsyncFunction, IsAsyncFunction) \ - V(isDataView, IsDataView) \ - V(isDate, IsDate) \ - V(isExternal, IsExternal) \ - V(isMap, IsMap) \ - V(isMapIterator, IsMapIterator) \ - V(isNativeError, IsNativeError) \ - V(isPromise, IsPromise) \ - V(isRegExp, IsRegExp) \ - V(isSet, IsSet) \ - V(isSetIterator, IsSetIterator) \ - V(isTypedArray, IsTypedArray) \ - V(isUint8Array, IsUint8Array) - - -#define V(_, ucname) \ - static void ucname(const FunctionCallbackInfo& args) { \ - args.GetReturnValue().Set(args[0]->ucname()); \ - } - - VALUE_METHOD_MAP(V) -#undef V - -static void IsAnyArrayBuffer(const FunctionCallbackInfo& args) { - args.GetReturnValue().Set( - args[0]->IsArrayBuffer() || args[0]->IsSharedArrayBuffer()); -} - static void GetPromiseDetails(const FunctionCallbackInfo& args) { // Return undefined if it's not a Promise. if (!args[0]->IsPromise()) @@ -181,18 +150,22 @@ void PromiseReject(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(ret.FromMaybe(false)); } +void SafeGetenv(const FunctionCallbackInfo& args) { + CHECK(args[0]->IsString()); + Utf8Value strenvtag(args.GetIsolate(), args[0]); + std::string text; + if (!node::SafeGetenv(*strenvtag, &text)) return; + args.GetReturnValue() + .Set(String::NewFromUtf8( + args.GetIsolate(), text.c_str(), + v8::NewStringType::kNormal).ToLocalChecked()); +} void Initialize(Local target, Local unused, Local context) { Environment* env = Environment::GetCurrent(context); -#define V(lcname, ucname) env->SetMethod(target, #lcname, ucname); - VALUE_METHOD_MAP(V) -#undef V - - env->SetMethod(target, "isAnyArrayBuffer", IsAnyArrayBuffer); - #define V(name, _) \ target->Set(context, \ FIXED_ONE_BYTE_STRING(env->isolate(), #name), \ @@ -232,6 +205,8 @@ void Initialize(Local target, env->SetMethod(target, "createPromise", CreatePromise); env->SetMethod(target, "promiseResolve", PromiseResolve); env->SetMethod(target, "promiseReject", PromiseReject); + + env->SetMethod(target, "safeGetenv", SafeGetenv); } } // namespace util diff --git a/src/node_zlib.cc b/src/node_zlib.cc index 500a62a52bf6c4..67987baf8a375d 100644 --- a/src/node_zlib.cc +++ b/src/node_zlib.cc @@ -21,6 +21,7 @@ #include "node.h" #include "node_buffer.h" +#include "node_internals.h" #include "async_wrap-inl.h" #include "env-inl.h" @@ -438,9 +439,17 @@ class ZCtx : public AsyncWrap { ZCtx* ctx; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); + // windowBits is special. On the compression side, 0 is an invalid value. + // But on the decompression side, a value of 0 for windowBits tells zlib + // to use the window size in the zlib header of the compressed stream. int windowBits = args[0]->Uint32Value(); - CHECK((windowBits >= Z_MIN_WINDOWBITS && windowBits <= Z_MAX_WINDOWBITS) && - "invalid windowBits"); + if (!((windowBits == 0) && + (ctx->mode_ == INFLATE || + ctx->mode_ == GUNZIP || + ctx->mode_ == UNZIP))) { + CHECK((windowBits >= Z_MIN_WINDOWBITS && + windowBits <= Z_MAX_WINDOWBITS) && "invalid windowBits"); + } int level = args[1]->Int32Value(); CHECK((level >= Z_MIN_LEVEL && level <= Z_MAX_LEVEL) && diff --git a/src/pipe_wrap.cc b/src/pipe_wrap.cc index 14ac671b92b5bb..0116051b3b6485 100644 --- a/src/pipe_wrap.cc +++ b/src/pipe_wrap.cc @@ -27,6 +27,7 @@ #include "handle_wrap.h" #include "node.h" #include "node_buffer.h" +#include "node_internals.h" #include "node_wrap.h" #include "connect_wrap.h" #include "stream_base-inl.h" diff --git a/src/process_wrap.cc b/src/process_wrap.cc index 314131e1dd319f..96d60cc900583c 100644 --- a/src/process_wrap.cc +++ b/src/process_wrap.cc @@ -21,6 +21,7 @@ #include "env-inl.h" #include "handle_wrap.h" +#include "node_internals.h" #include "node_wrap.h" #include "stream_base-inl.h" #include "util-inl.h" diff --git a/src/stream_base-inl.h b/src/stream_base-inl.h index 1534dcd1d53359..00b98d28844863 100644 --- a/src/stream_base-inl.h +++ b/src/stream_base-inl.h @@ -188,10 +188,16 @@ inline StreamWriteResult StreamBase::Write( v8::Local req_wrap_obj) { Environment* env = stream_env(); int err; + + size_t total_bytes = 0; + for (size_t i = 0; i < count; ++i) + total_bytes += bufs[i].len; + bytes_written_ += total_bytes; + if (send_handle == nullptr) { err = DoTryWrite(&bufs, &count); if (err != 0 || count == 0) { - return StreamWriteResult { false, err, nullptr }; + return StreamWriteResult { false, err, nullptr, total_bytes }; } } @@ -218,7 +224,7 @@ inline StreamWriteResult StreamBase::Write( ClearError(); } - return StreamWriteResult { async, err, req_wrap }; + return StreamWriteResult { async, err, req_wrap, total_bytes }; } template @@ -293,6 +299,12 @@ void StreamBase::AddMethods(Environment* env, env->as_external(), signature); + Local get_bytes_written_templ = + FunctionTemplate::New(env->isolate(), + GetBytesWritten, + env->as_external(), + signature); + t->PrototypeTemplate()->SetAccessorProperty(env->fd_string(), get_fd_templ, Local(), @@ -308,6 +320,11 @@ void StreamBase::AddMethods(Environment* env, Local(), attributes); + t->PrototypeTemplate()->SetAccessorProperty(env->bytes_written_string(), + get_bytes_written_templ, + Local(), + attributes); + env->SetProtoMethod(t, "readStart", JSMethod); env->SetProtoMethod(t, "readStop", JSMethod); if ((flags & kFlagNoShutdown) == 0) @@ -349,7 +366,6 @@ void StreamBase::GetFD(const FunctionCallbackInfo& args) { template void StreamBase::GetBytesRead(const FunctionCallbackInfo& args) { - // The handle instance hasn't been set. So no bytes could have been read. Base* handle; ASSIGN_OR_RETURN_UNWRAP(&handle, args.This(), @@ -360,6 +376,18 @@ void StreamBase::GetBytesRead(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(static_cast(wrap->bytes_read_)); } +template +void StreamBase::GetBytesWritten(const FunctionCallbackInfo& args) { + Base* handle; + ASSIGN_OR_RETURN_UNWRAP(&handle, + args.This(), + args.GetReturnValue().Set(0)); + + StreamBase* wrap = static_cast(handle); + // uint64_t -> double. 53bits is enough for all real cases. + args.GetReturnValue().Set(static_cast(wrap->bytes_written_)); +} + template void StreamBase::GetExternal(const FunctionCallbackInfo& args) { Base* handle; diff --git a/src/stream_base.cc b/src/stream_base.cc index 1d1d324841537f..8fca1af3f11da9 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -3,6 +3,7 @@ #include "node.h" #include "node_buffer.h" +#include "node_internals.h" #include "env-inl.h" #include "js_stream.h" #include "string_bytes.h" @@ -60,12 +61,11 @@ int StreamBase::Shutdown(const FunctionCallbackInfo& args) { inline void SetWriteResultPropertiesOnWrapObject( Environment* env, Local req_wrap_obj, - const StreamWriteResult& res, - size_t bytes) { + const StreamWriteResult& res) { req_wrap_obj->Set( env->context(), env->bytes_string(), - Number::New(env->isolate(), bytes)).FromJust(); + Number::New(env->isolate(), res.bytes)).FromJust(); req_wrap_obj->Set( env->context(), env->async(), @@ -91,7 +91,6 @@ int StreamBase::Writev(const FunctionCallbackInfo& args) { MaybeStackBuffer bufs(count); size_t storage_size = 0; - uint32_t bytes = 0; size_t offset; if (!all_buffers) { @@ -123,7 +122,6 @@ int StreamBase::Writev(const FunctionCallbackInfo& args) { Local chunk = chunks->Get(i); bufs[i].base = Buffer::Data(chunk); bufs[i].len = Buffer::Length(chunk); - bytes += bufs[i].len; } } @@ -140,7 +138,6 @@ int StreamBase::Writev(const FunctionCallbackInfo& args) { if (Buffer::HasInstance(chunk)) { bufs[i].base = Buffer::Data(chunk); bufs[i].len = Buffer::Length(chunk); - bytes += bufs[i].len; continue; } @@ -160,12 +157,11 @@ int StreamBase::Writev(const FunctionCallbackInfo& args) { bufs[i].base = str_storage; bufs[i].len = str_size; offset += str_size; - bytes += str_size; } } StreamWriteResult res = Write(*bufs, count, nullptr, req_wrap_obj); - SetWriteResultPropertiesOnWrapObject(env, req_wrap_obj, res, bytes); + SetWriteResultPropertiesOnWrapObject(env, req_wrap_obj, res); if (res.wrap != nullptr && storage) { res.wrap->SetAllocatedStorage(storage.release(), storage_size); } @@ -193,7 +189,7 @@ int StreamBase::WriteBuffer(const FunctionCallbackInfo& args) { if (res.async) req_wrap_obj->Set(env->context(), env->buffer_string(), args[1]).FromJust(); - SetWriteResultPropertiesOnWrapObject(env, req_wrap_obj, res, buf.len); + SetWriteResultPropertiesOnWrapObject(env, req_wrap_obj, res); return res.err; } @@ -228,6 +224,7 @@ int StreamBase::WriteString(const FunctionCallbackInfo& args) { // Try writing immediately if write size isn't too big char stack_storage[16384]; // 16kb size_t data_size; + size_t synchronously_written = 0; uv_buf_t buf; bool try_write = storage_size <= sizeof(stack_storage) && @@ -243,6 +240,11 @@ int StreamBase::WriteString(const FunctionCallbackInfo& args) { uv_buf_t* bufs = &buf; size_t count = 1; err = DoTryWrite(&bufs, &count); + // Keep track of the bytes written here, because we're taking a shortcut + // by using `DoTryWrite()` directly instead of using the utilities + // provided by `Write()`. + synchronously_written = count == 0 ? data_size : data_size - buf.len; + bytes_written_ += synchronously_written; // Immediate failure or success if (err != 0 || count == 0) { @@ -298,8 +300,9 @@ int StreamBase::WriteString(const FunctionCallbackInfo& args) { } StreamWriteResult res = Write(&buf, 1, send_handle, req_wrap_obj); + res.bytes += synchronously_written; - SetWriteResultPropertiesOnWrapObject(env, req_wrap_obj, res, data_size); + SetWriteResultPropertiesOnWrapObject(env, req_wrap_obj, res); if (res.wrap != nullptr) { res.wrap->SetAllocatedStorage(data.release(), data_size); } diff --git a/src/stream_base.h b/src/stream_base.h index d8fc6f331e57c7..e758357f13be00 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -23,6 +23,7 @@ struct StreamWriteResult { bool async; int err; WriteWrap* wrap; + size_t bytes; }; @@ -220,6 +221,7 @@ class StreamResource { StreamListener* listener_ = nullptr; uint64_t bytes_read_ = 0; + uint64_t bytes_written_ = 0; friend class StreamListener; }; @@ -297,6 +299,9 @@ class StreamBase : public StreamResource { template static void GetBytesRead(const v8::FunctionCallbackInfo& args); + template + static void GetBytesWritten(const v8::FunctionCallbackInfo& args); + template & args)> diff --git a/src/string_decoder-inl.h b/src/string_decoder-inl.h new file mode 100644 index 00000000000000..8a04211906f759 --- /dev/null +++ b/src/string_decoder-inl.h @@ -0,0 +1,38 @@ +#ifndef SRC_STRING_DECODER_INL_H_ +#define SRC_STRING_DECODER_INL_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "string_decoder.h" +#include "util.h" + +namespace node { + +void StringDecoder::SetEncoding(enum encoding encoding) { + state_[kBufferedBytes] = 0; + state_[kMissingBytes] = 0; + state_[kEncodingField] = encoding; +} + +enum encoding StringDecoder::Encoding() const { + return static_cast(state_[kEncodingField]); +} + +unsigned StringDecoder::BufferedBytes() const { + return state_[kBufferedBytes]; +} + +unsigned StringDecoder::MissingBytes() const { + return state_[kMissingBytes]; +} + +char* StringDecoder::IncompleteCharacterBuffer() { + return reinterpret_cast(state_ + kIncompleteCharactersStart); +} + + +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_STRING_DECODER_INL_H_ diff --git a/src/string_decoder.cc b/src/string_decoder.cc new file mode 100644 index 00000000000000..ad1bace918c678 --- /dev/null +++ b/src/string_decoder.cc @@ -0,0 +1,334 @@ +#include "string_decoder-inl.h" +#include "string_bytes.h" +#include "node_internals.h" +#include "node_buffer.h" + +using v8::Array; +using v8::Context; +using v8::FunctionCallbackInfo; +using v8::Integer; +using v8::Isolate; +using v8::Local; +using v8::MaybeLocal; +using v8::Object; +using v8::String; +using v8::Value; + +namespace node { + +namespace { + +MaybeLocal MakeString(Isolate* isolate, + const char* data, + size_t length, + enum encoding encoding) { + Local error; + MaybeLocal ret; + if (encoding == UTF8) { + return String::NewFromUtf8( + isolate, + data, + v8::NewStringType::kNormal, + length); + } else if (encoding == UCS2) { +#ifdef DEBUG + CHECK_EQ(reinterpret_cast(data) % 2, 0); + CHECK_EQ(length % 2, 0); +#endif + ret = StringBytes::Encode( + isolate, + reinterpret_cast(data), + length / 2, + &error); + } else { + ret = StringBytes::Encode( + isolate, + data, + length, + encoding, + &error); + } + + if (ret.IsEmpty()) { + CHECK(!error.IsEmpty()); + isolate->ThrowException(error); + } + +#ifdef DEBUG + CHECK(ret.IsEmpty() || ret.ToLocalChecked()->IsString()); +#endif + return ret.FromMaybe(Local()).As(); +} + +} // anonymous namespace + + +MaybeLocal StringDecoder::DecodeData(Isolate* isolate, + const char* data, + size_t* nread_ptr) { + Local prepend, body; + + size_t nread = *nread_ptr; + + if (Encoding() == UTF8 || Encoding() == UCS2 || Encoding() == BASE64) { + // See if we want bytes to finish a character from the previous + // chunk; if so, copy the new bytes to the missing bytes buffer + // and create a small string from it that is to be prepended to the + // main body. + if (MissingBytes() > 0) { + // There are never more bytes missing than the pre-calculated maximum. + CHECK_LE(MissingBytes() + BufferedBytes(), + kIncompleteCharactersEnd); + if (Encoding() == UTF8) { + // For UTF-8, we need special treatment to align with the V8 decoder: + // If an incomplete character is found at a chunk boundary, we turn + // that character into a single invalid one. + for (size_t i = 0; i < nread && i < MissingBytes(); ++i) { + if ((data[i] & 0xC0) != 0x80) { + // This byte is not a continuation byte even though it should have + // been one. + // Act as if there was a 1-byte incomplete character, which does + // not make sense but works here because we know it's invalid. + state_[kMissingBytes] = 0; + state_[kBufferedBytes] = 1; + data += i; + nread -= i; + break; + } + } + } + + size_t found_bytes = + std::min(nread, static_cast(MissingBytes())); + memcpy(IncompleteCharacterBuffer() + BufferedBytes(), + data, + found_bytes); + // Adjust the two buffers. + data += found_bytes; + nread -= found_bytes; + + state_[kMissingBytes] -= found_bytes; + state_[kBufferedBytes] += found_bytes; + + if (LIKELY(MissingBytes() == 0)) { + // If no more bytes are missing, create a small string that we + // will later prepend. + if (!MakeString(isolate, + IncompleteCharacterBuffer(), + BufferedBytes(), + Encoding()).ToLocal(&prepend)) { + return MaybeLocal(); + } + + *nread_ptr += BufferedBytes(); + // No more buffered bytes. + state_[kBufferedBytes] = 0; + } + } + + // It could be that trying to finish the previous chunk already + // consumed all data that we received in this chunk. + if (UNLIKELY(nread == 0)) { + body = !prepend.IsEmpty() ? prepend : String::Empty(isolate); + prepend = Local(); + } else { +#ifdef DEBUG + // If not, that means is no character left to finish at this point. + CHECK_EQ(MissingBytes(), 0); + CHECK_EQ(BufferedBytes(), 0); +#endif + + // See whether there is a character that we may have to cut off and + // finish when receiving the next chunk. + if (Encoding() == UTF8 && data[nread - 1] & 0x80) { + // This is UTF-8 encoded data and we ended on a non-ASCII UTF-8 byte. + // This means we'll need to figure out where the character to which + // the byte belongs begins. + for (size_t i = nread - 1; ; --i) { +#ifdef DEBUG + CHECK_LT(i, nread); +#endif + state_[kBufferedBytes]++; + if ((data[i] & 0xC0) == 0x80) { + // This byte does not start a character (a "trailing" byte). + if (state_[kBufferedBytes] >= 4 || i == 0) { + // We either have more then 4 trailing bytes (which means + // the current character would not be inside the range for + // valid Unicode, and in particular cannot be represented + // through JavaScript's UTF-16-based approach to strings), or the + // current buffer does not contain the start of an UTF-8 character + // at all. Either way, this is invalid UTF8 and we can just + // let the engine's decoder handle it. + state_[kBufferedBytes] = 0; + break; + } + } else { + // Found the first byte of a UTF-8 character. By looking at the + // upper bits we can tell how long the character *should* be. + if ((data[i] & 0xE0) == 0xC0) { + state_[kMissingBytes] = 2; + } else if ((data[i] & 0xF0) == 0xE0) { + state_[kMissingBytes] = 3; + } else if ((data[i] & 0xF8) == 0xF0) { + state_[kMissingBytes] = 4; + } else { + // This lead byte would indicate a character outside of the + // representable range. + state_[kBufferedBytes] = 0; + break; + } + + if (BufferedBytes() >= MissingBytes()) { + // Received more or exactly as many trailing bytes than the lead + // character would indicate. In the "==" case, we have valid + // data and don't need to slice anything off; + // in the ">" case, this is invalid UTF-8 anyway. + state_[kMissingBytes] = 0; + state_[kBufferedBytes] = 0; + } + + state_[kMissingBytes] -= state_[kBufferedBytes]; + break; + } + } + } else if (Encoding() == UCS2) { + if ((nread % 2) == 1) { + // We got half a codepoint, and need the second byte of it. + state_[kBufferedBytes] = 1; + state_[kMissingBytes] = 1; + } else if ((data[nread - 1] & 0xFC) == 0xD8) { + // Half a split UTF-16 character. + state_[kBufferedBytes] = 2; + state_[kMissingBytes] = 2; + } + } else if (Encoding() == BASE64) { + state_[kBufferedBytes] = nread % 3; + if (state_[kBufferedBytes] > 0) + state_[kMissingBytes] = 3 - BufferedBytes(); + } + + if (BufferedBytes() > 0) { + // Copy the requested number of buffered bytes from the end of the + // input into the incomplete character buffer. + nread -= BufferedBytes(); + *nread_ptr -= BufferedBytes(); + memcpy(IncompleteCharacterBuffer(), data + nread, BufferedBytes()); + } + + if (nread > 0) { + if (!MakeString(isolate, data, nread, Encoding()).ToLocal(&body)) + return MaybeLocal(); + } else { + body = String::Empty(isolate); + } + } + + if (prepend.IsEmpty()) { + return body; + } else { + return String::Concat(prepend, body); + } + } else { + CHECK(Encoding() == ASCII || Encoding() == HEX || Encoding() == LATIN1); + return MakeString(isolate, data, nread, Encoding()); + } +} + +MaybeLocal StringDecoder::FlushData(Isolate* isolate) { + if (Encoding() == ASCII || Encoding() == HEX || Encoding() == LATIN1) { + CHECK_EQ(MissingBytes(), 0); + CHECK_EQ(BufferedBytes(), 0); + } + + if (Encoding() == UCS2 && BufferedBytes() % 2 == 1) { + // Ignore a single trailing byte, like the JS decoder does. + state_[kMissingBytes]--; + state_[kBufferedBytes]--; + } + + if (BufferedBytes() == 0) + return String::Empty(isolate); + + MaybeLocal ret = + MakeString(isolate, + IncompleteCharacterBuffer(), + BufferedBytes(), + Encoding()); + + state_[kMissingBytes] = 0; + state_[kBufferedBytes] = 0; + + return ret; +} + +namespace { + +void DecodeData(const FunctionCallbackInfo& args) { + StringDecoder* decoder = + reinterpret_cast(Buffer::Data(args[0])); + CHECK_NE(decoder, nullptr); + size_t nread = Buffer::Length(args[1]); + MaybeLocal ret = + decoder->DecodeData(args.GetIsolate(), Buffer::Data(args[1]), &nread); + if (!ret.IsEmpty()) + args.GetReturnValue().Set(ret.ToLocalChecked()); +} + +void FlushData(const FunctionCallbackInfo& args) { + StringDecoder* decoder = + reinterpret_cast(Buffer::Data(args[0])); + CHECK_NE(decoder, nullptr); + MaybeLocal ret = decoder->FlushData(args.GetIsolate()); + if (!ret.IsEmpty()) + args.GetReturnValue().Set(ret.ToLocalChecked()); +} + +void InitializeStringDecoder(Local target, + Local unused, + Local context) { + Environment* env = Environment::GetCurrent(context); + Isolate* isolate = env->isolate(); + +#define SET_DECODER_CONSTANT(name) \ + target->Set(context, \ + FIXED_ONE_BYTE_STRING(isolate, #name), \ + Integer::New(isolate, StringDecoder::name)).FromJust() + + SET_DECODER_CONSTANT(kIncompleteCharactersStart); + SET_DECODER_CONSTANT(kIncompleteCharactersEnd); + SET_DECODER_CONSTANT(kMissingBytes); + SET_DECODER_CONSTANT(kBufferedBytes); + SET_DECODER_CONSTANT(kEncodingField); + SET_DECODER_CONSTANT(kNumFields); + + Local encodings = Array::New(isolate); +#define ADD_TO_ENCODINGS_ARRAY(cname, jsname) \ + encodings->Set(context, \ + static_cast(cname), \ + FIXED_ONE_BYTE_STRING(isolate, jsname)).FromJust() + ADD_TO_ENCODINGS_ARRAY(ASCII, "ascii"); + ADD_TO_ENCODINGS_ARRAY(UTF8, "utf8"); + ADD_TO_ENCODINGS_ARRAY(BASE64, "base64"); + ADD_TO_ENCODINGS_ARRAY(UCS2, "utf16le"); + ADD_TO_ENCODINGS_ARRAY(HEX, "hex"); + ADD_TO_ENCODINGS_ARRAY(BUFFER, "buffer"); + ADD_TO_ENCODINGS_ARRAY(LATIN1, "latin1"); + + target->Set(context, + FIXED_ONE_BYTE_STRING(isolate, "encodings"), + encodings).FromJust(); + + target->Set(context, + FIXED_ONE_BYTE_STRING(isolate, "kSize"), + Integer::New(isolate, sizeof(StringDecoder))).FromJust(); + + env->SetMethod(target, "decode", DecodeData); + env->SetMethod(target, "flush", FlushData); +} + +} // anonymous namespace + +} // namespace node + +NODE_MODULE_CONTEXT_AWARE_INTERNAL(string_decoder, + node::InitializeStringDecoder) diff --git a/src/string_decoder.h b/src/string_decoder.h new file mode 100644 index 00000000000000..9059eeaa9d2eb7 --- /dev/null +++ b/src/string_decoder.h @@ -0,0 +1,50 @@ +#ifndef SRC_STRING_DECODER_H_ +#define SRC_STRING_DECODER_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "node.h" + +namespace node { + +class StringDecoder { + public: + StringDecoder() { state_[kEncodingField] = BUFFER; } + inline void SetEncoding(enum encoding encoding); + inline enum encoding Encoding() const; + + inline char* IncompleteCharacterBuffer(); + inline unsigned MissingBytes() const; + inline unsigned BufferedBytes() const; + + // Decode a string from the specified encoding. + // The value pointed to by `nread` will be modified to reflect that + // less data may have been read because it ended on an incomplete character + // and more data may have been read because a previously incomplete character + // was finished. + v8::MaybeLocal DecodeData(v8::Isolate* isolate, + const char* data, + size_t* nread); + // Flush an incomplete character. For character encodings like UTF8 this + // means printing replacement characters, buf for e.g. Base64 the returned + // string contains more data. + v8::MaybeLocal FlushData(v8::Isolate* isolate); + + enum Fields { + kIncompleteCharactersStart = 0, + kIncompleteCharactersEnd = 4, + kMissingBytes = 4, + kBufferedBytes = 5, + kEncodingField = 6, + kNumFields = 7 + }; + + private: + uint8_t state_[kNumFields] = {}; +}; + +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_STRING_DECODER_H_ diff --git a/src/tcp_wrap.cc b/src/tcp_wrap.cc index cd6ed3cef0a5bf..6158a1c4a424eb 100644 --- a/src/tcp_wrap.cc +++ b/src/tcp_wrap.cc @@ -25,6 +25,7 @@ #include "env-inl.h" #include "handle_wrap.h" #include "node_buffer.h" +#include "node_internals.h" #include "node_wrap.h" #include "connect_wrap.h" #include "stream_base-inl.h" diff --git a/src/udp_wrap.cc b/src/udp_wrap.cc index a7b56c067b2c2d..1e56364fcbf951 100644 --- a/src/udp_wrap.cc +++ b/src/udp_wrap.cc @@ -22,6 +22,7 @@ #include "udp_wrap.h" #include "env-inl.h" #include "node_buffer.h" +#include "node_internals.h" #include "handle_wrap.h" #include "req_wrap-inl.h" #include "util-inl.h" diff --git a/test/.eslintrc.yaml b/test/.eslintrc.yaml index dbcb8ea6e0aa87..a3fa2c2c8d0465 100644 --- a/test/.eslintrc.yaml +++ b/test/.eslintrc.yaml @@ -8,15 +8,15 @@ rules: symbol-description: off # Custom rules in tools/eslint-rules - prefer-assert-iferror: error - prefer-assert-methods: error - prefer-common-expectserror: error - prefer-common-mustnotcall: error - crypto-check: error - inspector-check: error - number-isnan: error + node-core/prefer-assert-iferror: error + node-core/prefer-assert-methods: error + node-core/prefer-common-expectserror: error + node-core/prefer-common-mustnotcall: error + node-core/crypto-check: error + node-core/inspector-check: error + node-core/number-isnan: error ## common module is mandatory in tests - required-modules: [error, common] + node-core/required-modules: [error, common] # Global scoped methods and vars globals: diff --git a/test/addons-napi/test_typedarray/test.js b/test/addons-napi/test_typedarray/test.js index 4a4e79ebe7bcdb..4c139d92200fe3 100644 --- a/test/addons-napi/test_typedarray/test.js +++ b/test/addons-napi/test_typedarray/test.js @@ -60,7 +60,7 @@ arrayTypes.forEach((currentType) => { const template = Reflect.construct(currentType, buffer); assert.throws(() => { test_typedarray.CreateTypedArray(template, buffer, 0, 136); - }, /Invalid typed array length/); + }, RangeError); }); const nonByteArrayTypes = [ Int16Array, Uint16Array, Int32Array, Uint32Array, @@ -71,5 +71,5 @@ nonByteArrayTypes.forEach((currentType) => { test_typedarray.CreateTypedArray(template, buffer, currentType.BYTES_PER_ELEMENT + 1, 1); console.log(`start of offset ${currentType}`); - }, /start offset of/); + }, RangeError); }); diff --git a/test/addons/dlopen-ping-pong/binding.cc b/test/addons/dlopen-ping-pong/binding.cc index b35db4e708dd61..a3b9af1b8099a3 100644 --- a/test/addons/dlopen-ping-pong/binding.cc +++ b/test/addons/dlopen-ping-pong/binding.cc @@ -23,7 +23,7 @@ typedef const char* (*ping)(void); static ping ping_func; void LoadLibrary(const FunctionCallbackInfo& args) { - const String::Utf8Value filename(args[0]); + const String::Utf8Value filename(args.GetIsolate(), args[0]); void* handle = dlopen(*filename, RTLD_LAZY); assert(handle != nullptr); ping_func = reinterpret_cast(dlsym(handle, "dlopen_ping")); diff --git a/test/addons/hello-world-esm/test.mjs b/test/addons/hello-world-esm/test.mjs index 6e481ab4f72120..d98de5bf87c771 100644 --- a/test/addons/hello-world-esm/test.mjs +++ b/test/addons/hello-world-esm/test.mjs @@ -1,4 +1,4 @@ -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ import assert from 'assert'; import binding from './build/binding.node'; diff --git a/test/async-hooks/test-shutdownwrap.js b/test/async-hooks/test-shutdownwrap.js index dfaac2a1c05a70..fea4a3a166a270 100644 --- a/test/async-hooks/test-shutdownwrap.js +++ b/test/async-hooks/test-shutdownwrap.js @@ -24,11 +24,13 @@ let endedConnection = false; function onconnection(c) { assert.strictEqual(hooks.activitiesOfTypes('SHUTDOWNWRAP').length, 0); c.end(); - endedConnection = true; - const as = hooks.activitiesOfTypes('SHUTDOWNWRAP'); - assert.strictEqual(as.length, 1); - checkInvocations(as[0], { init: 1 }, 'after ending client connection'); - this.close(onserverClosed); + process.nextTick(() => { + endedConnection = true; + const as = hooks.activitiesOfTypes('SHUTDOWNWRAP'); + assert.strictEqual(as.length, 1); + checkInvocations(as[0], { init: 1 }, 'after ending client connection'); + this.close(onserverClosed); + }); } function onconnected() { diff --git a/test/cctest/test_environment.cc b/test/cctest/test_environment.cc index 4575d3b65ae318..19641ef3ac5cd0 100644 --- a/test/cctest/test_environment.cc +++ b/test/cctest/test_environment.cc @@ -39,6 +39,16 @@ TEST_F(EnvironmentTest, AtExitWithEnvironment) { EXPECT_TRUE(called_cb_1); } +TEST_F(EnvironmentTest, AtExitWithoutEnvironment) { + const v8::HandleScope handle_scope(isolate_); + const Argv argv; + Env env {handle_scope, argv}; + + AtExit(at_exit_callback1); // No Environment is passed to AtExit. + RunAtExit(*env); + EXPECT_TRUE(called_cb_1); +} + TEST_F(EnvironmentTest, AtExitWithArgument) { const v8::HandleScope handle_scope(isolate_); const Argv argv; diff --git a/test/common/README.md b/test/common/README.md index c7fd15b4afa364..01064a7a8b73f1 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -392,7 +392,7 @@ require a particular action to be taken after a given number of completed tasks (for instance, shutting down an HTTP server after a specific number of requests). The Countdown will fail the test if the remainder did not reach 0. - + ```js const Countdown = require('../common/countdown'); @@ -526,7 +526,7 @@ Returns the result of The http2.js module provides a handful of utilities for creating mock HTTP/2 frames for testing of HTTP/2 endpoints - + ```js const http2 = require('../common/http2'); ``` @@ -536,7 +536,7 @@ const http2 = require('../common/http2'); The `http2.Frame` is a base class that creates a `Buffer` containing a serialized HTTP/2 frame header. - + ```js // length is a 24-bit unsigned integer // type is an 8-bit unsigned integer identifying the frame type @@ -555,7 +555,7 @@ The serialized `Buffer` may be retrieved using the `frame.data` property. The `http2.DataFrame` is a subclass of `http2.Frame` that serializes a `DATA` frame. - + ```js // id is the 32-bit stream identifier // payload is a Buffer containing the DATA payload @@ -572,7 +572,7 @@ socket.write(frame.data); The `http2.HeadersFrame` is a subclass of `http2.Frame` that serializes a `HEADERS` frame. - + ```js // id is the 32-bit stream identifier // payload is a Buffer containing the HEADERS payload (see either @@ -590,7 +590,7 @@ socket.write(frame.data); The `http2.SettingsFrame` is a subclass of `http2.Frame` that serializes an empty `SETTINGS` frame. - + ```js // ack is a boolean indicating whether or not to set the ACK flag. const frame = new http2.SettingsFrame(ack); @@ -603,7 +603,7 @@ socket.write(frame.data); Set to a `Buffer` instance that contains a minimal set of serialized HTTP/2 request headers to be used as the payload of a `http2.HeadersFrame`. - + ```js const frame = new http2.HeadersFrame(1, http2.kFakeRequestHeaders, 0, true); @@ -615,7 +615,7 @@ socket.write(frame.data); Set to a `Buffer` instance that contains a minimal set of serialized HTTP/2 response headers to be used as the payload a `http2.HeadersFrame`. - + ```js const frame = new http2.HeadersFrame(1, http2.kFakeResponseHeaders, 0, true); @@ -627,7 +627,7 @@ socket.write(frame.data); Set to a `Buffer` containing the preamble bytes an HTTP/2 client must send upon initial establishment of a connection. - + ```js socket.write(http2.kClientMagic); ``` @@ -679,6 +679,12 @@ The realpath of the testing temporary directory. Deletes and recreates the testing temporary directory. +### getTTYfd() + +Attempts to get a valid TTY file descriptor. Returns `-1` if it fails. + +The TTY file descriptor is assumed to be capable of being writable. + ## WPT Module The wpt.js module is a port of parts of diff --git a/test/common/benchmark.js b/test/common/benchmark.js index 6496da1cfb9fe5..1fd4476ba55a35 100644 --- a/test/common/benchmark.js +++ b/test/common/benchmark.js @@ -1,4 +1,4 @@ -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ 'use strict'; diff --git a/test/common/countdown.js b/test/common/countdown.js index 5fcb77c4ed66a0..67252657ec28b7 100644 --- a/test/common/countdown.js +++ b/test/common/countdown.js @@ -1,4 +1,5 @@ -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ + 'use strict'; const assert = require('assert'); diff --git a/test/common/dns.js b/test/common/dns.js index 69c67ac541cf98..07f84d7a3703c1 100644 --- a/test/common/dns.js +++ b/test/common/dns.js @@ -1,4 +1,4 @@ -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ 'use strict'; const assert = require('assert'); diff --git a/test/common/duplexpair.js b/test/common/duplexpair.js index ea5bd86a041b24..fb4faca5483b76 100644 --- a/test/common/duplexpair.js +++ b/test/common/duplexpair.js @@ -1,4 +1,4 @@ -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ 'use strict'; const { Duplex } = require('stream'); const assert = require('assert'); diff --git a/test/common/fixtures.js b/test/common/fixtures.js index b4b7c042e805a9..b45e5bc8091865 100644 --- a/test/common/fixtures.js +++ b/test/common/fixtures.js @@ -1,4 +1,4 @@ -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ 'use strict'; const path = require('path'); diff --git a/test/common/http2.js b/test/common/http2.js index 1d4c269fffd5b5..0f3378e9b80f63 100644 --- a/test/common/http2.js +++ b/test/common/http2.js @@ -1,4 +1,4 @@ -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ 'use strict'; // An HTTP/2 testing tool used to create mock frames for direct testing diff --git a/test/common/index.js b/test/common/index.js index b24d2158e7d089..ad3c5dd6a09f1a 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -19,7 +19,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -/* eslint-disable required-modules, crypto-check */ +/* eslint-disable node-core/required-modules, node-core/crypto-check */ 'use strict'; const process = global.process; // Some tests tamper with the process global. const path = require('path'); @@ -681,7 +681,9 @@ exports.expectsError = function expectsError(fn, settings, exact) { fn = undefined; } function innerFn(error) { - assert.strictEqual(error.code, settings.code); + const descriptor = Object.getOwnPropertyDescriptor(error, 'message'); + assert.strictEqual(descriptor.enumerable, + false, 'The error message should be non-enumerable'); if ('type' in settings) { const type = settings.type; if (type !== Error && !Error.isPrototypeOf(type)) { @@ -704,18 +706,16 @@ exports.expectsError = function expectsError(fn, settings, exact) { `${error.message} does not match ${message}`); } } - if ('name' in settings) { - assert.strictEqual(error.name, settings.name); - } - if (error.constructor.name === 'AssertionError') { - ['generatedMessage', 'actual', 'expected', 'operator'].forEach((key) => { - if (key in settings) { - const actual = error[key]; - const expected = settings[key]; - assert.strictEqual(actual, expected, - `${key}: expected ${expected}, not ${actual}`); - } - }); + + // Check all error properties. + const keys = Object.keys(settings); + for (const key of keys) { + if (key === 'message' || key === 'type') + continue; + const actual = error[key]; + const expected = settings[key]; + assert.strictEqual(actual, expected, + `${key}: expected ${expected}, not ${actual}`); } return true; } @@ -775,6 +775,23 @@ exports.crashOnUnhandledRejection = function() { (err) => process.nextTick(() => { throw err; })); }; +exports.getTTYfd = function getTTYfd() { + // Do our best to grab a tty fd. + const tty = require('tty'); + // Don't attempt fd 0 as it is not writable on Windows. + // Ref: ef2861961c3d9e9ed6972e1e84d969683b25cf95 + const ttyFd = [1, 2, 4, 5].find(tty.isatty); + if (ttyFd === undefined) { + try { + return fs.openSync('/dev/tty'); + } catch (e) { + // There aren't any tty fd's available to use. + return -1; + } + } + return ttyFd; +}; + // Hijack stdout and stderr const stdWrite = {}; function hijackStdWritable(name, listener) { diff --git a/test/common/index.mjs b/test/common/index.mjs index 00eceb79d4c003..746834dadd1386 100644 --- a/test/common/index.mjs +++ b/test/common/index.mjs @@ -1,5 +1,5 @@ // Flags: --experimental-modules -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ import assert from 'assert'; @@ -27,7 +27,7 @@ export function allowGlobals(...whitelist) { } export function leakedGlobals() { - //add possible expected globals + // Add possible expected globals if (global.gc) { knownGlobals.push(global.gc); } diff --git a/test/common/internet.js b/test/common/internet.js index 48b532ca8e6606..3880aa114e3743 100644 --- a/test/common/internet.js +++ b/test/common/internet.js @@ -1,4 +1,4 @@ -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ 'use strict'; // Utilities for internet-related tests diff --git a/test/common/shared-lib-util.js b/test/common/shared-lib-util.js index 89b1231e5cc66c..e9659091567fa8 100644 --- a/test/common/shared-lib-util.js +++ b/test/common/shared-lib-util.js @@ -1,4 +1,4 @@ -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ 'use strict'; const common = require('../common'); const path = require('path'); diff --git a/test/common/tmpdir.js b/test/common/tmpdir.js index ed731b3e7a1ffb..d0221abbb2f069 100644 --- a/test/common/tmpdir.js +++ b/test/common/tmpdir.js @@ -1,4 +1,4 @@ -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ 'use strict'; const fs = require('fs'); diff --git a/test/common/wpt.js b/test/common/wpt.js index 52d8b7a580400c..7cd644dc88c097 100644 --- a/test/common/wpt.js +++ b/test/common/wpt.js @@ -1,4 +1,4 @@ -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ 'use strict'; const assert = require('assert'); diff --git a/test/es-module/esm-snapshot-mutator.js b/test/es-module/esm-snapshot-mutator.js index a0dfa0c28a92bd..6c9a707306fe14 100644 --- a/test/es-module/esm-snapshot-mutator.js +++ b/test/es-module/esm-snapshot-mutator.js @@ -1,4 +1,4 @@ -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ 'use strict'; const shouldSnapshotFilePath = require.resolve('./esm-snapshot.js'); require('./esm-snapshot.js'); diff --git a/test/es-module/esm-snapshot.js b/test/es-module/esm-snapshot.js index 2c3c3a459a738b..f52c6f3adff769 100644 --- a/test/es-module/esm-snapshot.js +++ b/test/es-module/esm-snapshot.js @@ -1,3 +1,3 @@ -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ 'use strict'; module.exports = 1; diff --git a/test/es-module/test-esm-example-loader.js b/test/es-module/test-esm-example-loader.js index 9d1348292cd5a5..f7f0fd059a4d76 100644 --- a/test/es-module/test-esm-example-loader.js +++ b/test/es-module/test-esm-example-loader.js @@ -1,5 +1,5 @@ // Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/example-loader.mjs -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ import assert from 'assert'; import ok from './test-esm-ok.mjs'; diff --git a/test/es-module/test-esm-forbidden-globals.mjs b/test/es-module/test-esm-forbidden-globals.mjs index d3e92b9238adba..d92df1611f043e 100644 --- a/test/es-module/test-esm-forbidden-globals.mjs +++ b/test/es-module/test-esm-forbidden-globals.mjs @@ -1,5 +1,5 @@ // Flags: --experimental-modules -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ if (typeof arguments !== 'undefined') { throw new Error('not an ESM'); diff --git a/test/es-module/test-esm-json.mjs b/test/es-module/test-esm-json.mjs index 39279b74e5407c..5b432e03958600 100644 --- a/test/es-module/test-esm-json.mjs +++ b/test/es-module/test-esm-json.mjs @@ -1,5 +1,5 @@ // Flags: --experimental-modules -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ import '../common/index'; import assert from 'assert'; import ok from './test-esm-ok.mjs'; diff --git a/test/es-module/test-esm-loader-dependency.mjs b/test/es-module/test-esm-loader-dependency.mjs index 5d05118dbf2879..260bf613a75ed8 100644 --- a/test/es-module/test-esm-loader-dependency.mjs +++ b/test/es-module/test-esm-loader-dependency.mjs @@ -1,5 +1,5 @@ // Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/loader-with-dep.mjs -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ import './test-esm-ok.mjs'; // We just test that this module doesn't fail loading diff --git a/test/es-module/test-esm-loader-modulemap.js b/test/es-module/test-esm-loader-modulemap.js index e9faa6d9f122f4..ed21afaf984d16 100644 --- a/test/es-module/test-esm-loader-modulemap.js +++ b/test/es-module/test-esm-loader-modulemap.js @@ -7,10 +7,11 @@ const common = require('../common'); const { URL } = require('url'); -const Loader = require('internal/modules/esm/Loader'); -const ModuleMap = require('internal/modules/esm/ModuleMap'); -const ModuleJob = require('internal/modules/esm/ModuleJob'); -const createDynamicModule = require('internal/modules/esm/CreateDynamicModule'); +const Loader = require('internal/modules/esm/loader'); +const ModuleMap = require('internal/modules/esm/module_map'); +const ModuleJob = require('internal/modules/esm/module_job'); +const createDynamicModule = require( + 'internal/modules/esm/create_dynamic_module'); const stubModuleUrl = new URL('file://tmp/test'); const stubModule = createDynamicModule(['default'], stubModuleUrl); diff --git a/test/es-module/test-esm-loader-search.js b/test/es-module/test-esm-loader-search.js index 5a1f5a562a0d3e..3f8044a5785d5c 100644 --- a/test/es-module/test-esm-loader-search.js +++ b/test/es-module/test-esm-loader-search.js @@ -5,7 +5,7 @@ const common = require('../common'); -const { search } = require('internal/modules/esm/DefaultResolve'); +const { search } = require('internal/modules/esm/default_resolve'); const errors = require('internal/errors'); common.expectsError( diff --git a/test/es-module/test-esm-main-lookup.mjs b/test/es-module/test-esm-main-lookup.mjs index 7c81cb647cff38..12f12c845caedf 100644 --- a/test/es-module/test-esm-main-lookup.mjs +++ b/test/es-module/test-esm-main-lookup.mjs @@ -1,5 +1,5 @@ // Flags: --experimental-modules -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ import assert from 'assert'; import main from '../fixtures/es-modules/pjson-main'; diff --git a/test/es-module/test-esm-named-exports.mjs b/test/es-module/test-esm-named-exports.mjs index c70e16e2167722..9698ae3d9c0907 100644 --- a/test/es-module/test-esm-named-exports.mjs +++ b/test/es-module/test-esm-named-exports.mjs @@ -1,5 +1,5 @@ // Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/builtin-named-exports-loader.mjs -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ import '../common/index'; import { readFile } from 'fs'; import assert from 'assert'; diff --git a/test/es-module/test-esm-namespace.mjs b/test/es-module/test-esm-namespace.mjs index f6e550c2e5245e..6a5ee28a54520f 100644 --- a/test/es-module/test-esm-namespace.mjs +++ b/test/es-module/test-esm-namespace.mjs @@ -1,5 +1,5 @@ // Flags: --experimental-modules -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ import '../common/index'; import * as fs from 'fs'; diff --git a/test/es-module/test-esm-ok.mjs b/test/es-module/test-esm-ok.mjs index 6712e1ab7dfca1..49de5c47ec23e7 100644 --- a/test/es-module/test-esm-ok.mjs +++ b/test/es-module/test-esm-ok.mjs @@ -1,5 +1,5 @@ // Flags: --experimental-modules -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ const isJs = true; export default isJs; diff --git a/test/es-module/test-esm-preserve-symlinks-not-found-plain.mjs b/test/es-module/test-esm-preserve-symlinks-not-found-plain.mjs index bfeb71ef3a607a..2ca0f5658119e9 100644 --- a/test/es-module/test-esm-preserve-symlinks-not-found-plain.mjs +++ b/test/es-module/test-esm-preserve-symlinks-not-found-plain.mjs @@ -1,3 +1,3 @@ // Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/not-found-assert-loader.mjs -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ import './not-found.js'; diff --git a/test/es-module/test-esm-preserve-symlinks-not-found.mjs b/test/es-module/test-esm-preserve-symlinks-not-found.mjs index 22c888028e7ba3..5119957bae7c6a 100644 --- a/test/es-module/test-esm-preserve-symlinks-not-found.mjs +++ b/test/es-module/test-esm-preserve-symlinks-not-found.mjs @@ -1,3 +1,3 @@ // Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/not-found-assert-loader.mjs -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ import './not-found'; diff --git a/test/es-module/test-esm-resolve-hook.mjs b/test/es-module/test-esm-resolve-hook.mjs index dd7ac80bec4331..e2d20a42d425dc 100644 --- a/test/es-module/test-esm-resolve-hook.mjs +++ b/test/es-module/test-esm-resolve-hook.mjs @@ -1,5 +1,5 @@ // Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/js-loader.mjs -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ import { namedExport } from '../fixtures/es-module-loaders/js-as-esm.js'; import assert from 'assert'; import ok from './test-esm-ok.mjs'; diff --git a/test/es-module/test-esm-shared-loader-dep.mjs b/test/es-module/test-esm-shared-loader-dep.mjs index 970bfd7121a721..03668cbc993a2f 100644 --- a/test/es-module/test-esm-shared-loader-dep.mjs +++ b/test/es-module/test-esm-shared-loader-dep.mjs @@ -1,5 +1,5 @@ // Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/loader-shared-dep.mjs -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ import assert from 'assert'; import './test-esm-ok.mjs'; import dep from '../fixtures/es-module-loaders/loader-dep.js'; diff --git a/test/es-module/test-esm-shebang.mjs b/test/es-module/test-esm-shebang.mjs index 43cc0f8367d8a2..96cec8ea98e921 100644 --- a/test/es-module/test-esm-shebang.mjs +++ b/test/es-module/test-esm-shebang.mjs @@ -1,6 +1,6 @@ #! }]) // isn't js // Flags: --experimental-modules -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ const isJs = true; export default isJs; diff --git a/test/es-module/test-esm-snapshot.mjs b/test/es-module/test-esm-snapshot.mjs index 878be79a34044c..4d3cf245a3ce9d 100644 --- a/test/es-module/test-esm-snapshot.mjs +++ b/test/es-module/test-esm-snapshot.mjs @@ -1,5 +1,5 @@ // Flags: --experimental-modules -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ import '../common/index'; import './esm-snapshot-mutator'; import one from './esm-snapshot'; diff --git a/test/es-module/test-esm-throw-undefined.mjs b/test/es-module/test-esm-throw-undefined.mjs new file mode 100644 index 00000000000000..877728178762ba --- /dev/null +++ b/test/es-module/test-esm-throw-undefined.mjs @@ -0,0 +1,16 @@ +// Flags: --experimental-modules +/* eslint-disable node-core/required-modules */ +import common from '../common/index.js'; +import assert from 'assert'; + +async function doTest() { + await assert.rejects( + async () => { + await import('../fixtures/es-module-loaders/throw-undefined'); + }, + (e) => e === undefined + ); +} + +common.crashOnUnhandledRejection(); +doTest(); diff --git a/test/fixtures/es-module-loaders/throw-undefined.mjs b/test/fixtures/es-module-loaders/throw-undefined.mjs new file mode 100644 index 00000000000000..f062276767408d --- /dev/null +++ b/test/fixtures/es-module-loaders/throw-undefined.mjs @@ -0,0 +1,3 @@ +'use strict'; + +throw undefined; diff --git a/test/fixtures/keys/Makefile b/test/fixtures/keys/Makefile index 27fda1eef27c87..8df48eef9b1d5c 100644 --- a/test/fixtures/keys/Makefile +++ b/test/fixtures/keys/Makefile @@ -1,4 +1,4 @@ -all: agent1-cert.pem agent2-cert.pem agent3-cert.pem agent4-cert.pem agent5-cert.pem ca2-crl.pem ec-cert.pem dh512.pem dh1024.pem dh2048.pem dsa1025.pem dsa_private_1025.pem dsa_public_1025.pem rsa_private_1024.pem rsa_private_2048.pem rsa_private_4096.pem rsa_public_1024.pem rsa_public_2048.pem rsa_public_4096.pem +all: agent1-cert.pem agent1-pfx.pem agent2-cert.pem agent3-cert.pem agent4-cert.pem agent5-cert.pem agent6-cert.pem agent7-cert.pem agent8-cert.pem agent9-cert.pem ca1-cert.pem ca2-crl.pem ca3-cert.pem ec-cert.pem dh512.pem dh1024.pem dh2048.pem dsa1025.pem dsa_private_1025.pem dsa_public_1025.pem rsa_private_1024.pem rsa_private_2048.pem rsa_private_4096.pem rsa_public_1024.pem rsa_public_2048.pem rsa_public_4096.pem # @@ -280,7 +280,7 @@ agent8-csr.pem: agent8.cnf agent8-key.pem openssl req -new -config agent8.cnf -key agent8-key.pem \ -out agent8-csr.pem -agent8-cert.pem: agent8-csr.pem +agent8-cert.pem: agent8-csr.pem fake-startcom-root-cert.pem fake-startcom-root-key.pem openssl ca \ -config fake-startcom-root.cnf \ -keyfile fake-startcom-root-key.pem \ @@ -289,7 +289,7 @@ agent8-cert.pem: agent8-csr.pem -days 9999 \ -passin "pass:password" \ -in agent8-csr.pem \ - -startdate 20161020235959Z \ + -startdate 161020235959Z \ -notext -out agent8-cert.pem @@ -319,7 +319,7 @@ agent9-cert.pem: agent9-csr.pem -days 9999 \ -passin "pass:password" \ -in agent9-csr.pem \ - -startdate 20161021000001Z \ + -startdate 161021000001Z \ -notext -out agent9-cert.pem ec-key.pem: @@ -380,7 +380,8 @@ rsa_public_4096.pem: rsa_private_4096.pem openssl rsa -in rsa_private_4096.pem -out rsa_public_4096.pem clean: - rm -f *.pem *.srl ca2-database.txt ca2-serial + rm -f *.pem *.srl ca2-database.txt ca2-serial fake-startcom-root-serial + @> fake-startcom-root-database.txt test: agent1-verify agent2-verify agent3-verify agent4-verify agent5-verify 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/internet/test-dgram-broadcast-multi-process.js b/test/internet/test-dgram-broadcast-multi-process.js index 40868d219acc4e..58e13c65e1baf1 100644 --- a/test/internet/test-dgram-broadcast-multi-process.js +++ b/test/internet/test-dgram-broadcast-multi-process.js @@ -64,7 +64,7 @@ if (process.argv[2] !== 'child') { let done = 0; let timer = null; - //exit the test if it doesn't succeed within TIMEOUT + // Exit the test if it doesn't succeed within TIMEOUT timer = setTimeout(function() { console.error('[PARENT] Responses were not received within %d ms.', TIMEOUT); @@ -75,7 +75,7 @@ if (process.argv[2] !== 'child') { process.exit(1); }, TIMEOUT); - //launch child processes + // Launch child processes for (let x = 0; x < listeners; x++) { (function() { const worker = fork(process.argv[1], ['child']); @@ -83,7 +83,7 @@ if (process.argv[2] !== 'child') { worker.messagesReceived = []; - //handle the death of workers + // Handle the death of workers worker.on('exit', function(code, signal) { // don't consider this the true death if the worker // has finished successfully @@ -113,7 +113,7 @@ if (process.argv[2] !== 'child') { listening += 1; if (listening === listeners) { - //all child process are listening, so start sending + // All child process are listening, so start sending sendSocket.sendNext(); } } else if (msg.message) { @@ -239,9 +239,9 @@ if (process.argv[2] === 'child') { }); listenSocket.on('close', function() { - //HACK: Wait to exit the process to ensure that the parent - //process has had time to receive all messages via process.send() - //This may be indicative of some other issue. + // HACK: Wait to exit the process to ensure that the parent + // process has had time to receive all messages via process.send() + // This may be indicative of some other issue. setTimeout(function() { process.exit(); }, 1000); diff --git a/test/message/esm_display_syntax_error.out b/test/message/esm_display_syntax_error.out index 73c9495824038e..b82f8fc33428fa 100644 --- a/test/message/esm_display_syntax_error.out +++ b/test/message/esm_display_syntax_error.out @@ -3,5 +3,5 @@ file:///*/test/message/esm_display_syntax_error.mjs:3 await async () => 0; ^^^^^ SyntaxError: Unexpected reserved word - at translators.set (internal/modules/esm/Translators.js:*:*) + at translators.set (internal/modules/esm/translators.js:*:*) at diff --git a/test/message/esm_display_syntax_error_import.out b/test/message/esm_display_syntax_error_import.out index e257f0eb7a94e5..fc140f58d5b1c1 100644 --- a/test/message/esm_display_syntax_error_import.out +++ b/test/message/esm_display_syntax_error_import.out @@ -3,5 +3,5 @@ file:///*/test/message/esm_display_syntax_error_import.mjs:6 notfound ^^^^^^^^ SyntaxError: The requested module does not provide an export named 'notfound' - at ModuleJob._instantiate (internal/modules/esm/ModuleJob.js:*:*) + at ModuleJob._instantiate (internal/modules/esm/module_job.js:*:*) at diff --git a/test/message/esm_display_syntax_error_import_module.out b/test/message/esm_display_syntax_error_import_module.out index c6286a2a987282..40d66e7b1b546d 100644 --- a/test/message/esm_display_syntax_error_import_module.out +++ b/test/message/esm_display_syntax_error_import_module.out @@ -3,5 +3,5 @@ file:///*/test/fixtures/es-module-loaders/syntax-error-import.mjs:1 import { foo, notfound } from './module-named-exports'; ^^^^^^^^ SyntaxError: The requested module does not provide an export named 'notfound' - at ModuleJob._instantiate (internal/modules/esm/ModuleJob.js:*:*) + at ModuleJob._instantiate (internal/modules/esm/module_job.js:*:*) at diff --git a/test/message/esm_display_syntax_error_module.out b/test/message/esm_display_syntax_error_module.out index 122e352102ad0f..0ab802f8ca95f0 100644 --- a/test/message/esm_display_syntax_error_module.out +++ b/test/message/esm_display_syntax_error_module.out @@ -3,5 +3,5 @@ file:///*/test/fixtures/es-module-loaders/syntax-error.mjs:2 await async () => 0; ^^^^^ SyntaxError: Unexpected reserved word - at translators.set (internal/modules/esm/Translators.js:*:*) + at translators.set (internal/modules/esm/translators.js:*:*) at diff --git a/test/parallel/test-accessor-properties.js b/test/parallel/test-accessor-properties.js index 13535ceda9667f..350e8125f8442c 100644 --- a/test/parallel/test-accessor-properties.js +++ b/test/parallel/test-accessor-properties.js @@ -51,7 +51,7 @@ const UDP = process.binding('udp_wrap').UDP; 'object' ); - if (common.hasCrypto) { // eslint-disable-line crypto-check + if (common.hasCrypto) { // eslint-disable-line node-core/crypto-check // There are accessor properties in crypto too const crypto = process.binding('crypto'); diff --git a/test/parallel/test-assert-async.js b/test/parallel/test-assert-async.js new file mode 100644 index 00000000000000..00a4fe7c722d7a --- /dev/null +++ b/test/parallel/test-assert-async.js @@ -0,0 +1,116 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +// Test assert.rejects() and assert.doesNotReject() by checking their +// expected output and by verifying that they do not work sync + +common.crashOnUnhandledRejection(); + +// Run all tests in parallel and check their outcome at the end. +const promises = []; + +// Check `assert.rejects`. +{ + const rejectingFn = async () => assert.fail(); + const errObj = { + code: 'ERR_ASSERTION', + name: 'AssertionError [ERR_ASSERTION]', + message: 'Failed' + }; + // `assert.rejects` accepts a function or a promise as first argument. + promises.push(assert.rejects(rejectingFn, errObj)); + promises.push(assert.rejects(rejectingFn(), errObj)); +} + +{ + const handler = (err) => { + assert(err instanceof assert.AssertionError, + `${err.name} is not instance of AssertionError`); + assert.strictEqual(err.code, 'ERR_ASSERTION'); + assert.strictEqual(err.message, + 'Missing expected rejection (handler).'); + assert.strictEqual(err.operator, 'rejects'); + assert.ok(!err.stack.includes('at Function.rejects')); + return true; + }; + + let promise = assert.rejects(async () => {}, handler); + promises.push(assert.rejects(promise, handler)); + + promise = assert.rejects(() => {}, handler); + promises.push(assert.rejects(promise, handler)); + + promise = assert.rejects(Promise.resolve(), handler); + promises.push(assert.rejects(promise, handler)); +} + +{ + const THROWN_ERROR = new Error(); + + promises.push(assert.rejects(() => { + throw THROWN_ERROR; + }, {}).catch(common.mustCall((err) => { + assert.strictEqual(err, THROWN_ERROR); + }))); +} + +promises.push(assert.rejects( + assert.rejects('fail', {}), + { + code: 'ERR_INVALID_ARG_TYPE', + message: 'The "block" argument must be one of type ' + + 'Function or Promise. Received type string' + } +)); + +// Check `assert.doesNotReject`. +{ + // `assert.doesNotReject` accepts a function or a promise as first argument. + promises.push(assert.doesNotReject(() => {})); + promises.push(assert.doesNotReject(async () => {})); + promises.push(assert.doesNotReject(Promise.resolve())); +} + +{ + const handler1 = (err) => { + assert(err instanceof assert.AssertionError, + `${err.name} is not instance of AssertionError`); + assert.strictEqual(err.code, 'ERR_ASSERTION'); + assert.strictEqual(err.message, 'Failed'); + return true; + }; + const handler2 = (err) => { + assert(err instanceof assert.AssertionError, + `${err.name} is not instance of AssertionError`); + assert.strictEqual(err.code, 'ERR_ASSERTION'); + assert.strictEqual(err.message, + 'Got unwanted rejection.\nActual message: "Failed"'); + assert.strictEqual(err.operator, 'doesNotReject'); + assert.ok(!err.stack.includes('at Function.doesNotReject')); + return true; + }; + + const rejectingFn = async () => assert.fail(); + + let promise = assert.doesNotReject(rejectingFn, handler1); + promises.push(assert.rejects(promise, handler2)); + + promise = assert.doesNotReject(rejectingFn(), handler1); + promises.push(assert.rejects(promise, handler2)); + + promise = assert.doesNotReject(() => assert.fail(), common.mustNotCall()); + promises.push(assert.rejects(promise, handler1)); +} + +promises.push(assert.rejects( + assert.doesNotReject(123), + { + code: 'ERR_INVALID_ARG_TYPE', + message: 'The "block" argument must be one of type ' + + 'Function or Promise. Received type number' + } +)); + +// Make sure all async code gets properly executed. +Promise.all(promises).then(common.mustCall()); diff --git a/test/parallel/test-assert-fail.js b/test/parallel/test-assert-fail.js index 8d67a6e63f51f5..e96ee12cb4fd55 100644 --- a/test/parallel/test-assert-fail.js +++ b/test/parallel/test-assert-fail.js @@ -1,6 +1,6 @@ 'use strict'; -/* eslint-disable prefer-common-expectserror */ +/* eslint-disable node-core/prefer-common-expectserror */ const common = require('../common'); const assert = require('assert'); @@ -70,10 +70,7 @@ common.expectsError(() => { assert.fail(typeof 1, 'object', new TypeError('another custom message')); }, { type: TypeError, - message: 'another custom message', - operator: undefined, - actual: 'number', - expected: 'object' + message: 'another custom message' }); // No third arg (but a fourth arg) diff --git a/test/parallel/test-assert.js b/test/parallel/test-assert.js index 6375311fa6e31c..71e23e86d1400e 100644 --- a/test/parallel/test-assert.js +++ b/test/parallel/test-assert.js @@ -21,13 +21,16 @@ 'use strict'; -/* eslint-disable prefer-common-expectserror */ +/* eslint-disable node-core/prefer-common-expectserror */ const common = require('../common'); const assert = require('assert'); const { inspect } = require('util'); const a = assert; +const start = 'Input A expected to deepStrictEqual input B:'; +const actExp = '+ expected - actual'; + assert.ok(a.AssertionError.prototype instanceof Error, 'a.AssertionError instanceof Error'); @@ -110,6 +113,7 @@ assert.throws(() => thrower(TypeError)); } catch (e) { threw = true; assert.ok(e instanceof a.AssertionError); + assert.ok(!e.stack.includes('at Function.doesNotThrow')); } assert.ok(threw, 'a.doesNotThrow is not catching type matching errors'); } @@ -194,32 +198,50 @@ a.throws(() => thrower(TypeError), (err) => { const noop = () => {}; assert.throws( () => { a.throws((noop)); }, - common.expectsError({ + { code: 'ERR_ASSERTION', - message: /^Missing expected exception\.$/, - operator: 'throws' - })); + message: 'Missing expected exception.', + operator: 'throws', + actual: undefined, + expected: undefined + }); assert.throws( () => { a.throws(noop, TypeError); }, - common.expectsError({ + { code: 'ERR_ASSERTION', - message: /^Missing expected exception \(TypeError\)\.$/ - })); + message: 'Missing expected exception (TypeError).', + actual: undefined, + expected: TypeError + }); assert.throws( () => { a.throws(noop, 'fhqwhgads'); }, - common.expectsError({ + { code: 'ERR_ASSERTION', - message: /^Missing expected exception: fhqwhgads$/ - })); + message: 'Missing expected exception: fhqwhgads', + actual: undefined, + expected: undefined + }); assert.throws( () => { a.throws(noop, TypeError, 'fhqwhgads'); }, - common.expectsError({ + { code: 'ERR_ASSERTION', - message: /^Missing expected exception \(TypeError\): fhqwhgads$/ - })); + message: 'Missing expected exception (TypeError): fhqwhgads', + actual: undefined, + expected: TypeError + }); + + let threw = false; + try { + a.throws(noop); + } catch (e) { + threw = true; + assert.ok(e instanceof a.AssertionError); + assert.ok(!e.stack.includes('at Function.throws')); + } + assert.ok(threw); } const circular = { y: 1 }; @@ -419,14 +441,7 @@ common.expectsError( } ); - // Test error diffs - const colors = process.stdout.isTTY && process.stdout.getColorDepth() > 1; - const start = 'Input A expected to deepStrictEqual input B:'; - const actExp = colors ? - '\u001b[32m+ expected\u001b[39m \u001b[31m- actual\u001b[39m' : - '+ expected - actual'; - const plus = colors ? '\u001b[32m+\u001b[39m' : '+'; - const minus = colors ? '\u001b[31m-\u001b[39m' : '-'; + // Test error diffs. let message = [ start, `${actExp} ... Lines skipped`, @@ -435,8 +450,8 @@ common.expectsError( ' [', '...', ' 2,', - `${minus} 3`, - `${plus} '3'`, + '- 3', + "+ '3'", ' ]', '...', ' 5', @@ -453,7 +468,7 @@ common.expectsError( ' 1,', '...', ' 0,', - `${plus} 1,`, + '+ 1,', ' 1,', '...', ' 1', @@ -473,7 +488,7 @@ common.expectsError( ' 1,', '...', ' 0,', - `${minus} 1,`, + '- 1,', ' 1,', '...', ' 1', @@ -491,12 +506,12 @@ common.expectsError( '', ' [', ' 1,', - `${minus} 2,`, - `${plus} 1,`, + '- 2,', + '+ 1,', ' 1,', ' 1,', ' 0,', - `${minus} 1,`, + '- 1,', ' 1', ' ]' ].join('\n'); @@ -510,12 +525,12 @@ common.expectsError( start, actExp, '', - `${minus} [`, - `${minus} 1,`, - `${minus} 2,`, - `${minus} 1`, - `${minus} ]`, - `${plus} undefined`, + '- [', + '- 1,', + '- 2,', + '- 1', + '- ]', + '+ undefined', ].join('\n'); assert.throws( () => assert.deepEqual([1, 2, 1]), @@ -526,7 +541,7 @@ common.expectsError( actExp, '', ' [', - `${minus} 1,`, + '- 1,', ' 2,', ' 1', ' ]' @@ -539,9 +554,9 @@ common.expectsError( `${actExp} ... Lines skipped\n` + '\n' + ' [\n' + - `${minus} 1,\n`.repeat(10) + + '- 1,\n'.repeat(10) + '...\n' + - `${plus} 2,\n`.repeat(10) + + '+ 2,\n'.repeat(10) + '...'; assert.throws( () => assert.deepEqual(Array(12).fill(1), Array(12).fill(2)), @@ -555,11 +570,11 @@ common.expectsError( message: `${start}\n` + `${actExp}\n` + '\n' + - `${minus} {}\n` + - `${plus} {\n` + - `${plus} loop: 'forever',\n` + - `${plus} [Symbol(util.inspect.custom)]: [Function]\n` + - `${plus} }` + '- {}\n' + + '+ {\n' + + "+ loop: 'forever',\n" + + '+ [Symbol(util.inspect.custom)]: [Function]\n' + + '+ }' }); // notDeepEqual tests @@ -592,8 +607,8 @@ common.expectsError( { code: 'ERR_INVALID_ARG_TYPE', type: TypeError, - message: 'The "error" argument must be one of type Function or RegExp. ' + - 'Received type string' + message: 'The "error" argument must be one of type Object, Error, ' + + 'Function, or RegExp. Received type string' } ); diff --git a/test/parallel/test-buffer-alloc.js b/test/parallel/test-buffer-alloc.js index 20e91377cd7fdf..0e00c27b9bc663 100644 --- a/test/parallel/test-buffer-alloc.js +++ b/test/parallel/test-buffer-alloc.js @@ -646,7 +646,7 @@ assert.strictEqual('', x.inspect()); { const buf = Buffer.allocUnsafe(2); - assert.strictEqual(buf.write(''), 0); //0bytes + assert.strictEqual(buf.write(''), 0); // 0bytes assert.strictEqual(buf.write('\0'), 1); // 1byte (v8 adds null terminator) assert.strictEqual(buf.write('a\0'), 2); // 1byte * 2 assert.strictEqual(buf.write('あ'), 0); // 3bytes @@ -915,7 +915,7 @@ common.expectsError( } } -if (common.hasCrypto) { // eslint-disable-line crypto-check +if (common.hasCrypto) { // eslint-disable-line node-core/crypto-check // Test truncation after decode const crypto = require('crypto'); diff --git a/test/parallel/test-buffer-arraybuffer.js b/test/parallel/test-buffer-arraybuffer.js index 41af1eecbea4b2..a774ea8c999cfe 100644 --- a/test/parallel/test-buffer-arraybuffer.js +++ b/test/parallel/test-buffer-arraybuffer.js @@ -139,7 +139,7 @@ b.writeDoubleBE(11.11, 0, true); // If length can be converted to a number, it will be. assert.deepStrictEqual(Buffer.from(ab, 0, [1]), Buffer.from(ab, 0, 1)); - //If length is Infinity, throw. + // If length is Infinity, throw. common.expectsError(() => { Buffer.from(ab, 0, Infinity); }, { diff --git a/test/parallel/test-buffer-concat.js b/test/parallel/test-buffer-concat.js index fb290568309ee2..9d6c6c7d351e45 100644 --- a/test/parallel/test-buffer-concat.js +++ b/test/parallel/test-buffer-concat.js @@ -62,7 +62,7 @@ function assertWrongList(value) { }); } -// eslint-disable-next-line crypto-check +// eslint-disable-next-line node-core/crypto-check const random10 = common.hasCrypto ? require('crypto').randomBytes(10) : Buffer.alloc(10, 1); diff --git a/test/parallel/test-buffer-sharedarraybuffer.js b/test/parallel/test-buffer-sharedarraybuffer.js index 0252847c054c1d..ce3241ec0e7a0f 100644 --- a/test/parallel/test-buffer-sharedarraybuffer.js +++ b/test/parallel/test-buffer-sharedarraybuffer.js @@ -1,4 +1,4 @@ -/*global SharedArrayBuffer*/ +/* global SharedArrayBuffer */ 'use strict'; require('../common'); diff --git a/test/parallel/test-child-process-internal.js b/test/parallel/test-child-process-internal.js index c99010aeb8935a..c6ce0a8e04718c 100644 --- a/test/parallel/test-child-process-internal.js +++ b/test/parallel/test-child-process-internal.js @@ -23,16 +23,16 @@ const common = require('../common'); const assert = require('assert'); -//messages +// Messages const PREFIX = 'NODE_'; const normal = { cmd: `foo${PREFIX}` }; const internal = { cmd: `${PREFIX}bar` }; if (process.argv[2] === 'child') { - //send non-internal message containing PREFIX at a non prefix position + // Send non-internal message containing PREFIX at a non prefix position process.send(normal); - //send internal message + // Send internal message process.send(internal); process.exit(0); diff --git a/test/parallel/test-child-process-silent.js b/test/parallel/test-child-process-silent.js index 20ad9683dfa622..ce916306d14e42 100644 --- a/test/parallel/test-child-process-silent.js +++ b/test/parallel/test-child-process-silent.js @@ -56,7 +56,7 @@ if (process.argv[2] === 'pipe') { const args = [process.argv[1], 'parent']; const parent = childProcess.spawn(process.execPath, args); - //got any stderr or std data + // Got any stderr or std data let stdoutData = false; parent.stdout.on('data', function() { stdoutData = true; diff --git a/test/parallel/test-child-process-spawnsync-validation-errors.js b/test/parallel/test-child-process-spawnsync-validation-errors.js index 0a1cc2ca577307..0b0df347e2630f 100644 --- a/test/parallel/test-child-process-spawnsync-validation-errors.js +++ b/test/parallel/test-child-process-spawnsync-validation-errors.js @@ -3,6 +3,11 @@ const common = require('../common'); const assert = require('assert'); const spawnSync = require('child_process').spawnSync; const signals = process.binding('constants').os.signals; +const rootUser = common.isWindows ? false : process.getuid() === 0; + +const invalidArgTypeError = common.expectsError( + { code: 'ERR_INVALID_ARG_TYPE', type: TypeError }, + common.isWindows || rootUser ? 42 : 62); function pass(option, value) { // Run the command with the specified option. Since it's not a real command, @@ -53,43 +58,39 @@ function fail(option, value, message) { if (!common.isWindows) { { // Validate the uid option - if (process.getuid() !== 0) { - const err = /^TypeError: "uid" must be an integer$/; - + if (!rootUser) { pass('uid', undefined); pass('uid', null); pass('uid', process.getuid()); - fail('uid', __dirname, err); - fail('uid', true, err); - fail('uid', false, err); - fail('uid', [], err); - fail('uid', {}, err); - fail('uid', common.mustNotCall(), err); - fail('uid', NaN, err); - fail('uid', Infinity, err); - fail('uid', 3.1, err); - fail('uid', -3.1, err); + fail('uid', __dirname, invalidArgTypeError); + fail('uid', true, invalidArgTypeError); + fail('uid', false, invalidArgTypeError); + fail('uid', [], invalidArgTypeError); + fail('uid', {}, invalidArgTypeError); + fail('uid', common.mustNotCall(), invalidArgTypeError); + fail('uid', NaN, invalidArgTypeError); + fail('uid', Infinity, invalidArgTypeError); + fail('uid', 3.1, invalidArgTypeError); + fail('uid', -3.1, invalidArgTypeError); } } { // Validate the gid option if (process.getgid() !== 0) { - const err = /^TypeError: "gid" must be an integer$/; - pass('gid', undefined); pass('gid', null); pass('gid', process.getgid()); - fail('gid', __dirname, err); - fail('gid', true, err); - fail('gid', false, err); - fail('gid', [], err); - fail('gid', {}, err); - fail('gid', common.mustNotCall(), err); - fail('gid', NaN, err); - fail('gid', Infinity, err); - fail('gid', 3.1, err); - fail('gid', -3.1, err); + fail('gid', __dirname, invalidArgTypeError); + fail('gid', true, invalidArgTypeError); + fail('gid', false, invalidArgTypeError); + fail('gid', [], invalidArgTypeError); + fail('gid', {}, invalidArgTypeError); + fail('gid', common.mustNotCall(), invalidArgTypeError); + fail('gid', NaN, invalidArgTypeError); + fail('gid', Infinity, invalidArgTypeError); + fail('gid', 3.1, invalidArgTypeError); + fail('gid', -3.1, invalidArgTypeError); } } } diff --git a/test/parallel/test-cli-node-print-help.js b/test/parallel/test-cli-node-print-help.js new file mode 100644 index 00000000000000..7e21f01b69c0d8 --- /dev/null +++ b/test/parallel/test-cli-node-print-help.js @@ -0,0 +1,58 @@ +'use strict'; + +const common = require('../common'); + +// The following tests assert that the node.cc PrintHelp() function +// returns the proper set of cli options when invoked + +const assert = require('assert'); +const { exec } = require('child_process'); +let stdOut; + + +function startPrintHelpTest() { + exec(`${process.execPath} --help`, common.mustCall((err, stdout, stderr) => { + assert.ifError(err); + stdOut = stdout; + validateNodePrintHelp(); + })); +} + +function validateNodePrintHelp() { + const config = process.config; + const HAVE_OPENSSL = common.hasCrypto; + const NODE_FIPS_MODE = common.hasFipsCrypto; + const NODE_HAVE_I18N_SUPPORT = common.hasIntl; + const HAVE_INSPECTOR = config.variables.v8_enable_inspector === 1; + + const cliHelpOptions = [ + { compileConstant: HAVE_OPENSSL, + flags: [ '--openssl-config=file', '--tls-cipher-list=val', + '--use-bundled-ca', '--use-openssl-ca' ] }, + { compileConstant: NODE_FIPS_MODE, + flags: [ '--enable-fips', '--force-fips' ] }, + { compileConstant: NODE_HAVE_I18N_SUPPORT, + flags: [ '--experimental-modules', '--experimental-vm-modules', + '--icu-data-dir=dir', '--preserve-symlinks', + 'NODE_ICU_DATA', 'NODE_PRESERVE_SYMLINKS' ] }, + { compileConstant: HAVE_INSPECTOR, + flags: [ '--inspect-brk[=[host:]port]', '--inspect-port=[host:]port', + '--inspect[=[host:]port]' ] }, + ]; + + cliHelpOptions.forEach(testForSubstring); +} + +function testForSubstring(options) { + if (options.compileConstant) { + options.flags.forEach((flag) => { + assert.strictEqual(stdOut.indexOf(flag) !== -1, true); + }); + } else { + options.flags.forEach((flag) => { + assert.strictEqual(stdOut.indexOf(flag), -1); + }); + } +} + +startPrintHelpTest(); diff --git a/test/parallel/test-cluster-basic.js b/test/parallel/test-cluster-basic.js index 8a0a41f90c59d9..0d51a18607f6bf 100644 --- a/test/parallel/test-cluster-basic.js +++ b/test/parallel/test-cluster-basic.js @@ -78,45 +78,45 @@ if (cluster.isWorker) { const stateNames = Object.keys(checks.worker.states); - //Check events, states, and emit arguments + // Check events, states, and emit arguments forEach(checks.cluster.events, (bool, name, index) => { - //Listen on event + // Listen on event cluster.on(name, common.mustCall(function(/* worker */) { - //Set event + // Set event checks.cluster.events[name] = true; - //Check argument + // Check argument checks.cluster.equal[name] = worker === arguments[0]; - //Check state + // Check state const state = stateNames[index]; checks.worker.states[state] = (state === worker.state); })); }); - //Kill worker when listening + // Kill worker when listening cluster.on('listening', common.mustCall(() => { worker.kill(); })); - //Kill process when worker is killed + // Kill process when worker is killed cluster.on('exit', common.mustCall()); - //Create worker + // Create worker const worker = cluster.fork(); assert.strictEqual(worker.id, 1); assert(worker instanceof cluster.Worker, 'the worker is not a instance of the Worker constructor'); - //Check event + // Check event forEach(checks.worker.events, function(bool, name, index) { worker.on(name, common.mustCall(function() { - //Set event + // Set event checks.worker.events[name] = true; - //Check argument + // Check argument checks.worker.equal[name] = (worker === this); switch (name) { @@ -146,33 +146,33 @@ if (cluster.isWorker) { })); }); - //Check all values + // Check all values process.once('exit', () => { - //Check cluster events + // Check cluster events forEach(checks.cluster.events, (check, name) => { assert(check, `The cluster event "${name}" on the cluster object did not fire`); }); - //Check cluster event arguments + // Check cluster event arguments forEach(checks.cluster.equal, (check, name) => { assert(check, `The cluster event "${name}" did not emit with correct argument`); }); - //Check worker states + // Check worker states forEach(checks.worker.states, (check, name) => { assert(check, `The worker state "${name}" was not set to true`); }); - //Check worker events + // Check worker events forEach(checks.worker.events, (check, name) => { assert(check, `The worker event "${name}" on the worker object did not fire`); }); - //Check worker event arguments + // Check worker event arguments forEach(checks.worker.equal, (check, name) => { assert(check, `The worker event "${name}" did not emit with correct argument`); diff --git a/test/parallel/test-cluster-disconnect.js b/test/parallel/test-cluster-disconnect.js index e35e45768850ca..219c084a7e7073 100644 --- a/test/parallel/test-cluster-disconnect.js +++ b/test/parallel/test-cluster-disconnect.js @@ -86,11 +86,11 @@ if (cluster.isWorker) { }; const test = (again) => { - //1. start cluster + // 1. start cluster startCluster(common.mustCall(() => { - //2. test cluster + // 2. test cluster testCluster(common.mustCall(() => { - //3. disconnect cluster + // 3. disconnect cluster cluster.disconnect(common.mustCall(() => { // run test again to confirm cleanup if (again) { diff --git a/test/parallel/test-cluster-rr-domain-listen.js b/test/parallel/test-cluster-rr-domain-listen.js index 45e1200a3d65d1..0f94eb8e6b8060 100644 --- a/test/parallel/test-cluster-rr-domain-listen.js +++ b/test/parallel/test-cluster-rr-domain-listen.js @@ -36,16 +36,16 @@ if (cluster.isWorker) { } else if (cluster.isMaster) { - //Kill worker when listening + // Kill worker when listening cluster.on('listening', function() { worker.kill(); }); - //Kill process when worker is killed + // Kill process when worker is killed cluster.on('exit', function() { process.exit(0); }); - //Create worker + // Create worker const worker = cluster.fork(); } diff --git a/test/parallel/test-console-table.js b/test/parallel/test-console-table.js new file mode 100644 index 00000000000000..dbe6b2467188e4 --- /dev/null +++ b/test/parallel/test-console-table.js @@ -0,0 +1,176 @@ +'use strict'; + +const common = require('../common'); + +const assert = require('assert'); +const { Console } = require('console'); + +const queue = []; + +const console = new Console({ write: (x) => { + queue.push(x); +}, removeListener: () => {} }, process.stderr, false); + +function test(data, only, expected) { + if (arguments.length === 2) { + expected = only; + only = undefined; + } + console.table(data, only); + assert.strictEqual(queue.shift(), expected.trimLeft()); +} + +common.expectsError(() => console.table([], false), { + code: 'ERR_INVALID_ARG_TYPE', +}); + +test(null, 'null\n'); +test(undefined, 'undefined\n'); +test(false, 'false\n'); +test('hi', 'hi\n'); +test(Symbol(), 'Symbol()\n'); + +test([1, 2, 3], ` +┌─────────┬────────┐ +│ (index) │ Values │ +├─────────┼────────┤ +│ 0 │ 1 │ +│ 1 │ 2 │ +│ 2 │ 3 │ +└─────────┴────────┘ +`); + +test([Symbol(), 5, [10]], ` +┌─────────┬──────────┐ +│ (index) │ Values │ +├─────────┼──────────┤ +│ 0 │ Symbol() │ +│ 1 │ 5 │ +│ 2 │ [ 10 ] │ +└─────────┴──────────┘ +`); + +test([undefined, 5], ` +┌─────────┬───────────┐ +│ (index) │ Values │ +├─────────┼───────────┤ +│ 0 │ undefined │ +│ 1 │ 5 │ +└─────────┴───────────┘ +`); + +test({ a: 1, b: Symbol(), c: [10] }, ` +┌─────────┬────┬──────────┐ +│ (index) │ 0 │ Values │ +├─────────┼────┼──────────┤ +│ a │ │ 1 │ +│ b │ │ Symbol() │ +│ c │ 10 │ │ +└─────────┴────┴──────────┘ +`); + +test(new Map([ ['a', 1], [Symbol(), [2]] ]), ` +┌───────────────────┬──────────┬────────┐ +│ (iteration index) │ Key │ Values │ +├───────────────────┼──────────┼────────┤ +│ 0 │ 'a' │ 1 │ +│ 1 │ Symbol() │ [ 2 ] │ +└───────────────────┴──────────┴────────┘ +`); + +test(new Set([1, 2, Symbol()]), ` +┌───────────────────┬──────────┐ +│ (iteration index) │ Values │ +├───────────────────┼──────────┤ +│ 0 │ 1 │ +│ 1 │ 2 │ +│ 2 │ Symbol() │ +└───────────────────┴──────────┘ +`); + +test({ a: 1, b: 2 }, ['a'], ` +┌─────────┬───┐ +│ (index) │ a │ +├─────────┼───┤ +│ a │ │ +│ b │ │ +└─────────┴───┘ +`); + +test([{ a: 1, b: 2 }, { a: 3, c: 4 }], ['a'], ` +┌─────────┬───┐ +│ (index) │ a │ +├─────────┼───┤ +│ 0 │ 1 │ +│ 1 │ 3 │ +└─────────┴───┘ +`); + + +test({ a: { a: 1, b: 2, c: 3 } }, ` +┌─────────┬───┬───┬───┐ +│ (index) │ a │ b │ c │ +├─────────┼───┼───┼───┤ +│ a │ 1 │ 2 │ 3 │ +└─────────┴───┴───┴───┘ +`); + +test({ a: [1, 2] }, ` +┌─────────┬───┬───┐ +│ (index) │ 0 │ 1 │ +├─────────┼───┼───┤ +│ a │ 1 │ 2 │ +└─────────┴───┴───┘ +`); + +test({ a: [1, 2, 3, 4, 5], b: 5, c: { e: 5 } }, ` +┌─────────┬───┬───┬───┬───┬───┬───┬────────┐ +│ (index) │ 0 │ 1 │ 2 │ 3 │ 4 │ e │ Values │ +├─────────┼───┼───┼───┼───┼───┼───┼────────┤ +│ a │ 1 │ 2 │ 3 │ 4 │ 5 │ │ │ +│ b │ │ │ │ │ │ │ 5 │ +│ c │ │ │ │ │ │ 5 │ │ +└─────────┴───┴───┴───┴───┴───┴───┴────────┘ +`); + +test(new Uint8Array([1, 2, 3]), ` +┌─────────┬────────┐ +│ (index) │ Values │ +├─────────┼────────┤ +│ 0 │ 1 │ +│ 1 │ 2 │ +│ 2 │ 3 │ +└─────────┴────────┘ +`); + +test(Buffer.from([1, 2, 3]), ` +┌─────────┬────────┐ +│ (index) │ Values │ +├─────────┼────────┤ +│ 0 │ 1 │ +│ 1 │ 2 │ +│ 2 │ 3 │ +└─────────┴────────┘ +`); + +test({ a: undefined }, ['x'], ` +┌─────────┬───┐ +│ (index) │ x │ +├─────────┼───┤ +│ a │ │ +└─────────┴───┘ +`); + +test([], ` +┌─────────┬────────┐ +│ (index) │ Values │ +├─────────┼────────┤ +└─────────┴────────┘ +`); + +test(new Map(), ` +┌───────────────────┬─────┬────────┐ +│ (iteration index) │ Key │ Values │ +├───────────────────┼─────┼────────┤ +└───────────────────┴─────┴────────┘ +`); diff --git a/test/parallel/test-console-tty-colors.js b/test/parallel/test-console-tty-colors.js new file mode 100644 index 00000000000000..945c21f28a27de --- /dev/null +++ b/test/parallel/test-console-tty-colors.js @@ -0,0 +1,46 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const util = require('util'); +const { Writable } = require('stream'); +const { Console } = require('console'); + +function check(isTTY, colorMode, expectedColorMode) { + const items = [ + 1, + { a: 2 }, + [ 'foo' ], + { '\\a': '\\bar' } + ]; + + let i = 0; + const stream = new Writable({ + write: common.mustCall((chunk, enc, cb) => { + assert.strictEqual(chunk.trim(), + util.inspect(items[i++], { + colors: expectedColorMode + })); + cb(); + }, items.length), + decodeStrings: false + }); + stream.isTTY = isTTY; + + // Set ignoreErrors to `false` here so that we see assertion failures + // from the `write()` call happen. + const testConsole = new Console({ + stdout: stream, + ignoreErrors: false, + colorMode + }); + for (const item of items) { + testConsole.log(item); + } +} + +check(true, 'auto', true); +check(false, 'auto', false); +check(true, true, true); +check(false, true, true); +check(true, false, false); +check(false, false, false); diff --git a/test/parallel/test-console.js b/test/parallel/test-console.js index 234723285d1556..c5f4d7a5fbcc5b 100644 --- a/test/parallel/test-console.js +++ b/test/parallel/test-console.js @@ -51,9 +51,11 @@ const custom_inspect = { foo: 'bar', inspect: () => 'inspect' }; const strings = []; const errStrings = []; +process.stdout.isTTY = false; common.hijackStdout(function(data) { strings.push(data); }); +process.stderr.isTTY = false; common.hijackStderr(function(data) { errStrings.push(data); }); diff --git a/test/parallel/test-crypto-fips.js b/test/parallel/test-crypto-fips.js index 5334668a4447ea..2003c390b94c5b 100644 --- a/test/parallel/test-crypto-fips.js +++ b/test/parallel/test-crypto-fips.js @@ -67,7 +67,7 @@ testHelper( 'stdout', [], FIPS_DISABLED, - 'require("crypto").fips', + 'require("crypto").getFips()', Object.assign({}, process.env, { 'OPENSSL_CONF': '' })); // --enable-fips should turn FIPS mode on @@ -75,15 +75,15 @@ testHelper( compiledWithFips() ? 'stdout' : 'stderr', ['--enable-fips'], compiledWithFips() ? FIPS_ENABLED : OPTION_ERROR_STRING, - 'require("crypto").fips', + 'require("crypto").getFips()', process.env); -//--force-fips should turn FIPS mode on +// --force-fips should turn FIPS mode on testHelper( compiledWithFips() ? 'stdout' : 'stderr', ['--force-fips'], compiledWithFips() ? FIPS_ENABLED : OPTION_ERROR_STRING, - 'require("crypto").fips', + 'require("crypto").getFips()', process.env); // If Node was configured using --shared-openssl fips support might be @@ -104,7 +104,7 @@ if (!sharedOpenSSL()) { 'stdout', [`--openssl-config=${CNF_FIPS_ON}`], compiledWithFips() ? FIPS_ENABLED : FIPS_DISABLED, - 'require("crypto").fips', + 'require("crypto").getFips()', process.env); // OPENSSL_CONF should be able to turn on FIPS mode @@ -112,7 +112,7 @@ if (!sharedOpenSSL()) { 'stdout', [], compiledWithFips() ? FIPS_ENABLED : FIPS_DISABLED, - 'require("crypto").fips', + 'require("crypto").getFips()', Object.assign({}, process.env, { 'OPENSSL_CONF': CNF_FIPS_ON })); // --openssl-config option should override OPENSSL_CONF @@ -120,7 +120,7 @@ if (!sharedOpenSSL()) { 'stdout', [`--openssl-config=${CNF_FIPS_ON}`], compiledWithFips() ? FIPS_ENABLED : FIPS_DISABLED, - 'require("crypto").fips', + 'require("crypto").getFips()', Object.assign({}, process.env, { 'OPENSSL_CONF': CNF_FIPS_OFF })); } @@ -128,7 +128,7 @@ testHelper( 'stdout', [`--openssl-config=${CNF_FIPS_OFF}`], FIPS_DISABLED, - 'require("crypto").fips', + 'require("crypto").getFips()', Object.assign({}, process.env, { 'OPENSSL_CONF': CNF_FIPS_ON })); // --enable-fips should take precedence over OpenSSL config file @@ -136,7 +136,7 @@ testHelper( compiledWithFips() ? 'stdout' : 'stderr', ['--enable-fips', `--openssl-config=${CNF_FIPS_OFF}`], compiledWithFips() ? FIPS_ENABLED : OPTION_ERROR_STRING, - 'require("crypto").fips', + 'require("crypto").getFips()', process.env); // OPENSSL_CONF should _not_ make a difference to --enable-fips @@ -144,7 +144,7 @@ testHelper( compiledWithFips() ? 'stdout' : 'stderr', ['--enable-fips'], compiledWithFips() ? FIPS_ENABLED : OPTION_ERROR_STRING, - 'require("crypto").fips', + 'require("crypto").getFips()', Object.assign({}, process.env, { 'OPENSSL_CONF': CNF_FIPS_OFF })); // --force-fips should take precedence over OpenSSL config file @@ -152,7 +152,7 @@ testHelper( compiledWithFips() ? 'stdout' : 'stderr', ['--force-fips', `--openssl-config=${CNF_FIPS_OFF}`], compiledWithFips() ? FIPS_ENABLED : OPTION_ERROR_STRING, - 'require("crypto").fips', + 'require("crypto").getFips()', process.env); // Using OPENSSL_CONF should not make a difference to --force-fips @@ -160,7 +160,7 @@ testHelper( compiledWithFips() ? 'stdout' : 'stderr', ['--force-fips'], compiledWithFips() ? FIPS_ENABLED : OPTION_ERROR_STRING, - 'require("crypto").fips', + 'require("crypto").getFips()', Object.assign({}, process.env, { 'OPENSSL_CONF': CNF_FIPS_OFF })); // setFipsCrypto should be able to turn FIPS mode on @@ -168,8 +168,8 @@ testHelper( compiledWithFips() ? 'stdout' : 'stderr', [], compiledWithFips() ? FIPS_ENABLED : FIPS_ERROR_STRING, - '(require("crypto").fips = true,' + - 'require("crypto").fips)', + '(require("crypto").setFips(true),' + + 'require("crypto").getFips())', process.env); // setFipsCrypto should be able to turn FIPS mode on and off @@ -177,9 +177,9 @@ testHelper( compiledWithFips() ? 'stdout' : 'stderr', [], compiledWithFips() ? FIPS_DISABLED : FIPS_ERROR_STRING, - '(require("crypto").fips = true,' + - 'require("crypto").fips = false,' + - 'require("crypto").fips)', + '(require("crypto").setFips(true),' + + 'require("crypto").setFips(false),' + + 'require("crypto").getFips())', process.env); // setFipsCrypto takes precedence over OpenSSL config file, FIPS on @@ -187,8 +187,8 @@ testHelper( compiledWithFips() ? 'stdout' : 'stderr', [`--openssl-config=${CNF_FIPS_OFF}`], compiledWithFips() ? FIPS_ENABLED : FIPS_ERROR_STRING, - '(require("crypto").fips = true,' + - 'require("crypto").fips)', + '(require("crypto").setFips(true),' + + 'require("crypto").getFips())', process.env); // setFipsCrypto takes precedence over OpenSSL config file, FIPS off @@ -196,8 +196,8 @@ testHelper( compiledWithFips() ? 'stdout' : 'stderr', [`--openssl-config=${CNF_FIPS_ON}`], compiledWithFips() ? FIPS_DISABLED : FIPS_ERROR_STRING, - '(require("crypto").fips = false,' + - 'require("crypto").fips)', + '(require("crypto").setFips(false),' + + 'require("crypto").getFips())', process.env); // --enable-fips does not prevent use of setFipsCrypto API @@ -205,8 +205,8 @@ testHelper( compiledWithFips() ? 'stdout' : 'stderr', ['--enable-fips'], compiledWithFips() ? FIPS_DISABLED : OPTION_ERROR_STRING, - '(require("crypto").fips = false,' + - 'require("crypto").fips)', + '(require("crypto").setFips(false),' + + 'require("crypto").getFips())', process.env); // --force-fips prevents use of setFipsCrypto API @@ -214,7 +214,7 @@ testHelper( 'stderr', ['--force-fips'], compiledWithFips() ? FIPS_ERROR_STRING2 : OPTION_ERROR_STRING, - 'require("crypto").fips = false', + 'require("crypto").setFips(false)', process.env); // --force-fips makes setFipsCrypto enable a no-op (FIPS stays on) @@ -222,8 +222,8 @@ testHelper( compiledWithFips() ? 'stdout' : 'stderr', ['--force-fips'], compiledWithFips() ? FIPS_ENABLED : OPTION_ERROR_STRING, - '(require("crypto").fips = true,' + - 'require("crypto").fips)', + '(require("crypto").setFips(true),' + + 'require("crypto").getFips())', process.env); // --force-fips and --enable-fips order does not matter @@ -231,13 +231,13 @@ testHelper( 'stderr', ['--force-fips', '--enable-fips'], compiledWithFips() ? FIPS_ERROR_STRING2 : OPTION_ERROR_STRING, - 'require("crypto").fips = false', + 'require("crypto").setFips(false)', process.env); -//--enable-fips and --force-fips order does not matter +// --enable-fips and --force-fips order does not matter testHelper( 'stderr', ['--enable-fips', '--force-fips'], compiledWithFips() ? FIPS_ERROR_STRING2 : OPTION_ERROR_STRING, - 'require("crypto").fips = false', + 'require("crypto").setFips(false)', process.env); diff --git a/test/parallel/test-crypto.js b/test/parallel/test-crypto.js index 1fc3db74828ca6..3047d37c962cff 100644 --- a/test/parallel/test-crypto.js +++ b/test/parallel/test-crypto.js @@ -260,7 +260,7 @@ assert.throws(function() { /** * Check if the stream function uses utf8 as a default encoding. - **/ + */ function testEncoding(options, assertionHash) { const hash = crypto.createHash('sha256', options); diff --git a/test/parallel/test-dgram-multicast-setTTL.js b/test/parallel/test-dgram-multicast-setTTL.js index 8cfa759ad9f382..2e3aad63fe3012 100644 --- a/test/parallel/test-dgram-multicast-setTTL.js +++ b/test/parallel/test-dgram-multicast-setTTL.js @@ -30,7 +30,7 @@ socket.on('listening', common.mustCall(() => { const result = socket.setMulticastTTL(16); assert.strictEqual(result, 16); - //Try to set an invalid TTL (valid ttl is > 0 and < 256) + // Try to set an invalid TTL (valid ttl is > 0 and < 256) assert.throws(() => { socket.setMulticastTTL(1000); }, /^Error: setMulticastTTL EINVAL$/); @@ -43,6 +43,6 @@ socket.on('listening', common.mustCall(() => { message: 'The "ttl" argument must be of type number. Received type string' }); - //close the socket + // Close the socket socket.close(); })); diff --git a/test/parallel/test-dns-lookup.js b/test/parallel/test-dns-lookup.js index d006355742e55b..b9c0dfc6dff7a5 100644 --- a/test/parallel/test-dns-lookup.js +++ b/test/parallel/test-dns-lookup.js @@ -92,6 +92,9 @@ dns.lookup('example.com', common.mustCall((error, result, addressType) => { assert(error); assert.strictEqual(tickValue, 1); assert.strictEqual(error.code, 'ENOENT'); + const descriptor = Object.getOwnPropertyDescriptor(error, 'message'); + assert.strictEqual(descriptor.enumerable, + false, 'The error message should be non-enumerable'); })); // Make sure that the error callback is called diff --git a/test/parallel/test-dns-resolveany-bad-ancount.js b/test/parallel/test-dns-resolveany-bad-ancount.js index 25ce15935caad9..63ed1774b11933 100644 --- a/test/parallel/test-dns-resolveany-bad-ancount.js +++ b/test/parallel/test-dns-resolveany-bad-ancount.js @@ -30,6 +30,9 @@ server.bind(0, common.mustCall(() => { assert.strictEqual(err.code, 'EBADRESP'); assert.strictEqual(err.syscall, 'queryAny'); assert.strictEqual(err.hostname, 'example.org'); + const descriptor = Object.getOwnPropertyDescriptor(err, 'message'); + assert.strictEqual(descriptor.enumerable, + false, 'The error message should be non-enumerable'); server.close(); })); })); diff --git a/test/parallel/test-fs-append-file-sync.js b/test/parallel/test-fs-append-file-sync.js index b836d81bd5985c..90e3f97d8cf252 100644 --- a/test/parallel/test-fs-append-file-sync.js +++ b/test/parallel/test-fs-append-file-sync.js @@ -100,7 +100,7 @@ const fileData5 = fs.readFileSync(filename5); assert.strictEqual(Buffer.byteLength(data) + currentFileData.length, fileData5.length); -//exit logic for cleanup +// Exit logic for cleanup process.on('exit', function() { fs.unlinkSync(filename); diff --git a/test/parallel/test-fs-read-stream-throw-type-error.js b/test/parallel/test-fs-read-stream-throw-type-error.js index c2b0d5452c55e3..5cba76fa394636 100644 --- a/test/parallel/test-fs-read-stream-throw-type-error.js +++ b/test/parallel/test-fs-read-stream-throw-type-error.js @@ -3,6 +3,9 @@ const common = require('../common'); const fixtures = require('../common/fixtures'); const fs = require('fs'); +// This test ensures that appropriate TypeError is thrown by createReadStream +// when an argument with invalid type is passed + const example = fixtures.path('x.txt'); // Should not throw. fs.createReadStream(example, undefined); @@ -25,3 +28,8 @@ createReadStreamErr(example, 123); createReadStreamErr(example, 0); createReadStreamErr(example, true); createReadStreamErr(example, false); + +// createReadSteam _should_ throw on NaN +createReadStreamErr(example, { start: NaN }); +createReadStreamErr(example, { end: NaN }); +createReadStreamErr(example, { start: NaN, end: NaN }); diff --git a/test/parallel/test-fs-watch.js b/test/parallel/test-fs-watch.js index 7affe370c7ed03..39bb2532f9a9be 100644 --- a/test/parallel/test-fs-watch.js +++ b/test/parallel/test-fs-watch.js @@ -54,6 +54,7 @@ for (const testCase of cases) { } assert.fail(err); }); + watcher.on('close', common.mustCall()); watcher.on('change', common.mustCall(function(eventType, argFilename) { if (interval) { clearInterval(interval); diff --git a/test/parallel/test-global-console-exists.js b/test/parallel/test-global-console-exists.js index 1434b76e12696e..f2e7ba5a9aa3a0 100644 --- a/test/parallel/test-global-console-exists.js +++ b/test/parallel/test-global-console-exists.js @@ -1,4 +1,4 @@ -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ 'use strict'; diff --git a/test/parallel/test-http-agent-destroyed-socket.js b/test/parallel/test-http-agent-destroyed-socket.js index 39f4ebef5374c1..137292cce66916 100644 --- a/test/parallel/test-http-agent-destroyed-socket.js +++ b/test/parallel/test-http-agent-destroyed-socket.js @@ -47,7 +47,6 @@ const server = http.createServer(common.mustCall((req, res) => { request1.socket.on('close', common.mustCall()); response.resume(); response.on('end', common.mustCall(() => { - ///////////////////////////////// // // THE IMPORTANT PART // diff --git a/test/parallel/test-http-agent-error-on-idle.js b/test/parallel/test-http-agent-error-on-idle.js index 7cf494816ae266..8edfa24811417a 100644 --- a/test/parallel/test-http-agent-error-on-idle.js +++ b/test/parallel/test-http-agent-error-on-idle.js @@ -27,10 +27,10 @@ server.listen(0, () => { res.on('end', common.mustCall(() => { process.nextTick(common.mustCall(() => { const freeSockets = agent.freeSockets[socketKey]; - //expect a free socket on socketKey + // Expect a free socket on socketKey assert.strictEqual(freeSockets.length, 1); - //generate a random error on the free socket + // Generate a random error on the free socket const freeSocket = freeSockets[0]; freeSocket.emit('error', new Error('ECONNRESET: test')); @@ -40,7 +40,7 @@ server.listen(0, () => { })); function done() { - //expect the freeSockets pool to be empty + // Expect the freeSockets pool to be empty assert.strictEqual(Object.keys(agent.freeSockets).length, 0); agent.destroy(); diff --git a/test/parallel/test-http-allow-req-after-204-res.js b/test/parallel/test-http-allow-req-after-204-res.js index 8de53a9e539a66..84dd876985e2e6 100644 --- a/test/parallel/test-http-allow-req-after-204-res.js +++ b/test/parallel/test-http-allow-req-after-204-res.js @@ -48,8 +48,9 @@ function nextRequest() { if (countdown.dec()) { // throws error: nextRequest(); + // TODO: investigate why this does not work fine even though it should. // works just fine: - //process.nextTick(nextRequest); + // process.nextTick(nextRequest); } })); response.resume(); diff --git a/test/parallel/test-http-client-timeout-agent.js b/test/parallel/test-http-client-timeout-agent.js index 1b628ea96798b1..8ce14f865c269c 100644 --- a/test/parallel/test-http-client-timeout-agent.js +++ b/test/parallel/test-http-client-timeout-agent.js @@ -32,8 +32,6 @@ const options = { host: '127.0.0.1', }; -//http.globalAgent.maxSockets = 15; - const server = http.createServer(function(req, res) { const m = /\/(.*)/.exec(req.url); const reqid = parseInt(m[1], 10); diff --git a/test/parallel/test-http-invalid-urls.js b/test/parallel/test-http-invalid-urls.js index 51e680071a92a3..9a5567aab897f8 100644 --- a/test/parallel/test-http-invalid-urls.js +++ b/test/parallel/test-http-invalid-urls.js @@ -1,4 +1,4 @@ -/* eslint-disable crypto-check */ +/* eslint-disable node-core/crypto-check */ 'use strict'; diff --git a/test/parallel/test-http-server-multiheaders2.js b/test/parallel/test-http-server-multiheaders2.js index c4a9151862ee6b..bb0fe0eaca57a9 100644 --- a/test/parallel/test-http-server-multiheaders2.js +++ b/test/parallel/test-http-server-multiheaders2.js @@ -66,7 +66,7 @@ const multipleForbidden = [ 'Max-Forwards', // special case, tested differently - //'Content-Length', + // 'Content-Length', ]; const srv = http.createServer(function(req, res) { diff --git a/test/parallel/test-http-set-trailers.js b/test/parallel/test-http-set-trailers.js index 204207dfa8dcb6..21c5d604363a36 100644 --- a/test/parallel/test-http-set-trailers.js +++ b/test/parallel/test-http-set-trailers.js @@ -24,6 +24,7 @@ const common = require('../common'); const assert = require('assert'); const http = require('http'); const net = require('net'); +const util = require('util'); let outstanding_reqs = 0; @@ -48,7 +49,6 @@ server.on('listening', function() { }); c.on('data', function(chunk) { - //console.log(chunk); res_buffer += chunk; }); @@ -78,7 +78,6 @@ server.on('listening', function() { }); c.on('data', function(chunk) { - //console.log(chunk); res_buffer += chunk; if (/0\r\n/.test(res_buffer)) { // got the end. outstanding_reqs--; @@ -103,8 +102,8 @@ server.on('listening', function() { headers: {} }, function(res) { res.on('end', function() { - //console.log(res.trailers); - assert.ok('x-foo' in res.trailers, 'Client doesn\'t see trailers.'); + assert.ok('x-foo' in res.trailers, + `${util.inspect(res.trailers)} misses the 'x-foo' property`); outstanding_reqs--; if (outstanding_reqs === 0) { server.close(); diff --git a/test/parallel/test-http-url.parse-post.js b/test/parallel/test-http-url.parse-post.js index d8803cbfd267db..91a567d1560f1a 100644 --- a/test/parallel/test-http-url.parse-post.js +++ b/test/parallel/test-http-url.parse-post.js @@ -28,11 +28,11 @@ const url = require('url'); let testURL; function check(request) { - //url.parse should not mess with the method + // url.parse should not mess with the method assert.strictEqual(request.method, 'POST'); - //everything else should be right + // Everything else should be right assert.strictEqual(request.url, '/asdf?qwer=zxcv'); - //the host header should use the url.parse.hostname + // The host header should use the url.parse.hostname assert.strictEqual(request.headers.host, `${testURL.hostname}:${testURL.port}`); } diff --git a/test/parallel/test-http2-compat-serverresponse-headers.js b/test/parallel/test-http2-compat-serverresponse-headers.js index 7b5313b8e7b037..a31cfeb32eedd3 100644 --- a/test/parallel/test-http2-compat-serverresponse-headers.js +++ b/test/parallel/test-http2-compat-serverresponse-headers.js @@ -76,7 +76,7 @@ server.listen(0, common.mustCall(function() { () => response.setHeader(header, 'foobar'), { code: 'ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED', - type: Error, + type: TypeError, message: 'Cannot set HTTP/2 pseudo-headers' }) ); diff --git a/test/parallel/test-http2-server-push-stream-errors-args.js b/test/parallel/test-http2-server-push-stream-errors-args.js index f752f44310c0df..24f7c9fcef9342 100644 --- a/test/parallel/test-http2-server-push-stream-errors-args.js +++ b/test/parallel/test-http2-server-push-stream-errors-args.js @@ -31,6 +31,7 @@ server.on('stream', common.mustCall((stream, headers) => { () => stream.pushStream({ 'connection': 'test' }, {}, () => {}), { code: 'ERR_HTTP2_INVALID_CONNECTION_HEADERS', + name: 'TypeError [ERR_HTTP2_INVALID_CONNECTION_HEADERS]', message: 'HTTP/1 Connection specific headers are forbidden: "connection"' } ); diff --git a/test/parallel/test-http2-util-headers-list.js b/test/parallel/test-http2-util-headers-list.js index 0e5c519264de7b..772e9c8679feef 100644 --- a/test/parallel/test-http2-util-headers-list.js +++ b/test/parallel/test-http2-util-headers-list.js @@ -283,6 +283,7 @@ const { ].forEach((name) => { common.expectsError({ code: 'ERR_HTTP2_INVALID_CONNECTION_HEADERS', + name: 'TypeError [ERR_HTTP2_INVALID_CONNECTION_HEADERS]', message: 'HTTP/1 Connection specific headers are forbidden: ' + `"${name.toLowerCase()}"` })(mapToHeaders({ [name]: 'abc' })); @@ -290,12 +291,14 @@ const { common.expectsError({ code: 'ERR_HTTP2_INVALID_CONNECTION_HEADERS', + name: 'TypeError [ERR_HTTP2_INVALID_CONNECTION_HEADERS]', message: 'HTTP/1 Connection specific headers are forbidden: ' + `"${HTTP2_HEADER_TE}"` })(mapToHeaders({ [HTTP2_HEADER_TE]: ['abc'] })); common.expectsError({ code: 'ERR_HTTP2_INVALID_CONNECTION_HEADERS', + name: 'TypeError [ERR_HTTP2_INVALID_CONNECTION_HEADERS]', message: 'HTTP/1 Connection specific headers are forbidden: ' + `"${HTTP2_HEADER_TE}"` })(mapToHeaders({ [HTTP2_HEADER_TE]: ['abc', 'trailers'] })); diff --git a/test/parallel/test-https-host-headers.js b/test/parallel/test-https-host-headers.js index 8f2dbfaca9f1c1..996b37a63385fb 100644 --- a/test/parallel/test-https-host-headers.js +++ b/test/parallel/test-https-host-headers.js @@ -24,7 +24,6 @@ function reqHandler(req, res) { `Wrong host header for req[${req.url}]: ${req.headers.host}`); } res.writeHead(200, {}); - //process.nextTick(function() { res.end('ok'); }); res.end('ok'); } @@ -55,7 +54,6 @@ function testHttps() { method: 'GET', path: `/${counter++}`, host: 'localhost', - //agent: false, port: this.address().port, rejectUnauthorized: false }, cb).on('error', thrower); @@ -64,7 +62,6 @@ function testHttps() { method: 'GET', path: `/${counter++}`, host: 'localhost', - //agent: false, port: this.address().port, rejectUnauthorized: false }, cb).on('error', thrower).end(); @@ -73,7 +70,6 @@ function testHttps() { method: 'POST', path: `/${counter++}`, host: 'localhost', - //agent: false, port: this.address().port, rejectUnauthorized: false }, cb).on('error', thrower).end(); @@ -82,7 +78,6 @@ function testHttps() { method: 'PUT', path: `/${counter++}`, host: 'localhost', - //agent: false, port: this.address().port, rejectUnauthorized: false }, cb).on('error', thrower).end(); @@ -91,7 +86,6 @@ function testHttps() { method: 'DELETE', path: `/${counter++}`, host: 'localhost', - //agent: false, port: this.address().port, rejectUnauthorized: false }, cb).on('error', thrower).end(); diff --git a/test/parallel/test-internal-module-map-asserts.js b/test/parallel/test-internal-module-map-asserts.js index 330f04cfd96800..4563fc605e0792 100644 --- a/test/parallel/test-internal-module-map-asserts.js +++ b/test/parallel/test-internal-module-map-asserts.js @@ -3,7 +3,7 @@ const common = require('../common'); const assert = require('assert'); -const ModuleMap = require('internal/modules/esm/ModuleMap'); +const ModuleMap = require('internal/modules/esm/module_map'); // ModuleMap.get, ModuleMap.has and ModuleMap.set should only accept string // values as url argument. diff --git a/test/parallel/test-internal-module-wrap.js b/test/parallel/test-internal-module-wrap.js index 050cc18a4dbee6..66a17a0f344701 100644 --- a/test/parallel/test-internal-module-wrap.js +++ b/test/parallel/test-internal-module-wrap.js @@ -6,7 +6,7 @@ const common = require('../common'); common.crashOnUnhandledRejection(); const assert = require('assert'); -const ModuleWrap = require('internal/modules/esm/ModuleWrap'); +const { ModuleWrap } = require('internal/test/binding'); const { getPromiseDetails, isPromise } = process.binding('util'); const setTimeoutAsync = require('util').promisify(setTimeout); 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-net-bytes-written-large.js b/test/parallel/test-net-bytes-written-large.js new file mode 100644 index 00000000000000..79a997ec5a38f1 --- /dev/null +++ b/test/parallel/test-net-bytes-written-large.js @@ -0,0 +1,67 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +// Regression test for https://github.com/nodejs/node/issues/19562: +// Writing to a socket first tries to push through as much data as possible +// without blocking synchronously, and, if that is not enough, queues more +// data up for asynchronous writing. +// Check that `bytesWritten` accounts for both parts of a write. + +const N = 10000000; +{ + // Variant 1: Write a Buffer. + const server = net.createServer(common.mustCall((socket) => { + socket.end(Buffer.alloc(N), common.mustCall(() => { + assert.strictEqual(socket.bytesWritten, N); + })); + assert.strictEqual(socket.bytesWritten, N); + })).listen(0, common.mustCall(() => { + const client = net.connect(server.address().port); + client.resume(); + client.on('close', common.mustCall(() => { + assert.strictEqual(client.bytesRead, N); + server.close(); + })); + })); +} + +{ + // Variant 2: Write a string. + const server = net.createServer(common.mustCall((socket) => { + socket.end('a'.repeat(N), common.mustCall(() => { + assert.strictEqual(socket.bytesWritten, N); + })); + assert.strictEqual(socket.bytesWritten, N); + })).listen(0, common.mustCall(() => { + const client = net.connect(server.address().port); + client.resume(); + client.on('close', common.mustCall(() => { + assert.strictEqual(client.bytesRead, N); + server.close(); + })); + })); +} + +{ + // Variant 2: writev() with mixed data. + const server = net.createServer(common.mustCall((socket) => { + socket.cork(); + socket.write('a'.repeat(N)); + assert.strictEqual(socket.bytesWritten, N); + socket.write(Buffer.alloc(N)); + assert.strictEqual(socket.bytesWritten, 2 * N); + socket.end('', common.mustCall(() => { + assert.strictEqual(socket.bytesWritten, 2 * N); + })); + socket.uncork(); + })).listen(0, common.mustCall(() => { + const client = net.connect(server.address().port); + client.resume(); + client.on('close', common.mustCall(() => { + assert.strictEqual(client.bytesRead, 2 * N); + server.close(); + })); + })); +} diff --git a/test/parallel/test-net-options-lookup.js b/test/parallel/test-net-options-lookup.js index 0b4e18f12f04f0..007be66f4516bb 100644 --- a/test/parallel/test-net-options-lookup.js +++ b/test/parallel/test-net-options-lookup.js @@ -29,5 +29,14 @@ function connectDoesNotThrow(input) { lookup: input }; - net.connect(opts); + return net.connect(opts); +} + +{ + // Verify that an error is emitted when an invalid address family is returned. + const s = connectDoesNotThrow((host, options, cb) => { + cb(null, '127.0.0.1', 100); + }); + + s.on('error', common.expectsError({ code: 'ERR_INVALID_ADDRESS_FAMILY' })); } diff --git a/test/parallel/test-os.js b/test/parallel/test-os.js index 47d4209c36a20b..62a50e6fe706a7 100644 --- a/test/parallel/test-os.js +++ b/test/parallel/test-os.js @@ -100,7 +100,7 @@ assert.ok(type.length > 0); const release = os.release(); is.string(release); assert.ok(release.length > 0); -//TODO: Check format on more than just AIX +// TODO: Check format on more than just AIX if (common.isAIX) assert.ok(/^\d+\.\d+$/.test(release)); diff --git a/test/parallel/test-performanceobserver.js b/test/parallel/test-performanceobserver.js index 2a6299952271e1..2c93e9d084b1d1 100644 --- a/test/parallel/test-performanceobserver.js +++ b/test/parallel/test-performanceobserver.js @@ -38,7 +38,6 @@ assert.strictEqual(counts[NODE_PERFORMANCE_ENTRY_TYPE_FUNCTION], 0); const observer = new PerformanceObserver(common.mustNotCall()); [1, null, undefined].forEach((i) => { - //observer.observe(i); common.expectsError( () => observer.observe(i), { diff --git a/test/parallel/test-querystring.js b/test/parallel/test-querystring.js index 2090d76b75ee34..7b77a42a167525 100644 --- a/test/parallel/test-querystring.js +++ b/test/parallel/test-querystring.js @@ -125,9 +125,9 @@ const qsColonTestCases = [ function extendedFunction() {} extendedFunction.prototype = { a: 'b' }; const qsWeirdObjects = [ - // eslint-disable-next-line no-unescaped-regexp-dot + // eslint-disable-next-line node-core/no-unescaped-regexp-dot [{ regexp: /./g }, 'regexp=', { 'regexp': '' }], - // eslint-disable-next-line no-unescaped-regexp-dot + // eslint-disable-next-line node-core/no-unescaped-regexp-dot [{ regexp: new RegExp('.', 'g') }, 'regexp=', { 'regexp': '' }], [{ fn: () => {} }, 'fn=', { 'fn': '' }], [{ fn: new Function('') }, 'fn=', { 'fn': '' }], diff --git a/test/parallel/test-regression-object-prototype.js b/test/parallel/test-regression-object-prototype.js index 01de440344d352..821c2af584ae3b 100644 --- a/test/parallel/test-regression-object-prototype.js +++ b/test/parallel/test-regression-object-prototype.js @@ -19,7 +19,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -/* eslint-disable required-modules */ +/* eslint-disable node-core/required-modules */ 'use strict'; Object.prototype.xadsadsdasasdxx = function() { diff --git a/test/parallel/test-stream-buffer-list.js b/test/parallel/test-stream-buffer-list.js index 6ea359b458f61b..d7728d1171caa3 100644 --- a/test/parallel/test-stream-buffer-list.js +++ b/test/parallel/test-stream-buffer-list.js @@ -2,7 +2,7 @@ 'use strict'; require('../common'); const assert = require('assert'); -const BufferList = require('internal/streams/BufferList'); +const BufferList = require('internal/streams/buffer_list'); // Test empty buffer list. const emptyList = new BufferList(); diff --git a/test/parallel/test-stream-finished.js b/test/parallel/test-stream-finished.js new file mode 100644 index 00000000000000..2b0c156eb06845 --- /dev/null +++ b/test/parallel/test-stream-finished.js @@ -0,0 +1,123 @@ +'use strict'; + +const common = require('../common'); +const { Writable, Readable, Transform, finished } = require('stream'); +const assert = require('assert'); +const fs = require('fs'); +const { promisify } = require('util'); + +common.crashOnUnhandledRejection(); + +{ + const rs = new Readable({ + read() {} + }); + + finished(rs, common.mustCall((err) => { + assert(!err, 'no error'); + })); + + rs.push(null); + rs.resume(); +} + +{ + const ws = new Writable({ + write(data, enc, cb) { + cb(); + } + }); + + finished(ws, common.mustCall((err) => { + assert(!err, 'no error'); + })); + + ws.end(); +} + +{ + const tr = new Transform({ + transform(data, enc, cb) { + cb(); + } + }); + + let finish = false; + let ended = false; + + tr.on('end', () => { + ended = true; + }); + + tr.on('finish', () => { + finish = true; + }); + + finished(tr, common.mustCall((err) => { + assert(!err, 'no error'); + assert(finish); + assert(ended); + })); + + tr.end(); + tr.resume(); +} + +{ + const rs = fs.createReadStream(__filename); + + rs.resume(); + finished(rs, common.mustCall()); +} + +{ + const finishedPromise = promisify(finished); + + async function run() { + const rs = fs.createReadStream(__filename); + const done = common.mustCall(); + + let ended = false; + rs.resume(); + rs.on('end', () => { + ended = true; + }); + await finishedPromise(rs); + assert(ended); + done(); + } + + run(); +} + +{ + const rs = fs.createReadStream('file-does-not-exist'); + + finished(rs, common.mustCall((err) => { + assert.strictEqual(err.code, 'ENOENT'); + })); +} + +{ + const rs = new Readable(); + + finished(rs, common.mustCall((err) => { + assert(!err, 'no error'); + })); + + rs.push(null); + rs.emit('close'); // should not trigger an error + rs.resume(); +} + +{ + const rs = new Readable(); + + finished(rs, common.mustCall((err) => { + assert(err, 'premature close error'); + })); + + rs.emit('close'); // should trigger error + rs.push(null); + rs.resume(); +} diff --git a/test/parallel/test-stream-pipeline.js b/test/parallel/test-stream-pipeline.js new file mode 100644 index 00000000000000..e63ee2ed117679 --- /dev/null +++ b/test/parallel/test-stream-pipeline.js @@ -0,0 +1,483 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const { Stream, Writable, Readable, Transform, pipeline } = require('stream'); +const assert = require('assert'); +const http = require('http'); +const http2 = require('http2'); +const { promisify } = require('util'); + +common.crashOnUnhandledRejection(); + +{ + let finished = false; + const processed = []; + const expected = [ + Buffer.from('a'), + Buffer.from('b'), + Buffer.from('c') + ]; + + const read = new Readable({ + read() {} + }); + + const write = new Writable({ + write(data, enc, cb) { + processed.push(data); + cb(); + } + }); + + write.on('finish', () => { + finished = true; + }); + + for (let i = 0; i < expected.length; i++) { + read.push(expected[i]); + } + read.push(null); + + pipeline(read, write, common.mustCall((err) => { + assert.ok(!err, 'no error'); + assert.ok(finished); + assert.deepStrictEqual(processed, expected); + })); +} + +{ + const read = new Readable({ + read() {} + }); + + assert.throws(() => { + pipeline(read, () => {}); + }, /ERR_MISSING_ARGS/); + assert.throws(() => { + pipeline(() => {}); + }, /ERR_MISSING_ARGS/); + assert.throws(() => { + pipeline(); + }, /ERR_MISSING_ARGS/); +} + +{ + const read = new Readable({ + read() {} + }); + + const write = new Writable({ + write(data, enc, cb) { + cb(); + } + }); + + read.push('data'); + setImmediate(() => read.destroy()); + + pipeline(read, write, common.mustCall((err) => { + assert.ok(err, 'should have an error'); + })); +} + +{ + const read = new Readable({ + read() {} + }); + + const write = new Writable({ + write(data, enc, cb) { + cb(); + } + }); + + read.push('data'); + setImmediate(() => read.destroy(new Error('kaboom'))); + + const dst = pipeline(read, write, common.mustCall((err) => { + assert.deepStrictEqual(err, new Error('kaboom')); + })); + + assert.strictEqual(dst, write); +} + +{ + const read = new Readable({ + read() {} + }); + + const transform = new Transform({ + transform(data, enc, cb) { + cb(new Error('kaboom')); + } + }); + + const write = new Writable({ + write(data, enc, cb) { + cb(); + } + }); + + read.on('close', common.mustCall()); + transform.on('close', common.mustCall()); + write.on('close', common.mustCall()); + + const dst = pipeline(read, transform, write, common.mustCall((err) => { + assert.deepStrictEqual(err, new Error('kaboom')); + })); + + assert.strictEqual(dst, write); + + read.push('hello'); +} + +{ + const server = http.createServer((req, res) => { + const rs = new Readable({ + read() { + rs.push('hello'); + rs.push(null); + } + }); + + pipeline(rs, res); + }); + + server.listen(0, () => { + const req = http.request({ + port: server.address().port + }); + + req.end(); + req.on('response', (res) => { + const buf = []; + res.on('data', (data) => buf.push(data)); + res.on('end', common.mustCall(() => { + assert.deepStrictEqual( + Buffer.concat(buf), + Buffer.from('hello') + ); + server.close(); + })); + }); + }); +} + +{ + const server = http.createServer((req, res) => { + const rs = new Readable({ + read() { + rs.push('hello'); + }, + destroy: common.mustCall((err, cb) => { + // prevents fd leaks by destroying http pipelines + cb(); + }) + }); + + pipeline(rs, res); + }); + + server.listen(0, () => { + const req = http.request({ + port: server.address().port + }); + + req.end(); + req.on('response', (res) => { + setImmediate(() => { + res.destroy(); + server.close(); + }); + }); + }); +} + +{ + const server = http.createServer((req, res) => { + const rs = new Readable({ + read() { + rs.push('hello'); + }, + destroy: common.mustCall((err, cb) => { + cb(); + }) + }); + + pipeline(rs, res); + }); + + let cnt = 10; + + const badSink = new Writable({ + write(data, enc, cb) { + cnt--; + if (cnt === 0) cb(new Error('kaboom')); + else cb(); + } + }); + + server.listen(0, () => { + const req = http.request({ + port: server.address().port + }); + + req.end(); + req.on('response', (res) => { + pipeline(res, badSink, common.mustCall((err) => { + assert.deepStrictEqual(err, new Error('kaboom')); + server.close(); + })); + }); + }); +} + +{ + const server = http.createServer((req, res) => { + pipeline(req, res, common.mustCall()); + }); + + server.listen(0, () => { + const req = http.request({ + port: server.address().port + }); + + const rs = new Readable({ + read() { + rs.push('hello'); + } + }); + + pipeline(rs, req, common.mustCall(() => { + server.close(); + })); + + req.on('response', (res) => { + let cnt = 10; + res.on('data', () => { + cnt--; + if (cnt === 0) rs.destroy(); + }); + }); + }); +} + +{ + const server = http2.createServer((req, res) => { + pipeline(req, res, common.mustCall()); + }); + + server.listen(0, () => { + const url = `http://localhost:${server.address().port}`; + const client = http2.connect(url); + const req = client.request({ ':method': 'POST' }); + + const rs = new Readable({ + read() { + rs.push('hello'); + } + }); + + pipeline(rs, req, common.mustCall((err) => { + // TODO: this is working around an http2 bug + // where the client keeps the event loop going + // (replacing the rs.destroy() with req.end() + // exits it so seems to be a destroy bug there + client.unref(); + + server.close(); + client.close(); + })); + + let cnt = 10; + req.on('data', (data) => { + cnt--; + if (cnt === 0) rs.destroy(); + }); + }); +} + +{ + const makeTransform = () => { + const tr = new Transform({ + transform(data, enc, cb) { + cb(null, data); + } + }); + + tr.on('close', common.mustCall()); + return tr; + }; + + const rs = new Readable({ + read() { + rs.push('hello'); + } + }); + + let cnt = 10; + + const ws = new Writable({ + write(data, enc, cb) { + cnt--; + if (cnt === 0) return cb(new Error('kaboom')); + cb(); + } + }); + + rs.on('close', common.mustCall()); + ws.on('close', common.mustCall()); + + pipeline( + rs, + makeTransform(), + makeTransform(), + makeTransform(), + makeTransform(), + makeTransform(), + makeTransform(), + ws, + common.mustCall((err) => { + assert.deepStrictEqual(err, new Error('kaboom')); + }) + ); +} + +{ + const oldStream = new Stream(); + + oldStream.pause = oldStream.resume = () => {}; + oldStream.write = (data) => { + oldStream.emit('data', data); + return true; + }; + oldStream.end = () => { + oldStream.emit('end'); + }; + + const expected = [ + Buffer.from('hello'), + Buffer.from('world') + ]; + + const rs = new Readable({ + read() { + for (let i = 0; i < expected.length; i++) { + rs.push(expected[i]); + } + rs.push(null); + } + }); + + const ws = new Writable({ + write(data, enc, cb) { + assert.deepStrictEqual(data, expected.shift()); + cb(); + } + }); + + let finished = false; + + ws.on('finish', () => { + finished = true; + }); + + pipeline( + rs, + oldStream, + ws, + common.mustCall((err) => { + assert(!err, 'no error'); + assert(finished, 'last stream finished'); + }) + ); +} + +{ + const oldStream = new Stream(); + + oldStream.pause = oldStream.resume = () => {}; + oldStream.write = (data) => { + oldStream.emit('data', data); + return true; + }; + oldStream.end = () => { + oldStream.emit('end'); + }; + + const destroyableOldStream = new Stream(); + + destroyableOldStream.pause = destroyableOldStream.resume = () => {}; + destroyableOldStream.destroy = common.mustCall(() => { + destroyableOldStream.emit('close'); + }); + destroyableOldStream.write = (data) => { + destroyableOldStream.emit('data', data); + return true; + }; + destroyableOldStream.end = () => { + destroyableOldStream.emit('end'); + }; + + const rs = new Readable({ + read() { + rs.destroy(new Error('stop')); + } + }); + + const ws = new Writable({ + write(data, enc, cb) { + cb(); + } + }); + + let finished = false; + + ws.on('finish', () => { + finished = true; + }); + + pipeline( + rs, + oldStream, + destroyableOldStream, + ws, + common.mustCall((err) => { + assert.deepStrictEqual(err, new Error('stop')); + assert(!finished, 'should not finish'); + }) + ); +} + +{ + const pipelinePromise = promisify(pipeline); + + async function run() { + const read = new Readable({ + read() {} + }); + + const write = new Writable({ + write(data, enc, cb) { + cb(); + } + }); + + read.push('data'); + read.push(null); + + let finished = false; + + write.on('finish', () => { + finished = true; + }); + + await pipelinePromise(read, write); + + assert(finished); + } + + run(); +} diff --git a/test/parallel/test-stream-writable-destroy.js b/test/parallel/test-stream-writable-destroy.js index 87e55eccc3f6fd..46c48511177813 100644 --- a/test/parallel/test-stream-writable-destroy.js +++ b/test/parallel/test-stream-writable-destroy.js @@ -185,3 +185,17 @@ const { inherits } = require('util'); assert.strictEqual(expected, err); })); } + +{ + // Checks that `._undestroy()` restores the state so that `final` will be + // called again. + const write = new Writable({ + write: common.mustNotCall(), + final: common.mustCall((cb) => cb(), 2) + }); + + write.end(); + write.destroy(); + write._undestroy(); + write.end(); +} diff --git a/test/parallel/test-stream2-basic.js b/test/parallel/test-stream2-basic.js index 21bd1f69313f66..0694adb5c5905e 100644 --- a/test/parallel/test-stream2-basic.js +++ b/test/parallel/test-stream2-basic.js @@ -65,8 +65,6 @@ class TestReader extends R { } } -///// - class TestWriter extends EE { constructor() { super(); diff --git a/test/parallel/test-stream2-readable-from-list.js b/test/parallel/test-stream2-readable-from-list.js index 965f962638d586..f812f75e7ca8b5 100644 --- a/test/parallel/test-stream2-readable-from-list.js +++ b/test/parallel/test-stream2-readable-from-list.js @@ -24,7 +24,7 @@ require('../common'); const assert = require('assert'); const fromList = require('_stream_readable')._fromList; -const BufferList = require('internal/streams/BufferList'); +const BufferList = require('internal/streams/buffer_list'); function bufferListFromArray(arr) { const bl = new BufferList(); diff --git a/test/parallel/test-string-decoder.js b/test/parallel/test-string-decoder.js index f1b11477008a7f..4e81f47db085e3 100644 --- a/test/parallel/test-string-decoder.js +++ b/test/parallel/test-string-decoder.js @@ -29,6 +29,11 @@ const StringDecoder = require('string_decoder').StringDecoder; let decoder = new StringDecoder(); assert.strictEqual(decoder.encoding, 'utf8'); +// Should work without 'new' keyword +const decoder2 = {}; +StringDecoder.call(decoder2); +assert.strictEqual(decoder2.encoding, 'utf8'); + // UTF-8 test('utf-8', Buffer.from('$', 'utf-8'), '$'); test('utf-8', Buffer.from('¢', 'utf-8'), '¢'); @@ -80,6 +85,11 @@ test('utf16le', Buffer.from('3DD84DDC', 'hex'), '\ud83d\udc4d'); // thumbs up // Additional UTF-8 tests decoder = new StringDecoder('utf8'); assert.strictEqual(decoder.write(Buffer.from('E1', 'hex')), ''); + +// A quick test for lastNeed & lastTotal which are undocumented. +assert.strictEqual(decoder.lastNeed, 2); +assert.strictEqual(decoder.lastTotal, 3); + assert.strictEqual(decoder.end(), '\ufffd'); decoder = new StringDecoder('utf8'); @@ -124,6 +134,10 @@ assert.strictEqual(decoder.write(Buffer.from('3DD8', 'hex')), ''); assert.strictEqual(decoder.write(Buffer.from('4D', 'hex')), ''); assert.strictEqual(decoder.end(), '\ud83d'); +decoder = new StringDecoder('utf16le'); +assert.strictEqual(decoder.write(Buffer.from('3DD84D', 'hex')), '\ud83d'); +assert.strictEqual(decoder.end(), ''); + common.expectsError( () => new StringDecoder(1), { diff --git a/test/parallel/test-tcp-wrap-connect.js b/test/parallel/test-tcp-wrap-connect.js index 9f5560a385086a..36f87d1863ef36 100644 --- a/test/parallel/test-tcp-wrap-connect.js +++ b/test/parallel/test-tcp-wrap-connect.js @@ -33,8 +33,6 @@ function makeConnection() { }; } -///// - let connectCount = 0; let endCount = 0; let shutdownCount = 0; diff --git a/test/parallel/test-tls-cnnic-whitelist.js b/test/parallel/test-tls-cnnic-whitelist.js index 37a96e4eb7e0c0..e08e93013f6aca 100644 --- a/test/parallel/test-tls-cnnic-whitelist.js +++ b/test/parallel/test-tls-cnnic-whitelist.js @@ -14,22 +14,6 @@ function loadPEM(n) { } const testCases = [ - { // Test 0: for the check of a cert not in the whitelist. - // agent7-cert.pem is issued by the fake CNNIC root CA so that its - // hash is not listed in the whitelist. - // fake-cnnic-root-cert has the same subject name as the original - // rootCA. - serverOpts: { - key: loadPEM('agent7-key'), - cert: loadPEM('agent7-cert') - }, - clientOpts: { - port: undefined, - rejectUnauthorized: true, - ca: [loadPEM('fake-cnnic-root-cert')] - }, - errorCode: 'UNABLE_TO_VERIFY_LEAF_SIGNATURE' - }, // Test 1: for the fix of node#2061 // agent6-cert.pem is signed by intermediate cert of ca3. // The server has a cert chain of agent6->ca3->ca1(root) but @@ -58,7 +42,7 @@ function runTest(tindex) { const server = tls.createServer(tcase.serverOpts, (s) => { s.resume(); }).listen(0, common.mustCall(function() { - tcase.clientOpts = this.address().port; + tcase.clientOpts.port = this.address().port; const client = tls.connect(tcase.clientOpts); client.on('error', common.mustCall((e) => { assert.strictEqual(e.code, tcase.errorCode); diff --git a/test/parallel/test-tls-server-verify.js b/test/parallel/test-tls-server-verify.js index eeea8b030da719..b8291af8d69f07 100644 --- a/test/parallel/test-tls-server-verify.js +++ b/test/parallel/test-tls-server-verify.js @@ -227,12 +227,7 @@ function runClient(prefix, port, options, cb) { } }); - //client.stdout.pipe(process.stdout); - client.on('exit', function(code) { - //assert.strictEqual( - // 0, code, - // `${prefix}${options.name}: s_client exited with error code ${code}`); if (options.shouldReject) { assert.strictEqual( true, rejected, diff --git a/test/parallel/test-tty-get-color-depth.js b/test/parallel/test-tty-get-color-depth.js deleted file mode 100644 index a4c7ffacd17f82..00000000000000 --- a/test/parallel/test-tty-get-color-depth.js +++ /dev/null @@ -1,52 +0,0 @@ -'use strict'; - -const common = require('../common'); -const assert = require('assert').strict; -/* eslint-disable no-restricted-properties */ -const { openSync } = require('fs'); -const tty = require('tty'); - -const { WriteStream } = require('tty'); - -// Do our best to grab a tty fd. -function getTTYfd() { - const ttyFd = [1, 2, 4, 5].find(tty.isatty); - if (ttyFd === undefined) { - try { - return openSync('/dev/tty'); - } catch (e) { - // There aren't any tty fd's available to use. - return -1; - } - } - return ttyFd; -} - -const fd = getTTYfd(); - -// Give up if we did not find a tty -if (fd === -1) - common.skip(); - -const writeStream = new WriteStream(fd); - -let depth = writeStream.getColorDepth(); - -assert.equal(typeof depth, 'number'); -assert(depth >= 1 && depth <= 24); - -// If the terminal does not support colors, skip the rest -if (depth === 1) - common.skip(); - -assert.notEqual(writeStream.getColorDepth({ TERM: 'dumb' }), depth); - -// Deactivate colors -const tmp = process.env.NODE_DISABLE_COLORS; -process.env.NODE_DISABLE_COLORS = 1; - -depth = writeStream.getColorDepth(); - -assert.equal(depth, 1); - -process.env.NODE_DISABLE_COLORS = tmp; diff --git a/test/parallel/test-types.js b/test/parallel/test-types.js deleted file mode 100644 index ea8adc6cb17d3a..00000000000000 --- a/test/parallel/test-types.js +++ /dev/null @@ -1,57 +0,0 @@ -'use strict'; - -// Flags: --expose-internals - -require('../common'); -const assert = require('assert'); -const types = require('internal/util/types'); - -const primitive = true; -const arrayBuffer = new ArrayBuffer(); -const dataView = new DataView(arrayBuffer); -const int32Array = new Int32Array(arrayBuffer); -const uint8Array = new Uint8Array(arrayBuffer); -const buffer = Buffer.from(arrayBuffer); - -const fakeDataView = Object.create(DataView.prototype); -const fakeInt32Array = Object.create(Int32Array.prototype); -const fakeUint8Array = Object.create(Uint8Array.prototype); -const fakeBuffer = Object.create(Buffer.prototype); - -const stealthyDataView = - Object.setPrototypeOf(new DataView(arrayBuffer), Uint8Array.prototype); -const stealthyInt32Array = - Object.setPrototypeOf(new Int32Array(arrayBuffer), uint8Array); -const stealthyUint8Array = - Object.setPrototypeOf(new Uint8Array(arrayBuffer), ArrayBuffer.prototype); - -const all = [ - primitive, arrayBuffer, dataView, int32Array, uint8Array, buffer, - fakeDataView, fakeInt32Array, fakeUint8Array, fakeBuffer, - stealthyDataView, stealthyInt32Array, stealthyUint8Array -]; - -const expected = { - isArrayBufferView: [ - dataView, int32Array, uint8Array, buffer, - stealthyDataView, stealthyInt32Array, stealthyUint8Array - ], - isTypedArray: [ - int32Array, uint8Array, buffer, stealthyInt32Array, stealthyUint8Array - ], - isUint8Array: [ - uint8Array, buffer, stealthyUint8Array - ] -}; - -for (const testedFunc of Object.keys(expected)) { - const func = types[testedFunc]; - const yup = []; - for (const value of all) { - if (func(value)) { - yup.push(value); - } - } - console.log('Testing', testedFunc); - assert.deepStrictEqual(yup, expected[testedFunc]); -} diff --git a/test/parallel/test-url-parse-format.js b/test/parallel/test-url-parse-format.js index 0e12fe52516f16..f4e72ee5ef4896 100644 --- a/test/parallel/test-url-parse-format.js +++ b/test/parallel/test-url-parse-format.js @@ -581,17 +581,17 @@ const parseTests = { href: 'git+http://github.com/joyent/node.git' }, - //if local1@domain1 is uses as a relative URL it may - //be parse into auth@hostname, but here there is no - //way to make it work in url.parse, I add the test to be explicit + // If local1@domain1 is uses as a relative URL it may + // be parse into auth@hostname, but here there is no + // way to make it work in url.parse, I add the test to be explicit 'local1@domain1': { pathname: 'local1@domain1', path: 'local1@domain1', href: 'local1@domain1' }, - //While this may seem counter-intuitive, a browser will parse - // as a path. + // While this may seem counter-intuitive, a browser will parse + // as a path. 'www.example.com': { href: 'www.example.com', pathname: 'www.example.com', diff --git a/test/parallel/test-url-relative.js b/test/parallel/test-url-relative.js index bd690c27afc777..d8532fcfee0ed6 100644 --- a/test/parallel/test-url-relative.js +++ b/test/parallel/test-url-relative.js @@ -81,7 +81,7 @@ const bases = [ 'http:///s//a/b/c' ]; -//[to, from, result] +// [to, from, result] const relativeTests2 = [ // http://lists.w3.org/Archives/Public/uri/2004Feb/0114.html ['../c', 'foo:a/b', 'foo:c'], @@ -106,11 +106,11 @@ const relativeTests2 = [ ['/g', bases[0], 'http://a/g'], ['//g', bases[0], 'http://g/'], // changed with RFC 2396bis - //('?y', bases[0], 'http://a/b/c/d;p?y'], + // ('?y', bases[0], 'http://a/b/c/d;p?y'], ['?y', bases[0], 'http://a/b/c/d;p?y'], ['g?y', bases[0], 'http://a/b/c/g?y'], // changed with RFC 2396bis - //('#s', bases[0], CURRENT_DOC_URI + '#s'], + // ('#s', bases[0], CURRENT_DOC_URI + '#s'], ['#s', bases[0], 'http://a/b/c/d;p?q#s'], ['g#s', bases[0], 'http://a/b/c/g#s'], ['g?y#s', bases[0], 'http://a/b/c/g?y#s'], @@ -118,7 +118,7 @@ const relativeTests2 = [ ['g;x', bases[0], 'http://a/b/c/g;x'], ['g;x?y#s', bases[0], 'http://a/b/c/g;x?y#s'], // changed with RFC 2396bis - //('', bases[0], CURRENT_DOC_URI], + // ('', bases[0], CURRENT_DOC_URI], ['', bases[0], 'http://a/b/c/d;p?q'], ['.', bases[0], 'http://a/b/c/'], ['./', bases[0], 'http://a/b/c/'], @@ -131,10 +131,10 @@ const relativeTests2 = [ ['../../../g', bases[0], ('http://a/../g', 'http://a/g')], ['../../../../g', bases[0], ('http://a/../../g', 'http://a/g')], // changed with RFC 2396bis - //('/./g', bases[0], 'http://a/./g'], + // ('/./g', bases[0], 'http://a/./g'], ['/./g', bases[0], 'http://a/g'], // changed with RFC 2396bis - //('/../g', bases[0], 'http://a/../g'], + // ('/../g', bases[0], 'http://a/../g'], ['/../g', bases[0], 'http://a/g'], ['g.', bases[0], 'http://a/b/c/g.'], ['.g', bases[0], 'http://a/b/c/.g'], @@ -163,7 +163,7 @@ const relativeTests2 = [ ['/g', bases[1], 'http://a/g'], ['//g', bases[1], 'http://g/'], // changed in RFC 2396bis - //('?y', bases[1], 'http://a/b/c/?y'], + // ('?y', bases[1], 'http://a/b/c/?y'], ['?y', bases[1], 'http://a/b/c/d;p?y'], ['g?y', bases[1], 'http://a/b/c/g?y'], ['g?y/./x', bases[1], 'http://a/b/c/g?y/./x'], @@ -345,7 +345,7 @@ const relativeTests2 = [ 'file:///C:/DEV/Haskell/lib/HXmlToolbox-3.01/examples/mini1.xml'], ['../b/c', 'foo:a/y/z', 'foo:a/b/c'], - //changeing auth + // changeing auth ['http://diff:auth@www.example.com', 'http://asdf:qwer@www.example.com', 'http://diff:auth@www.example.com/'], @@ -383,10 +383,10 @@ relativeTests2.forEach(function(relativeTest) { ` == ${e}\n actual=${a}`); }); -//if format and parse are inverse operations then -//resolveObject(parse(x), y) == parse(resolve(x, y)) +// If format and parse are inverse operations then +// resolveObject(parse(x), y) == parse(resolve(x, y)) -//format: [from, path, expected] +// format: [from, path, expected] relativeTests.forEach(function(relativeTest) { let actual = url.resolveObject(url.parse(relativeTest[0]), relativeTest[1]); let expected = url.parse(relativeTest[2]); @@ -402,7 +402,7 @@ relativeTests.forEach(function(relativeTest) { `actual: ${actual}`); }); -//format: [to, from, result] +// format: [to, from, result] // the test: ['.//g', 'f:/a', 'f://g'] is a fundamental problem // url.parse('f:/a') does not have a host // url.resolve('f:/a', './/g') does not have a host because you have moved diff --git a/test/parallel/test-util-internal.js b/test/parallel/test-util-internal.js index cb3d3122f9be4e..8be20a86ef6c2b 100644 --- a/test/parallel/test-util-internal.js +++ b/test/parallel/test-util-internal.js @@ -6,7 +6,17 @@ const assert = require('assert'); const fixtures = require('../common/fixtures'); const binding = process.binding('util'); -const kArrowMessagePrivateSymbolIndex = binding.arrow_message_private_symbol; +const { + arrow_message_private_symbol: kArrowMessagePrivateSymbolIndex, + safeGetenv +} = binding; + +for (const oneEnv in process.env) { + assert.strictEqual( + safeGetenv(oneEnv), + process.env[oneEnv] + ); +} function getHiddenValue(obj, index) { return function() { diff --git a/test/parallel/test-util-types.js b/test/parallel/test-util-types.js new file mode 100644 index 00000000000000..e2c7fd6dad0994 --- /dev/null +++ b/test/parallel/test-util-types.js @@ -0,0 +1,134 @@ +// Flags: --experimental-vm-modules +/* global SharedArrayBuffer */ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const { types, inspect } = require('util'); +const vm = require('vm'); +const { JSStream } = process.binding('js_stream'); + +common.crashOnUnhandledRejection(); + +const external = (new JSStream())._externalStream; +const wasmBuffer = fixtures.readSync('test.wasm'); + +for (const [ value, _method ] of [ + [ external, 'isExternal' ], + [ new Date() ], + [ (function() { return arguments; })(), 'isArgumentsObject' ], + [ new Boolean(), 'isBooleanObject' ], + [ new Number(), 'isNumberObject' ], + [ new String(), 'isStringObject' ], + [ Object(Symbol()), 'isSymbolObject' ], + [ new Error(), 'isNativeError' ], + [ new RegExp() ], + [ async function() {}, 'isAsyncFunction' ], + [ function*() {}, 'isGeneratorFunction' ], + [ (function*() {})(), 'isGeneratorObject' ], + [ Promise.resolve() ], + [ new Map() ], + [ new Set() ], + [ (new Map())[Symbol.iterator](), 'isMapIterator' ], + [ (new Set())[Symbol.iterator](), 'isSetIterator' ], + [ new WeakMap() ], + [ new WeakSet() ], + [ new ArrayBuffer() ], + [ new Uint8Array() ], + [ new Uint8ClampedArray() ], + [ new Uint16Array() ], + [ new Uint32Array() ], + [ new Int8Array() ], + [ new Int16Array() ], + [ new Int32Array() ], + [ new Float32Array() ], + [ new Float64Array() ], + [ Object.defineProperty(new Uint8Array(), + Symbol.toStringTag, + { value: 'foo' }) ], + [ new DataView(new ArrayBuffer()) ], + [ new SharedArrayBuffer() ], + [ new Proxy({}, {}), 'isProxy' ], + [ new WebAssembly.Module(wasmBuffer), 'isWebAssemblyCompiledModule' ], +]) { + const method = _method || `is${value.constructor.name}`; + assert(method in types, `Missing ${method} for ${inspect(value)}`); + assert(types[method](value), `Want ${inspect(value)} to match ${method}`); + + for (const key of Object.keys(types)) { + if ((types.isArrayBufferView(value) || + types.isAnyArrayBuffer(value)) && key.includes('Array')) { + continue; + } + + assert.strictEqual(types[key](value), + key === method, + `${inspect(value)}: ${key}, ` + + `${method}, ${types[key](value)}`); + } +} + +{ + assert(!types.isUint8Array({ [Symbol.toStringTag]: 'Uint8Array' })); + assert(types.isUint8Array(vm.runInNewContext('new Uint8Array'))); +} + +{ + const primitive = true; + const arrayBuffer = new ArrayBuffer(); + const dataView = new DataView(arrayBuffer); + const int32Array = new Int32Array(arrayBuffer); + const uint8Array = new Uint8Array(arrayBuffer); + const buffer = Buffer.from(arrayBuffer); + + const fakeDataView = Object.create(DataView.prototype); + const fakeInt32Array = Object.create(Int32Array.prototype); + const fakeUint8Array = Object.create(Uint8Array.prototype); + const fakeBuffer = Object.create(Buffer.prototype); + + const stealthyDataView = + Object.setPrototypeOf(new DataView(arrayBuffer), Uint8Array.prototype); + const stealthyInt32Array = + Object.setPrototypeOf(new Int32Array(arrayBuffer), uint8Array); + const stealthyUint8Array = + Object.setPrototypeOf(new Uint8Array(arrayBuffer), ArrayBuffer.prototype); + + const all = [ + primitive, arrayBuffer, dataView, int32Array, uint8Array, buffer, + fakeDataView, fakeInt32Array, fakeUint8Array, fakeBuffer, + stealthyDataView, stealthyInt32Array, stealthyUint8Array + ]; + + const expected = { + isArrayBufferView: [ + dataView, int32Array, uint8Array, buffer, + stealthyDataView, stealthyInt32Array, stealthyUint8Array + ], + isTypedArray: [ + int32Array, uint8Array, buffer, stealthyInt32Array, stealthyUint8Array + ], + isUint8Array: [ + uint8Array, buffer, stealthyUint8Array + ] + }; + + for (const testedFunc of Object.keys(expected)) { + const func = types[testedFunc]; + const yup = []; + for (const value of all) { + if (func(value)) { + yup.push(value); + } + } + console.log('Testing', testedFunc); + assert.deepStrictEqual(yup, expected[testedFunc]); + } +} + +(async () => { + const m = new vm.Module(''); + await m.link(() => 0); + m.instantiate(); + await m.evaluate(); + assert.ok(types.isModuleNamespaceObject(m.namespace)); +})(); diff --git a/test/parallel/test-util.js b/test/parallel/test-util.js index a4aec080a9a0ad..ecdb32b58d8b1f 100644 --- a/test/parallel/test-util.js +++ b/test/parallel/test-util.js @@ -25,7 +25,6 @@ const common = require('../common'); const assert = require('assert'); const util = require('util'); const errors = require('internal/errors'); -const binding = process.binding('util'); const context = require('vm').runInNewContext; // isArray @@ -156,22 +155,23 @@ util.debug('test'); util.error('test'); { - // binding.isNativeError() - assert.strictEqual(binding.isNativeError(new Error()), true); - assert.strictEqual(binding.isNativeError(new TypeError()), true); - assert.strictEqual(binding.isNativeError(new SyntaxError()), true); - assert.strictEqual(binding.isNativeError(new (context('Error'))()), true); - assert.strictEqual(binding.isNativeError(new (context('TypeError'))()), true); - assert.strictEqual(binding.isNativeError(new (context('SyntaxError'))()), + assert.strictEqual(util.types.isNativeError(new Error()), true); + assert.strictEqual(util.types.isNativeError(new TypeError()), true); + assert.strictEqual(util.types.isNativeError(new SyntaxError()), true); + assert.strictEqual(util.types.isNativeError(new (context('Error'))()), true); - assert.strictEqual(binding.isNativeError({}), false); - assert.strictEqual(binding.isNativeError({ name: 'Error', message: '' }), + assert.strictEqual(util.types.isNativeError(new (context('TypeError'))()), + true); + assert.strictEqual(util.types.isNativeError(new (context('SyntaxError'))()), + true); + assert.strictEqual(util.types.isNativeError({}), false); + assert.strictEqual(util.types.isNativeError({ name: 'Error', message: '' }), false); - assert.strictEqual(binding.isNativeError([]), false); - assert.strictEqual(binding.isNativeError(Object.create(Error.prototype)), + assert.strictEqual(util.types.isNativeError([]), false); + assert.strictEqual(util.types.isNativeError(Object.create(Error.prototype)), false); assert.strictEqual( - binding.isNativeError(new errors.Error('ERR_IPC_CHANNEL_CLOSED')), + util.types.isNativeError(new errors.Error('ERR_IPC_CHANNEL_CLOSED')), true ); } diff --git a/test/parallel/test-v8-serdes-sharedarraybuffer.js b/test/parallel/test-v8-serdes-sharedarraybuffer.js index 6ba12e38d85894..01db30818153b8 100644 --- a/test/parallel/test-v8-serdes-sharedarraybuffer.js +++ b/test/parallel/test-v8-serdes-sharedarraybuffer.js @@ -1,4 +1,4 @@ -/*global SharedArrayBuffer*/ +/* global SharedArrayBuffer */ 'use strict'; const common = require('../common'); diff --git a/test/parallel/test-vm-global-property-interceptors.js b/test/parallel/test-vm-global-property-interceptors.js index e946b3fc5e463c..8571fbe19fcb60 100644 --- a/test/parallel/test-vm-global-property-interceptors.js +++ b/test/parallel/test-vm-global-property-interceptors.js @@ -44,7 +44,7 @@ const result = { result; `, ctx); -//eslint-disable-next-line no-restricted-properties +// eslint-disable-next-line no-restricted-properties assert.deepEqual(result, { a: { value: 'a', writable: true, enumerable: true, configurable: true }, b: { value: 'b', writable: false, enumerable: false, configurable: false }, diff --git a/test/parallel/test-vm-new-script-new-context.js b/test/parallel/test-vm-new-script-new-context.js index dfb43e42d1cc82..b0ef06756bee6d 100644 --- a/test/parallel/test-vm-new-script-new-context.js +++ b/test/parallel/test-vm-new-script-new-context.js @@ -54,7 +54,7 @@ const Script = require('vm').Script; script.runInNewContext(); assert.strictEqual(5, global.hello); - // cleanup + // Cleanup delete global.hello; } @@ -72,7 +72,7 @@ const Script = require('vm').Script; assert.strictEqual(2, global.obj.bar); assert.strictEqual(2, global.foo); - //cleanup + // cleanup delete global.code; delete global.foo; delete global.obj; diff --git a/test/parallel/test-zlib-zero-windowBits.js b/test/parallel/test-zlib-zero-windowBits.js new file mode 100644 index 00000000000000..a62e6148e33df7 --- /dev/null +++ b/test/parallel/test-zlib-zero-windowBits.js @@ -0,0 +1,33 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + + +// windowBits is a special case in zlib. On the compression side, 0 is invalid. +// On the decompression side, it indicates that zlib should use the value from +// the header of the compressed stream. +{ + const inflate = zlib.createInflate({ windowBits: 0 }); + assert(inflate instanceof zlib.Inflate); +} + +{ + const gunzip = zlib.createGunzip({ windowBits: 0 }); + assert(gunzip instanceof zlib.Gunzip); +} + +{ + const unzip = zlib.createUnzip({ windowBits: 0 }); + assert(unzip instanceof zlib.Unzip); +} + +{ + common.expectsError(() => zlib.createGzip({ windowBits: 0 }), { + code: 'ERR_OUT_OF_RANGE', + type: RangeError, + message: 'The value of "options.windowBits" is out of range. ' + + 'It must be >= 8 and <= 15. Received 0' + }); +} diff --git a/test/pseudo-tty/console_colors.js b/test/pseudo-tty/console_colors.js new file mode 100644 index 00000000000000..dd16a0c028dccd --- /dev/null +++ b/test/pseudo-tty/console_colors.js @@ -0,0 +1,9 @@ +'use strict'; +require('../common'); +// Make this test OS-independent by overriding stdio getColorDepth(). +process.stdout.getColorDepth = () => 8; +process.stderr.getColorDepth = () => 8; + +console.log({ foo: 'bar' }); +console.log('%s q', 'string'); +console.log('%o with object format param', { foo: 'bar' }); diff --git a/test/pseudo-tty/console_colors.out b/test/pseudo-tty/console_colors.out new file mode 100644 index 00000000000000..f0ee5e42d60db5 --- /dev/null +++ b/test/pseudo-tty/console_colors.out @@ -0,0 +1,3 @@ +{ foo: *[32m'bar'*[39m } +string q +{ foo: *[32m'bar'*[39m } with object format param diff --git a/test/pseudo-tty/test-assert-colors.js b/test/pseudo-tty/test-assert-colors.js new file mode 100644 index 00000000000000..7c5845bdaa271a --- /dev/null +++ b/test/pseudo-tty/test-assert-colors.js @@ -0,0 +1,18 @@ +'use strict'; +require('../common'); +const assert = require('assert').strict; + +try { + // Activate colors even if the tty does not support colors. + process.env.COLORTERM = '1'; + assert.deepStrictEqual([1, 2], [2, 2]); +} catch (err) { + const expected = 'Input A expected to deepStrictEqual input B:\n' + + '\u001b[32m+ expected\u001b[39m \u001b[31m- actual\u001b[39m\n\n' + + ' [\n' + + '\u001b[31m-\u001b[39m 1,\n' + + '\u001b[32m+\u001b[39m 2,\n' + + ' 2\n' + + ' ]'; + assert.strictEqual(err.message, expected); +} diff --git a/test/pseudo-tty/test-assert-colors.out b/test/pseudo-tty/test-assert-colors.out new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/test/pseudo-tty/test-async-wrap-getasyncid-tty.js b/test/pseudo-tty/test-async-wrap-getasyncid-tty.js new file mode 100644 index 00000000000000..d931a7fdb060cb --- /dev/null +++ b/test/pseudo-tty/test-async-wrap-getasyncid-tty.js @@ -0,0 +1,43 @@ +'use strict'; + +// see also test/sequential/test-async-wrap-getasyncid.js + +const common = require('../common'); +const assert = require('assert'); +const tty_wrap = process.binding('tty_wrap'); +const { TTYWRAP } = process.binding('async_wrap').Providers; +const providers = { TTYWRAP }; + +// Make sure that the TTYWRAP Provider is tested. +{ + const hooks = require('async_hooks').createHook({ + init(id, type) { + if (type === 'NONE') + throw new Error('received a provider type of NONE'); + delete providers[type]; + }, + }).enable(); + process.on('beforeExit', common.mustCall(() => { + process.removeAllListeners('uncaughtException'); + hooks.disable(); + + const objKeys = Object.keys(providers); + if (objKeys.length > 0) + process._rawDebug(objKeys); + assert.strictEqual(objKeys.length, 0); + })); +} + +function testInitialized(req, ctor_name) { + assert.strictEqual(typeof req.getAsyncId, 'function'); + assert(Number.isSafeInteger(req.getAsyncId())); + assert(req.getAsyncId() > 0); + assert.strictEqual(req.constructor.name, ctor_name); +} + +{ + const ttyFd = common.getTTYfd(); + + const handle = new tty_wrap.TTY(ttyFd, false); + testInitialized(handle, 'TTY'); +} diff --git a/test/pseudo-tty/test-async-wrap-getasyncid-tty.out b/test/pseudo-tty/test-async-wrap-getasyncid-tty.out new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/test/pseudo-tty/test-tty-get-color-depth.js b/test/pseudo-tty/test-tty-get-color-depth.js new file mode 100644 index 00000000000000..d802add1189db2 --- /dev/null +++ b/test/pseudo-tty/test-tty-get-color-depth.js @@ -0,0 +1,64 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert').strict; +/* eslint-disable no-restricted-properties */ +const { WriteStream } = require('tty'); + +const fd = common.getTTYfd(); +const writeStream = new WriteStream(fd); + +{ + const depth = writeStream.getColorDepth(); + assert.equal(typeof depth, 'number'); + assert(depth >= 1 && depth <= 24); +} + +// Check different environment variables. +[ + [{ COLORTERM: '1' }, 4], + [{ TMUX: '1' }, 8], + [{ CI: '1' }, 1], + [{ CI: '1', TRAVIS: '1' }, 8], + [{ CI: '1', CIRCLECI: '1' }, 8], + [{ CI: '1', APPVEYOR: '1' }, 8], + [{ CI: '1', GITLAB_CI: '1' }, 8], + [{ CI: '1', CI_NAME: 'codeship' }, 8], + [{ TEAMCITY_VERSION: '1.0.0' }, 1], + [{ TEAMCITY_VERSION: '9.11.0' }, 4], + [{ TERM_PROGRAM: 'iTerm.app' }, 8], + [{ TERM_PROGRAM: 'iTerm.app', TERM_PROGRAM_VERSION: '3.0' }, 24], + [{ TERM_PROGRAM: 'iTerm.app', TERM_PROGRAM_VERSION: '2.0' }, 8], + [{ TERM_PROGRAM: 'HyperTerm' }, 24], + [{ TERM_PROGRAM: 'Hyper' }, 24], + [{ TERM_PROGRAM: 'MacTerm' }, 24], + [{ TERM_PROGRAM: 'Apple_Terminal' }, 8], + [{ TERM: 'xterm-256' }, 8], + [{ TERM: 'ansi' }, 4], + [{ TERM: 'ANSI' }, 4], + [{ TERM: 'color' }, 4], + [{ TERM: 'linux' }, 4], + [{ TERM: 'fail' }, 1], + [{ NODE_DISABLE_COLORS: '1' }, 1], + [{ TERM: 'dumb' }, 1], + [{ TERM: 'dumb', COLORTERM: '1' }, 4], +].forEach(([env, depth], i) => { + const actual = writeStream.getColorDepth(env); + assert.equal( + actual, + depth, + `i: ${i}, expected: ${depth}, actual: ${actual}, env: ${env}` + ); +}); + +// OS settings +{ + const platform = Object.getOwnPropertyDescriptor(process, 'platform'); + const [ value, depth1, depth2 ] = process.platform !== 'win32' ? + ['win32', 1, 4] : ['linux', 4, 1]; + + assert.equal(writeStream.getColorDepth({}), depth1); + Object.defineProperty(process, 'platform', { value }); + assert.equal(writeStream.getColorDepth({}), depth2); + Object.defineProperty(process, 'platform', platform); +} diff --git a/test/pseudo-tty/test-tty-get-color-depth.out b/test/pseudo-tty/test-tty-get-color-depth.out new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/test/pummel/test-https-ci-reneg-attack.js b/test/pummel/test-https-ci-reneg-attack.js index 9e132b7df9a488..9411d24e077097 100644 --- a/test/pummel/test-https-ci-reneg-attack.js +++ b/test/pummel/test-https-ci-reneg-attack.js @@ -69,9 +69,6 @@ function test(next) { const args = (`s_client -connect 127.0.0.1:${common.PORT}`).split(' '); const child = spawn(common.opensslCli, args); - //child.stdout.pipe(process.stdout); - //child.stderr.pipe(process.stderr); - child.stdout.resume(); child.stderr.resume(); diff --git a/test/pummel/test-regress-GH-814.js b/test/pummel/test-regress-GH-814.js index a62df944863062..d7af574e0ab87e 100644 --- a/test/pummel/test-regress-GH-814.js +++ b/test/pummel/test-regress-GH-814.js @@ -32,7 +32,6 @@ function newBuffer(size, value) { while (size--) { buffer[size] = value; } - //buffer[buffer.length-2]= 0x0d; buffer[buffer.length - 1] = 0x0a; return buffer; } @@ -45,7 +44,7 @@ console.log(testFileName); const kBufSize = 128 * 1024; let PASS = true; -const neverWrittenBuffer = newBuffer(kBufSize, 0x2e); //0x2e === '.' +const neverWrittenBuffer = newBuffer(kBufSize, 0x2e); // 0x2e === '.' const bufPool = []; @@ -57,7 +56,7 @@ function tailCB(data) { } -const timeToQuit = Date.now() + 8e3; //Test during no more than this seconds. +const timeToQuit = Date.now() + 8e3; // Test during no more than this seconds. (function main() { if (PASS) { diff --git a/test/pummel/test-regress-GH-814_2.js b/test/pummel/test-regress-GH-814_2.js index a183e082f86aa6..1b40ca7b37cdfa 100644 --- a/test/pummel/test-regress-GH-814_2.js +++ b/test/pummel/test-regress-GH-814_2.js @@ -38,9 +38,7 @@ tailProc.stdout.on('data', tailCB); function tailCB(data) { PASS = !data.toString().includes('.'); - if (PASS) { - //console.error('i'); - } else { + if (!PASS) { console.error('[FAIL]\n DATA -> '); console.error(data); console.error('\n'); @@ -52,9 +50,9 @@ function tailCB(data) { let PASS = true; const bufPool = []; const kBufSize = 16 * 1024 * 1024; -const neverWrittenBuffer = newBuffer(kBufSize, 0x2e); //0x2e === '.' +const neverWrittenBuffer = newBuffer(kBufSize, 0x2e); // 0x2e === '.' -const timeToQuit = Date.now() + 5e3; //Test should last no more than this. +const timeToQuit = Date.now() + 5e3; // Test should last no more than this. writer(); function writer() { @@ -79,14 +77,12 @@ function writer() { bufPool.length = 0; } process.nextTick(writer); - //console.error('o'); } } } function writerCB(err, written) { - //console.error('cb.'); assert.ifError(err); } diff --git a/test/pummel/test-tls-ci-reneg-attack.js b/test/pummel/test-tls-ci-reneg-attack.js index dede8ec9db7056..528ebe4516c71d 100644 --- a/test/pummel/test-tls-ci-reneg-attack.js +++ b/test/pummel/test-tls-ci-reneg-attack.js @@ -67,9 +67,6 @@ function test(next) { const args = (`s_client -connect 127.0.0.1:${common.PORT}`).split(' '); const child = spawn(common.opensslCli, args); - //child.stdout.pipe(process.stdout); - //child.stderr.pipe(process.stderr); - child.stdout.resume(); child.stderr.resume(); diff --git a/test/sequential/test-async-wrap-getasyncid.js b/test/sequential/test-async-wrap-getasyncid.js index 45f53ee9d7a82d..90dbef312a436a 100644 --- a/test/sequential/test-async-wrap-getasyncid.js +++ b/test/sequential/test-async-wrap-getasyncid.js @@ -23,16 +23,20 @@ const { getSystemErrorName } = require('util'); hooks.disable(); delete providers.NONE; // Should never be used. + // See test/pseudo-tty/test-async-wrap-getasyncid-tty.js + // Requires an 'actual' tty fd to be available. + delete providers.TTYWRAP; + // TODO(jasnell): Test for these delete providers.HTTP2SESSION; delete providers.HTTP2STREAM; delete providers.HTTP2PING; delete providers.HTTP2SETTINGS; - const obj_keys = Object.keys(providers); - if (obj_keys.length > 0) - process._rawDebug(obj_keys); - assert.strictEqual(obj_keys.length, 0); + const objKeys = Object.keys(providers); + if (objKeys.length > 0) + process._rawDebug(objKeys); + assert.strictEqual(objKeys.length, 0); })); } @@ -89,14 +93,14 @@ function testInitialized(req, ctor_name) { } -if (common.hasCrypto) { // eslint-disable-line crypto-check +if (common.hasCrypto) { // eslint-disable-line node-core/crypto-check const tls = require('tls'); // SecurePair testInitialized(tls.createSecurePair().ssl, 'Connection'); } -if (common.hasCrypto) { // eslint-disable-line crypto-check +if (common.hasCrypto) { // eslint-disable-line node-core/crypto-check const crypto = require('crypto'); // The handle for PBKDF2 and RandomBytes isn't returned by the function call, @@ -237,7 +241,7 @@ if (common.hasCrypto) { // eslint-disable-line crypto-check } -if (common.hasCrypto) { // eslint-disable-line crypto-check +if (common.hasCrypto) { // eslint-disable-line node-core/crypto-check const { TCP, constants: TCPConstants } = process.binding('tcp_wrap'); const tcp = new TCP(TCPConstants.SOCKET); @@ -253,44 +257,6 @@ if (common.hasCrypto) { // eslint-disable-line crypto-check tls_wrap.wrap(tcp._externalStream, credentials.context, true), 'TLSWrap'); } - -{ - // Do our best to grab a tty fd. - function getTTYfd() { - const tty = require('tty'); - let ttyFd = [0, 1, 2].find(tty.isatty); - if (ttyFd === undefined) { - try { - ttyFd = fs.openSync('/dev/tty'); - } catch (e) { - // There aren't any tty fd's available to use. - return -1; - } - } - return ttyFd; - } - - const ttyFd = getTTYfd(); - if (ttyFd >= 0) { - const tty_wrap = process.binding('tty_wrap'); - // fd may still be invalid, so guard against it. - const handle = (() => { - try { - return new tty_wrap.TTY(ttyFd, false); - } catch (e) { - return null; - } - })(); - if (handle !== null) - testInitialized(handle, 'TTY'); - else - delete providers.TTYWRAP; - } else { - delete providers.TTYWRAP; - } -} - - { const binding = process.binding('udp_wrap'); const handle = new binding.UDP(); diff --git a/test/sequential/test-child-process-exit.js b/test/sequential/test-child-process-exit.js index 303f015cf0aebd..64e188fbf87efc 100644 --- a/test/sequential/test-child-process-exit.js +++ b/test/sequential/test-child-process-exit.js @@ -56,7 +56,6 @@ var timer = setTimeout(function() { child.on('exit', function(code) { console.error('exit %d from gen %d', code, gen + 1); - //clearTimeout(timer); }); child.stdout.pipe(process.stdout); diff --git a/test/sequential/test-inspector-overwrite-config.js b/test/sequential/test-inspector-overwrite-config.js index 9aa3ca9d963828..8b641a0048484a 100644 --- a/test/sequential/test-inspector-overwrite-config.js +++ b/test/sequential/test-inspector-overwrite-config.js @@ -9,7 +9,7 @@ // We cannot do a check for the inspector because the configuration variables // were reset/removed by overwrite-config-preload-module.js. -/* eslint-disable inspector-check */ +/* eslint-disable node-core/inspector-check */ const common = require('../common'); const assert = require('assert'); diff --git a/test/sequential/test-next-tick-error-spin.js b/test/sequential/test-next-tick-error-spin.js index 8bc323510a4da6..565cdb458cfdbf 100644 --- a/test/sequential/test-next-tick-error-spin.js +++ b/test/sequential/test-next-tick-error-spin.js @@ -26,7 +26,7 @@ const assert = require('assert'); if (process.argv[2] !== 'child') { const spawn = require('child_process').spawn; const child = spawn(process.execPath, [__filename, 'child'], { - stdio: 'pipe'//'inherit' + stdio: 'pipe'// 'inherit' }); const timer = setTimeout(function() { throw new Error('child is hung'); diff --git a/test/sequential/test-performance.js b/test/sequential/test-performance.js index 770ca7bf6407be..037168dffb9aac 100644 --- a/test/sequential/test-performance.js +++ b/test/sequential/test-performance.js @@ -102,7 +102,7 @@ assert(inited < 20000); assert.strictEqual(entry.entryType, 'measure'); assert.strictEqual(entry.startTime, markA.startTime); // TODO(jasnell): This comparison is too imprecise on some systems - //assert.strictEqual(entry.duration.toPrecision(3), + // assert.strictEqual(entry.duration.toPrecision(3), // (markB.startTime - markA.startTime).toPrecision(3)); }); } diff --git a/tools/bootstrap/README.md b/tools/bootstrap/README.md index a61e947a2af0b4..68beb2f64653f9 100644 --- a/tools/bootstrap/README.md +++ b/tools/bootstrap/README.md @@ -8,6 +8,7 @@ the following [Chocolatey] packages: * [Git for Windows][] with the `git` and Unix tools added to the `PATH` * [Python 2.x][] * [Visual Studio 2017 Build Tools][] with [Visual C++ workload][] + * [NetWide Assembler][] To install Node.js prerequisites using [Boxstarter WebLauncher][], just open [this link](http://boxstarter.org/package/nr/url?https://raw.githubusercontent.com/nodejs/node/master/tools/bootstrap/windows_boxstarter) @@ -52,4 +53,4 @@ xcode-select --install [Python 2.x]: https://chocolatey.org/packages/python2 [Visual Studio 2017 Build Tools]: https://chocolatey.org/packages/visualstudio2017buildtools [Visual C++ workload]: https://chocolatey.org/packages/visualstudio2017-workload-vctools - +[NetWide Assembler]: https://chocolatey.org/packages/nasm diff --git a/tools/bootstrap/windows_boxstarter b/tools/bootstrap/windows_boxstarter index 144c309ae11ad0..dd2281fa11c38a 100644 --- a/tools/bootstrap/windows_boxstarter +++ b/tools/bootstrap/windows_boxstarter @@ -19,3 +19,6 @@ choco install python2 -y # Installs VS 2017 Build Tools choco install visualstudio2017buildtools -y choco install visualstudio2017-workload-vctools -y + +# NASM +choco install nasm -y diff --git a/tools/doc/README.md b/tools/doc/README.md index 2d5317db9e6fd7..8c183b38989684 100644 --- a/tools/doc/README.md +++ b/tools/doc/README.md @@ -29,7 +29,7 @@ added: v0.10.0 * `x` {string} The description of the string. * `y` {boolean} Should I stay or should I go? -* `z` {number} How many zebras to bring. +* `z` {number} How many zebras to bring. **Default:** `100`. A description of the function. @@ -66,7 +66,7 @@ added: v0.10.0 * `anArg` {Object} Just an argument. * `field` {string} `anArg` can have this field. - * `field2` {boolean} Another field. Default: `false`. + * `field2` {boolean} Another field. **Default:** `false`. * Returns: {boolean} `true` if it worked. A description of the method for humans. @@ -78,6 +78,9 @@ added: v0.10.0 * Returns: {SomeClass | null} The next `SomeClass` in line. +`SomeClass` must be registered in `tools/doc/type-parser.js` +to be properly parsed in `{type}` fields. + ### SomeClass.someProperty - `icu-system.gyp` is an alternate build file used when `--with-intl=system-icu` is invoked. It builds against the `pkg-config` located ICU. -- `iculslocs.cc` is source for the `iculslocs` utility, invoked by `icutrim.py` +- `iculslocs.cc` is source for the `iculslocs` utility, invoked by `icutrim.py` as part of repackaging. Not used separately. See source for more details. - `no-op.cc` — empty function to convince gyp to use a C++ compiler. - `README.md` — you are here @@ -20,7 +20,7 @@ integration. ICU is used to provide internationalization functionality. ## How to upgrade ICU -- Make sure your node workspace is clean (clean `git status`) should be +- Make sure your node workspace is clean (clean `git status`) should be sufficient. - Configure Node with the specific [ICU version](http://icu-project.org/download) you want to upgrade to, for example: @@ -35,9 +35,9 @@ make > _Note_ in theory, the equivalent `vcbuild.bat` commands should work also, > but the commands below are makefile-centric. -- If there are ICU version-specific changes needed, you may need to make +- If there are ICU version-specific changes needed, you may need to make changes in `icu-generic.gyp` or add patch files to `tools/icu/patches`. - - Specifically, look for the lists in `sources!` in the `icu-generic.gyp` for + - Specifically, look for the lists in `sources!` in the `icu-generic.gyp` for files to exclude. - Verify the node build works: @@ -106,10 +106,10 @@ make test-ci - commit the change to `configure` along with the updated `LICENSE` file. - - Note: To simplify review, I often will “pre-land” this patch, meaning that + - Note: To simplify review, I often will “pre-land” this patch, meaning that I run the patch through `curl -L https://github.com/nodejs/node/pull/xxx.patch | git am -3 --whitespace=fix` per the collaborator’s guide… and then push that - patched branch into my PR's branch. This reduces the whitespace changes that + patched branch into my PR's branch. This reduces the whitespace changes that show up in the PR, since the final land will eliminate those anyway. ----- diff --git a/tools/node_modules/eslint-plugin-node-core/index.js b/tools/node_modules/eslint-plugin-node-core/index.js new file mode 100644 index 00000000000000..e27945688ee691 --- /dev/null +++ b/tools/node_modules/eslint-plugin-node-core/index.js @@ -0,0 +1,24 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); + +let cache; +module.exports = { + get rules() { + const RULES_DIR = module.exports.RULES_DIR; + if (!RULES_DIR) + return {}; + + if (!cache) { + cache = {}; + const files = fs.readdirSync(RULES_DIR) + .filter(filename => filename.endsWith('.js')) + for (const file of files) { + const name = file.slice(0, -3); + cache[name] = require(path.resolve(RULES_DIR, file)); + } + } + return cache; + }, +}; diff --git a/tools/remark-preset-lint-node/package-lock.json b/tools/remark-preset-lint-node/package-lock.json index 8c84d7f782b61d..55f29b9aba9e73 100644 --- a/tools/remark-preset-lint-node/package-lock.json +++ b/tools/remark-preset-lint-node/package-lock.json @@ -185,6 +185,17 @@ "unist-util-visit": "1.2.0" } }, + "remark-lint-maximum-line-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/remark-lint-maximum-line-length/-/remark-lint-maximum-line-length-1.0.2.tgz", + "integrity": "sha512-M4UIXAAbtLgoQbTDVwdKOEFbTKtJSZ+pCW7ZqMFs+cbIN0Svm32LM9+xpVfVU0hLYt3Ypl++EAPfguBNe1PZEw==", + "requires": { + "unified-lint-rule": "1.0.2", + "unist-util-generated": "1.1.1", + "unist-util-position": "3.0.0", + "unist-util-visit": "1.2.0" + } + }, "remark-lint-no-auto-link-without-protocol": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/remark-lint-no-auto-link-without-protocol/-/remark-lint-no-auto-link-without-protocol-1.0.1.tgz", diff --git a/tools/test.py b/tools/test.py index 0a142f602be4e7..8eaa2611d02427 100755 --- a/tools/test.py +++ b/tools/test.py @@ -582,8 +582,7 @@ def HasCrashed(self): # Timed out tests will have exit_code -signal.SIGTERM. if self.output.timed_out: return False - return self.output.exit_code < 0 and \ - self.output.exit_code != -signal.SIGABRT + return self.output.exit_code < 0 def HasTimedOut(self): return self.output.timed_out; diff --git a/vcbuild.bat b/vcbuild.bat index a2aa4061ae4464..5a46c5d05f9b73 100644 --- a/vcbuild.bat +++ b/vcbuild.bat @@ -557,7 +557,7 @@ if defined lint_js_ci goto lint-js-ci if not defined lint_js goto exit if not exist tools\node_modules\eslint goto no-lint echo running lint-js -%config%\node tools\node_modules\eslint\bin\eslint.js --cache --rule "linebreak-style: 0" --rulesdir=tools\eslint-rules --ext=.js,.mjs,.md benchmark doc lib test tools +%config%\node tools\node_modules\eslint\bin\eslint.js --cache --rule "linebreak-style: 0" --ext=.js,.mjs,.md benchmark doc lib test tools goto exit :lint-js-ci