From efbfeff62bf22ad47e2805a427303cc93fe8fd82 Mon Sep 17 00:00:00 2001 From: Bryan Field Date: Wed, 4 Nov 2020 10:04:52 -0600 Subject: [PATCH 01/66] doc: fix incorrect heading level PR-URL: https://github.com/nodejs/node/pull/35965 Reviewed-By: Colin Ihrig Reviewed-By: Anna Henningsen Reviewed-By: Antoine du Hamel Reviewed-By: Richard Lau Reviewed-By: Luigi Pinca --- doc/api/dns.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/dns.md b/doc/api/dns.md index c9508f871194b9..a284c9d0c57bd5 100644 --- a/doc/api/dns.md +++ b/doc/api/dns.md @@ -950,7 +950,7 @@ Here is an example of the result object: minttl: 60 } ] ``` -## `dnsPromises.resolveCaa(hostname)` +### `dnsPromises.resolveCaa(hostname)` From 6968b0fd498e6f0cf027c93b42710c48116a9441 Mon Sep 17 00:00:00 2001 From: Daijiro Wachi Date: Mon, 2 Nov 2020 19:45:47 +0900 Subject: [PATCH 02/66] doc: fix release-schedule link in backport guide PR-URL: https://github.com/nodejs/node/pull/35920 Reviewed-By: Richard Lau Reviewed-By: Rich Trott Reviewed-By: Luigi Pinca --- doc/guides/backporting-to-release-lines.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/guides/backporting-to-release-lines.md b/doc/guides/backporting-to-release-lines.md index 90b4a3dcaf328c..eba2a72e9a6040 100644 --- a/doc/guides/backporting-to-release-lines.md +++ b/doc/guides/backporting-to-release-lines.md @@ -90,5 +90,5 @@ After the pull request lands, replace the `backport-requested-v10.x` label on the original pull request with `backported-to-v10.x`. [Release Plan]: https://github.com/nodejs/Release#release-plan -[Release Schedule]: https://github.com/nodejs/Release#release-schedule1 +[Release Schedule]: https://github.com/nodejs/Release#release-schedule [`node-test-pull-request`]: https://ci.nodejs.org/job/node-test-pull-request/build From 1b277d97f342cf7a71de3b60d35c2b3c2478415f Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Thu, 29 Oct 2020 17:28:28 +0100 Subject: [PATCH 03/66] src: remove ERR prefix in crypto status enums MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit removes the ERR prefix of the remaining status enums in crypto so they are consistent with Commit 923f76d5231256e69985e1bd897fd490ea0cbe9c ("src: remove ERR prefix in WebCryptoKeyExportStatus"). PR-URL: https://github.com/nodejs/node/pull/35867 Reviewed-By: Rich Trott Reviewed-By: Colin Ihrig Reviewed-By: Tobias Nießen Reviewed-By: David Carlier Reviewed-By: Franziska Hinkelmann --- src/crypto/crypto_aes.cc | 44 ++++++++++++++++++------------------- src/crypto/crypto_cipher.h | 12 +++++----- src/crypto/crypto_keygen.cc | 2 +- src/crypto/crypto_keygen.h | 20 ++++++++--------- src/crypto/crypto_rsa.cc | 16 +++++++------- 5 files changed, 47 insertions(+), 47 deletions(-) diff --git a/src/crypto/crypto_aes.cc b/src/crypto/crypto_aes.cc index 889bb487fa04d5..584839e813e570 100644 --- a/src/crypto/crypto_aes.cc +++ b/src/crypto/crypto_aes.cc @@ -31,7 +31,7 @@ namespace { // Implements general AES encryption and decryption for CBC // The key_data must be a secret key. // On success, this function sets out to a new AllocatedBuffer -// instance containing the results and returns WebCryptoCipherStatus::ERR_OK. +// instance containing the results and returns WebCryptoCipherStatus::OK. WebCryptoCipherStatus AES_Cipher( Environment* env, KeyObjectData* key_data, @@ -59,7 +59,7 @@ WebCryptoCipherStatus AES_Cipher( nullptr, encrypt)) { // Cipher init failed - return WebCryptoCipherStatus::ERR_FAILED; + return WebCryptoCipherStatus::FAILED; } if (mode == EVP_CIPH_GCM_MODE && !EVP_CIPHER_CTX_ctrl( @@ -67,7 +67,7 @@ WebCryptoCipherStatus AES_Cipher( EVP_CTRL_AEAD_SET_IVLEN, params.iv.size(), nullptr)) { - return WebCryptoCipherStatus::ERR_FAILED; + return WebCryptoCipherStatus::FAILED; } if (!EVP_CIPHER_CTX_set_key_length( @@ -80,7 +80,7 @@ WebCryptoCipherStatus AES_Cipher( reinterpret_cast(key_data->GetSymmetricKey()), params.iv.data(), encrypt)) { - return WebCryptoCipherStatus::ERR_FAILED; + return WebCryptoCipherStatus::FAILED; } size_t tag_len = 0; @@ -95,7 +95,7 @@ WebCryptoCipherStatus AES_Cipher( EVP_CTRL_AEAD_SET_TAG, params.tag.size(), const_cast(params.tag.get()))) { - return WebCryptoCipherStatus::ERR_FAILED; + return WebCryptoCipherStatus::FAILED; } break; case kWebCryptoCipherEncrypt: @@ -123,7 +123,7 @@ WebCryptoCipherStatus AES_Cipher( &out_len, params.additional_data.data(), params.additional_data.size())) { - return WebCryptoCipherStatus::ERR_FAILED; + return WebCryptoCipherStatus::FAILED; } char* data = MallocOpenSSL(buf_len); @@ -136,7 +136,7 @@ WebCryptoCipherStatus AES_Cipher( &out_len, in.data(), in.size())) { - return WebCryptoCipherStatus::ERR_FAILED; + return WebCryptoCipherStatus::FAILED; } total += out_len; @@ -144,7 +144,7 @@ WebCryptoCipherStatus AES_Cipher( ptr += out_len; out_len = EVP_CIPHER_CTX_block_size(ctx.get()); if (!EVP_CipherFinal_ex(ctx.get(), ptr, &out_len)) { - return WebCryptoCipherStatus::ERR_FAILED; + return WebCryptoCipherStatus::FAILED; } total += out_len; @@ -153,7 +153,7 @@ WebCryptoCipherStatus AES_Cipher( if (cipher_mode == kWebCryptoCipherEncrypt && mode == EVP_CIPH_GCM_MODE) { data += out_len; if (!EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_GET_TAG, tag_len, ptr)) - return WebCryptoCipherStatus::ERR_FAILED; + return WebCryptoCipherStatus::FAILED; total += tag_len; } @@ -161,7 +161,7 @@ WebCryptoCipherStatus AES_Cipher( buf.Resize(total); *out = std::move(buf); - return WebCryptoCipherStatus::ERR_OK; + return WebCryptoCipherStatus::OK; } // The AES_CTR implementation here takes it's inspiration from the chromium @@ -232,7 +232,7 @@ WebCryptoCipherStatus AES_CTR_Cipher2( counter, encrypt)) { // Cipher init failed - return WebCryptoCipherStatus::ERR_FAILED; + return WebCryptoCipherStatus::FAILED; } int out_len = 0; @@ -243,17 +243,17 @@ WebCryptoCipherStatus AES_CTR_Cipher2( &out_len, in.data(), in.size())) { - return WebCryptoCipherStatus::ERR_FAILED; + return WebCryptoCipherStatus::FAILED; } if (!EVP_CipherFinal_ex(ctx.get(), out + out_len, &final_len)) - return WebCryptoCipherStatus::ERR_FAILED; + return WebCryptoCipherStatus::FAILED; out_len += final_len; if (static_cast(out_len) != in.size()) - return WebCryptoCipherStatus::ERR_FAILED; + return WebCryptoCipherStatus::FAILED; - return WebCryptoCipherStatus::ERR_OK; + return WebCryptoCipherStatus::OK; } WebCryptoCipherStatus AES_CTR_Cipher( @@ -265,25 +265,25 @@ WebCryptoCipherStatus AES_CTR_Cipher( ByteSource* out) { BignumPointer num_counters(BN_new()); if (!BN_lshift(num_counters.get(), BN_value_one(), params.length)) - return WebCryptoCipherStatus::ERR_FAILED; + return WebCryptoCipherStatus::FAILED; BignumPointer current_counter = GetCounter(params); BignumPointer num_output(BN_new()); if (!BN_set_word(num_output.get(), CeilDiv(in.size(), kAesBlockSize))) - return WebCryptoCipherStatus::ERR_FAILED; + return WebCryptoCipherStatus::FAILED; // Just like in chromium's implementation, if the counter will // be incremented more than there are counter values, we fail. if (BN_cmp(num_output.get(), num_counters.get()) > 0) - return WebCryptoCipherStatus::ERR_FAILED; + return WebCryptoCipherStatus::FAILED; BignumPointer remaining_until_reset(BN_new()); if (!BN_sub(remaining_until_reset.get(), num_counters.get(), current_counter.get())) { - return WebCryptoCipherStatus::ERR_FAILED; + return WebCryptoCipherStatus::FAILED; } // Output size is identical to the input size @@ -302,7 +302,7 @@ WebCryptoCipherStatus AES_CTR_Cipher( in, params.iv.data(), ptr); - if (status == WebCryptoCipherStatus::ERR_OK) + if (status == WebCryptoCipherStatus::OK) *out = std::move(buf); return status; } @@ -319,7 +319,7 @@ WebCryptoCipherStatus AES_CTR_Cipher( params.iv.data(), ptr); - if (status != WebCryptoCipherStatus::ERR_OK) + if (status != WebCryptoCipherStatus::OK) return status; // Wrap the counter around to zero @@ -336,7 +336,7 @@ WebCryptoCipherStatus AES_CTR_Cipher( new_counter_block.data(), ptr + input_size_part1); - if (status == WebCryptoCipherStatus::ERR_OK) + if (status == WebCryptoCipherStatus::OK) *out = std::move(buf); return status; diff --git a/src/crypto/crypto_cipher.h b/src/crypto/crypto_cipher.h index 725807d509525d..bae187a6e139a3 100644 --- a/src/crypto/crypto_cipher.h +++ b/src/crypto/crypto_cipher.h @@ -128,9 +128,9 @@ enum WebCryptoCipherMode { }; enum class WebCryptoCipherStatus { - ERR_OK, - ERR_INVALID_KEY_TYPE, - ERR_FAILED + OK, + INVALID_KEY_TYPE, + FAILED }; // CipherJob is a base implementation class for implementations of @@ -222,13 +222,13 @@ class CipherJob final : public CryptoJob { *CryptoJob::params(), in_, &out_)) { - case WebCryptoCipherStatus::ERR_OK: + case WebCryptoCipherStatus::OK: // Success! break; - case WebCryptoCipherStatus::ERR_INVALID_KEY_TYPE: + case WebCryptoCipherStatus::INVALID_KEY_TYPE: // Fall through // TODO(@jasnell): Separate error for this - case WebCryptoCipherStatus::ERR_FAILED: { + case WebCryptoCipherStatus::FAILED: { CryptoErrorVector* errors = CryptoJob::errors(); errors->Capture(); if (errors->empty()) diff --git a/src/crypto/crypto_keygen.cc b/src/crypto/crypto_keygen.cc index c6ce46584f4104..ab78b7aabc694b 100644 --- a/src/crypto/crypto_keygen.cc +++ b/src/crypto/crypto_keygen.cc @@ -84,7 +84,7 @@ KeyGenJobStatus SecretKeyGenTraits::DoKeyGen( CHECK_LE(params->length, INT_MAX); params->out = MallocOpenSSL(params->length); EntropySource(reinterpret_cast(params->out), params->length); - return KeyGenJobStatus::ERR_OK; + return KeyGenJobStatus::OK; } Maybe SecretKeyGenTraits::EncodeKey( diff --git a/src/crypto/crypto_keygen.h b/src/crypto/crypto_keygen.h index 28dc1273a9e27c..c4197c6eaed8f9 100644 --- a/src/crypto/crypto_keygen.h +++ b/src/crypto/crypto_keygen.h @@ -19,8 +19,8 @@ void Initialize(Environment* env, v8::Local target); } // namespace Keygen enum class KeyGenJobStatus { - ERR_OK, - ERR_FAILED + OK, + FAILED }; // A Base CryptoJob for generating secret keys or key pairs. @@ -77,11 +77,11 @@ class KeyGenJob final : public CryptoJob { AdditionalParams* params = CryptoJob::params(); switch (KeyGenTraits::DoKeyGen(AsyncWrap::env(), params)) { - case KeyGenJobStatus::ERR_OK: - status_ = KeyGenJobStatus::ERR_OK; + case KeyGenJobStatus::OK: + status_ = KeyGenJobStatus::OK; // Success! break; - case KeyGenJobStatus::ERR_FAILED: { + case KeyGenJobStatus::FAILED: { CryptoErrorVector* errors = CryptoJob::errors(); errors->Capture(); if (errors->empty()) @@ -96,7 +96,7 @@ class KeyGenJob final : public CryptoJob { Environment* env = AsyncWrap::env(); CryptoErrorVector* errors = CryptoJob::errors(); AdditionalParams* params = CryptoJob::params(); - if (status_ == KeyGenJobStatus::ERR_OK && + if (status_ == KeyGenJobStatus::OK && LIKELY(!KeyGenTraits::EncodeKey(env, params, result).IsNothing())) { *err = Undefined(env->isolate()); return v8::Just(true); @@ -112,7 +112,7 @@ class KeyGenJob final : public CryptoJob { SET_SELF_SIZE(KeyGenJob); private: - KeyGenJobStatus status_ = KeyGenJobStatus::ERR_FAILED; + KeyGenJobStatus status_ = KeyGenJobStatus::FAILED; }; // A Base KeyGenTraits for Key Pair generation algorithms. @@ -162,15 +162,15 @@ struct KeyPairGenTraits final { AdditionalParameters* params) { EVPKeyCtxPointer ctx = KeyPairAlgorithmTraits::Setup(params); if (!ctx || EVP_PKEY_keygen_init(ctx.get()) <= 0) - return KeyGenJobStatus::ERR_FAILED; + return KeyGenJobStatus::FAILED; // Generate the key EVP_PKEY* pkey = nullptr; if (!EVP_PKEY_keygen(ctx.get(), &pkey)) - return KeyGenJobStatus::ERR_FAILED; + return KeyGenJobStatus::FAILED; params->key = ManagedEVPPKey(EVPKeyPointer(pkey)); - return KeyGenJobStatus::ERR_OK; + return KeyGenJobStatus::OK; } static v8::Maybe EncodeKey( diff --git a/src/crypto/crypto_rsa.cc b/src/crypto/crypto_rsa.cc index 70022b3bce3bf5..ac5dbd5106e678 100644 --- a/src/crypto/crypto_rsa.cc +++ b/src/crypto/crypto_rsa.cc @@ -196,16 +196,16 @@ WebCryptoCipherStatus RSA_Cipher( EVP_PKEY_CTX_new(key_data->GetAsymmetricKey().get(), nullptr)); if (!ctx || init(ctx.get()) <= 0) - return WebCryptoCipherStatus::ERR_FAILED; + return WebCryptoCipherStatus::FAILED; if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), params.padding) <= 0) { - return WebCryptoCipherStatus::ERR_FAILED; + return WebCryptoCipherStatus::FAILED; } if (params.digest != nullptr && (EVP_PKEY_CTX_set_rsa_oaep_md(ctx.get(), params.digest) <= 0 || EVP_PKEY_CTX_set_rsa_mgf1_md(ctx.get(), params.digest) <= 0)) { - return WebCryptoCipherStatus::ERR_FAILED; + return WebCryptoCipherStatus::FAILED; } size_t label_len = params.label.size(); @@ -214,7 +214,7 @@ WebCryptoCipherStatus RSA_Cipher( CHECK_NOT_NULL(label); if (EVP_PKEY_CTX_set0_rsa_oaep_label(ctx.get(), label, label_len) <= 0) { OPENSSL_free(label); - return WebCryptoCipherStatus::ERR_FAILED; + return WebCryptoCipherStatus::FAILED; } } @@ -225,7 +225,7 @@ WebCryptoCipherStatus RSA_Cipher( &out_len, in.data(), in.size()) <= 0) { - return WebCryptoCipherStatus::ERR_FAILED; + return WebCryptoCipherStatus::FAILED; } char* data = MallocOpenSSL(out_len); @@ -238,13 +238,13 @@ WebCryptoCipherStatus RSA_Cipher( &out_len, in.data(), in.size()) <= 0) { - return WebCryptoCipherStatus::ERR_FAILED; + return WebCryptoCipherStatus::FAILED; } buf.Resize(out_len); *out = std::move(buf); - return WebCryptoCipherStatus::ERR_OK; + return WebCryptoCipherStatus::OK; } } // namespace @@ -356,7 +356,7 @@ WebCryptoCipherStatus RSACipherTraits::DoCipher( return RSA_Cipher( env, key_data.get(), params, in, out); } - return WebCryptoCipherStatus::ERR_FAILED; + return WebCryptoCipherStatus::FAILED; } Maybe ExportJWKRsaKey( From 9b6512f7ded1729c0770c042dd8816a0e2a1e2ed Mon Sep 17 00:00:00 2001 From: Gabriel Schulhof Date: Mon, 2 Nov 2020 15:06:23 -0800 Subject: [PATCH 04/66] n-api: unlink reference during its destructor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, a reference is being unlinked from the list of references tracked by the environment when `v8impl::Reference::Delete` is called. This causes a leak when deletion must be deferred because the finalizer hasn't yet run, but the finalizer does not run because environment teardown is in progress, and so no more gc runs will happen, and the `FinalizeAll` run that happens during environment teardown does not catch the reference because it's no longer in the list. The test below will fail when running with ASAN: ``` ./node ./test/node-api/test_worker_terminate_finalization/test.js ``` OTOH if, to address the above leak, we make a special case to not unlink a reference during environment teardown, we run into a situation where the reference gets deleted by `v8impl::Reference::Delete` but does not get unlinked because it's environment teardown time. This leaves a stale pointer in the linked list which will result in a use-after-free in `FinalizeAll` during environment teardown. The test below will fail if we make the above change: ``` ./node -e "require('./test/node-api/test_instance_data/build/Release/test_ref_then_set.node');" ``` Thus, we unlink a reference precisely when we destroy it – in its destructor. Refs: https://github.com/nodejs/node/issues/34731 Refs: https://github.com/nodejs/node/pull/34839 Refs: https://github.com/nodejs/node/issues/35620 Refs: https://github.com/nodejs/node/issues/35777 Fixes: https://github.com/nodejs/node/issues/35778 Signed-off-by: Gabriel Schulhof PR-URL: https://github.com/nodejs/node/pull/35933 Reviewed-By: Rich Trott Reviewed-By: Michael Dawson Reviewed-By: Zeyu Yang --- src/js_native_api_v8.cc | 3 ++- test/node-api/test_worker_terminate_finalization/test.js | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc index 3b8a5ff51b54ab..e7a16401369f42 100644 --- a/src/js_native_api_v8.cc +++ b/src/js_native_api_v8.cc @@ -220,6 +220,8 @@ class RefBase : protected Finalizer, RefTracker { finalize_hint); } + virtual ~RefBase() { Unlink(); } + inline void* Data() { return _finalize_data; } @@ -240,7 +242,6 @@ class RefBase : protected Finalizer, RefTracker { // the finalizer and _delete_self is set. In this case we // know we need to do the deletion so just do it. static inline void Delete(RefBase* reference) { - reference->Unlink(); if ((reference->RefCount() != 0) || (reference->_delete_self) || (reference->_finalize_ran)) { diff --git a/test/node-api/test_worker_terminate_finalization/test.js b/test/node-api/test_worker_terminate_finalization/test.js index 171a32b812334f..937079968f722a 100644 --- a/test/node-api/test_worker_terminate_finalization/test.js +++ b/test/node-api/test_worker_terminate_finalization/test.js @@ -1,11 +1,9 @@ 'use strict'; const common = require('../../common'); -// TODO(addaleax): Run this test once it stops failing under ASAN/valgrind. // Refs: https://github.com/nodejs/node/issues/34731 // Refs: https://github.com/nodejs/node/pull/35777 // Refs: https://github.com/nodejs/node/issues/35778 -common.skip('Reference management in N-API leaks memory'); const { Worker, isMainThread } = require('worker_threads'); From 4c6de854be70cdb099c63ab30cc2d37797dacdaa Mon Sep 17 00:00:00 2001 From: Richard Lau Date: Wed, 4 Nov 2020 19:43:34 +0000 Subject: [PATCH 05/66] benchmark: remove modules that require intl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `inspector` and `trace_events` will throw errors if Node.js has been compiled with `--without-intl`. Refs: https://github.com/nodejs/node/pull/35816 PR-URL: https://github.com/nodejs/node/pull/35968 Fixes: https://github.com/nodejs/node/issues/35962 Reviewed-By: Luigi Pinca Reviewed-By: Antoine du Hamel Reviewed-By: Gireesh Punathil Reviewed-By: Harshitha K P Reviewed-By: Gerhard Stöbich --- benchmark/fixtures/require-builtins.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/benchmark/fixtures/require-builtins.js b/benchmark/fixtures/require-builtins.js index a100e9efd0cae5..685eef1875b301 100644 --- a/benchmark/fixtures/require-builtins.js +++ b/benchmark/fixtures/require-builtins.js @@ -17,7 +17,6 @@ const list = [ 'http', 'http2', 'https', - 'inspector', 'module', 'net', 'os', @@ -32,7 +31,6 @@ const list = [ 'string_decoder', 'timers', 'tls', - 'trace_events', 'tty', 'url', 'util', From acd3617e1a3692615424d88516f798641ed6f9ed Mon Sep 17 00:00:00 2001 From: krank2me Date: Sun, 30 Aug 2020 18:28:41 -0500 Subject: [PATCH 06/66] doc: option --prof documentation help added MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/34991 Reviewed-By: Anna Henningsen Reviewed-By: Gireesh Punathil Reviewed-By: Juan José Arboleda --- src/node_options.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/node_options.cc b/src/node_options.cc index 04893fee408e18..e90dcd93231fca 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -395,6 +395,9 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { "preserve symbolic links when resolving the main module", &EnvironmentOptions::preserve_symlinks_main, kAllowedInEnvironment); + AddOption("--prof", + "Generate V8 profiler output.", + V8Option{}); AddOption("--prof-process", "process V8 profiler output generated using --prof", &EnvironmentOptions::prof_process); From 8bd364a9b378b215f2880fe5e74304e9b31e4ce8 Mon Sep 17 00:00:00 2001 From: "Pooja D.P" Date: Sat, 10 Oct 2020 22:13:10 +0400 Subject: [PATCH 07/66] doc: add new wordings to the API description PR-URL: https://github.com/nodejs/node/pull/35588 Reviewed-By: Gireesh Punathil Reviewed-By: Rich Trott --- doc/api/process.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/api/process.md b/doc/api/process.md index e9472c66b0a1f6..f2dd0c18d5cc00 100644 --- a/doc/api/process.md +++ b/doc/api/process.md @@ -2272,7 +2272,8 @@ exception value itself as its first argument. 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. +not abort. Actions configured to take place on exceptions such as report +generations will be affected too To unset the capture function, `process.setUncaughtExceptionCaptureCallback(null)` may be used. Calling this From 23f0d0c45c591a4cf239cbe79f06a0087b0384cd Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Wed, 4 Nov 2020 11:57:08 -0800 Subject: [PATCH 08/66] test: fix error in test/internet/test-dns.js Refs: https://github.com/nodejs/node/pull/35466#issuecomment-721936009 PR-URL: https://github.com/nodejs/node/pull/35969 Reviewed-By: Richard Lau Reviewed-By: Luigi Pinca Reviewed-By: Anna Henningsen Reviewed-By: Antoine du Hamel --- test/internet/test-dns.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/internet/test-dns.js b/test/internet/test-dns.js index 2bf6c22dfc43e5..14a26c873f17fe 100644 --- a/test/internet/test-dns.js +++ b/test/internet/test-dns.js @@ -401,7 +401,8 @@ TEST(function test_resolveSoa_failure(done) { TEST(async function test_resolveCaa(done) { function validateResult(result) { - assert.ok(Array.isArray(result[0])); + assert.ok(Array.isArray(result), + `expected array, got ${util.inspect(result)}`); assert.strictEqual(result.length, 1); assert.strictEqual(typeof result[0].critical, 'number'); assert.strictEqual(result[0].critical, 0); From 6d56ba03e27fd263e9e4c82a152e10d73e9d0144 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Tue, 3 Nov 2020 05:23:04 -0800 Subject: [PATCH 09/66] doc: update benchmark CI test indicator in README As of c7627da837af55e3a37ca0933b6a3247fc6c06bb, benchmark tests are run in CI, but the README was not updated to indicate this. PR-URL: https://github.com/nodejs/node/pull/35945 Reviewed-By: Richard Lau Reviewed-By: Daijiro Wachi Reviewed-By: Luigi Pinca --- test/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/README.md b/test/README.md index a9569235fdde1b..834af7dc5896a2 100644 --- a/test/README.md +++ b/test/README.md @@ -18,7 +18,7 @@ For the tests to run on Windows, be sure to clone Node.js source code with the | `abort` | Yes | Tests for when the `--abort-on-uncaught-exception` flag is used. | | `addons` | Yes | Tests for [addon](https://nodejs.org/api/addons.html) functionality along with some tests that require an addon. | | `async-hooks` | Yes | Tests for [async_hooks](https://nodejs.org/api/async_hooks.html) functionality. | -| `benchmark` | No | Test minimal functionality of benchmarks. | +| `benchmark` | Yes | Test minimal functionality of benchmarks. | | `cctest` | Yes | C++ tests that are run as part of the build process. | | `code-cache` | No | Tests for a Node.js binary compiled with V8 code cache. | | `common` | | Common modules shared among many tests. [Documentation](./common/README.md) | From dc4936ba50ad40a8f24ab7a3a46589250057b09e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Thu, 5 Nov 2020 00:05:52 +0100 Subject: [PATCH 10/66] crypto: fix comment in ByteSource PR-URL: https://github.com/nodejs/node/pull/35972 Refs: https://github.com/nodejs/node/pull/35821 Reviewed-By: Daniel Bevenius Reviewed-By: Zeyu Yang Reviewed-By: David Carlier Reviewed-By: Jiawen Geng Reviewed-By: Ricky Zhou <0x19951125@gmail.com> Reviewed-By: Rich Trott Reviewed-By: Luigi Pinca --- src/crypto/crypto_util.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crypto/crypto_util.cc b/src/crypto/crypto_util.cc index 13d5a7f607841d..30cafa62a51704 100644 --- a/src/crypto/crypto_util.cc +++ b/src/crypto/crypto_util.cc @@ -237,7 +237,7 @@ ByteSource& ByteSource::operator=(ByteSource&& other) noexcept { std::unique_ptr ByteSource::ReleaseToBackingStore() { // It's ok for allocated_data_ to be nullptr but - // only if size_ is not zero. + // only if size_ is zero. CHECK_IMPLIES(size_ > 0, allocated_data_ != nullptr); std::unique_ptr ptr = ArrayBuffer::NewBackingStore( allocated_data_, From 9757b47c4463647cf466607132f2ecaa0f4f7afa Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Wed, 21 Oct 2020 11:34:41 +0200 Subject: [PATCH 11/66] console: use more primordials PR-URL: https://github.com/nodejs/node/pull/35734 Reviewed-By: Rich Trott --- lib/internal/console/constructor.js | 66 ++++++++++++++++++----------- lib/internal/console/global.js | 3 +- 2 files changed, 43 insertions(+), 26 deletions(-) diff --git a/lib/internal/console/constructor.js b/lib/internal/console/constructor.js index 8684bea5ad4966..dc2817ad2a54ec 100644 --- a/lib/internal/console/constructor.js +++ b/lib/internal/console/constructor.js @@ -6,21 +6,31 @@ const { ArrayFrom, ArrayIsArray, + ArrayPrototypePush, + ArrayPrototypeUnshift, Boolean, ErrorCaptureStackTrace, - Map, + FunctionPrototypeBind, MathFloor, Number, + NumberPrototypeToFixed, ObjectDefineProperties, ObjectDefineProperty, ObjectKeys, ObjectPrototypeHasOwnProperty, ObjectValues, ReflectOwnKeys, + SafeMap, + SafeWeakMap, + StringPrototypeIncludes, + StringPrototypePadStart, + StringPrototypeRepeat, + StringPrototypeReplace, + StringPrototypeSlice, + StringPrototypeSplit, Symbol, SymbolHasInstance, SymbolToStringTag, - WeakMap, } = primordials; const { trace } = internalBinding('trace_events'); @@ -80,7 +90,7 @@ const kBindStreamsLazy = Symbol('kBindStreamsLazy'); const kUseStdout = Symbol('kUseStdout'); const kUseStderr = Symbol('kUseStderr'); -const optionsMap = new WeakMap(); +const optionsMap = new SafeWeakMap(); function Console(options /* or: stdout, stderr, ignoreErrors = true */) { // We have to test new.target here to see if this function is called @@ -142,7 +152,7 @@ function Console(options /* or: stdout, stderr, ignoreErrors = true */) { // We have to bind the methods grabbed from the instance instead of from // the prototype so that users extending the Console can override them // from the prototype chain of the subclass. - this[key] = this[key].bind(this); + this[key] = FunctionPrototypeBind(this[key], this); ObjectDefineProperty(this[key], 'name', { value: key }); @@ -224,9 +234,9 @@ ObjectDefineProperties(Console.prototype, { ...consolePropAttributes, value: Boolean(ignoreErrors) }, - '_times': { ...consolePropAttributes, value: new Map() }, + '_times': { ...consolePropAttributes, value: new SafeMap() }, // Corresponds to https://console.spec.whatwg.org/#count-map - [kCounts]: { ...consolePropAttributes, value: new Map() }, + [kCounts]: { ...consolePropAttributes, value: new SafeMap() }, [kColorMode]: { ...consolePropAttributes, value: colorMode }, [kIsConsole]: { ...consolePropAttributes, value: true }, [kGroupIndent]: { ...consolePropAttributes, value: '' }, @@ -255,8 +265,8 @@ ObjectDefineProperties(Console.prototype, { this._stdoutErrorHandler : this._stderrErrorHandler; if (groupIndent.length !== 0) { - if (string.includes('\n')) { - string = string.replace(/\n/g, `\n${groupIndent}`); + if (StringPrototypeIncludes(string, '\n')) { + string = StringPrototypeReplace(string, /\n/g, `\n${groupIndent}`); } string = groupIndent + string; } @@ -450,13 +460,16 @@ const consoleMethods = { if (data.length > 0) { this.log(...data); } - this[kGroupIndent] += ' '.repeat(this[kGroupIndentationWidth]); + this[kGroupIndent] += + StringPrototypeRepeat(' ', this[kGroupIndentationWidth]); }, groupEnd() { - this[kGroupIndent] = - this[kGroupIndent].slice(0, this[kGroupIndent].length - - this[kGroupIndentationWidth]); + this[kGroupIndent] = StringPrototypeSlice( + this[kGroupIndent], + 0, + this[kGroupIndent].length - this[kGroupIndentationWidth] + ); }, // https://console.spec.whatwg.org/#table @@ -501,14 +514,14 @@ const consoleMethods = { let length = 0; if (mapIter) { for (; i < tabularData.length / 2; ++i) { - keys.push(_inspect(tabularData[i * 2])); - values.push(_inspect(tabularData[i * 2 + 1])); + ArrayPrototypePush(keys, _inspect(tabularData[i * 2])); + ArrayPrototypePush(values, _inspect(tabularData[i * 2 + 1])); length++; } } else { for (const [k, v] of tabularData) { - keys.push(_inspect(k)); - values.push(_inspect(v)); + ArrayPrototypePush(keys, _inspect(k)); + ArrayPrototypePush(values, _inspect(v)); length++; } } @@ -530,7 +543,7 @@ const consoleMethods = { const values = []; let length = 0; for (const v of tabularData) { - values.push(_inspect(v)); + ArrayPrototypePush(values, _inspect(v)); length++; } return final([iterKey, valuesKey], [getIndexArray(length), values]); @@ -565,11 +578,11 @@ const consoleMethods = { const keys = ObjectKeys(map); const values = ObjectValues(map); if (hasPrimitives) { - keys.push(valuesKey); - values.push(valuesKeyArray); + ArrayPrototypePush(keys, valuesKey); + ArrayPrototypePush(values, valuesKeyArray); } - keys.unshift(indexKey); - values.unshift(indexKeyArray); + ArrayPrototypeUnshift(keys, indexKey); + ArrayPrototypeUnshift(values, indexKeyArray); return final(keys, values); }, @@ -596,7 +609,7 @@ function timeLogImpl(self, name, label, data) { } function pad(value) { - return `${value}`.padStart(2, '0'); + return StringPrototypePadStart(`${value}`, 2, '0'); } function formatTime(ms) { @@ -617,16 +630,19 @@ function formatTime(ms) { } if (hours !== 0 || minutes !== 0) { - [seconds, ms] = seconds.toFixed(3).split('.'); + [seconds, ms] = StringPrototypeSplit( + NumberPrototypeToFixed(seconds, 3), + '.' + ); const res = hours !== 0 ? `${hours}:${pad(minutes)}` : minutes; return `${res}:${pad(seconds)}.${ms} (${hours !== 0 ? 'h:m' : ''}m:ss.mmm)`; } if (seconds !== 0) { - return `${seconds.toFixed(3)}s`; + return `${NumberPrototypeToFixed(seconds, 3)}s`; } - return `${Number(ms.toFixed(3))}ms`; + return `${Number(NumberPrototypeToFixed(ms, 3))}ms`; } const keyKey = 'Key'; diff --git a/lib/internal/console/global.js b/lib/internal/console/global.js index 1c615c78451510..d6c0c24d529dcc 100644 --- a/lib/internal/console/global.js +++ b/lib/internal/console/global.js @@ -13,6 +13,7 @@ // in the global console prototype chain anymore. const { + FunctionPrototypeBind, ObjectCreate, ReflectDefineProperty, ReflectGetOwnPropertyDescriptor, @@ -37,7 +38,7 @@ for (const prop of ReflectOwnKeys(Console.prototype)) { const desc = ReflectGetOwnPropertyDescriptor(Console.prototype, prop); if (typeof desc.value === 'function') { // fix the receiver const name = desc.value.name; - desc.value = desc.value.bind(globalConsole); + desc.value = FunctionPrototypeBind(desc.value, globalConsole); ReflectDefineProperty(desc.value, 'name', { value: name }); } ReflectDefineProperty(globalConsole, prop, desc); From b700900d026a2fcbb55c633a12ccbe5aba680001 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Thu, 29 Oct 2020 19:43:42 +0100 Subject: [PATCH 12/66] lib: refactor to use more primordials PR-URL: https://github.com/nodejs/node/pull/35875 Reviewed-By: Rich Trott --- lib/internal/v8_prof_processor.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/internal/v8_prof_processor.js b/lib/internal/v8_prof_processor.js index d647d4749f9adc..daae650b2ad8ef 100644 --- a/lib/internal/v8_prof_processor.js +++ b/lib/internal/v8_prof_processor.js @@ -1,6 +1,8 @@ 'use strict'; const { + ArrayPrototypePush, + ArrayPrototypeSlice, JSONStringify, } = primordials; @@ -22,17 +24,18 @@ const scriptFiles = [ ]; let script = ''; -scriptFiles.forEach((s) => { +for (const s of scriptFiles) { script += internalBinding('natives')[s] + '\n'; -}); +} const tickArguments = []; if (process.platform === 'darwin') { - tickArguments.push('--mac'); + ArrayPrototypePush(tickArguments, '--mac'); } else if (process.platform === 'win32') { - tickArguments.push('--windows'); + ArrayPrototypePush(tickArguments, '--windows'); } -tickArguments.push.apply(tickArguments, process.argv.slice(1)); +ArrayPrototypePush(tickArguments, + ...ArrayPrototypeSlice(process.argv, 1)); script = `(function(module, require) { arguments = ${JSONStringify(tickArguments)}; function write (s) { process.stdout.write(s) } From d83e2530650d6d40fd0787984ca14ce8da896275 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Tue, 3 Nov 2020 12:03:27 +0100 Subject: [PATCH 13/66] errors: refactor to use more primordials PR-URL: https://github.com/nodejs/node/pull/35944 Reviewed-By: Benjamin Gruenbaum Reviewed-By: Ben Coe Reviewed-By: Rich Trott --- lib/internal/errors.js | 105 +++++++++++------- .../source_map/prepare_stack_trace.js | 24 ++-- 2 files changed, 79 insertions(+), 50 deletions(-) diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 6f81595a742de8..20a9e3e7df3e8a 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -11,31 +11,49 @@ // message may change, the code should not. const { + ArrayFrom, ArrayIsArray, + ArrayPrototypeIncludes, + ArrayPrototypeIndexOf, + ArrayPrototypeJoin, + ArrayPrototypeMap, + ArrayPrototypePop, + ArrayPrototypePush, + ArrayPrototypeSlice, + ArrayPrototypeSplice, + ArrayPrototypeUnshift, Error, ErrorCaptureStackTrace, ErrorPrototypeToString, JSONStringify, - Map, MathAbs, MathMax, + Number, NumberIsInteger, ObjectDefineProperty, ObjectKeys, RangeError, + ReflectApply, + RegExpPrototypeTest, + SafeMap, + SafeWeakMap, String, + StringPrototypeEndsWith, + StringPrototypeIncludes, + StringPrototypeSlice, + StringPrototypeSplit, StringPrototypeStartsWith, + StringPrototypeToLowerCase, Symbol, SymbolFor, SyntaxError, TypeError, URIError, - WeakMap, } = primordials; const isWindows = process.platform === 'win32'; -const messages = new Map(); +const messages = new SafeMap(); const codes = {}; const classRegExp = /^([A-Z][a-z0-9]*)+$/; @@ -54,7 +72,7 @@ const kTypes = [ ]; const MainContextError = Error; -const overrideStackTrace = new WeakMap(); +const overrideStackTrace = new SafeWeakMap(); const kNoOverride = Symbol('kNoOverride'); const prepareStackTrace = (globalThis, error, trace) => { // API for node internals to override error stack formatting @@ -370,8 +388,8 @@ function getMessage(key, args, self) { if (args.length === 0) return msg; - args.unshift(msg); - return lazyInternalUtilInspect().format.apply(null, args); + ArrayPrototypeUnshift(args, msg); + return ReflectApply(lazyInternalUtilInspect().format, null, args); } let uvBinding; @@ -644,9 +662,9 @@ function addNumericalSeparator(val) { let i = val.length; const start = val[0] === '-' ? 1 : 0; for (; i >= start + 4; i -= 3) { - res = `_${val.slice(i - 3, i)}${res}`; + res = `_${StringPrototypeSlice(val, i - 3, i)}${res}`; } - return `${val.slice(0, i)}${res}`; + return `${StringPrototypeSlice(val, 0, i)}${res}`; } // Used to enhance the stack that will be picked up by the inspector @@ -681,7 +699,8 @@ const fatalExceptionStackEnhancers = { // ANSI escape sequences is not reliable. if (process.platform === 'win32') { const info = internalBinding('os').getOSInformation(); - const ver = info[2].split('.').map((a) => +a); + const ver = ArrayPrototypeMap(StringPrototypeSplit(info[2], '.'), + Number); if (ver[0] !== 10 || ver[2] < 14393) { useColors = false; } @@ -975,11 +994,11 @@ E('ERR_INVALID_ARG_TYPE', } let msg = 'The '; - if (name.endsWith(' argument')) { + if (StringPrototypeEndsWith(name, ' argument')) { // For cases like 'first argument' msg += `${name} `; } else { - const type = name.includes('.') ? 'property' : 'argument'; + const type = StringPrototypeIncludes(name, '.') ? 'property' : 'argument'; msg += `"${name}" ${type} `; } msg += 'must be '; @@ -991,31 +1010,31 @@ E('ERR_INVALID_ARG_TYPE', for (const value of expected) { assert(typeof value === 'string', 'All expected entries have to be of type string'); - if (kTypes.includes(value)) { - types.push(value.toLowerCase()); - } else if (classRegExp.test(value)) { - instances.push(value); + if (ArrayPrototypeIncludes(kTypes, value)) { + ArrayPrototypePush(types, StringPrototypeToLowerCase(value)); + } else if (RegExpPrototypeTest(classRegExp, value)) { + ArrayPrototypePush(instances, value); } else { assert(value !== 'object', 'The value "object" should be written as "Object"'); - other.push(value); + ArrayPrototypePush(other, value); } } // Special handle `object` in case other instances are allowed to outline // the differences between each other. if (instances.length > 0) { - const pos = types.indexOf('object'); + const pos = ArrayPrototypeIndexOf(types, 'object'); if (pos !== -1) { - types.splice(pos, 1); - instances.push('Object'); + ArrayPrototypeSplice(types, pos, 1); + ArrayPrototypePush(instances, 'Object'); } } if (types.length > 0) { if (types.length > 2) { - const last = types.pop(); - msg += `one of type ${types.join(', ')}, or ${last}`; + const last = ArrayPrototypePop(types); + msg += `one of type ${ArrayPrototypeJoin(types, ', ')}, or ${last}`; } else if (types.length === 2) { msg += `one of type ${types[0]} or ${types[1]}`; } else { @@ -1027,8 +1046,9 @@ E('ERR_INVALID_ARG_TYPE', if (instances.length > 0) { if (instances.length > 2) { - const last = instances.pop(); - msg += `an instance of ${instances.join(', ')}, or ${last}`; + const last = ArrayPrototypePop(instances); + msg += + `an instance of ${ArrayPrototypeJoin(instances, ', ')}, or ${last}`; } else { msg += `an instance of ${instances[0]}`; if (instances.length === 2) { @@ -1041,12 +1061,12 @@ E('ERR_INVALID_ARG_TYPE', if (other.length > 0) { if (other.length > 2) { - const last = other.pop(); - msg += `one of ${other.join(', ')}, or ${last}`; + const last = ArrayPrototypePop(other); + msg += `one of ${ArrayPrototypeJoin(other, ', ')}, or ${last}`; } else if (other.length === 2) { msg += `one of ${other[0]} or ${other[1]}`; } else { - if (other[0].toLowerCase() !== other[0]) + if (StringPrototypeToLowerCase(other[0]) !== other[0]) msg += 'an '; msg += `${other[0]}`; } @@ -1068,7 +1088,7 @@ E('ERR_INVALID_ARG_TYPE', let inspected = lazyInternalUtilInspect() .inspect(actual, { colors: false }); if (inspected.length > 25) - inspected = `${inspected.slice(0, 25)}...`; + inspected = `${StringPrototypeSlice(inspected, 0, 25)}...`; msg += `. Received type ${typeof actual} (${inspected})`; } return msg; @@ -1076,9 +1096,9 @@ E('ERR_INVALID_ARG_TYPE', E('ERR_INVALID_ARG_VALUE', (name, value, reason = 'is invalid') => { let inspected = lazyInternalUtilInspect().inspect(value); if (inspected.length > 128) { - inspected = `${inspected.slice(0, 128)}...`; + inspected = `${StringPrototypeSlice(inspected, 0, 128)}...`; } - const type = name.includes('.') ? 'property' : 'argument'; + const type = StringPrototypeIncludes(name, '.') ? 'property' : 'argument'; return `The ${type} '${name}' ${reason}. Received ${inspected}`; }, TypeError, RangeError); E('ERR_INVALID_ASYNC_ID', 'Invalid %s value: %s', RangeError); @@ -1195,9 +1215,10 @@ E('ERR_MANIFEST_ASSERT_INTEGRITY', moduleURL }" does not match the expected integrity.`; if (realIntegrities.size) { - const sri = [...realIntegrities.entries()].map(([alg, dgs]) => { - return `${alg}-${dgs}`; - }).join(' '); + const sri = ArrayPrototypeJoin( + ArrayFrom(realIntegrities.entries(), ([alg, dgs]) => `${alg}-${dgs}`), + ' ' + ); msg += ` Integrities found are: ${sri}`; } else { msg += ' The resource was not found in the policy.'; @@ -1225,8 +1246,11 @@ E('ERR_MISSING_ARGS', let msg = 'The '; const len = args.length; const wrap = (a) => `"${a}"`; - args = args.map( - (a) => (ArrayIsArray(a) ? a.map(wrap).join(' or ') : wrap(a)) + args = ArrayPrototypeMap( + args, + (a) => (ArrayIsArray(a) ? + ArrayPrototypeJoin(ArrayPrototypeMap(a, wrap), ' or ') : + wrap(a)) ); switch (len) { case 1: @@ -1236,7 +1260,7 @@ E('ERR_MISSING_ARGS', msg += `${args[0]} and ${args[1]} arguments`; break; default: - msg += args.slice(0, len - 1).join(', '); + msg += ArrayPrototypeJoin(ArrayPrototypeSlice(args, 0, len - 1), ', '); msg += `, and ${args[len - 1]} arguments`; break; } @@ -1299,9 +1323,10 @@ E('ERR_QUIC_INVALID_TLS_SESSION_TICKET', 'Invalid TLS session ticket', Error); E('ERR_QUIC_VERSION_NEGOTIATION', (version, requestedVersions, supportedVersions) => { + const requestedVersionsString = ArrayPrototypeJoin(requestedVersions, ', '); return 'QUIC session received version negotiation from server. ' + - `Version: ${version}. Requested: ${requestedVersions.join(', ')} ` + - `Supported: ${supportedVersions.join(', ')}`; + `Version: ${version}. Requested: ${requestedVersionsString} ` + + `Supported: ${ArrayPrototypeJoin(supportedVersions, ', ')}`; }, Error); E('ERR_REQUIRE_ESM', @@ -1447,7 +1472,7 @@ E('ERR_VM_MODULE_STATUS', 'Module status %s', Error); E('ERR_WASI_ALREADY_STARTED', 'WASI instance has already started', Error); E('ERR_WORKER_INIT_FAILED', 'Worker initialization failure: %s', Error); E('ERR_WORKER_INVALID_EXEC_ARGV', (errors, msg = 'invalid execArgv flags') => - `Initiated Worker with ${msg}: ${errors.join(', ')}`, + `Initiated Worker with ${msg}: ${ArrayPrototypeJoin(errors, ', ')}`, Error); E('ERR_WORKER_NOT_RUNNING', 'Worker instance not running', Error); E('ERR_WORKER_OUT_OF_MEMORY', @@ -1455,10 +1480,10 @@ E('ERR_WORKER_OUT_OF_MEMORY', E('ERR_WORKER_PATH', (filename) => 'The worker script or module filename must be an absolute path or a ' + 'relative path starting with \'./\' or \'../\'.' + - (filename.startsWith('file://') ? + (StringPrototypeStartsWith(filename, 'file://') ? ' Wrap file:// URLs with `new URL`.' : '' ) + - (filename.startsWith('data:text/javascript') ? + (StringPrototypeStartsWith(filename, 'data:text/javascript') ? ' Wrap data: URLs with `new URL`.' : '' ) + ` Received "${filename}"`, diff --git a/lib/internal/source_map/prepare_stack_trace.js b/lib/internal/source_map/prepare_stack_trace.js index e9c3536c963504..7053a890a8e5a1 100644 --- a/lib/internal/source_map/prepare_stack_trace.js +++ b/lib/internal/source_map/prepare_stack_trace.js @@ -2,7 +2,12 @@ const { ArrayPrototypeIndexOf, - Error, + ArrayPrototypeJoin, + ArrayPrototypeMap, + ErrorPrototypeToString, + StringPrototypeRepeat, + StringPrototypeSlice, + StringPrototypeSplit, StringPrototypeStartsWith, } = primordials; @@ -21,7 +26,6 @@ const { fileURLToPath } = require('internal/url'); // Create a prettified stacktrace, inserting context from source maps // if possible. -const ErrorToString = Error.prototype.toString; // Capture original toString. const prepareStackTrace = (globalThis, error, trace) => { // API for node internals to override error stack formatting // without interfering with userland code. @@ -36,7 +40,7 @@ const prepareStackTrace = (globalThis, error, trace) => { maybeOverridePrepareStackTrace(globalThis, error, trace); if (globalOverride !== kNoOverride) return globalOverride; - const errorString = ErrorToString.call(error); + const errorString = ErrorPrototypeToString(error); if (trace.length === 0) { return errorString; @@ -45,7 +49,7 @@ const prepareStackTrace = (globalThis, error, trace) => { let errorSource = ''; let firstLine; let firstColumn; - const preparedTrace = trace.map((t, i) => { + const preparedTrace = ArrayPrototypeJoin(ArrayPrototypeMap(trace, (t, i) => { if (i === 0) { firstLine = t.getLineNumber(); firstColumn = t.getColumnNumber(); @@ -88,8 +92,8 @@ const prepareStackTrace = (globalThis, error, trace) => { debug(err.stack); } return str; - }); - return `${errorSource}${errorString}\n at ${preparedTrace.join('')}`; + }), ''); + return `${errorSource}${errorString}\n at ${preparedTrace}`; }; // Places a snippet of code from where the exception was originally thrown @@ -118,18 +122,18 @@ function getErrorSource(payload, originalSource, firstLine, firstColumn) { } } - const lines = source.split(/\r?\n/, firstLine); + const lines = StringPrototypeSplit(source, /\r?\n/, firstLine); const line = lines[firstLine - 1]; if (!line) return exceptionLine; // Display ^ in appropriate position, regardless of whether tabs or // spaces are used: let prefix = ''; - for (const character of line.slice(0, firstColumn)) { + for (const character of StringPrototypeSlice(line, 0, firstColumn)) { prefix += (character === '\t') ? '\t' : - ' '.repeat(getStringWidth(character)); + StringPrototypeRepeat(' ', getStringWidth(character)); } - prefix = prefix.slice(0, -1); // The last character is the '^'. + prefix = StringPrototypeSlice(prefix, 0, -1); // The last character is '^'. exceptionLine = `${originalSourceNoScheme}:${firstLine}\n${line}\n${prefix}^\n\n`; From 92bdfd141b5d147c0b22e6d495b59c1b119f656b Mon Sep 17 00:00:00 2001 From: Benjamin Gruenbaum Date: Sun, 1 Nov 2020 18:22:20 +0200 Subject: [PATCH 14/66] fs: add support for AbortSignal in readFile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/35911 Reviewed-By: James M Snell Reviewed-By: Anna Henningsen Reviewed-By: Matteo Collina Reviewed-By: Rich Trott Reviewed-By: Michaël Zasso --- doc/api/fs.md | 42 +++++++++++++++++++ lib/fs.js | 3 ++ lib/internal/fs/promises.js | 25 ++++++++++- lib/internal/fs/read_file_context.js | 16 +++++++ lib/internal/fs/utils.js | 5 +++ test/parallel/test-fs-promises-readfile.js | 49 ++++++++++++++++------ test/parallel/test-fs-readfile.js | 18 ++++++++ 7 files changed, 144 insertions(+), 14 deletions(-) diff --git a/doc/api/fs.md b/doc/api/fs.md index 24768ac17f97e3..e20ab0f1fd9c1b 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -3031,6 +3031,10 @@ If `options.withFileTypes` is set to `true`, the result will contain * `path` {string|Buffer|URL|FileHandle} filename or `FileHandle` * `options` {Object|string} * `encoding` {string|null} **Default:** `null` * `flag` {string} See [support of file system `flags`][]. **Default:** `'r'`. + * `signal` {AbortSignal} allows aborting an in-progress readFile * Returns: {Promise} Asynchronously reads the entire contents of a file. @@ -5459,6 +5487,20 @@ platform-specific. On macOS, Linux, and Windows, the promise will be rejected with an error. On FreeBSD, a representation of the directory's contents will be returned. +It is possible to abort an ongoing `readFile` using an `AbortSignal`. If a +request is aborted the promise returned is rejected with an `AbortError`: + +```js +const controller = new AbortController(); +const signal = controller.signal; +readFile(fileName, { signal }).then((file) => { /* ... */ }); +// Abort the request +controller.abort(); +``` + +Aborting an ongoing request does not abort individual operating +system requests but rather the internal buffering `fs.readFile` performs. + Any specified `FileHandle` has to support reading. ### `fsPromises.readlink(path[, options])` diff --git a/lib/fs.js b/lib/fs.js index abd12e131904c8..fa64ae7e53d79c 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -315,6 +315,9 @@ function readFile(path, options, callback) { const context = new ReadFileContext(callback, options.encoding); context.isUserFd = isFd(path); // File descriptor ownership + if (options.signal) { + context.signal = options.signal; + } if (context.isUserFd) { process.nextTick(function tick(context) { readFileAfterOpen.call({ context }, null, path); diff --git a/lib/internal/fs/promises.js b/lib/internal/fs/promises.js index 497605aaefa72a..39c662fdc5b9c8 100644 --- a/lib/internal/fs/promises.js +++ b/lib/internal/fs/promises.js @@ -29,12 +29,14 @@ const { } = internalBinding('constants').fs; const binding = internalBinding('fs'); const { Buffer } = require('buffer'); + +const { codes, hideStackFrames } = require('internal/errors'); const { ERR_FS_FILE_TOO_LARGE, ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_VALUE, - ERR_METHOD_NOT_IMPLEMENTED -} = require('internal/errors').codes; + ERR_METHOD_NOT_IMPLEMENTED, +} = codes; const { isArrayBufferView } = require('internal/util/types'); const { rimrafPromises } = require('internal/fs/rimraf'); const { @@ -82,6 +84,13 @@ const { const getDirectoryEntriesPromise = promisify(getDirents); const validateRmOptionsPromise = promisify(validateRmOptions); +let DOMException; +const lazyDOMException = hideStackFrames((message, name) => { + if (DOMException === undefined) + DOMException = internalBinding('messaging').DOMException; + return new DOMException(message, name); +}); + class FileHandle extends JSTransferable { constructor(filehandle) { super(); @@ -259,8 +268,17 @@ async function writeFileHandle(filehandle, data) { } async function readFileHandle(filehandle, options) { + const signal = options && options.signal; + + if (signal && signal.aborted) { + throw lazyDOMException('The operation was aborted', 'AbortError'); + } const statFields = await binding.fstat(filehandle.fd, false, kUsePromises); + if (signal && signal.aborted) { + throw lazyDOMException('The operation was aborted', 'AbortError'); + } + let size; if ((statFields[1/* mode */] & S_IFMT) === S_IFREG) { size = statFields[8/* size */]; @@ -277,6 +295,9 @@ async function readFileHandle(filehandle, options) { MathMin(size, kReadFileMaxChunkSize); let endOfFile = false; do { + if (signal && signal.aborted) { + throw lazyDOMException('The operation was aborted', 'AbortError'); + } const buf = Buffer.alloc(chunkSize); const { bytesRead, buffer } = await read(filehandle, buf, 0, chunkSize, -1); diff --git a/lib/internal/fs/read_file_context.js b/lib/internal/fs/read_file_context.js index 5091a34231665b..faca0e0c331e39 100644 --- a/lib/internal/fs/read_file_context.js +++ b/lib/internal/fs/read_file_context.js @@ -8,6 +8,16 @@ const { Buffer } = require('buffer'); const { FSReqCallback, close, read } = internalBinding('fs'); +const { hideStackFrames } = require('internal/errors'); + + +let DOMException; +const lazyDOMException = hideStackFrames((message, name) => { + if (DOMException === undefined) + DOMException = internalBinding('messaging').DOMException; + return new DOMException(message, name); +}); + // Use 64kb in case the file type is not a regular file and thus do not know the // actual file size. Increasing the value further results in more frequent over // allocation for small files and consumes CPU time and memory that should be @@ -74,6 +84,7 @@ class ReadFileContext { this.pos = 0; this.encoding = encoding; this.err = null; + this.signal = undefined; } read() { @@ -81,6 +92,11 @@ class ReadFileContext { let offset; let length; + if (this.signal && this.signal.aborted) { + return this.close( + lazyDOMException('The operation was aborted', 'AbortError') + ); + } if (this.size === 0) { buffer = Buffer.allocUnsafeSlow(kReadFileUnknownBufferLength); offset = 0; diff --git a/lib/internal/fs/utils.js b/lib/internal/fs/utils.js index a68a9a32b9cc4a..24e2224c2e3945 100644 --- a/lib/internal/fs/utils.js +++ b/lib/internal/fs/utils.js @@ -35,6 +35,7 @@ const { const { once } = require('internal/util'); const { toPathIfFileURL } = require('internal/url'); const { + validateAbortSignal, validateBoolean, validateInt32, validateUint32 @@ -296,6 +297,10 @@ function getOptions(options, defaultOptions) { if (options.encoding !== 'buffer') assertEncoding(options.encoding); + + if (options.signal !== undefined) { + validateAbortSignal(options.signal, 'options.signal'); + } return options; } diff --git a/test/parallel/test-fs-promises-readfile.js b/test/parallel/test-fs-promises-readfile.js index ff25be75b84ff0..93729153ff3019 100644 --- a/test/parallel/test-fs-promises-readfile.js +++ b/test/parallel/test-fs-promises-readfile.js @@ -10,18 +10,21 @@ tmpdir.refresh(); const fn = path.join(tmpdir.path, 'large-file'); -async function validateReadFile() { - // Creating large buffer with random content - const buffer = Buffer.from( - Array.apply(null, { length: 16834 * 2 }) - .map(Math.random) - .map((number) => (number * (1 << 8))) - ); +// Creating large buffer with random content +const largeBuffer = Buffer.from( + Array.apply(null, { length: 16834 * 2 }) + .map(Math.random) + .map((number) => (number * (1 << 8))) +); +async function createLargeFile() { // Writing buffer to a file then try to read it - await writeFile(fn, buffer); + await writeFile(fn, largeBuffer); +} + +async function validateReadFile() { const readBuffer = await readFile(fn); - assert.strictEqual(readBuffer.equals(buffer), true); + assert.strictEqual(readBuffer.equals(largeBuffer), true); } async function validateReadFileProc() { @@ -39,6 +42,28 @@ async function validateReadFileProc() { assert.ok(hostname.length > 0); } -validateReadFile() - .then(() => validateReadFileProc()) - .then(common.mustCall()); +function validateReadFileAbortLogicBefore() { + const controller = new AbortController(); + const signal = controller.signal; + controller.abort(); + assert.rejects(readFile(fn, { signal }), { + name: 'AbortError' + }); +} + +function validateReadFileAbortLogicDuring() { + const controller = new AbortController(); + const signal = controller.signal; + process.nextTick(() => controller.abort()); + assert.rejects(readFile(fn, { signal }), { + name: 'AbortError' + }); +} + +(async () => { + await createLargeFile(); + await validateReadFile(); + await validateReadFileProc(); + await validateReadFileAbortLogicBefore(); + await validateReadFileAbortLogicDuring(); +})().then(common.mustCall()); diff --git a/test/parallel/test-fs-readfile.js b/test/parallel/test-fs-readfile.js index 648bf692d1dcc8..751043886f0d96 100644 --- a/test/parallel/test-fs-readfile.js +++ b/test/parallel/test-fs-readfile.js @@ -57,3 +57,21 @@ for (const e of fileInfo) { assert.deepStrictEqual(buf, e.contents); })); } +{ + // Test cancellation, before + const controller = new AbortController(); + const signal = controller.signal; + controller.abort(); + fs.readFile(fileInfo[0].name, { signal }, common.mustCall((err, buf) => { + assert.strictEqual(err.name, 'AbortError'); + })); +} +{ + // Test cancellation, during read + const controller = new AbortController(); + const signal = controller.signal; + fs.readFile(fileInfo[0].name, { signal }, common.mustCall((err, buf) => { + assert.strictEqual(err.name, 'AbortError'); + })); + process.nextTick(() => controller.abort()); +} From 76332a043933e26cdc45f38677e24b54303c090f Mon Sep 17 00:00:00 2001 From: Benjamin Gruenbaum Date: Thu, 28 May 2020 20:12:10 +0300 Subject: [PATCH 15/66] events: port some wpt tests PR-URL: https://github.com/nodejs/node/pull/33621 Reviewed-By: James M Snell --- test/parallel/test-eventtarget-whatwg-once.js | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 test/parallel/test-eventtarget-whatwg-once.js diff --git a/test/parallel/test-eventtarget-whatwg-once.js b/test/parallel/test-eventtarget-whatwg-once.js new file mode 100644 index 00000000000000..ba43c10916cfb7 --- /dev/null +++ b/test/parallel/test-eventtarget-whatwg-once.js @@ -0,0 +1,91 @@ +// Flags: --expose-internals --no-warnings +'use strict'; + +const common = require('../common'); + +const { + Event, + EventTarget, +} = require('internal/event_target'); + +const { + strictEqual, +} = require('assert'); + +// Manually ported from: wpt@dom/events/AddEventListenerOptions-once.html +{ + const document = new EventTarget(); + let invoked_once = false; + let invoked_normal = false; + function handler_once() { + invoked_once = true; + } + + function handler_normal() { + invoked_normal = true; + } + + document.addEventListener('test', handler_once, { once: true }); + document.addEventListener('test', handler_normal); + document.dispatchEvent(new Event('test')); + strictEqual(invoked_once, true, 'Once handler should be invoked'); + strictEqual(invoked_normal, true, 'Normal handler should be invoked'); + + invoked_once = false; + invoked_normal = false; + document.dispatchEvent(new Event('test')); + strictEqual(invoked_once, false, 'Once handler shouldn\'t be invoked again'); + strictEqual(invoked_normal, true, 'Normal handler should be invoked again'); + document.removeEventListener('test', handler_normal); +} + + +{ + // Manually ported from AddEventListenerOptions-once.html + const document = new EventTarget(); + let invoked_count = 0; + function handler() { + invoked_count++; + } + document.addEventListener('test', handler, { once: true }); + document.addEventListener('test', handler); + document.dispatchEvent(new Event('test')); + strictEqual(invoked_count, 1, 'The handler should only be added once'); + + invoked_count = 0; + document.dispatchEvent(new Event('test')); + strictEqual(invoked_count, 0, 'The handler was added as a once listener'); + + invoked_count = 0; + document.addEventListener('test', handler, { once: true }); + document.removeEventListener('test', handler); + document.dispatchEvent(new Event('test')); + strictEqual(invoked_count, 0, 'The handler should have been removed'); +} + +{ + // TODO(benjamingr) fix EventTarget recursion + common.skip('EventTarget recursion is currently broken'); + const document = new EventTarget(); + let invoked_count = 0; + function handler() { + invoked_count++; + if (invoked_count === 1) + document.dispatchEvent(new Event('test')); + } + document.addEventListener('test', handler, { once: true }); + document.dispatchEvent(new Event('test')); + strictEqual(invoked_count, 1, 'Once handler should only be invoked once'); + + invoked_count = 0; + function handler2() { + invoked_count++; + if (invoked_count === 1) + document.addEventListener('test', handler2, { once: true }); + if (invoked_count <= 2) + document.dispatchEvent(new Event('test')); + } + document.addEventListener('test', handler2, { once: true }); + document.dispatchEvent(new Event('test')); + strictEqual(invoked_count, 2, 'Once handler should only be invoked once'); +} From a7d0c76f86786eb43787d13f9504021c1171badd Mon Sep 17 00:00:00 2001 From: Benjamin Gruenbaum Date: Wed, 28 Oct 2020 16:23:44 +0200 Subject: [PATCH 16/66] events: support emit on nodeeventtarget PR-URL: https://github.com/nodejs/node/pull/35851 Reviewed-By: Anna Henningsen Reviewed-By: Rich Trott Reviewed-By: Yongsheng Zhang Reviewed-By: Ricky Zhou <0x19951125@gmail.com> --- lib/internal/event_target.js | 20 ++++++++++++++++++++ lib/internal/worker/io.js | 4 ++++ test/parallel/test-nodeeventtarget.js | 22 ++++++++++++++++++++++ test/parallel/test-worker-message-port.js | 13 ++++++++++++- 4 files changed, 58 insertions(+), 1 deletion(-) diff --git a/lib/internal/event_target.js b/lib/internal/event_target.js index 8c3ca67337baea..12e04098320bc3 100644 --- a/lib/internal/event_target.js +++ b/lib/internal/event_target.js @@ -172,6 +172,14 @@ ObjectDefineProperty(Event.prototype, SymbolToStringTag, { value: 'Event', }); +class NodeCustomEvent extends Event { + constructor(type, options) { + super(type, options); + if (options && options.detail) { + this.detail = options.detail; + } + } +} // The listeners for an EventTarget are maintained as a linked list. // Unfortunately, the way EventTarget is defined, listeners are accounted // using the tuple [handler,capture], and even if we don't actually make @@ -377,6 +385,9 @@ class EventTarget { event[kTarget] = undefined; } + [kCreateEvent](nodeValue, type) { + return new NodeCustomEvent(type, { detail: nodeValue }); + } [customInspectSymbol](depth, options) { const name = this.constructor.name; if (depth < 0) @@ -474,6 +485,14 @@ class NodeEventTarget extends EventTarget { this.addEventListener(type, listener, { [kIsNodeStyleListener]: true }); return this; } + emit(type, arg) { + if (typeof type !== 'string') { + throw new ERR_INVALID_ARG_TYPE('type', 'string', type); + } + const hadListeners = this.listenerCount(type) > 0; + this[kHybridDispatch](arg, type); + return hadListeners; + } once(type, listener) { this.addEventListener(type, listener, @@ -502,6 +521,7 @@ ObjectDefineProperties(NodeEventTarget.prototype, { on: { enumerable: true }, addListener: { enumerable: true }, once: { enumerable: true }, + emit: { enumerable: true }, removeAllListeners: { enumerable: true }, }); diff --git a/lib/internal/worker/io.js b/lib/internal/worker/io.js index e3ca6fe0cc10b5..763d06856cbd42 100644 --- a/lib/internal/worker/io.js +++ b/lib/internal/worker/io.js @@ -127,11 +127,15 @@ ObjectDefineProperties(MessageEvent.prototype, { }, }); +const originalCreateEvent = EventTarget.prototype[kCreateEvent]; ObjectDefineProperty( MessagePort.prototype, kCreateEvent, { value: function(data, type) { + if (type !== 'message' && type !== 'messageerror') { + return originalCreateEvent.call(this, data, type); + } return new MessageEvent(type, { data }); }, configurable: false, diff --git a/test/parallel/test-nodeeventtarget.js b/test/parallel/test-nodeeventtarget.js index 0f9218f540860d..276bd9feb4da7c 100644 --- a/test/parallel/test-nodeeventtarget.js +++ b/test/parallel/test-nodeeventtarget.js @@ -11,6 +11,7 @@ const { deepStrictEqual, ok, strictEqual, + throws, } = require('assert'); const { on } = require('events'); @@ -145,6 +146,27 @@ const { on } = require('events'); target.on('foo', () => {}); target.on('foo', () => {}); } +{ + // Test NodeEventTarget emit + const emitter = new NodeEventTarget(); + emitter.addEventListener('foo', common.mustCall((e) => { + strictEqual(e.type, 'foo'); + strictEqual(e.detail, 'bar'); + ok(e instanceof Event); + }), { once: true }); + emitter.once('foo', common.mustCall((e, droppedAdditionalArgument) => { + strictEqual(e, 'bar'); + strictEqual(droppedAdditionalArgument, undefined); + })); + emitter.emit('foo', 'bar', 'baz'); +} +{ + // Test NodeEventTarget emit unsupported usage + const emitter = new NodeEventTarget(); + throws(() => { + emitter.emit(); + }, /ERR_INVALID_ARG_TYPE/); +} (async () => { // test NodeEventTarget async-iterability diff --git a/test/parallel/test-worker-message-port.js b/test/parallel/test-worker-message-port.js index 4f4863c45ed516..6f4e09d029d168 100644 --- a/test/parallel/test-worker-message-port.js +++ b/test/parallel/test-worker-message-port.js @@ -16,7 +16,18 @@ const { MessageChannel, MessagePort } = require('worker_threads'); port2.close(common.mustCall()); })); } - +{ + // Test emitting non-message events on a port + const { port2 } = new MessageChannel(); + port2.addEventListener('foo', common.mustCall((received) => { + assert.strictEqual(received.type, 'foo'); + assert.strictEqual(received.detail, 'bar'); + })); + port2.on('foo', common.mustCall((received) => { + assert.strictEqual(received, 'bar'); + })); + port2.emit('foo', 'bar'); +} { const { port1, port2 } = new MessageChannel(); From 77d33c9b2f0cb3701a6617e9ea5dcf898d79c160 Mon Sep 17 00:00:00 2001 From: Daijiro Wachi Date: Tue, 3 Nov 2020 12:34:30 +0900 Subject: [PATCH 17/66] doc: update core-validate-commit link in guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/35938 Reviewed-By: Shelley Vohr Reviewed-By: Richard Lau Reviewed-By: Rich Trott Reviewed-By: Colin Ihrig Reviewed-By: Mary Marchini Reviewed-By: Luigi Pinca Reviewed-By: Tobias Nießen --- doc/guides/collaborator-guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/guides/collaborator-guide.md b/doc/guides/collaborator-guide.md index 8b40efaa5543d6..e1dbf51386f7f2 100644 --- a/doc/guides/collaborator-guide.md +++ b/doc/guides/collaborator-guide.md @@ -611,7 +611,7 @@ Other changes might have landed on master since the successful CI run. As a precaution, run tests (`make -j4 test` or `vcbuild test`). Confirm that the commit message format is correct using -[core-validate-commit](https://github.com/evanlucas/core-validate-commit). +[core-validate-commit](https://github.com/nodejs/core-validate-commit). ```text $ git rev-list upstream/master...HEAD | xargs core-validate-commit From 01129a7b39337841231147fb3f5212501e169563 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Wed, 28 Oct 2020 06:58:45 -0700 Subject: [PATCH 18/66] doc: revise v8.getHeapSnapshot() * move entry above v8.getHeapSpaceStatistics() (where it belongs alphabetically and is not out of place logically) * split lengthy sentence into two sentences PR-URL: https://github.com/nodejs/node/pull/35849 Reviewed-By: Michael Dawson --- doc/api/v8.md | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/doc/api/v8.md b/doc/api/v8.md index 0dc8eddc3dc903..aad3c77b947d73 100644 --- a/doc/api/v8.md +++ b/doc/api/v8.md @@ -32,6 +32,26 @@ v8.setFlagsFromString('--allow_natives_syntax'); console.log(v8.cachedDataVersionTag()); // 183726201 ``` +## `v8.getHeapSnapshot()` + + +* Returns: {stream.Readable} A Readable Stream containing the V8 heap snapshot + +Generates a snapshot of the current V8 heap and returns a Readable +Stream that may be used to read the JSON serialized representation. +This JSON stream format is intended to be used with tools such as +Chrome DevTools. The JSON schema is undocumented and specific to the +V8 engine. Therefore, the schema may change from one version of V8 to the next. + +```js +// Print heap snapshot to the console +const v8 = require('v8'); +const stream = v8.getHeapSnapshot(); +stream.pipe(process.stdout); +``` + ## `v8.getHeapSpaceStatistics()` - -* Returns: {stream.Readable} A Readable Stream containing the V8 heap snapshot - -Generates a snapshot of the current V8 heap and returns a Readable -Stream that may be used to read the JSON serialized representation. -This JSON stream format is intended to be used with tools such as -Chrome DevTools. The JSON schema is undocumented and specific to the -V8 engine, and may change from one version of V8 to the next. - -```js -// Print heap snapshot to the console -const v8 = require('v8'); -const stream = v8.getHeapSnapshot(); -stream.pipe(process.stdout); -``` - ## `v8.getHeapStatistics()` + +### `module.findSourceMap(path)` + * `path` {string} -* `error` {Error} * Returns: {module.SourceMap} `path` is the resolved path for the file for which a corresponding source map should be fetched. -The `error` instance should be passed as the second parameter to `findSourceMap` -in exceptional flows, such as when an overridden -[`Error.prepareStackTrace(error, trace)`][] is invoked. Modules are not added to -the module cache until they are successfully loaded. In these cases, source maps -are associated with the `error` instance along with the `path`. - ### Class: `module.SourceMap` -* `module.findSourceMap(path[, error])` +* `module.findSourceMap(path)` * Class: `module.SourceMap` * `new SourceMap(payload)` * `sourceMap.payload` diff --git a/lib/internal/per_context/primordials.js b/lib/internal/per_context/primordials.js index 7b17ef8353a7b4..8527e65f2ba7e1 100644 --- a/lib/internal/per_context/primordials.js +++ b/lib/internal/per_context/primordials.js @@ -86,6 +86,7 @@ function makeSafe(unsafe, safe) { Object.freeze(safe); return safe; } +primordials.makeSafe = makeSafe; // Subclass the constructors because we need to use their prototype // methods later. diff --git a/lib/internal/source_map/prepare_stack_trace.js b/lib/internal/source_map/prepare_stack_trace.js index 7053a890a8e5a1..267f5dc91a9b4f 100644 --- a/lib/internal/source_map/prepare_stack_trace.js +++ b/lib/internal/source_map/prepare_stack_trace.js @@ -57,7 +57,7 @@ const prepareStackTrace = (globalThis, error, trace) => { let str = i !== 0 ? '\n at ' : ''; str = `${str}${t}`; try { - const sm = findSourceMap(t.getFileName(), error); + const sm = findSourceMap(t.getFileName()); if (sm) { // Source Map V3 lines/columns use zero-based offsets whereas, in // stack traces, they start at 1/1. @@ -119,6 +119,7 @@ function getErrorSource(payload, originalSource, firstLine, firstColumn) { source = readFileSync(originalSourceNoScheme, 'utf8'); } catch (err) { debug(err); + return ''; } } diff --git a/lib/internal/source_map/source_map_cache.js b/lib/internal/source_map/source_map_cache.js index f94ffa8387a451..ff259ee4325d1d 100644 --- a/lib/internal/source_map/source_map_cache.js +++ b/lib/internal/source_map/source_map_cache.js @@ -1,6 +1,7 @@ 'use strict'; const { + ArrayPrototypeMap, JSONParse, ObjectCreate, ObjectKeys, @@ -8,8 +9,10 @@ const { ObjectPrototypeHasOwnProperty, Map, MapPrototypeEntries, - WeakMap, - WeakMapPrototypeGet, + RegExpPrototypeTest, + SafeMap, + StringPrototypeMatch, + StringPrototypeSplit, uncurryThis, } = primordials; @@ -27,17 +30,17 @@ let debug = require('internal/util/debuglog').debuglog('source_map', (fn) => { }); const fs = require('fs'); const { getOptionValue } = require('internal/options'); +const { IterableWeakMap } = require('internal/util/iterable_weak_map'); const { normalizeReferrerURL, } = require('internal/modules/cjs/helpers'); -// For cjs, since Module._cache is exposed to users, we use a WeakMap -// keyed on module, facilitating garbage collection. -const cjsSourceMapCache = new WeakMap(); -// The esm cache is not exposed to users, so we can use a Map keyed -// on filenames. -const esmSourceMapCache = new Map(); -const { fileURLToPath, URL } = require('url'); -let Module; +// Since the CJS module cache is mutable, which leads to memory leaks when +// modules are deleted, we use a WeakMap so that the source map cache will +// be purged automatically: +const cjsSourceMapCache = new IterableWeakMap(); +// The esm cache is not mutable, so we can use a Map without memory concerns: +const esmSourceMapCache = new SafeMap(); +const { fileURLToPath, pathToFileURL, URL } = require('internal/url'); let SourceMap; let sourceMapsEnabled; @@ -70,13 +73,14 @@ function maybeCacheSourceMap(filename, content, cjsModuleInstance) { debug(err.stack); return; } - - const match = content.match(/\/[*/]#\s+sourceMappingURL=(?[^\s]+)/); + const match = StringPrototypeMatch( + content, + /\/[*/]#\s+sourceMappingURL=(?[^\s]+)/ + ); if (match) { const data = dataFromUrl(filename, match.groups.sourceMappingURL); const url = data ? null : match.groups.sourceMappingURL; if (cjsModuleInstance) { - if (!Module) Module = require('internal/modules/cjs/loader').Module; cjsSourceMapCache.set(cjsModuleInstance, { filename, lineLengths: lineLengths(content), @@ -120,7 +124,7 @@ function lineLengths(content) { // We purposefully keep \r as part of the line-length calculation, in // cases where there is a \r\n separator, so that this can be taken into // account in coverage calculations. - return content.split(/\n|\u2028|\u2029/).map((line) => { + return ArrayPrototypeMap(StringPrototypeSplit(content, /\n|\u2028|\u2029/), (line) => { return line.length; }); } @@ -139,8 +143,8 @@ function sourceMapFromFile(mapURL) { // data:[][;base64], see: // https://tools.ietf.org/html/rfc2397#section-2 function sourceMapFromDataUrl(sourceURL, url) { - const [format, data] = url.split(','); - const splitFormat = format.split(';'); + const [format, data] = StringPrototypeSplit(url, ','); + const splitFormat = StringPrototypeSplit(format, ';'); const contentType = splitFormat[0]; const base64 = splitFormat[splitFormat.length - 1] === 'base64'; if (contentType === 'application/json') { @@ -207,48 +211,32 @@ function sourceMapCacheToObject() { return obj; } -// Since WeakMap can't be iterated over, we use Module._cache's -// keys to facilitate Source Map serialization. -// -// TODO(bcoe): this means we don't currently serialize source-maps attached -// to error instances, only module instances. function appendCJSCache(obj) { - if (!Module) return; - const cjsModuleCache = ObjectGetValueSafe(Module, '_cache'); - const cjsModules = ObjectKeys(cjsModuleCache); - for (let i = 0; i < cjsModules.length; i++) { - const key = cjsModules[i]; - const module = ObjectGetValueSafe(cjsModuleCache, key); - const value = WeakMapPrototypeGet(cjsSourceMapCache, module); - if (value) { - // This is okay because `obj` has a null prototype. - obj[`file://${key}`] = { - lineLengths: ObjectGetValueSafe(value, 'lineLengths'), - data: ObjectGetValueSafe(value, 'data'), - url: ObjectGetValueSafe(value, 'url') - }; - } + for (const value of cjsSourceMapCache) { + obj[ObjectGetValueSafe(value, 'filename')] = { + lineLengths: ObjectGetValueSafe(value, 'lineLengths'), + data: ObjectGetValueSafe(value, 'data'), + url: ObjectGetValueSafe(value, 'url') + }; } } -// Attempt to lookup a source map, which is either attached to a file URI, or -// keyed on an error instance. -// TODO(bcoe): once WeakRefs are available in Node.js, refactor to drop -// requirement of error parameter. -function findSourceMap(uri, error) { - if (!Module) Module = require('internal/modules/cjs/loader').Module; +function findSourceMap(sourceURL) { + if (!RegExpPrototypeTest(/^\w+:\/\//, sourceURL)) { + sourceURL = pathToFileURL(sourceURL).href; + } if (!SourceMap) { SourceMap = require('internal/source_map/source_map').SourceMap; } - let sourceMap = cjsSourceMapCache.get(Module._cache[uri]); - if (!uri.startsWith('file://')) uri = normalizeReferrerURL(uri); - if (sourceMap === undefined) { - sourceMap = esmSourceMapCache.get(uri); - } + let sourceMap = esmSourceMapCache.get(sourceURL); if (sourceMap === undefined) { - const candidateSourceMap = cjsSourceMapCache.get(error); - if (candidateSourceMap && uri === candidateSourceMap.filename) { - sourceMap = candidateSourceMap; + for (const value of cjsSourceMapCache) { + const filename = ObjectGetValueSafe(value, 'filename'); + if (sourceURL === filename) { + sourceMap = { + data: ObjectGetValueSafe(value, 'data') + }; + } } } if (sourceMap && sourceMap.data) { diff --git a/lib/internal/util/iterable_weak_map.js b/lib/internal/util/iterable_weak_map.js new file mode 100644 index 00000000000000..3fa139b23e4b4e --- /dev/null +++ b/lib/internal/util/iterable_weak_map.js @@ -0,0 +1,86 @@ +'use strict'; + +const { + makeSafe, + Object, + SafeSet, + SafeWeakMap, + SymbolIterator, +} = primordials; + +// TODO(aduh95): Add FinalizationRegistry to primordials +const SafeFinalizationRegistry = makeSafe( + globalThis.FinalizationRegistry, + class SafeFinalizationRegistry extends globalThis.FinalizationRegistry {} +); + +// TODO(aduh95): Add WeakRef to primordials +const SafeWeakRef = makeSafe( + globalThis.WeakRef, + class SafeWeakRef extends globalThis.WeakRef {} +); + +// This class is modified from the example code in the WeakRefs specification: +// https://github.com/tc39/proposal-weakrefs +// Licensed under ECMA's MIT-style license, see: +// https://github.com/tc39/ecma262/blob/master/LICENSE.md +class IterableWeakMap { + #weakMap = new SafeWeakMap(); + #refSet = new SafeSet(); + #finalizationGroup = new SafeFinalizationRegistry(cleanup); + + set(key, value) { + const entry = this.#weakMap.get(key); + if (entry) { + // If there's already an entry for the object represented by "key", + // the value can be updated without creating a new WeakRef: + this.#weakMap.set(key, { value, ref: entry.ref }); + } else { + const ref = new SafeWeakRef(key); + this.#weakMap.set(key, { value, ref }); + this.#refSet.add(ref); + this.#finalizationGroup.register(key, { + set: this.#refSet, + ref + }, ref); + } + } + + get(key) { + return this.#weakMap.get(key)?.value; + } + + has(key) { + return this.#weakMap.has(key); + } + + delete(key) { + const entry = this.#weakMap.get(key); + if (!entry) { + return false; + } + this.#weakMap.delete(key); + this.#refSet.delete(entry.ref); + this.#finalizationGroup.unregister(entry.ref); + return true; + } + + *[SymbolIterator]() { + for (const ref of this.#refSet) { + const key = ref.deref(); + if (!key) continue; + const { value } = this.#weakMap.get(key); + yield value; + } + } +} + +function cleanup({ set, ref }) { + set.delete(ref); +} + +Object.freeze(IterableWeakMap.prototype); + +module.exports = { + IterableWeakMap, +}; diff --git a/node.gyp b/node.gyp index 763af9128c466d..d4dc83d257b0a3 100644 --- a/node.gyp +++ b/node.gyp @@ -227,6 +227,7 @@ 'lib/internal/util/debuglog.js', 'lib/internal/util/inspect.js', 'lib/internal/util/inspector.js', + 'lib/internal/util/iterable_weak_map.js', 'lib/internal/util/types.js', 'lib/internal/http2/core.js', 'lib/internal/http2/compat.js', diff --git a/test/fixtures/source-map/throw-on-require-entry.js b/test/fixtures/source-map/throw-on-require-entry.js new file mode 100644 index 00000000000000..94b14810a52ef1 --- /dev/null +++ b/test/fixtures/source-map/throw-on-require-entry.js @@ -0,0 +1,4 @@ +"use strict"; +exports.__esModule = true; +require("./throw-on-require"); +//# sourceMappingURL=throw-on-require-entry.js.map diff --git a/test/fixtures/source-map/throw-on-require-entry.js.map b/test/fixtures/source-map/throw-on-require-entry.js.map new file mode 100644 index 00000000000000..a5f0ca2253fe82 --- /dev/null +++ b/test/fixtures/source-map/throw-on-require-entry.js.map @@ -0,0 +1 @@ +{"version":3,"file":"throw-on-require-entry.js","sourceRoot":"","sources":["throw-on-require-entry.ts"],"names":[],"mappings":";;AAAA,8BAA2B"} \ No newline at end of file diff --git a/test/fixtures/source-map/throw-on-require.js b/test/fixtures/source-map/throw-on-require.js new file mode 100644 index 00000000000000..5654f2bfb917ab --- /dev/null +++ b/test/fixtures/source-map/throw-on-require.js @@ -0,0 +1,15 @@ +var ATrue; +(function (ATrue) { + ATrue[ATrue["IsTrue"] = 1] = "IsTrue"; + ATrue[ATrue["IsFalse"] = 0] = "IsFalse"; +})(ATrue || (ATrue = {})); +if (false) { + console.info('unreachable'); +} +else if (true) { + throw Error('throw early'); +} +else { + console.info('unreachable'); +} +//# sourceMappingURL=throw-on-require.js.map \ No newline at end of file diff --git a/test/fixtures/source-map/throw-on-require.js.map b/test/fixtures/source-map/throw-on-require.js.map new file mode 100644 index 00000000000000..94d0df9c03abbd --- /dev/null +++ b/test/fixtures/source-map/throw-on-require.js.map @@ -0,0 +1 @@ +{"version":3,"file":"throw-on-require.js","sourceRoot":"","sources":["throw-on-require.ts"],"names":[],"mappings":"AAAA,IAAK,KAGJ;AAHD,WAAK,KAAK;IACR,qCAAU,CAAA;IACV,uCAAW,CAAA;AACb,CAAC,EAHI,KAAK,KAAL,KAAK,QAGT;AAED,IAAI,KAAK,EAAE;IACT,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;CAC5B;KAAM,IAAI,IAAI,EAAE;IACf,MAAM,KAAK,CAAC,aAAa,CAAC,CAAA;CAC3B;KAAM;IACL,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;CAC5B"} \ No newline at end of file diff --git a/test/fixtures/source-map/throw-on-require.ts b/test/fixtures/source-map/throw-on-require.ts new file mode 100644 index 00000000000000..4aa570302778f4 --- /dev/null +++ b/test/fixtures/source-map/throw-on-require.ts @@ -0,0 +1,12 @@ +enum ATrue { + IsTrue = 1, + IsFalse = 0 +} + +if (false) { + console.info('unreachable') +} else if (true) { + throw Error('throw early') +} else { + console.info('unreachable') +} diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index 2a811d65cc2633..b9844dc4caf683 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -92,6 +92,7 @@ const expectedModules = new Set([ 'NativeModule internal/util', 'NativeModule internal/util/debuglog', 'NativeModule internal/util/inspect', + 'NativeModule internal/util/iterable_weak_map', 'NativeModule internal/util/types', 'NativeModule internal/validators', 'NativeModule internal/vm/module', diff --git a/test/parallel/test-internal-iterable-weak-map.js b/test/parallel/test-internal-iterable-weak-map.js new file mode 100644 index 00000000000000..04636c20903a2e --- /dev/null +++ b/test/parallel/test-internal-iterable-weak-map.js @@ -0,0 +1,95 @@ +// Flags: --expose-gc --expose-internals +'use strict'; + +require('../common'); +const { deepStrictEqual, strictEqual } = require('assert'); +const { IterableWeakMap } = require('internal/util/iterable_weak_map'); + +// It drops entry if a reference is no longer held. +{ + const wm = new IterableWeakMap(); + const _cache = { + moduleA: {}, + moduleB: {}, + moduleC: {}, + }; + wm.set(_cache.moduleA, 'hello'); + wm.set(_cache.moduleB, 'discard'); + wm.set(_cache.moduleC, 'goodbye'); + delete _cache.moduleB; + setImmediate(() => { + _cache; + globalThis.gc(); + const values = [...wm]; + deepStrictEqual(values, ['hello', 'goodbye']); + }); +} + +// It updates an existing entry, if the same key is provided twice. +{ + const wm = new IterableWeakMap(); + const _cache = { + moduleA: {}, + moduleB: {}, + }; + wm.set(_cache.moduleA, 'hello'); + wm.set(_cache.moduleB, 'goodbye'); + wm.set(_cache.moduleB, 'goodnight'); + const values = [...wm]; + deepStrictEqual(values, ['hello', 'goodnight']); +} + +// It allows entry to be deleted by key. +{ + const wm = new IterableWeakMap(); + const _cache = { + moduleA: {}, + moduleB: {}, + moduleC: {}, + }; + wm.set(_cache.moduleA, 'hello'); + wm.set(_cache.moduleB, 'discard'); + wm.set(_cache.moduleC, 'goodbye'); + wm.delete(_cache.moduleB); + const values = [...wm]; + deepStrictEqual(values, ['hello', 'goodbye']); +} + +// It handles delete for key that does not exist. +{ + const wm = new IterableWeakMap(); + const _cache = { + moduleA: {}, + moduleB: {}, + moduleC: {}, + }; + wm.set(_cache.moduleA, 'hello'); + wm.set(_cache.moduleC, 'goodbye'); + wm.delete(_cache.moduleB); + const values = [...wm]; + deepStrictEqual(values, ['hello', 'goodbye']); +} + +// It allows an entry to be fetched by key. +{ + const wm = new IterableWeakMap(); + const _cache = { + moduleA: {}, + moduleB: {}, + moduleC: {}, + }; + wm.set(_cache.moduleA, 'hello'); + wm.set(_cache.moduleB, 'discard'); + wm.set(_cache.moduleC, 'goodbye'); + strictEqual(wm.get(_cache.moduleB), 'discard'); +} + +// It returns true for has() if key exists. +{ + const wm = new IterableWeakMap(); + const _cache = { + moduleA: {}, + }; + wm.set(_cache.moduleA, 'hello'); + strictEqual(wm.has(_cache.moduleA), true); +} diff --git a/test/parallel/test-source-map-api.js b/test/parallel/test-source-map-api.js index 60bbb661e1c801..1a3d180619fe05 100644 --- a/test/parallel/test-source-map-api.js +++ b/test/parallel/test-source-map-api.js @@ -31,7 +31,7 @@ const { readFileSync } = require('fs'); Error.prepareStackTrace = (error, trace) => { const throwingRequireCallSite = trace[0]; if (throwingRequireCallSite.getFileName().endsWith('typescript-throw.js')) { - sourceMap = findSourceMap(throwingRequireCallSite.getFileName(), error); + sourceMap = findSourceMap(throwingRequireCallSite.getFileName()); callSite = throwingRequireCallSite; } }; diff --git a/test/parallel/test-source-map-enable.js b/test/parallel/test-source-map-enable.js index 0887ae8811c45b..cea597b4f7b0c0 100644 --- a/test/parallel/test-source-map-enable.js +++ b/test/parallel/test-source-map-enable.js @@ -283,6 +283,24 @@ function nextdir() { assert.ok(output.stderr.toString().includes('webpack:///webpack.js:14:9')); } +// Stores and applies source map associated with file that throws while +// being required. +{ + const coverageDirectory = nextdir(); + const output = spawnSync(process.execPath, [ + '--enable-source-maps', + require.resolve('../fixtures/source-map/throw-on-require-entry.js') + ], { env: { ...process.env, NODE_V8_COVERAGE: coverageDirectory } }); + const sourceMap = getSourceMapFromCache( + 'throw-on-require.js', + coverageDirectory + ); + // Rewritten stack trace. + assert.match(output.stderr.toString(), /throw-on-require\.ts:9:9/); + // Source map should have been serialized. + assert.ok(sourceMap); +} + function getSourceMapFromCache(fixtureFile, coverageDirectory) { const jsonFiles = fs.readdirSync(coverageDirectory); for (const jsonFile of jsonFiles) { From 9d26c4d496840c65cc1f1c91a8d6c7c065eadf2a Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Fri, 30 Oct 2020 16:03:02 +0100 Subject: [PATCH 22/66] domain: refactor to use more primordials PR-URL: https://github.com/nodejs/node/pull/35885 Reviewed-By: Rich Trott --- lib/domain.js | 65 ++++++++++++++++++--------------------------------- 1 file changed, 23 insertions(+), 42 deletions(-) diff --git a/lib/domain.js b/lib/domain.js index febd7620f21500..1d6d75ee91527a 100644 --- a/lib/domain.js +++ b/lib/domain.js @@ -27,11 +27,17 @@ // unless they address existing, critical bugs. const { - Array, + ArrayPrototypeEvery, + ArrayPrototypeIndexOf, + ArrayPrototypeLastIndexOf, + ArrayPrototypePush, + ArrayPrototypeSlice, + ArrayPrototypeSplice, Error, - Map, + FunctionPrototypeCall, ObjectDefineProperty, ReflectApply, + SafeMap, Symbol, } = primordials; @@ -61,7 +67,7 @@ ObjectDefineProperty(process, 'domain', { } }); -const pairing = new Map(); +const pairing = new SafeMap(); const asyncHook = createHook({ init(asyncId, type, triggerAsyncId, resource) { if (process.domain !== null && process.domain !== undefined) { @@ -149,7 +155,8 @@ exports._stack = stack; useDomainTrampoline(topLevelDomainCallback); function updateExceptionCapture() { - if (stack.every((domain) => domain.listenerCount('error') === 0)) { + if (ArrayPrototypeEvery(stack, + (domain) => domain.listenerCount('error') === 0)) { setUncaughtExceptionCaptureCallback(null); } else { setUncaughtExceptionCaptureCallback(null); @@ -296,18 +303,18 @@ Domain.prototype.enter = function() { // Note that this might be a no-op, but we still need // to push it onto the stack so that we can pop it later. exports.active = process.domain = this; - stack.push(this); + ArrayPrototypePush(stack, this); updateExceptionCapture(); }; Domain.prototype.exit = function() { // Don't do anything if this domain is not on the stack. - const index = stack.lastIndexOf(this); + const index = ArrayPrototypeLastIndexOf(stack, this); if (index === -1) return; // Exit all domains until this one. - stack.splice(index); + ArrayPrototypeSplice(stack, index); exports.active = stack[stack.length - 1]; process.domain = exports.active; @@ -346,33 +353,21 @@ Domain.prototype.add = function(ee) { value: this, writable: true }); - this.members.push(ee); + ArrayPrototypePush(this.members, ee); }; Domain.prototype.remove = function(ee) { ee.domain = null; - const index = this.members.indexOf(ee); + const index = ArrayPrototypeIndexOf(this.members, ee); if (index !== -1) - this.members.splice(index, 1); + ArrayPrototypeSplice(this.members, index, 1); }; Domain.prototype.run = function(fn) { - let ret; - this.enter(); - if (arguments.length >= 2) { - const len = arguments.length; - const args = new Array(len - 1); - - for (let i = 1; i < len; i++) - args[i - 1] = arguments[i]; - - ret = fn.apply(this, args); - } else { - ret = fn.call(this); - } + const ret = ReflectApply(fn, this, ArrayPrototypeSlice(arguments, 1)); this.exit(); return ret; @@ -394,17 +389,8 @@ function intercepted(_this, self, cb, fnargs) { return; } - const args = []; - let ret; - self.enter(); - if (fnargs.length > 1) { - for (let i = 1; i < fnargs.length; i++) - args.push(fnargs[i]); - ret = cb.apply(_this, args); - } else { - ret = cb.call(_this); - } + const ret = ReflectApply(cb, _this, ArrayPrototypeSlice(fnargs, 1)); self.exit(); return ret; @@ -423,13 +409,8 @@ Domain.prototype.intercept = function(cb) { function bound(_this, self, cb, fnargs) { - let ret; - self.enter(); - if (fnargs.length > 0) - ret = cb.apply(_this, fnargs); - else - ret = cb.call(_this); + const ret = ReflectApply(cb, _this, fnargs); self.exit(); return ret; @@ -468,7 +449,7 @@ EventEmitter.init = function() { this.domain = exports.active; } - return eventInit.call(this); + return FunctionPrototypeCall(eventInit, this); }; const eventEmit = EventEmitter.prototype.emit; @@ -506,7 +487,7 @@ EventEmitter.prototype.emit = function(...args) { // handler doesn't run in its own context. This prevents any event emitter // created or any exception thrown in that error handler from recursively // executing that error handler. - const origDomainsStack = stack.slice(); + const origDomainsStack = ArrayPrototypeSlice(stack); const origActiveDomain = process.domain; // Travel the domains stack from top to bottom to find the first domain @@ -521,7 +502,7 @@ EventEmitter.prototype.emit = function(...args) { if (idx < 0) { stack.length = 0; } else { - stack.splice(idx + 1); + ArrayPrototypeSplice(stack, idx + 1); } // Change the current active domain From da3c2ab828cc9bdc85015e81550a3a42f5f124ea Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Fri, 6 Nov 2020 13:08:30 +0100 Subject: [PATCH 23/66] tools,doc: enable ecmaVersion 2021 in acorn parser PR-URL: https://github.com/nodejs/node/pull/35994 Reviewed-By: Benjamin Gruenbaum Reviewed-By: Rich Trott Reviewed-By: Jiawen Geng Reviewed-By: Derek Lewis Reviewed-By: Luigi Pinca --- tools/doc/apilinks.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tools/doc/apilinks.js b/tools/doc/apilinks.js index 461805dac3a811..c9ce87ba08ad48 100644 --- a/tools/doc/apilinks.js +++ b/tools/doc/apilinks.js @@ -54,9 +54,11 @@ inputs.forEach((file) => { // Parse source. const source = fs.readFileSync(file, 'utf8'); - const ast = acorn.parse( - source, - { allowReturnOutsideFunction: true, ecmaVersion: 10, locations: true }); + const ast = acorn.parse(source, { + allowReturnOutsideFunction: true, + ecmaVersion: 'latest', + locations: true, + }); const program = ast.body; // Build link From 9d9a044c1b5f214f840cd809d38bc1206f1e888d Mon Sep 17 00:00:00 2001 From: Richard Lau Date: Wed, 4 Nov 2020 20:00:37 +0000 Subject: [PATCH 24/66] benchmark: ignore build artifacts for napi addons Add `.gitignore` to ignore the `build` directory in a similar way to the other addons under `benchmark/napi`. PR-URL: https://github.com/nodejs/node/pull/35970 Reviewed-By: Luigi Pinca Reviewed-By: Anna Henningsen Reviewed-By: Rich Trott --- benchmark/napi/type-tag-check/.gitignore | 1 + benchmark/napi/type-tag/.gitignore | 1 + 2 files changed, 2 insertions(+) create mode 100644 benchmark/napi/type-tag-check/.gitignore create mode 100644 benchmark/napi/type-tag/.gitignore diff --git a/benchmark/napi/type-tag-check/.gitignore b/benchmark/napi/type-tag-check/.gitignore new file mode 100644 index 00000000000000..567609b1234a9b --- /dev/null +++ b/benchmark/napi/type-tag-check/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/benchmark/napi/type-tag/.gitignore b/benchmark/napi/type-tag/.gitignore new file mode 100644 index 00000000000000..567609b1234a9b --- /dev/null +++ b/benchmark/napi/type-tag/.gitignore @@ -0,0 +1 @@ +build/ From 6011bfdec54c6d2228b8bb3c1a0d5da64901f9c6 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Fri, 30 Oct 2020 07:50:22 -0700 Subject: [PATCH 25/66] fs: remove unused assignment PR-URL: https://github.com/nodejs/node/pull/35882 Reviewed-By: Colin Ihrig Reviewed-By: Daijiro Wachi --- lib/fs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fs.js b/lib/fs.js index fa64ae7e53d79c..dc51e042f12168 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -862,7 +862,7 @@ function rmdir(path, options, callback) { path = pathModule.toNamespacedPath(getValidatedPath(path)); if (options && options.recursive) { - options = validateRmOptions( + validateRmOptions( path, { ...options, force: true }, true, From 1f272144805c6f473d5881ce58fc898af948ab20 Mon Sep 17 00:00:00 2001 From: Leko Date: Wed, 30 Sep 2020 02:29:40 +0800 Subject: [PATCH 26/66] tools: add new ESLint rule: prefer-primordials MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I added a new custom ESLint rule to fix these problems. We have a lot of replaceable codes with primordials. Accessing built-in objects is restricted by existing rule (no-restricted-globals), but accessing property in the built-in objects is not restricted right now. We manually review codes that can be replaced by primordials, but there's a lot of code that actually needs to be fixed. We have often made pull requests to replace the primordials with. Restrict accessing global built-in objects such as `Promise`. Restrict calling static methods such as `Array.from` or `Symbol.for`. Don't restrict prototype methods to prevent false-positive. PR-URL: https://github.com/nodejs/node/pull/35448 Reviewed-By: Michaël Zasso Reviewed-By: Rich Trott Reviewed-By: Antoine du Hamel Reviewed-By: Daijiro Wachi Reviewed-By: Ben Coe --- lib/.eslintrc.yaml | 103 +++++------- lib/internal/event_target.js | 2 +- lib/internal/freeze_intrinsics.js | 130 +++++++++----- lib/internal/fs/utils.js | 4 +- lib/internal/http.js | 1 + lib/internal/main/print_help.js | 19 ++- lib/internal/per_context/primordials.js | 2 +- lib/internal/quic/core.js | 9 +- lib/internal/readline/utils.js | 4 +- lib/internal/timers.js | 3 +- lib/internal/util/iterable_weak_map.js | 4 +- lib/internal/vm/module.js | 3 +- lib/querystring.js | 5 +- lib/util.js | 1 + lib/v8.js | 1 + .../test-eslint-prefer-primordials.js | 158 ++++++++++++++++++ tools/eslint-rules/prefer-primordials.js | 156 +++++++++++++++++ 17 files changed, 477 insertions(+), 128 deletions(-) create mode 100644 test/parallel/test-eslint-prefer-primordials.js create mode 100644 tools/eslint-rules/prefer-primordials.js diff --git a/lib/.eslintrc.yaml b/lib/.eslintrc.yaml index 20e01898695507..37a43a223af2fd 100644 --- a/lib/.eslintrc.yaml +++ b/lib/.eslintrc.yaml @@ -7,105 +7,80 @@ rules: no-mixed-operators: - error - groups: [[ "&&", "||" ]] - no-restricted-globals: + no-restricted-syntax: + # Config copied from .eslintrc.js + - error + - selector: "CallExpression[callee.object.name='assert']:not([callee.property.name='ok']):not([callee.property.name='fail']):not([callee.property.name='ifError'])" + message: "Please only use simple assertions in ./lib" + - 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." + - selector: "CallExpression[callee.object.name='Error'][callee.property.name='captureStackTrace']" + message: "Please use `require('internal/errors').hideStackFrames()` instead." + - selector: "AssignmentExpression:matches([left.name='prepareStackTrace'], [left.property.name='prepareStackTrace'])" + message: "Use 'overrideStackTrace' from 'lib/internal/errors.js' instead of 'Error.prepareStackTrace'." + # Custom rules in tools/eslint-rules + node-core/lowercase-name-for-primitive: error + node-core/non-ascii-character: error + node-core/prefer-primordials: - error - name: Array - message: "Use `const { Array } = primordials;` instead of the global." - name: ArrayBuffer - message: "Use `const { ArrayBuffer } = primordials;` instead of the global." - name: BigInt - message: "Use `const { BigInt } = primordials;` instead of the global." - name: BigInt64Array - message: "Use `const { BigInt64Array } = primordials;` instead of the global." - name: BigUint64Array - message: "Use `const { BigUint64Array } = primordials;` instead of the global." - name: Boolean - message: "Use `const { Boolean } = primordials;` instead of the global." + - name: DataView + - name: Date - name: Error - message: "Use `const { Error } = primordials;` instead of the global." + ignore: + - stackTraceLimit + - captureStackTrace + - prepareStackTrace + - isPrototypeOf - name: EvalError - message: "Use `const { EvalError } = primordials;` instead of the global." - name: Float32Array - message: "Use `const { Float32Array } = primordials;` instead of the global." - name: Float64Array - message: "Use `const { Float64Array } = primordials;` instead of the global." + - name: Function - name: Int16Array - message: "Use `const { Int16Array } = primordials;` instead of the global." - name: Int32Array - message: "Use `const { Int32Array } = primordials;` instead of the global." - name: Int8Array - message: "Use `const { Int8Array } = primordials;` instead of the global." + - name: isFinite + into: Number + - name: isNaN + into: Number - name: JSON - message: "Use `const { JSON } = primordials;` instead of the global." - name: Map - message: "Use `const { Map } = primordials;` instead of the global." - name: Math - message: "Use `const { Math } = primordials;` instead of the global." - name: Number - message: "Use `const { Number } = primordials;` instead of the global." - name: Object - message: "Use `const { Object } = primordials;` instead of the global." + - name: parseFloat + into: Number + - name: parseInt + into: Number - name: Promise - message: "Use `const { Promise } = primordials;` instead of the global." - name: RangeError - message: "Use `const { RangeError } = primordials;` instead of the global." - name: ReferenceError - message: "Use `const { ReferenceError } = primordials;` instead of the global." - name: Reflect - message: "Use `const { Reflect } = primordials;` instead of the global." - name: RegExp - message: "Use `const { RegExp } = primordials;` instead of the global." - name: Set - message: "Use `const { Set } = primordials;` instead of the global." - name: String - message: "Use `const { String } = primordials;` instead of the global." - name: Symbol - message: "Use `const { Symbol } = primordials;` instead of the global." - name: SyntaxError - message: "Use `const { SyntaxError } = primordials;` instead of the global." - name: TypeError - message: "Use `const { TypeError } = primordials;` instead of the global." - - name: URIError - message: "Use `const { URIError } = primordials;` instead of the global." - name: Uint16Array - message: "Use `const { Uint16Array } = primordials;` instead of the global." - name: Uint32Array - message: "Use `const { Uint32Array } = primordials;` instead of the global." - name: Uint8Array - message: "Use `const { Uint8Array } = primordials;` instead of the global." - name: Uint8ClampedArray - message: "Use `const { Uint8ClampedArray } = primordials;` instead of the global." + - name: URIError - name: WeakMap - message: "Use `const { WeakMap } = primordials;` instead of the global." - name: WeakSet - message: "Use `const { WeakSet } = primordials;` instead of the global." - - name: parseFloat - message: "Use `const { NumberParseFloat } = primordials;` instead of the global." - - name: parseInt - message: "Use `const { NumberParseInt } = primordials;` instead of the global." - no-restricted-syntax: - # Config copied from .eslintrc.js - - error - - selector: "CallExpression[callee.object.name='assert']:not([callee.property.name='ok']):not([callee.property.name='fail']):not([callee.property.name='ifError'])" - message: "Please only use simple assertions in ./lib" - - 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." - - selector: "CallExpression[callee.object.name='Error'][callee.property.name='captureStackTrace']" - message: "Please use `require('internal/errors').hideStackFrames()` instead." - - selector: "AssignmentExpression:matches([left.name='prepareStackTrace'], [left.property.name='prepareStackTrace'])" - message: "Use 'overrideStackTrace' from 'lib/internal/errors.js' instead of 'Error.prepareStackTrace'." - - selector: "CallExpression[callee.name='isNaN']" - message: "Use NumberIsNaN() primordial instead of the global isNaN() function." - # Custom rules in tools/eslint-rules - node-core/lowercase-name-for-primitive: error - node-core/non-ascii-character: error globals: Intl: false # Parameters passed to internal modules diff --git a/lib/internal/event_target.js b/lib/internal/event_target.js index 12e04098320bc3..5fb9f4c6d62aa7 100644 --- a/lib/internal/event_target.js +++ b/lib/internal/event_target.js @@ -36,7 +36,7 @@ const kEvents = Symbol('kEvents'); const kStop = Symbol('kStop'); const kTarget = Symbol('kTarget'); -const kHybridDispatch = Symbol.for('nodejs.internal.kHybridDispatch'); +const kHybridDispatch = SymbolFor('nodejs.internal.kHybridDispatch'); const kCreateEvent = Symbol('kCreateEvent'); const kNewListener = Symbol('kNewListener'); const kRemoveListener = Symbol('kRemoveListener'); diff --git a/lib/internal/freeze_intrinsics.js b/lib/internal/freeze_intrinsics.js index dc64c13409b2f1..21612790ce749c 100644 --- a/lib/internal/freeze_intrinsics.js +++ b/lib/internal/freeze_intrinsics.js @@ -20,21 +20,61 @@ // https://github.com/tc39/proposal-ses/blob/e5271cc42a257a05dcae2fd94713ed2f46c08620/shim/src/freeze.js /* global WebAssembly, SharedArrayBuffer, console */ -/* eslint-disable no-restricted-globals */ 'use strict'; +const { + Array, + ArrayBuffer, + ArrayPrototypeForEach, + BigInt, + BigInt64Array, + BigUint64Array, + Boolean, + DataView, + Date, + Error, + EvalError, + Float32Array, + Float64Array, + Function, + Int16Array, + Int32Array, + Int8Array, + JSON, + Map, + Math, + Number, + Object, + ObjectDefineProperty, + ObjectFreeze, + ObjectGetOwnPropertyDescriptor, + ObjectGetOwnPropertyDescriptors, + ObjectGetOwnPropertyNames, + ObjectGetOwnPropertySymbols, + ObjectGetPrototypeOf, + ObjectPrototypeHasOwnProperty, + Promise, + RangeError, + ReferenceError, + Reflect, + ReflectOwnKeys, + RegExp, + Set, + String, + Symbol, + SymbolIterator, + SyntaxError, + TypeError, + Uint16Array, + Uint32Array, + Uint8Array, + Uint8ClampedArray, + URIError, + WeakMap, + WeakSet, +} = primordials; + module.exports = function() { - const { - defineProperty, - freeze, - getOwnPropertyDescriptor, - getOwnPropertyDescriptors, - getOwnPropertyNames, - getOwnPropertySymbols, - getPrototypeOf, - } = Object; - const objectHasOwnProperty = Object.prototype.hasOwnProperty; - const { ownKeys } = Reflect; const { clearImmediate, clearInterval, @@ -47,25 +87,25 @@ module.exports = function() { const intrinsicPrototypes = [ // Anonymous Intrinsics // IteratorPrototype - getPrototypeOf( - getPrototypeOf(new Array()[Symbol.iterator]()) + ObjectGetPrototypeOf( + ObjectGetPrototypeOf(new Array()[SymbolIterator]()) ), // ArrayIteratorPrototype - getPrototypeOf(new Array()[Symbol.iterator]()), + ObjectGetPrototypeOf(new Array()[SymbolIterator]()), // StringIteratorPrototype - getPrototypeOf(new String()[Symbol.iterator]()), + ObjectGetPrototypeOf(new String()[SymbolIterator]()), // MapIteratorPrototype - getPrototypeOf(new Map()[Symbol.iterator]()), + ObjectGetPrototypeOf(new Map()[SymbolIterator]()), // SetIteratorPrototype - getPrototypeOf(new Set()[Symbol.iterator]()), + ObjectGetPrototypeOf(new Set()[SymbolIterator]()), // GeneratorFunction - getPrototypeOf(function* () {}), + ObjectGetPrototypeOf(function* () {}), // AsyncFunction - getPrototypeOf(async function() {}), + ObjectGetPrototypeOf(async function() {}), // AsyncGeneratorFunction - getPrototypeOf(async function* () {}), + ObjectGetPrototypeOf(async function* () {}), // TypedArray - getPrototypeOf(Uint8Array), + ObjectGetPrototypeOf(Uint8Array), // 19 Fundamental Objects Object.prototype, // 19.1 @@ -129,33 +169,37 @@ module.exports = function() { const intrinsics = [ // Anonymous Intrinsics // ThrowTypeError - getOwnPropertyDescriptor(Function.prototype, 'caller').get, + ObjectGetOwnPropertyDescriptor(Function.prototype, 'caller').get, // IteratorPrototype - getPrototypeOf( - getPrototypeOf(new Array()[Symbol.iterator]()) + ObjectGetPrototypeOf( + ObjectGetPrototypeOf(new Array()[SymbolIterator]()) ), // ArrayIteratorPrototype - getPrototypeOf(new Array()[Symbol.iterator]()), + ObjectGetPrototypeOf(new Array()[SymbolIterator]()), // StringIteratorPrototype - getPrototypeOf(new String()[Symbol.iterator]()), + ObjectGetPrototypeOf(new String()[SymbolIterator]()), // MapIteratorPrototype - getPrototypeOf(new Map()[Symbol.iterator]()), + ObjectGetPrototypeOf(new Map()[SymbolIterator]()), // SetIteratorPrototype - getPrototypeOf(new Set()[Symbol.iterator]()), + ObjectGetPrototypeOf(new Set()[SymbolIterator]()), // GeneratorFunction - getPrototypeOf(function* () {}), + ObjectGetPrototypeOf(function* () {}), // AsyncFunction - getPrototypeOf(async function() {}), + ObjectGetPrototypeOf(async function() {}), // AsyncGeneratorFunction - getPrototypeOf(async function* () {}), + ObjectGetPrototypeOf(async function* () {}), // TypedArray - getPrototypeOf(Uint8Array), + ObjectGetPrototypeOf(Uint8Array), // 18 The Global Object eval, + // eslint-disable-next-line node-core/prefer-primordials isFinite, + // eslint-disable-next-line node-core/prefer-primordials isNaN, + // eslint-disable-next-line node-core/prefer-primordials parseFloat, + // eslint-disable-next-line node-core/prefer-primordials parseInt, decodeURI, decodeURIComponent, @@ -289,16 +333,16 @@ module.exports = function() { // Object are verified before being enqueued, // therefore this is a valid candidate. // Throws if this fails (strict mode). - freeze(obj); + ObjectFreeze(obj); // We rely upon certain commitments of Object.freeze and proxies here // Get stable/immutable outbound links before a Proxy has a chance to do // something sneaky. - const proto = getPrototypeOf(obj); - const descs = getOwnPropertyDescriptors(obj); + const proto = ObjectGetPrototypeOf(obj); + const descs = ObjectGetOwnPropertyDescriptors(obj); enqueue(proto); - ownKeys(descs).forEach((name) => { + ArrayPrototypeForEach(ReflectOwnKeys(descs), (name) => { // TODO: Uncurried form // TODO: getOwnPropertyDescriptors is guaranteed to return well-formed // descriptors, but they still inherit from Object.prototype. If @@ -378,10 +422,10 @@ module.exports = function() { `Cannot assign to read only property '${prop}' of object '${obj}'` ); } - if (objectHasOwnProperty.call(this, prop)) { + if (ObjectPrototypeHasOwnProperty(this, prop)) { this[prop] = newValue; } else { - defineProperty(this, prop, { + ObjectDefineProperty(this, prop, { value: newValue, writable: true, enumerable: true, @@ -390,7 +434,7 @@ module.exports = function() { } } - defineProperty(obj, prop, { + ObjectDefineProperty(obj, prop, { get: getter, set: setter, enumerable: desc.enumerable, @@ -403,14 +447,14 @@ module.exports = function() { if (!obj) { return; } - const descs = getOwnPropertyDescriptors(obj); + const descs = ObjectGetOwnPropertyDescriptors(obj); if (!descs) { return; } - getOwnPropertyNames(obj).forEach((prop) => { + ArrayPrototypeForEach(ObjectGetOwnPropertyNames(obj), (prop) => { return enableDerivedOverride(obj, prop, descs[prop]); }); - getOwnPropertySymbols(obj).forEach((prop) => { + ArrayPrototypeForEach(ObjectGetOwnPropertySymbols(obj), (prop) => { return enableDerivedOverride(obj, prop, descs[prop]); }); } diff --git a/lib/internal/fs/utils.js b/lib/internal/fs/utils.js index 24e2224c2e3945..dcef28c556da46 100644 --- a/lib/internal/fs/utils.js +++ b/lib/internal/fs/utils.js @@ -3,11 +3,13 @@ const { ArrayIsArray, BigInt, + Date, DateNow, ErrorCaptureStackTrace, ObjectPrototypeHasOwnProperty, Number, NumberIsFinite, + NumberIsInteger, MathMin, ObjectSetPrototypeOf, ReflectOwnKeys, @@ -785,7 +787,7 @@ const getValidMode = hideStackFrames((mode, type) => { if (mode == null) { return def; } - if (Number.isInteger(mode) && mode >= min && mode <= max) { + if (NumberIsInteger(mode) && mode >= min && mode <= max) { return mode; } if (typeof mode !== 'number') { diff --git a/lib/internal/http.js b/lib/internal/http.js index aab4170a2f0e06..1d5973593e9ada 100644 --- a/lib/internal/http.js +++ b/lib/internal/http.js @@ -2,6 +2,7 @@ const { Symbol, + Date, } = primordials; const { setUnrefTimeout } = require('internal/timers'); diff --git a/lib/internal/main/print_help.js b/lib/internal/main/print_help.js index 34a8e19f08ec31..0850681882759f 100644 --- a/lib/internal/main/print_help.js +++ b/lib/internal/main/print_help.js @@ -1,6 +1,13 @@ 'use strict'; -/* eslint-disable no-restricted-globals */ +const { + Boolean, + Map, + MathFloor, + MathMax, + ObjectKeys, + RegExp, +} = primordials; const { types } = internalBinding('options'); const hasCrypto = Boolean(process.versions.openssl); @@ -10,7 +17,7 @@ const { } = require('internal/bootstrap/pre_execution'); const typeLookup = []; -for (const key of Object.keys(types)) +for (const key of ObjectKeys(types)) typeLookup[types[key]] = key; // Environment variables are parsed ad-hoc throughout the code base, @@ -127,7 +134,7 @@ function format({ options, aliases = new Map(), firstColumn, secondColumn }) { } text += displayName; - maxFirstColumnUsed = Math.max(maxFirstColumnUsed, displayName.length); + maxFirstColumnUsed = MathMax(maxFirstColumnUsed, displayName.length); if (displayName.length >= firstColumn) text += '\n' + ' '.repeat(firstColumn); else @@ -154,9 +161,9 @@ function print(stream) { const { options, aliases } = require('internal/options'); // Use 75 % of the available width, and at least 70 characters. - const width = Math.max(70, (stream.columns || 0) * 0.75); - const firstColumn = Math.floor(width * 0.4); - const secondColumn = Math.floor(width * 0.57); + const width = MathMax(70, (stream.columns || 0) * 0.75); + const firstColumn = MathFloor(width * 0.4); + const secondColumn = MathFloor(width * 0.57); options.set('-', { helpText: 'script read from stdin ' + '(default if no file name is provided, ' + diff --git a/lib/internal/per_context/primordials.js b/lib/internal/per_context/primordials.js index 8527e65f2ba7e1..ddecb18861c258 100644 --- a/lib/internal/per_context/primordials.js +++ b/lib/internal/per_context/primordials.js @@ -1,6 +1,6 @@ 'use strict'; -/* eslint-disable no-restricted-globals */ +/* eslint-disable node-core/prefer-primordials */ // This file subclasses and stores the JS builtins that come from the VM // so that Node.js's builtin modules do not need to later look these up from diff --git a/lib/internal/quic/core.js b/lib/internal/quic/core.js index 2df63c2be67cdf..98a2861e1a76fe 100644 --- a/lib/internal/quic/core.js +++ b/lib/internal/quic/core.js @@ -10,7 +10,7 @@ const { assertCrypto(); const { - Array, + ArrayFrom, BigInt64Array, Boolean, Error, @@ -22,6 +22,7 @@ const { PromiseResolve, Set, Symbol, + SymbolFor, } = primordials; const { Buffer } = require('buffer'); @@ -242,7 +243,7 @@ const kUsePreferredAddress = Symbol('kUsePreferredAddress'); const kVersionNegotiation = Symbol('kVersionNegotiation'); const kWriteGeneric = Symbol('kWriteGeneric'); -const kRejections = Symbol.for('nodejs.rejection'); +const kRejections = SymbolFor('nodejs.rejection'); const kSocketUnbound = 0; const kSocketPending = 1; @@ -1020,7 +1021,7 @@ class QuicSocket extends EventEmitter { const state = this[kInternalState]; return customInspect(this, { endpoints: this.endpoints, - sessions: Array.from(state.sessions), + sessions: ArrayFrom(state.sessions), bound: this.bound, pending: this.pending, closing: this.closing, @@ -1470,7 +1471,7 @@ class QuicSocket extends EventEmitter { } get endpoints() { - return Array.from(this[kInternalState].endpoints); + return ArrayFrom(this[kInternalState].endpoints); } get serverSecureContext() { diff --git a/lib/internal/readline/utils.js b/lib/internal/readline/utils.js index 408d12d01e151d..cce4045a5a0b02 100644 --- a/lib/internal/readline/utils.js +++ b/lib/internal/readline/utils.js @@ -1,7 +1,7 @@ 'use strict'; const { - String, + StringFromCharCode, Symbol, } = primordials; @@ -325,7 +325,7 @@ function* emitKeys(stream) { key.meta = escaped; } else if (!escaped && ch <= '\x1a') { // ctrl+letter - key.name = String.fromCharCode(ch.charCodeAt(0) + 'a'.charCodeAt(0) - 1); + key.name = StringFromCharCode(ch.charCodeAt(0) + 'a'.charCodeAt(0) - 1); key.ctrl = true; } else if (/^[0-9A-Za-z]$/.test(ch)) { // Letter, number, shift+letter diff --git a/lib/internal/timers.js b/lib/internal/timers.js index d39bab9d074895..5009572f90f265 100644 --- a/lib/internal/timers.js +++ b/lib/internal/timers.js @@ -75,6 +75,7 @@ const { MathMax, MathTrunc, + NumberIsFinite, NumberMIN_SAFE_INTEGER, ObjectCreate, Symbol, @@ -380,7 +381,7 @@ function setUnrefTimeout(callback, after) { // Type checking used by timers.enroll() and Socket#setTimeout() function getTimerDuration(msecs, name) { validateNumber(msecs, name); - if (msecs < 0 || !isFinite(msecs)) { + if (msecs < 0 || !NumberIsFinite(msecs)) { throw new ERR_OUT_OF_RANGE(name, 'a non-negative finite number', msecs); } diff --git a/lib/internal/util/iterable_weak_map.js b/lib/internal/util/iterable_weak_map.js index 3fa139b23e4b4e..c9715a7e313b20 100644 --- a/lib/internal/util/iterable_weak_map.js +++ b/lib/internal/util/iterable_weak_map.js @@ -2,7 +2,7 @@ const { makeSafe, - Object, + ObjectFreeze, SafeSet, SafeWeakMap, SymbolIterator, @@ -79,7 +79,7 @@ function cleanup({ set, ref }) { set.delete(ref); } -Object.freeze(IterableWeakMap.prototype); +ObjectFreeze(IterableWeakMap.prototype); module.exports = { IterableWeakMap, diff --git a/lib/internal/vm/module.js b/lib/internal/vm/module.js index c10433a36f6dcb..ce37312d652943 100644 --- a/lib/internal/vm/module.js +++ b/lib/internal/vm/module.js @@ -9,6 +9,7 @@ const { ObjectSetPrototypeOf, SafePromise, Symbol, + SymbolToStringTag, TypeError, WeakMap, } = primordials; @@ -239,7 +240,7 @@ class Module { o.context = this.context; ObjectSetPrototypeOf(o, ObjectGetPrototypeOf(this)); - ObjectDefineProperty(o, Symbol.toStringTag, { + ObjectDefineProperty(o, SymbolToStringTag, { value: constructor.name, configurable: true }); diff --git a/lib/querystring.js b/lib/querystring.js index 04a21e8d07f24f..21ba41c7b4e1ad 100644 --- a/lib/querystring.js +++ b/lib/querystring.js @@ -27,6 +27,7 @@ const { Array, ArrayIsArray, MathAbs, + NumberIsFinite, ObjectCreate, ObjectKeys, String, @@ -156,7 +157,7 @@ function qsEscape(str) { function stringifyPrimitive(v) { if (typeof v === 'string') return v; - if (typeof v === 'number' && isFinite(v)) + if (typeof v === 'number' && NumberIsFinite(v)) return '' + v; if (typeof v === 'boolean') return v ? 'true' : 'false'; @@ -167,7 +168,7 @@ function stringifyPrimitive(v) { function encodeStringified(v, encode) { if (typeof v === 'string') return (v.length ? encode(v) : ''); - if (typeof v === 'number' && isFinite(v)) { + if (typeof v === 'number' && NumberIsFinite(v)) { // Values >= 1e21 automatically switch to scientific notation which requires // escaping due to the inclusion of a '+' in the output return (MathAbs(v) < 1e21 ? '' + v : encode('' + v)); diff --git a/lib/util.js b/lib/util.js index 1414c0d11d58a8..2d8687ac9c569a 100644 --- a/lib/util.js +++ b/lib/util.js @@ -23,6 +23,7 @@ const { ArrayIsArray, + Date, Error, NumberIsSafeInteger, ObjectDefineProperties, diff --git a/lib/v8.js b/lib/v8.js index 15fe57e63e36ce..9cc3412c341b54 100644 --- a/lib/v8.js +++ b/lib/v8.js @@ -17,6 +17,7 @@ const { Array, ArrayBuffer, + DataView, Error, Float32Array, Float64Array, diff --git a/test/parallel/test-eslint-prefer-primordials.js b/test/parallel/test-eslint-prefer-primordials.js new file mode 100644 index 00000000000000..d9417e857c2089 --- /dev/null +++ b/test/parallel/test-eslint-prefer-primordials.js @@ -0,0 +1,158 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/prefer-primordials'); + +new RuleTester({ + parserOptions: { ecmaVersion: 6 }, + env: { es6: true } +}) + .run('prefer-primordials', rule, { + valid: [ + 'new Array()', + 'JSON.stringify({})', + 'class A { *[Symbol.iterator] () { yield "a"; } }', + 'const a = { *[Symbol.iterator] () { yield "a"; } }', + 'Object.defineProperty(o, Symbol.toStringTag, { value: "o" })', + 'parseInt("10")', + ` + const { Reflect } = primordials; + module.exports = function() { + const { ownKeys } = Reflect; + } + `, + { + code: 'const { Array } = primordials; new Array()', + options: [{ name: 'Array' }] + }, + { + code: 'const { JSONStringify } = primordials; JSONStringify({})', + options: [{ name: 'JSON' }] + }, + { + code: 'const { SymbolFor } = primordials; SymbolFor("xxx")', + options: [{ name: 'Symbol' }] + }, + { + code: ` + const { SymbolIterator } = primordials; + class A { *[SymbolIterator] () { yield "a"; } } + `, + options: [{ name: 'Symbol' }] + }, + { + code: ` + const { Symbol } = primordials; + const a = { *[Symbol.iterator] () { yield "a"; } } + `, + options: [{ name: 'Symbol', ignore: ['iterator'] }] + }, + { + code: ` + const { ObjectDefineProperty, Symbol } = primordials; + ObjectDefineProperty(o, Symbol.toStringTag, { value: "o" }) + `, + options: [{ name: 'Symbol', ignore: ['toStringTag'] }] + }, + { + code: 'const { Symbol } = primordials; Symbol.for("xxx")', + options: [{ name: 'Symbol', ignore: ['for'] }] + }, + { + code: 'const { NumberParseInt } = primordials; NumberParseInt("xxx")', + options: [{ name: 'parseInt' }] + }, + { + code: ` + const { ReflectOwnKeys } = primordials; + module.exports = function() { + ReflectOwnKeys({}) + } + `, + options: [{ name: 'Reflect' }], + }, + ], + invalid: [ + { + code: 'new Array()', + options: [{ name: 'Array' }], + errors: [{ message: /const { Array } = primordials/ }] + }, + { + code: 'JSON.parse("{}")', + options: [{ name: 'JSON' }], + errors: [ + { message: /const { JSONParse } = primordials/ }, + ] + }, + { + code: 'const { JSON } = primordials; JSON.parse("{}")', + options: [{ name: 'JSON' }], + errors: [{ message: /const { JSONParse } = primordials/ }] + }, + { + code: 'Symbol.for("xxx")', + options: [{ name: 'Symbol' }], + errors: [ + { message: /const { SymbolFor } = primordials/ }, + ] + }, + { + code: 'const { Symbol } = primordials; Symbol.for("xxx")', + options: [{ name: 'Symbol' }], + errors: [{ message: /const { SymbolFor } = primordials/ }] + }, + { + code: ` + const { Symbol } = primordials; + class A { *[Symbol.iterator] () { yield "a"; } } + `, + options: [{ name: 'Symbol' }], + errors: [{ message: /const { SymbolIterator } = primordials/ }] + }, + { + code: ` + const { Symbol } = primordials; + const a = { *[Symbol.iterator] () { yield "a"; } } + `, + options: [{ name: 'Symbol' }], + errors: [{ message: /const { SymbolIterator } = primordials/ }] + }, + { + code: ` + const { ObjectDefineProperty, Symbol } = primordials; + ObjectDefineProperty(o, Symbol.toStringTag, { value: "o" }) + `, + options: [{ name: 'Symbol' }], + errors: [{ message: /const { SymbolToStringTag } = primordials/ }] + }, + { + code: ` + const { Number } = primordials; + Number.parseInt('10') + `, + options: [{ name: 'Number', into: Number }], + errors: [{ message: /const { NumberParseInt } = primordials/ }] + }, + { + code: 'parseInt("10")', + options: [{ name: 'parseInt', into: 'Number' }], + errors: [{ message: /const { NumberParseInt } = primordials/ }] + }, + { + code: ` + module.exports = function() { + const { ownKeys } = Reflect; + } + `, + options: [{ name: 'Reflect' }], + errors: [{ message: /const { ReflectOwnKeys } = primordials/ }] + }, + ] + }); diff --git a/tools/eslint-rules/prefer-primordials.js b/tools/eslint-rules/prefer-primordials.js new file mode 100644 index 00000000000000..ffbb1e6e308c95 --- /dev/null +++ b/tools/eslint-rules/prefer-primordials.js @@ -0,0 +1,156 @@ +/** + * @fileoverview We shouldn't use global built-in object for security and + * performance reason. This linter rule reports replacable codes + * that can be replaced with primordials. + * @author Leko + */ +'use strict'; + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +function toPrimordialsName(obj, prop) { + return obj + toUcFirst(prop); +} + +function toUcFirst(str) { + return str[0].toUpperCase() + str.slice(1); +} + +function isTarget(map, varName) { + return map.has(varName); +} + +function isIgnored(map, varName, propName) { + if (!map.has(varName) || !map.get(varName).has(propName)) { + return false; + } + return map.get(varName).get(propName).ignored; +} + +function getReportName({ name, parentName, into }) { + if (into) { + return toPrimordialsName(into, name); + } + if (parentName) { + return toPrimordialsName(parentName, name); + } + return name; +} + +/** + * Get identifier of object spread assignment + * + * code: 'const { ownKeys } = Reflect;' + * argument: 'ownKeys' + * return: 'Reflect' + */ +function getDestructuringAssignmentParent(scope, node) { + const declaration = scope.set.get(node.name); + if ( + !declaration || + !declaration.defs || + declaration.defs.length === 0 || + declaration.defs[0].type !== 'Variable' || + !declaration.defs[0].node.init + ) { + return null; + } + return declaration.defs[0].node.init.name; +} + +const identifierSelector = + '[type!=VariableDeclarator][type!=MemberExpression]>Identifier'; + +module.exports = { + meta: { + messages: { + error: 'Use `const { {{name}} } = primordials;` instead of the global.' + } + }, + create(context) { + const globalScope = context.getSourceCode().scopeManager.globalScope; + const nameMap = context.options.reduce((acc, option) => + acc.set( + option.name, + (option.ignore || []) + .concat(['prototype']) + .reduce((acc, name) => acc.set(name, { + ignored: true + }), new Map()) + ) + , new Map()); + const renameMap = context.options + .filter((option) => option.into) + .reduce((acc, option) => + acc.set(option.name, option.into) + , new Map()); + let reported; + + return { + Program() { + reported = new Map(); + }, + [identifierSelector](node) { + if (reported.has(node.range[0])) { + return; + } + const name = node.name; + const parentName = getDestructuringAssignmentParent( + context.getScope(), + node + ); + if (!isTarget(nameMap, name) && !isTarget(nameMap, parentName)) { + return; + } + + const defs = (globalScope.set.get(name) || {}).defs || null; + if (parentName && isTarget(nameMap, parentName)) { + if (!defs || defs[0].name.name !== 'primordials') { + reported.set(node.range[0], true); + const into = renameMap.get(name); + context.report({ + node, + messageId: 'error', + data: { + name: getReportName({ into, parentName, name }) + } + }); + } + return; + } + if (defs.length === 0 || defs[0].node.init.name !== 'primordials') { + reported.set(node.range[0], true); + const into = renameMap.get(name); + context.report({ + node, + messageId: 'error', + data: { + name: getReportName({ into, parentName, name }) + } + }); + } + }, + MemberExpression(node) { + const obj = node.object.name; + const prop = node.property.name; + if (!prop || !isTarget(nameMap, obj) || isIgnored(nameMap, obj, prop)) { + return; + } + + const variables = + context.getSourceCode().scopeManager.getDeclaredVariables(node); + if (variables.length === 0) { + context.report({ + node, + messageId: 'error', + data: { + name: toPrimordialsName(obj, prop), + } + }); + } + } + }; + } +}; From a7350b3a8fbf02e5e4901361ef0cc00f4c99bef1 Mon Sep 17 00:00:00 2001 From: Myles Borins Date: Tue, 3 Nov 2020 22:07:34 -0500 Subject: [PATCH 27/66] tools: don't print gold linker warning w/o flag Currently warning is printed called even if the selection ordering flag has not been passed. Only print warning if `--limit-configure-section-file` has been passed to configure. Fixes: https://github.com/nodejs/node/issues/35872 PR-URL: https://github.com/nodejs/node/pull/35955 Reviewed-By: Joyee Cheung Reviewed-By: Richard Lau Reviewed-By: Rich Trott --- configure.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/configure.py b/configure.py index d68d8fc96abeb7..eb78598d9d32f0 100755 --- a/configure.py +++ b/configure.py @@ -1761,7 +1761,8 @@ def configure_section_file(o): proc = subprocess.Popen(['ld.gold'] + ['-v'], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE) except OSError: - warn('''No acceptable ld.gold linker found!''') + if options.node_section_ordering_info != "": + warn('''No acceptable ld.gold linker found!''') return 0 match = re.match(r"^GNU gold.*([0-9]+)\.([0-9]+)$", From 7a2edea7edb2c3f51bcca23d01acfde631a36e72 Mon Sep 17 00:00:00 2001 From: Bartosz Sosnowski Date: Mon, 2 Nov 2020 21:59:13 +0100 Subject: [PATCH 28/66] win, build: fix build time on Windows Sets MSBuild experimental switches to make it build in parallel project files generated by gyp 0.5.0. Fixes: https://github.com/nodejs/node/issues/35921 PR-URL: https://github.com/nodejs/node/pull/35932 Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig Reviewed-By: Richard Lau Reviewed-By: Jiawen Geng Reviewed-By: Zeyu Yang Reviewed-By: Gireesh Punathil Reviewed-By: Rich Trott --- vcbuild.bat | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/vcbuild.bat b/vcbuild.bat index 516726a6697f14..86a986b3dec2f3 100644 --- a/vcbuild.bat +++ b/vcbuild.bat @@ -338,6 +338,10 @@ if "%target%"=="Build" ( ) if "%target%"=="node" if exist "%config%\cctest.exe" del "%config%\cctest.exe" if defined msbuild_args set "extra_msbuild_args=%extra_msbuild_args% %msbuild_args%" +@rem Setup env variables to use multiprocessor build +set UseMultiToolTask=True +set EnforceProcessCountAcrossBuilds=True +set MultiProcMaxCount=%NUMBER_OF_PROCESSORS% msbuild node.sln %msbcpu% /t:%target% /p:Configuration=%config% /p:Platform=%msbplatform% /clp:NoItemAndPropertyList;Verbosity=minimal /nologo %extra_msbuild_args% if errorlevel 1 ( if not defined project_generated echo Building Node with reused solution failed. To regenerate project files use "vcbuild projgen" From 33e2ee58a79da134c0d4f26f96a70b084923247a Mon Sep 17 00:00:00 2001 From: Benjamin Gruenbaum Date: Mon, 2 Nov 2020 22:40:00 +0200 Subject: [PATCH 29/66] events: define event handler as enumerable PR-URL: https://github.com/nodejs/node/pull/35931 Reviewed-By: Anna Henningsen Reviewed-By: Antoine du Hamel Reviewed-By: Daijiro Wachi Reviewed-By: Rich Trott --- lib/internal/event_target.js | 4 +++- test/parallel/test-eventtarget.js | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/internal/event_target.js b/lib/internal/event_target.js index 5fb9f4c6d62aa7..1802cb833e38ca 100644 --- a/lib/internal/event_target.js +++ b/lib/internal/event_target.js @@ -592,7 +592,9 @@ function defineEventHandler(emitter, name) { emitter.addEventListener(name, value); } eventHandlerValue = value; - } + }, + configurable: true, + enumerable: true }); } module.exports = { diff --git a/test/parallel/test-eventtarget.js b/test/parallel/test-eventtarget.js index accacb8a328c03..96d755a8f32fe2 100644 --- a/test/parallel/test-eventtarget.js +++ b/test/parallel/test-eventtarget.js @@ -517,3 +517,10 @@ let asyncTest = Promise.resolve(); })); target.dispatchEvent(new Event('foo')); } +{ + const target = new EventTarget(); + defineEventHandler(target, 'foo'); + const descriptor = Object.getOwnPropertyDescriptor(target, 'onfoo'); + strictEqual(descriptor.configurable, true); + strictEqual(descriptor.enumerable, true); +} From ab0eb4f2c9033c3dcd3b24698c0bd80eaf875703 Mon Sep 17 00:00:00 2001 From: Benjamin Gruenbaum Date: Mon, 2 Nov 2020 22:53:59 +0200 Subject: [PATCH 30/66] events: support event handlers on prototypes PR-URL: https://github.com/nodejs/node/pull/35931 Reviewed-By: Anna Henningsen Reviewed-By: Antoine du Hamel Reviewed-By: Daijiro Wachi Reviewed-By: Rich Trott --- lib/internal/event_target.js | 19 +++++++++++++------ lib/internal/worker/io.js | 5 +++-- test/parallel/test-worker-message-port.js | 6 ++---- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/lib/internal/event_target.js b/lib/internal/event_target.js index 1802cb833e38ca..4841306c3348bd 100644 --- a/lib/internal/event_target.js +++ b/lib/internal/event_target.js @@ -14,6 +14,7 @@ const { Symbol, SymbolFor, SymbolToStringTag, + SafeWeakMap, SafeWeakSet, } = primordials; @@ -577,21 +578,27 @@ function emitUnhandledRejectionOrErr(that, err, event) { process.emit('error', err, event); } +// A map of emitter -> map of name -> handler +const eventHandlerValueMap = new SafeWeakMap(); + function defineEventHandler(emitter, name) { // 8.1.5.1 Event handlers - basically `on[eventName]` attributes - let eventHandlerValue; ObjectDefineProperty(emitter, `on${name}`, { get() { - return eventHandlerValue; + return eventHandlerValueMap.get(this)?.get(name); }, set(value) { - if (eventHandlerValue) { - emitter.removeEventListener(name, eventHandlerValue); + const oldValue = eventHandlerValueMap.get(this)?.get(name); + if (oldValue) { + this.removeEventListener(name, oldValue); } if (typeof value === 'function') { - emitter.addEventListener(name, value); + this.addEventListener(name, value); + } + if (!eventHandlerValueMap.has(this)) { + eventHandlerValueMap.set(this, new Map()); } - eventHandlerValue = value; + eventHandlerValueMap.get(this).set(name, value); }, configurable: true, enumerable: true diff --git a/lib/internal/worker/io.js b/lib/internal/worker/io.js index 763d06856cbd42..6f4cc325714ecf 100644 --- a/lib/internal/worker/io.js +++ b/lib/internal/worker/io.js @@ -146,13 +146,14 @@ ObjectDefineProperty( // This is called from inside the `MessagePort` constructor. function oninit() { initNodeEventTarget(this); - // TODO(addaleax): This should be on MessagePort.prototype, but - // defineEventHandler() does not support that. defineEventHandler(this, 'message'); defineEventHandler(this, 'messageerror'); setupPortReferencing(this, this, 'message'); } +defineEventHandler(MessagePort.prototype, 'message'); +defineEventHandler(MessagePort.prototype, 'messageerror'); + ObjectDefineProperty(MessagePort.prototype, onInitSymbol, { enumerable: true, writable: false, diff --git a/test/parallel/test-worker-message-port.js b/test/parallel/test-worker-message-port.js index 6f4e09d029d168..51618e4fab1850 100644 --- a/test/parallel/test-worker-message-port.js +++ b/test/parallel/test-worker-message-port.js @@ -165,9 +165,7 @@ const { MessageChannel, MessagePort } = require('worker_threads'); assert.deepStrictEqual( Object.getOwnPropertyNames(MessagePort.prototype).sort(), [ - // TODO(addaleax): This should include onmessage (and eventually - // onmessageerror). - 'close', 'constructor', 'postMessage', 'ref', 'start', - 'unref' + 'close', 'constructor', 'onmessage', 'onmessageerror', 'postMessage', + 'ref', 'start', 'unref' ]); } From ff59fcdf7bd381f225dcbbc94c5fd1caa86f3818 Mon Sep 17 00:00:00 2001 From: Benjamin Gruenbaum Date: Mon, 2 Nov 2020 22:59:54 +0200 Subject: [PATCH 31/66] events: define abort on prototype PR-URL: https://github.com/nodejs/node/pull/35931 Reviewed-By: Anna Henningsen Reviewed-By: Antoine du Hamel Reviewed-By: Daijiro Wachi Reviewed-By: Rich Trott --- lib/internal/abort_controller.js | 3 ++- lib/internal/event_target.js | 19 ++++++++----------- lib/internal/worker/io.js | 2 -- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/lib/internal/abort_controller.js b/lib/internal/abort_controller.js index 592e46e5320ce9..b387e5f4e2cea0 100644 --- a/lib/internal/abort_controller.js +++ b/lib/internal/abort_controller.js @@ -48,6 +48,8 @@ ObjectDefineProperties(AbortSignal.prototype, { aborted: { enumerable: true } }); +defineEventHandler(AbortSignal.prototype, 'abort'); + function abortSignal(signal) { if (signal[kAborted]) return; signal[kAborted] = true; @@ -65,7 +67,6 @@ class AbortController { constructor() { this[kSignal] = new AbortSignal(); emitExperimentalWarning('AbortController'); - defineEventHandler(this[kSignal], 'abort'); } get signal() { return this[kSignal]; } diff --git a/lib/internal/event_target.js b/lib/internal/event_target.js index 4841306c3348bd..e476603b9cec14 100644 --- a/lib/internal/event_target.js +++ b/lib/internal/event_target.js @@ -4,17 +4,16 @@ const { ArrayFrom, Boolean, Error, - Map, NumberIsInteger, ObjectAssign, ObjectDefineProperties, ObjectDefineProperty, ObjectGetOwnPropertyDescriptor, + SafeMap, String, Symbol, SymbolFor, SymbolToStringTag, - SafeWeakMap, SafeWeakSet, } = primordials; @@ -36,6 +35,7 @@ const kIsEventTarget = SymbolFor('nodejs.event_target'); const kEvents = Symbol('kEvents'); const kStop = Symbol('kStop'); const kTarget = Symbol('kTarget'); +const kHandlers = Symbol('khandlers'); const kHybridDispatch = SymbolFor('nodejs.internal.kHybridDispatch'); const kCreateEvent = Symbol('kCreateEvent'); @@ -219,7 +219,7 @@ class Listener { } function initEventTarget(self) { - self[kEvents] = new Map(); + self[kEvents] = new SafeMap(); } class EventTarget { @@ -578,27 +578,24 @@ function emitUnhandledRejectionOrErr(that, err, event) { process.emit('error', err, event); } -// A map of emitter -> map of name -> handler -const eventHandlerValueMap = new SafeWeakMap(); - function defineEventHandler(emitter, name) { // 8.1.5.1 Event handlers - basically `on[eventName]` attributes ObjectDefineProperty(emitter, `on${name}`, { get() { - return eventHandlerValueMap.get(this)?.get(name); + return this[kHandlers]?.get(name); }, set(value) { - const oldValue = eventHandlerValueMap.get(this)?.get(name); + const oldValue = this[kHandlers]?.get(name); if (oldValue) { this.removeEventListener(name, oldValue); } if (typeof value === 'function') { this.addEventListener(name, value); } - if (!eventHandlerValueMap.has(this)) { - eventHandlerValueMap.set(this, new Map()); + if (!this[kHandlers]) { + this[kHandlers] = new SafeMap(); } - eventHandlerValueMap.get(this).set(name, value); + this[kHandlers].set(name, value); }, configurable: true, enumerable: true diff --git a/lib/internal/worker/io.js b/lib/internal/worker/io.js index 6f4cc325714ecf..73958dbc2d78f5 100644 --- a/lib/internal/worker/io.js +++ b/lib/internal/worker/io.js @@ -146,8 +146,6 @@ ObjectDefineProperty( // This is called from inside the `MessagePort` constructor. function oninit() { initNodeEventTarget(this); - defineEventHandler(this, 'message'); - defineEventHandler(this, 'messageerror'); setupPortReferencing(this, this, 'message'); } From 9e673723e392d6b7f40a1edec9518846cb8d65cc Mon Sep 17 00:00:00 2001 From: Benjamin Gruenbaum Date: Fri, 6 Nov 2020 11:01:59 +0200 Subject: [PATCH 32/66] events: fire handlers in correct oder PR-URL: https://github.com/nodejs/node/pull/35931 Reviewed-By: Anna Henningsen Reviewed-By: Antoine du Hamel Reviewed-By: Daijiro Wachi Reviewed-By: Rich Trott --- lib/internal/event_target.js | 42 ++++++++++++++++++++++++------- test/parallel/test-eventtarget.js | 12 +++++++++ 2 files changed, 45 insertions(+), 9 deletions(-) diff --git a/lib/internal/event_target.js b/lib/internal/event_target.js index e476603b9cec14..3b7e6803f6ffa0 100644 --- a/lib/internal/event_target.js +++ b/lib/internal/event_target.js @@ -9,6 +9,7 @@ const { ObjectDefineProperties, ObjectDefineProperty, ObjectGetOwnPropertyDescriptor, + ReflectApply, SafeMap, String, Symbol, @@ -578,24 +579,47 @@ function emitUnhandledRejectionOrErr(that, err, event) { process.emit('error', err, event); } +function makeEventHandler(handler) { + // Event handlers are dispatched in the order they were first set + // See https://github.com/nodejs/node/pull/35949#issuecomment-722496598 + function eventHandler(...args) { + if (typeof eventHandler.handler !== 'function') { + return; + } + return ReflectApply(eventHandler.handler, this, args); + } + eventHandler.handler = handler; + return eventHandler; +} + function defineEventHandler(emitter, name) { // 8.1.5.1 Event handlers - basically `on[eventName]` attributes ObjectDefineProperty(emitter, `on${name}`, { get() { - return this[kHandlers]?.get(name); + return this[kHandlers]?.get(name)?.handler; }, set(value) { - const oldValue = this[kHandlers]?.get(name); - if (oldValue) { - this.removeEventListener(name, oldValue); - } - if (typeof value === 'function') { - this.addEventListener(name, value); - } if (!this[kHandlers]) { this[kHandlers] = new SafeMap(); } - this[kHandlers].set(name, value); + let wrappedHandler = this[kHandlers]?.get(name); + if (wrappedHandler) { + if (typeof wrappedHandler.handler === 'function') { + this[kEvents].get(name).size--; + const size = this[kEvents].get(name).size; + this[kRemoveListener](size, name, wrappedHandler.handler, false); + } + wrappedHandler.handler = value; + if (typeof wrappedHandler.handler === 'function') { + this[kEvents].get(name).size++; + const size = this[kEvents].get(name).size; + this[kNewListener](size, name, value, false, false, false); + } + } else { + wrappedHandler = makeEventHandler(value); + this.addEventListener(name, wrappedHandler); + } + this[kHandlers].set(name, wrappedHandler); }, configurable: true, enumerable: true diff --git a/test/parallel/test-eventtarget.js b/test/parallel/test-eventtarget.js index 96d755a8f32fe2..fa280027c61f29 100644 --- a/test/parallel/test-eventtarget.js +++ b/test/parallel/test-eventtarget.js @@ -524,3 +524,15 @@ let asyncTest = Promise.resolve(); strictEqual(descriptor.configurable, true); strictEqual(descriptor.enumerable, true); } +{ + const target = new EventTarget(); + defineEventHandler(target, 'foo'); + const output = []; + target.addEventListener('foo', () => output.push(1)); + target.onfoo = common.mustNotCall(); + target.addEventListener('foo', () => output.push(3)); + target.onfoo = () => output.push(2); + target.addEventListener('foo', () => output.push(4)); + target.dispatchEvent(new Event('foo')); + deepStrictEqual(output, [1, 2, 3, 4]); +} From ccbe26751501f2a24bc37406932db449e950f9ed Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Tue, 15 Sep 2020 18:40:39 +0200 Subject: [PATCH 33/66] fs: remove unnecessary Function#bind() in fs/promises PR-URL: https://github.com/nodejs/node/pull/35208 Reviewed-By: Colin Ihrig Reviewed-By: Masashi Hirano Reviewed-By: Anna Henningsen --- lib/internal/fs/promises.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/fs/promises.js b/lib/internal/fs/promises.js index 39c662fdc5b9c8..df80efb514d2dc 100644 --- a/lib/internal/fs/promises.js +++ b/lib/internal/fs/promises.js @@ -431,7 +431,7 @@ async function rename(oldPath, newPath) { async function truncate(path, len = 0) { const fd = await open(path, 'r+'); - return ftruncate(fd, len).finally(fd.close.bind(fd)); + return ftruncate(fd, len).finally(fd.close); } async function ftruncate(handle, len = 0) { From 9c4b360d080ed8f418b04d6604f932a6dd185a52 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Mon, 12 Oct 2020 12:38:29 +0200 Subject: [PATCH 34/66] doc,crypto: added sign/verify method changes about dsaEncoding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/35480 Reviewed-By: Anna Henningsen Reviewed-By: Luigi Pinca Reviewed-By: James M Snell Reviewed-By: Tobias Nießen --- doc/api/crypto.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/doc/api/crypto.md b/doc/api/crypto.md index e42051d4efca8e..e3b70390e49382 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -1458,6 +1458,11 @@ changes: - version: v15.0.0 pr-url: https://github.com/nodejs/node/pull/35093 description: The privateKey can also be an ArrayBuffer and CryptoKey. + - version: + - v12.16.0 + - v13.2.0 + pr-url: https://github.com/nodejs/node/pull/29292 + description: This function now supports IEEE-P1363 DSA and ECDSA signatures. - version: v12.0.0 pr-url: https://github.com/nodejs/node/pull/26960 description: This function now supports RSA-PSS keys. @@ -1576,6 +1581,11 @@ changes: - version: v15.0.0 pr-url: https://github.com/nodejs/node/pull/35093 description: The object can also be an ArrayBuffer and CryptoKey. + - version: + - v12.16.0 + - v13.2.0 + pr-url: https://github.com/nodejs/node/pull/29292 + description: This function now supports IEEE-P1363 DSA and ECDSA signatures. - version: v12.0.0 pr-url: https://github.com/nodejs/node/pull/26960 description: This function now supports RSA-PSS keys. @@ -3314,6 +3324,12 @@ Throws an error if FIPS mode is not available. ### `crypto.sign(algorithm, data, key)` @@ -3380,6 +3396,11 @@ changes: - version: v15.0.0 pr-url: https://github.com/nodejs/node/pull/35093 description: The data, key, and signature arguments can also be ArrayBuffer. + - version: + - v12.16.0 + - v13.2.0 + pr-url: https://github.com/nodejs/node/pull/29292 + description: This function now supports IEEE-P1363 DSA and ECDSA signatures. --> From 06cc400160e40f3f135c3928d2d2bc62619c8c6f Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sun, 8 Nov 2020 13:35:46 +0100 Subject: [PATCH 35/66] doc: fix crypto doc linter errors PR-URL: https://github.com/nodejs/node/pull/36035 Reviewed-By: Richard Lau Reviewed-By: Rich Trott --- doc/api/crypto.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/api/crypto.md b/doc/api/crypto.md index e3b70390e49382..eadd5453614c51 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -1459,8 +1459,8 @@ changes: pr-url: https://github.com/nodejs/node/pull/35093 description: The privateKey can also be an ArrayBuffer and CryptoKey. - version: - - v12.16.0 - v13.2.0 + - v12.16.0 pr-url: https://github.com/nodejs/node/pull/29292 description: This function now supports IEEE-P1363 DSA and ECDSA signatures. - version: v12.0.0 @@ -1582,8 +1582,8 @@ changes: pr-url: https://github.com/nodejs/node/pull/35093 description: The object can also be an ArrayBuffer and CryptoKey. - version: - - v12.16.0 - v13.2.0 + - v12.16.0 pr-url: https://github.com/nodejs/node/pull/29292 description: This function now supports IEEE-P1363 DSA and ECDSA signatures. - version: v12.0.0 @@ -3326,8 +3326,8 @@ Throws an error if FIPS mode is not available. added: v12.0.0 changes: - version: - - v12.16.0 - v13.2.0 + - v12.16.0 pr-url: https://github.com/nodejs/node/pull/29292 description: This function now supports IEEE-P1363 DSA and ECDSA signatures. --> @@ -3397,8 +3397,8 @@ changes: pr-url: https://github.com/nodejs/node/pull/35093 description: The data, key, and signature arguments can also be ArrayBuffer. - version: - - v12.16.0 - v13.2.0 + - v12.16.0 pr-url: https://github.com/nodejs/node/pull/29292 description: This function now supports IEEE-P1363 DSA and ECDSA signatures. --> From 429113ebfb1efe28e0ab441d4ec9627f0ef09fa5 Mon Sep 17 00:00:00 2001 From: Momtchil Momtchev Date: Fri, 23 Oct 2020 13:16:15 +0200 Subject: [PATCH 36/66] http2: move events to the JSStreamSocket When using a JSStreamSocket, the HTTP2Session constructor will replace the socket object http2 events should be attached to the JSStreamSocket object because the http2 session handle lives there Fixes: https://github.com/nodejs/node/issues/35695 PR-URL: https://github.com/nodejs/node/pull/35772 Reviewed-By: Matteo Collina Reviewed-By: Anna Henningsen Reviewed-By: Rich Trott Reviewed-By: James M Snell Reviewed-By: Ricky Zhou <0x19951125@gmail.com> --- lib/internal/http2/core.js | 9 ++- .../test-http2-client-jsstream-destroy.js | 55 +++++++++++++++++++ 2 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 test/parallel/test-http2-client-jsstream-destroy.js diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index 7bd30eeaf10a10..dd7501c40b3ea3 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -3132,11 +3132,14 @@ function connect(authority, options, listener) { } } - socket.on('error', socketOnError); - socket.on('close', socketOnClose); - const session = new ClientHttp2Session(options, socket); + // ClientHttp2Session may create a new socket object + // when socket is a JSSocket (socket != kSocket) + // https://github.com/nodejs/node/issues/35695 + session[kSocket].on('error', socketOnError); + session[kSocket].on('close', socketOnClose); + session[kAuthority] = `${options.servername || host}:${port}`; session[kProtocol] = protocol; diff --git a/test/parallel/test-http2-client-jsstream-destroy.js b/test/parallel/test-http2-client-jsstream-destroy.js new file mode 100644 index 00000000000000..f2f0669170c4bc --- /dev/null +++ b/test/parallel/test-http2-client-jsstream-destroy.js @@ -0,0 +1,55 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const h2 = require('http2'); +const tls = require('tls'); +const fixtures = require('../common/fixtures'); +const { Duplex } = require('stream'); + +const server = h2.createSecureServer({ + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}); + +class JSSocket extends Duplex { + constructor(socket) { + super({ emitClose: true }); + socket.on('close', () => this.destroy()); + socket.on('data', (data) => this.push(data)); + this.socket = socket; + } + + _write(data, encoding, callback) { + this.socket.write(data, encoding, callback); + } + + _read(size) { + } + + _final(cb) { + cb(); + } +} + +server.listen(0, common.mustCall(function() { + const socket = tls.connect({ + rejectUnauthorized: false, + host: 'localhost', + port: this.address().port, + ALPNProtocols: ['h2'] + }, () => { + const proxy = new JSSocket(socket); + const client = h2.connect(`https://localhost:${this.address().port}`, { + createConnection: () => proxy + }); + const req = client.request(); + + setTimeout(() => socket.destroy(), 200); + setTimeout(() => client.close(), 400); + setTimeout(() => server.close(), 600); + + req.on('close', common.mustCall(() => { })); + }); +})); From 28ed7d062eaaf0d3aef6bd571039b8ddda17d61d Mon Sep 17 00:00:00 2001 From: Momtchil Momtchev Date: Sat, 24 Oct 2020 15:50:20 +0200 Subject: [PATCH 37/66] http2: centralise socket event binding in Http2Session Move the socket event binding to the HTTP2Session constructor so that an error event could be delivered should the constructor fail Ref: https://github.com/nodejs/node/pull/35772 PR-URL: https://github.com/nodejs/node/pull/35772 Fixes: https://github.com/nodejs/node/issues/35695 Reviewed-By: Matteo Collina Reviewed-By: Anna Henningsen Reviewed-By: Rich Trott Reviewed-By: James M Snell Reviewed-By: Ricky Zhou <0x19951125@gmail.com> --- lib/internal/http2/core.js | 11 ++--------- test/parallel/test-http2-client-jsstream-destroy.js | 6 +++--- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index dd7501c40b3ea3..b585d61cd1db92 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -1129,6 +1129,8 @@ class Http2Session extends EventEmitter { if (!socket._handle || !socket._handle.isStreamBase) { socket = new JSStreamSocket(socket); } + socket.on('error', socketOnError); + socket.on('close', socketOnClose); // No validation is performed on the input parameters because this // constructor is not exported directly for users. @@ -2909,9 +2911,6 @@ function connectionListener(socket) { return; } - socket.on('error', socketOnError); - socket.on('close', socketOnClose); - // Set up the Session const session = new ServerHttp2Session(options, socket, this); @@ -3134,12 +3133,6 @@ function connect(authority, options, listener) { const session = new ClientHttp2Session(options, socket); - // ClientHttp2Session may create a new socket object - // when socket is a JSSocket (socket != kSocket) - // https://github.com/nodejs/node/issues/35695 - session[kSocket].on('error', socketOnError); - session[kSocket].on('close', socketOnClose); - session[kAuthority] = `${options.servername || host}:${port}`; session[kProtocol] = protocol; diff --git a/test/parallel/test-http2-client-jsstream-destroy.js b/test/parallel/test-http2-client-jsstream-destroy.js index f2f0669170c4bc..f881eac47aa809 100644 --- a/test/parallel/test-http2-client-jsstream-destroy.js +++ b/test/parallel/test-http2-client-jsstream-destroy.js @@ -46,9 +46,9 @@ server.listen(0, common.mustCall(function() { }); const req = client.request(); - setTimeout(() => socket.destroy(), 200); - setTimeout(() => client.close(), 400); - setTimeout(() => server.close(), 600); + setTimeout(() => socket.destroy(), common.platformTimeout(100)); + setTimeout(() => client.close(), common.platformTimeout(200)); + setTimeout(() => server.close(), common.platformTimeout(300)); req.on('close', common.mustCall(() => { })); }); From b7aa5e22966dbc8e819ed0ea1eebd0c2010c5146 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Mon, 2 Nov 2020 16:34:24 +0100 Subject: [PATCH 38/66] stream: remove isPromise utility function The function was not checking if the parameter was actually a Promise instance, but if it has a `then` method. Removing the utility function in favor of a clearer `typeof` check, handling the case when the thenable throws if then method is accessed more than once. PR-URL: https://github.com/nodejs/node/pull/35925 Reviewed-By: Robert Nagy Reviewed-By: Benjamin Gruenbaum Reviewed-By: Matteo Collina --- lib/internal/streams/pipeline.js | 20 +++++++++++--------- test/parallel/test-stream-pipeline.js | 21 +++++++++++++++++++++ 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/lib/internal/streams/pipeline.js b/lib/internal/streams/pipeline.js index c662bce8db1ab3..a73e17f1f29eed 100644 --- a/lib/internal/streams/pipeline.js +++ b/lib/internal/streams/pipeline.js @@ -5,8 +5,9 @@ const { ArrayIsArray, + ReflectApply, SymbolAsyncIterator, - SymbolIterator + SymbolIterator, } = primordials; let eos; @@ -77,10 +78,6 @@ function popCallback(streams) { return streams.pop(); } -function isPromise(obj) { - return !!(obj && typeof obj.then === 'function'); -} - function isReadable(obj) { return !!(obj && typeof obj.pipe === 'function'); } @@ -222,14 +219,19 @@ function pipeline(...streams) { const pt = new PassThrough({ objectMode: true }); - if (isPromise(ret)) { - ret - .then((val) => { + + // Handle Promises/A+ spec, `then` could be a getter that throws on + // second use. + const then = ret?.then; + if (typeof then === 'function') { + ReflectApply(then, ret, [ + (val) => { value = val; pt.end(val); }, (err) => { pt.destroy(err); - }); + } + ]); } else if (isIterable(ret, true)) { finishCount++; pump(ret, pt, finish); diff --git a/test/parallel/test-stream-pipeline.js b/test/parallel/test-stream-pipeline.js index b7d3720d0d05dd..23887122fc78d8 100644 --- a/test/parallel/test-stream-pipeline.js +++ b/test/parallel/test-stream-pipeline.js @@ -1240,3 +1240,24 @@ const net = require('net'); }), ); } +{ + function createThenable() { + let counter = 0; + return { + get then() { + if (counter++) { + throw new Error('Cannot access `then` more than once'); + } + return Function.prototype; + }, + }; + } + + pipeline( + function* () { + yield 0; + }, + createThenable, + () => common.mustNotCall(), + ); +} From 567f8d8caf4948a6d02aa809db6b0285b6347673 Mon Sep 17 00:00:00 2001 From: Benjamin Gruenbaum Date: Fri, 6 Nov 2020 12:05:08 +0200 Subject: [PATCH 39/66] events: getEventListeners static PR-URL: https://github.com/nodejs/node/pull/35991 Reviewed-By: James M Snell Reviewed-By: Matteo Collina --- doc/api/events.md | 34 ++++++++++++++++++ lib/events.js | 26 +++++++++++++- lib/internal/event_target.js | 2 ++ .../test-events-static-geteventlisteners.js | 36 +++++++++++++++++++ 4 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 test/parallel/test-events-static-geteventlisteners.js diff --git a/doc/api/events.md b/doc/api/events.md index 17420a249be731..b593cfac44969f 100644 --- a/doc/api/events.md +++ b/doc/api/events.md @@ -829,6 +829,40 @@ class MyClass extends EventEmitter { } ``` +## `events.getEventListeners(emitterOrTarget, eventName)` + +* `emitterOrTarget` {EventEmitter|EventTarget} +* `eventName` {string|symbol} +* Returns: {Function[]} + +Returns a copy of the array of listeners for the event named `eventName`. + +For `EventEmitter`s this behaves exactly the same as calling `.listeners` on +the emitter. + +For `EventTarget`s this is the only way to get the event listeners for the +event target. This is useful for debugging and diagnostic purposes. + +```js +const { getEventListeners, EventEmitter } = require('events'); + +{ + const ee = new EventEmitter(); + const listener = () => console.log('Events are fun'); + ee.on('foo', listener); + getEventListeners(ee, 'foo'); // [listener] +} +{ + const et = new EventTarget(); + const listener = () => console.log('Events are fun'); + ee.addEventListener('foo', listener); + getEventListeners(ee, 'foo'); // [listener] +} +``` + ## `events.once(emitter, name[, options])` + +* {number|undefined} + +The socket timeout in milliseconds as set by [`socket.setTimeout()`][]. +It is `undefined` if a timeout has not been set. + ### `socket.unref()` + +* {boolean} + +Is `true` if the stream's buffer has been full and stream will emit `'drain'`. + ##### `writable.writableObjectMode` + +* Returns: {Object} + +Returns an object with the following properties: + +* `code_and_metadata_size` {number} +* `bytecode_and_metadata_size` {number} +* `external_script_source_size` {number} + + +```js +{ + code_and_metadata_size: 212208, + bytecode_and_metadata_size: 161368, + external_script_source_size: 1410794 +} +``` + ## `v8.getHeapSnapshot()` - -* Returns: {Object} - -Returns an object with the following properties: - -* `code_and_metadata_size` {number} -* `bytecode_and_metadata_size` {number} -* `external_script_source_size` {number} - - -```js -{ - code_and_metadata_size: 212208, - bytecode_and_metadata_size: 161368, - external_script_source_size: 1410794 -} -``` - ## `v8.setFlagsFromString(flags)` * `emitterOrTarget` {EventEmitter|EventTarget} * `eventName` {string|symbol} diff --git a/doc/api/fs.md b/doc/api/fs.md index 713ba1bf26bbee..065ae645652af0 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -3035,7 +3035,7 @@ If `options.withFileTypes` is set to `true`, the result will contain * {boolean} diff --git a/doc/changelogs/CHANGELOG_V15.md b/doc/changelogs/CHANGELOG_V15.md index 6e6a497a049da3..064f851eb2d06d 100644 --- a/doc/changelogs/CHANGELOG_V15.md +++ b/doc/changelogs/CHANGELOG_V15.md @@ -10,6 +10,7 @@ +15.2.0
15.1.0
15.0.1
15.0.0
@@ -34,6 +35,87 @@ * [io.js](CHANGELOG_IOJS.md) * [Archive](CHANGELOG_ARCHIVE.md) + +## 2020-11-10, Version 15.2.0 (Current), @danielleadams + +### Notable changes + +* **events**: + * getEventListeners static (Benjamin Gruenbaum) [#35991](https://github.com/nodejs/node/pull/35991) +* **fs**: + * support abortsignal in writeFile (Benjamin Gruenbaum) [#35993](https://github.com/nodejs/node/pull/35993) + * add support for AbortSignal in readFile (Benjamin Gruenbaum) [#35911](https://github.com/nodejs/node/pull/35911) +* **stream**: + * fix thrown object reference (Gil Pedersen) [#36065](https://github.com/nodejs/node/pull/36065) + +### Commits + +* [[`9d9a044c1b`](https://github.com/nodejs/node/commit/9d9a044c1b)] - **benchmark**: ignore build artifacts for napi addons (Richard Lau) [#35970](https://github.com/nodejs/node/pull/35970) +* [[`4c6de854be`](https://github.com/nodejs/node/commit/4c6de854be)] - **benchmark**: remove modules that require intl (Richard Lau) [#35968](https://github.com/nodejs/node/pull/35968) +* [[`292915a6a8`](https://github.com/nodejs/node/commit/292915a6a8)] - **bootstrap**: refactor to use more primordials (Antoine du Hamel) [#35999](https://github.com/nodejs/node/pull/35999) +* [[`10c9ea771d`](https://github.com/nodejs/node/commit/10c9ea771d)] - **build**: fix zlib inlining for IA-32 (raisinten) [#35679](https://github.com/nodejs/node/pull/35679) +* [[`6ac9c8f31b`](https://github.com/nodejs/node/commit/6ac9c8f31b)] - **build, tools**: look for local installation of NASM (Richard Lau) [#36014](https://github.com/nodejs/node/pull/36014) +* [[`9757b47c44`](https://github.com/nodejs/node/commit/9757b47c44)] - **console**: use more primordials (Antoine du Hamel) [#35734](https://github.com/nodejs/node/pull/35734) +* [[`0d7422651b`](https://github.com/nodejs/node/commit/0d7422651b)] - **crypto**: refactor to use more primordials (Antoine du Hamel) [#36012](https://github.com/nodejs/node/pull/36012) +* [[`dc4936ba50`](https://github.com/nodejs/node/commit/dc4936ba50)] - **crypto**: fix comment in ByteSource (Tobias Nießen) [#35972](https://github.com/nodejs/node/pull/35972) +* [[`7cb5c0911e`](https://github.com/nodejs/node/commit/7cb5c0911e)] - **deps**: cherry-pick 9a49b22 from V8 upstream (Daniel Bevenius) [#35939](https://github.com/nodejs/node/pull/35939) +* [[`4b03670877`](https://github.com/nodejs/node/commit/4b03670877)] - **dns**: fix trace\_events name for resolveCaa() (Rich Trott) [#35979](https://github.com/nodejs/node/pull/35979) +* [[`dcb27600da`](https://github.com/nodejs/node/commit/dcb27600da)] - **doc**: escape asterisk in cctest gtest-filter (raisinten) [#36034](https://github.com/nodejs/node/pull/36034) +* [[`923276ca53`](https://github.com/nodejs/node/commit/923276ca53)] - **doc**: move v8.getHeapCodeStatistics() (Rich Trott) [#36027](https://github.com/nodejs/node/pull/36027) +* [[`71fa9c6b24`](https://github.com/nodejs/node/commit/71fa9c6b24)] - **doc**: add note regarding file structure in src/README.md (Denys Otrishko) [#35000](https://github.com/nodejs/node/pull/35000) +* [[`99cb36238d`](https://github.com/nodejs/node/commit/99cb36238d)] - **doc**: advise users to import the full set of trusted release keys (Reşat SABIQ) [#32655](https://github.com/nodejs/node/pull/32655) +* [[`06cc400160`](https://github.com/nodejs/node/commit/06cc400160)] - **doc**: fix crypto doc linter errors (Antoine du Hamel) [#36035](https://github.com/nodejs/node/pull/36035) +* [[`01129a7b39`](https://github.com/nodejs/node/commit/01129a7b39)] - **doc**: revise v8.getHeapSnapshot() (Rich Trott) [#35849](https://github.com/nodejs/node/pull/35849) +* [[`77d33c9b2f`](https://github.com/nodejs/node/commit/77d33c9b2f)] - **doc**: update core-validate-commit link in guide (Daijiro Wachi) [#35938](https://github.com/nodejs/node/pull/35938) +* [[`6d56ba03e2`](https://github.com/nodejs/node/commit/6d56ba03e2)] - **doc**: update benchmark CI test indicator in README (Rich Trott) [#35945](https://github.com/nodejs/node/pull/35945) +* [[`8bd364a9b3`](https://github.com/nodejs/node/commit/8bd364a9b3)] - **doc**: add new wordings to the API description (Pooja D.P) [#35588](https://github.com/nodejs/node/pull/35588) +* [[`acd3617e1a`](https://github.com/nodejs/node/commit/acd3617e1a)] - **doc**: option --prof documentation help added (krank2me) [#34991](https://github.com/nodejs/node/pull/34991) +* [[`6968b0fd49`](https://github.com/nodejs/node/commit/6968b0fd49)] - **doc**: fix release-schedule link in backport guide (Daijiro Wachi) [#35920](https://github.com/nodejs/node/pull/35920) +* [[`efbfeff62b`](https://github.com/nodejs/node/commit/efbfeff62b)] - **doc**: fix incorrect heading level (Bryan Field) [#35965](https://github.com/nodejs/node/pull/35965) +* [[`9c4b360d08`](https://github.com/nodejs/node/commit/9c4b360d08)] - **doc,crypto**: added sign/verify method changes about dsaEncoding (Filip Skokan) [#35480](https://github.com/nodejs/node/pull/35480) +* [[`85cf30541d`](https://github.com/nodejs/node/commit/85cf30541d)] - **doc,fs**: document value of stats.isDirectory on symbolic links (coderaiser) [#27413](https://github.com/nodejs/node/pull/27413) +* [[`d6bd78ff82`](https://github.com/nodejs/node/commit/d6bd78ff82)] - **doc,net**: document socket.timeout (Brandon Kobel) [#34543](https://github.com/nodejs/node/pull/34543) +* [[`36c20d939a`](https://github.com/nodejs/node/commit/36c20d939a)] - **doc,stream**: write(chunk, encoding, cb) encoding can be null (dev-script) [#35372](https://github.com/nodejs/node/pull/35372) +* [[`9d26c4d496`](https://github.com/nodejs/node/commit/9d26c4d496)] - **domain**: refactor to use more primordials (Antoine du Hamel) [#35885](https://github.com/nodejs/node/pull/35885) +* [[`d83e253065`](https://github.com/nodejs/node/commit/d83e253065)] - **errors**: refactor to use more primordials (Antoine du Hamel) [#35944](https://github.com/nodejs/node/pull/35944) +* [[`567f8d8caf`](https://github.com/nodejs/node/commit/567f8d8caf)] - **(SEMVER-MINOR)** **events**: getEventListeners static (Benjamin Gruenbaum) [#35991](https://github.com/nodejs/node/pull/35991) +* [[`9e673723e3`](https://github.com/nodejs/node/commit/9e673723e3)] - **events**: fire handlers in correct oder (Benjamin Gruenbaum) [#35931](https://github.com/nodejs/node/pull/35931) +* [[`ff59fcdf7b`](https://github.com/nodejs/node/commit/ff59fcdf7b)] - **events**: define abort on prototype (Benjamin Gruenbaum) [#35931](https://github.com/nodejs/node/pull/35931) +* [[`ab0eb4f2c9`](https://github.com/nodejs/node/commit/ab0eb4f2c9)] - **events**: support event handlers on prototypes (Benjamin Gruenbaum) [#35931](https://github.com/nodejs/node/pull/35931) +* [[`33e2ee58a7`](https://github.com/nodejs/node/commit/33e2ee58a7)] - **events**: define event handler as enumerable (Benjamin Gruenbaum) [#35931](https://github.com/nodejs/node/pull/35931) +* [[`a7d0c76f86`](https://github.com/nodejs/node/commit/a7d0c76f86)] - **events**: support emit on nodeeventtarget (Benjamin Gruenbaum) [#35851](https://github.com/nodejs/node/pull/35851) +* [[`76332a0439`](https://github.com/nodejs/node/commit/76332a0439)] - **events**: port some wpt tests (Benjamin Gruenbaum) [#33621](https://github.com/nodejs/node/pull/33621) +* [[`ccf9f0e62e`](https://github.com/nodejs/node/commit/ccf9f0e62e)] - **(SEMVER-MINOR)** **fs**: support abortsignal in writeFile (Benjamin Gruenbaum) [#35993](https://github.com/nodejs/node/pull/35993) +* [[`7ef9c707e9`](https://github.com/nodejs/node/commit/7ef9c707e9)] - **fs**: replace finally with PromisePrototypeFinally (Baruch Odem (Rothkoff)) [#35995](https://github.com/nodejs/node/pull/35995) +* [[`ccbe267515`](https://github.com/nodejs/node/commit/ccbe267515)] - **fs**: remove unnecessary Function#bind() in fs/promises (Ben Noordhuis) [#35208](https://github.com/nodejs/node/pull/35208) +* [[`6011bfdec5`](https://github.com/nodejs/node/commit/6011bfdec5)] - **fs**: remove unused assignment (Rich Trott) [#35882](https://github.com/nodejs/node/pull/35882) +* [[`92bdfd141b`](https://github.com/nodejs/node/commit/92bdfd141b)] - **(SEMVER-MINOR)** **fs**: add support for AbortSignal in readFile (Benjamin Gruenbaum) [#35911](https://github.com/nodejs/node/pull/35911) +* [[`11f592450b`](https://github.com/nodejs/node/commit/11f592450b)] - **http2**: add has method to proxySocketHandler (masx200) [#35197](https://github.com/nodejs/node/pull/35197) +* [[`28ed7d062e`](https://github.com/nodejs/node/commit/28ed7d062e)] - **http2**: centralise socket event binding in Http2Session (Momtchil Momtchev) [#35772](https://github.com/nodejs/node/pull/35772) +* [[`429113ebfb`](https://github.com/nodejs/node/commit/429113ebfb)] - **http2**: move events to the JSStreamSocket (Momtchil Momtchev) [#35772](https://github.com/nodejs/node/pull/35772) +* [[`1dd744a420`](https://github.com/nodejs/node/commit/1dd744a420)] - **http2**: fix error stream write followed by destroy (David Halls) [#35951](https://github.com/nodejs/node/pull/35951) +* [[`af2a560c42`](https://github.com/nodejs/node/commit/af2a560c42)] - **lib**: add %TypedArray% abstract constructor to primordials (ExE Boss) [#36016](https://github.com/nodejs/node/pull/36016) +* [[`b700900d02`](https://github.com/nodejs/node/commit/b700900d02)] - **lib**: refactor to use more primordials (Antoine du Hamel) [#35875](https://github.com/nodejs/node/pull/35875) +* [[`7a375902ff`](https://github.com/nodejs/node/commit/7a375902ff)] - **module**: refactor to use more primordials (Antoine du Hamel) [#36024](https://github.com/nodejs/node/pull/36024) +* [[`8d76db86b5`](https://github.com/nodejs/node/commit/8d76db86b5)] - **module**: refactor to use iterable-weak-map (Benjamin Coe) [#35915](https://github.com/nodejs/node/pull/35915) +* [[`9b6512f7de`](https://github.com/nodejs/node/commit/9b6512f7de)] - **n-api**: unlink reference during its destructor (Gabriel Schulhof) [#35933](https://github.com/nodejs/node/pull/35933) +* [[`1b277d97f3`](https://github.com/nodejs/node/commit/1b277d97f3)] - **src**: remove ERR prefix in crypto status enums (Daniel Bevenius) [#35867](https://github.com/nodejs/node/pull/35867) +* [[`9774b4cc72`](https://github.com/nodejs/node/commit/9774b4cc72)] - **stream**: fix thrown object reference (Gil Pedersen) [#36065](https://github.com/nodejs/node/pull/36065) +* [[`359a6590b0`](https://github.com/nodejs/node/commit/359a6590b0)] - **stream**: writableNeedDrain (Robert Nagy) [#35348](https://github.com/nodejs/node/pull/35348) +* [[`b7aa5e2296`](https://github.com/nodejs/node/commit/b7aa5e2296)] - **stream**: remove isPromise utility function (Antoine du Hamel) [#35925](https://github.com/nodejs/node/pull/35925) +* [[`fdae9ad188`](https://github.com/nodejs/node/commit/fdae9ad188)] - **test**: fix races in test-performance-eventlooputil (Gerhard Stoebich) [#36028](https://github.com/nodejs/node/pull/36028) +* [[`0a4c96a7df`](https://github.com/nodejs/node/commit/0a4c96a7df)] - **test**: use global.EventTarget instead of internals (Antoine du Hamel) [#36002](https://github.com/nodejs/node/pull/36002) +* [[`f73b8d84db`](https://github.com/nodejs/node/commit/f73b8d84db)] - **test**: improve error message for policy failures (Bradley Meck) [#35633](https://github.com/nodejs/node/pull/35633) +* [[`cb6f0d3d89`](https://github.com/nodejs/node/commit/cb6f0d3d89)] - **test**: update old comment style test\_util.cc (raisinten) [#35884](https://github.com/nodejs/node/pull/35884) +* [[`23f0d0c45c`](https://github.com/nodejs/node/commit/23f0d0c45c)] - **test**: fix error in test/internet/test-dns.js (Rich Trott) [#35969](https://github.com/nodejs/node/pull/35969) +* [[`77e4f19701`](https://github.com/nodejs/node/commit/77e4f19701)] - **timers**: cleanup abort listener on awaitable timers (James M Snell) [#36006](https://github.com/nodejs/node/pull/36006) +* [[`a7350b3a8f`](https://github.com/nodejs/node/commit/a7350b3a8f)] - **tools**: don't print gold linker warning w/o flag (Myles Borins) [#35955](https://github.com/nodejs/node/pull/35955) +* [[`1f27214480`](https://github.com/nodejs/node/commit/1f27214480)] - **tools**: add new ESLint rule: prefer-primordials (Leko) [#35448](https://github.com/nodejs/node/pull/35448) +* [[`da3c2ab828`](https://github.com/nodejs/node/commit/da3c2ab828)] - **tools,doc**: enable ecmaVersion 2021 in acorn parser (Antoine du Hamel) [#35994](https://github.com/nodejs/node/pull/35994) +* [[`f8098c3e43`](https://github.com/nodejs/node/commit/f8098c3e43)] - **tools,lib**: recommend using safe primordials (Antoine du Hamel) [#36026](https://github.com/nodejs/node/pull/36026) +* [[`eea7e3b0d0`](https://github.com/nodejs/node/commit/eea7e3b0d0)] - **tools,lib**: tighten prefer-primordials rules for Error statics (Antoine du Hamel) [#36017](https://github.com/nodejs/node/pull/36017) +* [[`7a2edea7ed`](https://github.com/nodejs/node/commit/7a2edea7ed)] - **win, build**: fix build time on Windows (Bartosz Sosnowski) [#35932](https://github.com/nodejs/node/pull/35932) + ## 2020-11-04, Version 15.1.0 (Current), @targos diff --git a/src/node_version.h b/src/node_version.h index 79e7191bdb990d..7ae6730832ced7 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -23,13 +23,13 @@ #define SRC_NODE_VERSION_H_ #define NODE_MAJOR_VERSION 15 -#define NODE_MINOR_VERSION 1 -#define NODE_PATCH_VERSION 1 +#define NODE_MINOR_VERSION 2 +#define NODE_PATCH_VERSION 0 #define NODE_VERSION_IS_LTS 0 #define NODE_VERSION_LTS_CODENAME "" -#define NODE_VERSION_IS_RELEASE 0 +#define NODE_VERSION_IS_RELEASE 1 #ifndef NODE_STRINGIFY #define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n)