From 8a2e21a347b67990da5f94a0b44d3458ad9946ef Mon Sep 17 00:00:00 2001 From: Jeremiah Senkpiel Date: Mon, 25 Apr 2016 13:17:08 -0400 Subject: [PATCH] lib,src: throw on unhanded promise rejections Refs: https://github.com/nodejs/node/pull/5292 Refs: https://github.com/nodejs/promises/issues/26 Refs: https://github.com/nodejs/node/pull/6355 PR-URL: https://github.com/nodejs/node/pull/6375 --- lib/internal/bootstrap_node.js | 4 +- lib/internal/process/promises.js | 52 ++------ node.gyp | 2 + src/env.h | 6 +- src/node.cc | 123 +++++++++++++++++- src/node_internals.h | 13 ++ src/track-promise.cc | 63 +++++++++ src/track-promise.h | 31 +++++ test/message/promise_fast_handled_reject.js | 26 ++++ test/message/promise_fast_handled_reject.out | 16 +++ test/message/promise_fast_reject.js | 18 +++ test/message/promise_fast_reject.out | 16 +++ test/message/promise_reject.js | 27 ++++ test/message/promise_reject.out | 16 +++ .../unhandled_promise_trace_warnings.js | 5 - .../test-promises-gc-before-handled.js | 28 ++++ test/parallel/test-promises-handled-reject.js | 19 +++ ...promises-warning-on-unhandled-rejection.js | 29 ----- 18 files changed, 406 insertions(+), 88 deletions(-) create mode 100644 src/track-promise.cc create mode 100644 src/track-promise.h create mode 100644 test/message/promise_fast_handled_reject.js create mode 100644 test/message/promise_fast_handled_reject.out create mode 100644 test/message/promise_fast_reject.js create mode 100644 test/message/promise_fast_reject.out create mode 100644 test/message/promise_reject.js create mode 100644 test/message/promise_reject.out delete mode 100644 test/message/unhandled_promise_trace_warnings.js create mode 100644 test/parallel/test-promises-gc-before-handled.js create mode 100644 test/parallel/test-promises-handled-reject.js delete mode 100644 test/parallel/test-promises-warning-on-unhandled-rejection.js diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index efdbc5d9e3ac48..94569d6a705eaa 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -282,13 +282,13 @@ function setupProcessFatal() { - process._fatalException = function(er) { + process._fatalException = function(er, fromPromise) { var caught; if (process.domain && process.domain._errorHandler) caught = process.domain._errorHandler(er) || caught; - if (!caught) + if (!caught && !fromPromise) caught = process.emit('uncaughtException', er); // If someone handled it, then great. otherwise, die in C++ land diff --git a/lib/internal/process/promises.js b/lib/internal/process/promises.js index 0e382d11d5523b..f8399cfb3e3daa 100644 --- a/lib/internal/process/promises.js +++ b/lib/internal/process/promises.js @@ -2,18 +2,13 @@ const promiseRejectEvent = process._promiseRejectEvent; const hasBeenNotifiedProperty = new WeakMap(); -const promiseToGuidProperty = new WeakMap(); const pendingUnhandledRejections = []; -let lastPromiseId = 1; exports.setup = setupPromises; -function getAsynchronousRejectionWarningObject(uid) { - return new Error('Promise rejection was handled ' + - `asynchronously (rejection id: ${uid})`); -} - function setupPromises(scheduleMicrotasks) { + const promiseInternals = {}; + process._setupPromises(function(event, promise, reason) { if (event === promiseRejectEvent.unhandled) unhandledRejection(promise, reason); @@ -21,11 +16,12 @@ function setupPromises(scheduleMicrotasks) { rejectionHandled(promise); else require('assert').fail(null, null, 'unexpected PromiseRejectEvent'); - }); + }, function getPromiseReason(data) { + return data[data.indexOf('[[PromiseValue]]') + 1]; + }, promiseInternals); function unhandledRejection(promise, reason) { hasBeenNotifiedProperty.set(promise, false); - promiseToGuidProperty.set(promise, lastPromiseId++); addPendingUnhandledRejection(promise, reason); } @@ -33,47 +29,16 @@ function setupPromises(scheduleMicrotasks) { const hasBeenNotified = hasBeenNotifiedProperty.get(promise); if (hasBeenNotified !== undefined) { hasBeenNotifiedProperty.delete(promise); - const uid = promiseToGuidProperty.get(promise); - promiseToGuidProperty.delete(promise); if (hasBeenNotified === true) { - let warning = null; - if (!process.listenerCount('rejectionHandled')) { - // Generate the warning object early to get a good stack trace. - warning = getAsynchronousRejectionWarningObject(uid); - } + promiseInternals.untrackPromise(promise); process.nextTick(function() { - if (!process.emit('rejectionHandled', promise)) { - if (warning === null) - warning = getAsynchronousRejectionWarningObject(uid); - warning.name = 'PromiseRejectionHandledWarning'; - warning.id = uid; - process.emitWarning(warning); - } + process.emit('rejectionHandled', promise); }); } } } - function emitWarning(uid, reason) { - const warning = new Error('Unhandled promise rejection ' + - `(rejection id: ${uid}): ${reason}`); - warning.name = 'UnhandledPromiseRejectionWarning'; - warning.id = uid; - if (reason instanceof Error) { - warning.stack = reason.stack; - } - process.emitWarning(warning); - if (!deprecationWarned) { - deprecationWarned = true; - process.emitWarning( - 'Unhandled promise rejections are deprecated. In the future, ' + - 'promise rejections that are not handled will terminate the ' + - 'Node.js process with a non-zero exit code.', - 'DeprecationWarning', 'DEP0018'); - } - } - var deprecationWarned = false; function emitPendingUnhandledRejections() { let hadListeners = false; while (pendingUnhandledRejections.length > 0) { @@ -81,9 +46,8 @@ function setupPromises(scheduleMicrotasks) { const reason = pendingUnhandledRejections.shift(); if (hasBeenNotifiedProperty.get(promise) === false) { hasBeenNotifiedProperty.set(promise, true); - const uid = promiseToGuidProperty.get(promise); if (!process.emit('unhandledRejection', reason, promise)) { - emitWarning(uid, reason); + promiseInternals.trackPromise(promise); } else { hadListeners = true; } diff --git a/node.gyp b/node.gyp index 6cccf74a89f771..734a0cb592c3d8 100644 --- a/node.gyp +++ b/node.gyp @@ -184,6 +184,7 @@ 'src/stream_wrap.cc', 'src/tcp_wrap.cc', 'src/timer_wrap.cc', + 'src/track-promise.cc', 'src/tracing/agent.cc', 'src/tracing/node_trace_buffer.cc', 'src/tracing/node_trace_writer.cc', @@ -220,6 +221,7 @@ 'src/node_revert.h', 'src/node_i18n.h', 'src/pipe_wrap.h', + 'src/track-promise.h', 'src/tty_wrap.h', 'src/tcp_wrap.h', 'src/udp_wrap.h', diff --git a/src/env.h b/src/env.h index 581d7e9aef3c67..c4fb14b05b4f78 100644 --- a/src/env.h +++ b/src/env.h @@ -233,6 +233,7 @@ namespace node { V(zero_return_string, "ZERO_RETURN") \ #define ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) \ + V(array_from, v8::Function) \ V(as_external, v8::External) \ V(async_hooks_destroy_function, v8::Function) \ V(async_hooks_init_function, v8::Function) \ @@ -250,7 +251,10 @@ namespace node { V(module_load_list_array, v8::Array) \ V(pipe_constructor_template, v8::FunctionTemplate) \ V(process_object, v8::Object) \ - V(promise_reject_function, v8::Function) \ + V(promise_unhandled_rejection_function, v8::Function) \ + V(promise_unhandled_rejection, v8::Function) \ + V(promise_unhandled_reject_map, v8::NativeWeakMap) \ + V(promise_unhandled_reject_keys, v8::Set) \ V(push_values_to_array_function, v8::Function) \ V(script_context_constructor_template, v8::FunctionTemplate) \ V(script_data_constructor_function, v8::Function) \ diff --git a/src/node.cc b/src/node.cc index 8c5e811d6e9130..1a4a4db9dfd1b7 100644 --- a/src/node.cc +++ b/src/node.cc @@ -39,6 +39,7 @@ #include "req-wrap-inl.h" #include "string_bytes.h" #include "tracing/agent.h" +#include "track-promise.h" #include "util.h" #include "uv.h" #if NODE_USE_V8_PLATFORM @@ -102,6 +103,7 @@ using v8::Array; using v8::ArrayBuffer; using v8::Boolean; using v8::Context; +using v8::Debug; using v8::EscapableHandleScope; using v8::Exception; using v8::Float64Array; @@ -117,6 +119,7 @@ using v8::MaybeLocal; using v8::Message; using v8::Name; using v8::NamedPropertyHandlerConfiguration; +using v8::NativeWeakMap; using v8::Null; using v8::Number; using v8::Object; @@ -126,6 +129,7 @@ using v8::PromiseRejectMessage; using v8::PropertyCallbackInfo; using v8::ScriptOrigin; using v8::SealHandleScope; +using v8::Set; using v8::String; using v8::TryCatch; using v8::Uint32Array; @@ -1173,7 +1177,7 @@ void PromiseRejectCallback(PromiseRejectMessage message) { Local event = Integer::New(isolate, message.GetEvent()); Environment* env = Environment::GetCurrent(isolate); - Local callback = env->promise_reject_function(); + Local callback = env->promise_unhandled_rejection_function(); if (value.IsEmpty()) value = Undefined(isolate); @@ -1184,14 +1188,78 @@ void PromiseRejectCallback(PromiseRejectMessage message) { callback->Call(process, arraysize(args), args); } +Local GetPromiseReason(Environment* env, Local promise) { + Local fn = env->promise_unhandled_rejection(); + + Local internal_props = + Debug::GetInternalProperties(env->isolate(), + promise).ToLocalChecked().As(); + + // If fn is empty we'll almost certainly have to panic anyways + return fn->Call(env->context(), Null(env->isolate()), 1, + &internal_props).ToLocalChecked(); +} + +void TrackPromise(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + CHECK(args[0]->IsObject()); + Local promise = args[0].As(); + + TrackPromise::New(env->isolate(), promise); + + Local promise_value = GetPromiseReason(env, promise); + Local unhandled_reject_map = + env->promise_unhandled_reject_map(); + Local unhandled_reject_keys = + env->promise_unhandled_reject_keys(); + + if (unhandled_reject_keys->Size() > 1000) { + return; + } + + if (!unhandled_reject_map->Has(promise_value) && + !promise_value->IsUndefined()) { + unhandled_reject_map->Set(promise_value, promise); + CHECK(!unhandled_reject_keys->Add(env->context(), promise_value).IsEmpty()); + } +} + +void UntrackPromise(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + CHECK(args[0]->IsObject()); + Local promise = args[0].As(); + + Local err = GetPromiseReason(env, promise); + Local unhandled_reject_map = + env->promise_unhandled_reject_map(); + Local unhandled_reject_keys = + env->promise_unhandled_reject_keys(); + + if (unhandled_reject_keys->Has(env->context(), err).IsJust()) { + CHECK(unhandled_reject_keys->Delete(env->context(), err).IsJust()); + unhandled_reject_map->Delete(err); + } +} + void SetupPromises(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); Isolate* isolate = env->isolate(); + env->set_promise_unhandled_reject_map(NativeWeakMap::New(isolate)); + env->set_promise_unhandled_reject_keys(Set::New(isolate)); + CHECK(args[0]->IsFunction()); + CHECK(args[1]->IsFunction()); + CHECK(args[2]->IsObject()); isolate->SetPromiseRejectCallback(PromiseRejectCallback); - env->set_promise_reject_function(args[0].As()); + env->set_promise_unhandled_rejection_function(args[0].As()); + env->set_promise_unhandled_rejection(args[1].As()); + + env->SetMethod(args[2].As(), "trackPromise", TrackPromise); + env->SetMethod(args[2].As(), "untrackPromise", UntrackPromise); env->process_object()->Delete( env->context(), @@ -1621,10 +1689,9 @@ void AppendExceptionLine(Environment* env, arrow_str).FromMaybe(false)); } - -static void ReportException(Environment* env, - Local er, - Local message) { +void ReportException(Environment* env, + Local er, + Local message) { HandleScope scope(env->isolate()); AppendExceptionLine(env, er, message, FATAL_ERROR); @@ -2512,6 +2579,14 @@ NO_RETURN void FatalError(const char* location, const char* message) { void FatalException(Isolate* isolate, Local error, Local message) { + InternalFatalException(isolate, error, message, false); +} + + +void InternalFatalException(Isolate* isolate, + Local error, + Local message, + bool from_promise) { HandleScope scope(isolate); Environment* env = Environment::GetCurrent(isolate); @@ -2534,9 +2609,12 @@ void FatalException(Isolate* isolate, // Do not call FatalException when _fatalException handler throws fatal_try_catch.SetVerbose(false); + Local argv[2] = { error, + Boolean::New(env->isolate(), from_promise) }; + // this will return true if the JS layer handled it, false otherwise Local caught = - fatal_exception_function->Call(process_object, 1, &error); + fatal_exception_function->Call(process_object, 2, argv); if (fatal_try_catch.HasCaught()) { // the fatal exception function threw, so we must exit @@ -3464,6 +3542,12 @@ void LoadEnvironment(Environment* env) { // Add a reference to the global object Local global = env->context()->Global(); + Local js_array_object = global->Get( + FIXED_ONE_BYTE_STRING(env->isolate(), "Array")).As(); + Local js_array_from_function = js_array_object->Get( + FIXED_ONE_BYTE_STRING(env->isolate(), "from")).As(); + env->set_array_from(js_array_from_function); + #if defined HAVE_DTRACE || defined HAVE_ETW InitDTrace(env, global); #endif @@ -4463,6 +4547,31 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data, } while (more == true); } + Local promise_keys_set = + env.promise_unhandled_reject_keys().As(); + Local convert = env.array_from(); + Local ret = convert->Call(env.context(), + Null(env.isolate()), 1, &promise_keys_set).ToLocalChecked(); + Local promise_keys = ret.As(); + uint32_t key_count = promise_keys->Length(); + Local unhandled_reject_map = + env.promise_unhandled_reject_map(); + + for (uint32_t key_iter = 0; key_iter < key_count; key_iter++) { + Local key = promise_keys->Get(env.context(), + key_iter).ToLocalChecked(); + + if (unhandled_reject_map->Has(key)) { + Local promise = unhandled_reject_map->Get(key); + Local err = GetPromiseReason(&env, promise); + Local message = Exception::CreateMessage(isolate, err); + + // XXX(Fishrock123): Should this just call ReportException and + // set exit_code = 1 instead? + InternalFatalException(isolate, err, message, true); + } + } + env.set_trace_sync_io(false); const int exit_code = EmitExit(&env); diff --git a/src/node_internals.h b/src/node_internals.h index 9d57becc26ceb9..eea05855032564 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -130,6 +130,19 @@ constexpr size_t arraysize(const T(&)[N]) { return N; } bool IsExceptionDecorated(Environment* env, v8::Local er); enum ErrorHandlingMode { FATAL_ERROR, CONTEXTIFY_ERROR }; + +v8::Local GetPromiseReason(Environment* env, + v8::Local promise); + +void InternalFatalException(v8::Isolate* isolate, + v8::Local error, + v8::Local message, + bool from_promise); + +void ReportException(Environment* env, + v8::Local er, + v8::Local message); + void AppendExceptionLine(Environment* env, v8::Local er, v8::Local message, diff --git a/src/track-promise.cc b/src/track-promise.cc new file mode 100644 index 00000000000000..896be59c923e26 --- /dev/null +++ b/src/track-promise.cc @@ -0,0 +1,63 @@ +#include "track-promise.h" +#include "env.h" +#include "env-inl.h" +#include "node_internals.h" + +namespace node { + +using v8::Exception; +using v8::Function; +using v8::Isolate; +using v8::Local; +using v8::Message; +using v8::Object; +using v8::Persistent; +using v8::Value; +using v8::WeakCallbackInfo; +using v8::WeakCallbackType; + +typedef void (*FreeCallback)(Local object, Local fn); + + +TrackPromise* TrackPromise::New(Isolate* isolate, + Local object) { + return new TrackPromise(isolate, object); +} + + +Persistent* TrackPromise::persistent() { + return &persistent_; +} + + +TrackPromise::TrackPromise(Isolate* isolate, + Local object) + : persistent_(isolate, object) { + persistent_.SetWeak(this, WeakCallback, WeakCallbackType::kFinalizer); + persistent_.MarkIndependent(); +} + + +TrackPromise::~TrackPromise() { + persistent_.Reset(); +} + + +void TrackPromise::WeakCallback( + const WeakCallbackInfo& data) { + data.GetParameter()->WeakCallback(data.GetIsolate()); +} + + +void TrackPromise::WeakCallback(Isolate* isolate) { + Environment* env = Environment::GetCurrent(isolate); + + Local promise = persistent_.Get(isolate); + Local err = node::GetPromiseReason(env, promise); + Local message = Exception::CreateMessage(isolate, err); + + node::InternalFatalException(isolate, err, message, true); + delete this; +} + +} // namespace node diff --git a/src/track-promise.h b/src/track-promise.h new file mode 100644 index 00000000000000..43f7b006eddaad --- /dev/null +++ b/src/track-promise.h @@ -0,0 +1,31 @@ +#ifndef SRC_TRACK_PROMISE_H_ +#define SRC_TRACK_PROMISE_H_ + +#include "v8.h" + +namespace node { + +class Environment; + +class TrackPromise { + public: + TrackPromise(v8::Isolate* isolate, v8::Local object); + virtual ~TrackPromise(); + + static TrackPromise* New(v8::Isolate* isolate, + v8::Local object); + + inline v8::Persistent* persistent(); + + static inline void WeakCallback( + const v8::WeakCallbackInfo& data); + + private: + inline void WeakCallback(v8::Isolate* isolate); + + v8::Persistent persistent_; +}; + +} // namespace node + +#endif // SRC_TRACK_PROMISE_H_ diff --git a/test/message/promise_fast_handled_reject.js b/test/message/promise_fast_handled_reject.js new file mode 100644 index 00000000000000..3925e63ae7c74e --- /dev/null +++ b/test/message/promise_fast_handled_reject.js @@ -0,0 +1,26 @@ +'use strict'; +const common = require('../common'); + +const p1 = new Promise((res, rej) => { + consol.log('One'); // eslint-disable-line no-undef +}); + +const p2 = new Promise((res, rej) => { //eslint-disable-line no-unused-vars + consol.log('Two'); // eslint-disable-line no-undef +}); + +const p3 = new Promise((res, rej) => { + consol.log('Three'); // eslint-disable-line no-undef +}); + +new Promise((res, rej) => { + setTimeout(common.mustCall(() => { + p1.catch(() => {}); + p3.catch(() => {}); + })); +}); + +process.on('uncaughtException', (err) => + common.fail('Should not trigger uncaught exception')); + +process.on('exit', () => process._rawDebug('exit event emitted')); diff --git a/test/message/promise_fast_handled_reject.out b/test/message/promise_fast_handled_reject.out new file mode 100644 index 00000000000000..d5970ed4e1a661 --- /dev/null +++ b/test/message/promise_fast_handled_reject.out @@ -0,0 +1,16 @@ +exit event emitted +*test*message*promise_fast_handled_reject.js:* + consol.log('Two'); // eslint-disable-line no-undef + ^ + +ReferenceError: consol is not defined + at *test*message*promise_fast_handled_reject.js:*:* + at Object. (*test*message*promise_fast_handled_reject.js:*:*) + at Module._compile (module.js:*:*) + at Object.Module._extensions..js (module.js:*:*) + at Module.load (module.js:*:*) + at tryModuleLoad (module.js:*:*) + at Function.Module._load (module.js:*:*) + at Module.runMain (module.js:*:*) + at run (bootstrap_node.js:*:*) + at startup (bootstrap_node.js:*:*) diff --git a/test/message/promise_fast_reject.js b/test/message/promise_fast_reject.js new file mode 100644 index 00000000000000..cafc2a27b353d5 --- /dev/null +++ b/test/message/promise_fast_reject.js @@ -0,0 +1,18 @@ +'use strict'; + +// We should always have the stacktrace of the oldest rejection. + +const common = require('../common'); + +new Promise(function(res, rej) { + consol.log('One'); // eslint-disable-line no-undef +}); + +new Promise(function(res, rej) { + consol.log('Two'); // eslint-disable-line no-undef +}); + +process.on('uncaughtException', (err) => + common.fail('Should not trigger uncaught exception')); + +process.on('exit', () => process._rawDebug('exit event emitted')); diff --git a/test/message/promise_fast_reject.out b/test/message/promise_fast_reject.out new file mode 100644 index 00000000000000..ea434d914e4ecd --- /dev/null +++ b/test/message/promise_fast_reject.out @@ -0,0 +1,16 @@ +exit event emitted +*test*message*promise_fast_reject.js:* + consol.log('One'); // eslint-disable-line no-undef + ^ + +ReferenceError: consol is not defined + at *test*message*promise_fast_reject.js:*:* + at Object. (*test*message*promise_fast_reject.js:*:*) + at Module._compile (module.js:*:*) + at Object.Module._extensions..js (module.js:*:*) + at Module.load (module.js:*:*) + at tryModuleLoad (module.js:*:*) + at Function.Module._load (module.js:*:*) + at Module.runMain (module.js:*:*) + at run (bootstrap_node.js:*:*) + at startup (bootstrap_node.js:*:*) diff --git a/test/message/promise_reject.js b/test/message/promise_reject.js new file mode 100644 index 00000000000000..c4e60cfd3bf33a --- /dev/null +++ b/test/message/promise_reject.js @@ -0,0 +1,27 @@ +'use strict'; +const common = require('../common'); + +// Flags: --expose-gc + +Promise.reject(new Error('oops')); + +// Manually call GC due to possible memory contraints with attempting to +// trigger it "naturally". +setTimeout(common.mustCall(() => { + /* eslint-disable no-undef */ + gc(); + gc(); + gc(); + /* eslint-enable no-undef */ +}, 1), 2); + +process.on('beforeExit', () => + common.fail('beforeExit should not be reached')); + +process.on('uncaughtException', (err) => { + // XXX(Fishrock123): This test is currently broken... + console.log(err.stack); + common.fail('Should not trigger uncaught exception'); +}); + +process.on('exit', () => process._rawDebug('exit event emitted')); diff --git a/test/message/promise_reject.out b/test/message/promise_reject.out new file mode 100644 index 00000000000000..af65d739e6ea46 --- /dev/null +++ b/test/message/promise_reject.out @@ -0,0 +1,16 @@ +exit event emitted +*test*message*promise_reject.js:* +Promise.reject(new Error('oops')); + ^ + +Error: oops + at *test*message*promise_reject.js:*:* + at Module._compile (module.js:*:*) + at Object.Module._extensions..js (module.js:*:*) + at Module.load (module.js:*:*) + at tryModuleLoad (module.js:*:*) + at Function.Module._load (module.js:*:*) + at Module.runMain (module.js:*:*) + at run (bootstrap_node.js:*:*) + at startup (bootstrap_node.js:*:*) + at bootstrap_node.js:*:* diff --git a/test/message/unhandled_promise_trace_warnings.js b/test/message/unhandled_promise_trace_warnings.js deleted file mode 100644 index 48450fb21e2169..00000000000000 --- a/test/message/unhandled_promise_trace_warnings.js +++ /dev/null @@ -1,5 +0,0 @@ -// Flags: --trace-warnings -'use strict'; -require('../common'); -const p = Promise.reject(new Error('This was rejected')); -setImmediate(() => p.catch(() => {})); diff --git a/test/parallel/test-promises-gc-before-handled.js b/test/parallel/test-promises-gc-before-handled.js new file mode 100644 index 00000000000000..2bf8ac2e96d61f --- /dev/null +++ b/test/parallel/test-promises-gc-before-handled.js @@ -0,0 +1,28 @@ +'use strict'; +const common = require('../common'); + +// Flags: --expose-gc + +var p = new Promise((res, rej) => { + consol.log('oops'); // eslint-disable-line no-undef +}); + +// Manually call GC due to possible memory contraints with attempting to +// trigger it "naturally". +setTimeout(common.mustCall(() => { + /* eslint-disable no-undef */ + gc(); + gc(); + gc(); + /* eslint-enable no-undef */ + setTimeout(common.mustCall(() => { + /* eslint-disable no-undef */ + gc(); + gc(); + gc(); + /* eslint-enable no-undef */ + setTimeout(common.mustCall(() => { + p.catch(() => {}); + }, 1), 250); + }), 20); +}), 20); diff --git a/test/parallel/test-promises-handled-reject.js b/test/parallel/test-promises-handled-reject.js new file mode 100644 index 00000000000000..5b040e1f8b6491 --- /dev/null +++ b/test/parallel/test-promises-handled-reject.js @@ -0,0 +1,19 @@ +'use strict'; +const common = require('../common'); + +// Flags: --expose-gc + +var p = new Promise((res, rej) => { + consol.log('oops'); // eslint-disable-line no-undef +}); + +// Manually call GC due to possible memory contraints with attempting to +// trigger it "naturally". +setTimeout(common.mustCall(() => { + p.catch(() => {}); + /* eslint-disable no-undef */ + gc(); + gc(); + gc(); + /* eslint-enable no-undef */ +}, 1), 2); diff --git a/test/parallel/test-promises-warning-on-unhandled-rejection.js b/test/parallel/test-promises-warning-on-unhandled-rejection.js deleted file mode 100644 index 10f95162a09597..00000000000000 --- a/test/parallel/test-promises-warning-on-unhandled-rejection.js +++ /dev/null @@ -1,29 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -// Test that warnings are emitted when a Promise experiences an uncaught -// rejection, and then again if the rejection is handled later on. - -const common = require('../common'); -const assert = require('assert'); - -let b = 0; - -process.on('warning', common.mustCall((warning) => { - switch (b++) { - case 0: - assert.strictEqual(warning.name, 'UnhandledPromiseRejectionWarning'); - assert(/Unhandled promise rejection/.test(warning.message)); - break; - case 1: - assert.strictEqual(warning.name, 'DeprecationWarning'); - break; - case 2: - assert.strictEqual(warning.name, 'PromiseRejectionHandledWarning'); - assert(/Promise rejection was handled asynchronously/ - .test(warning.message)); - } -}, 3)); - -const p = Promise.reject('This was rejected'); -setImmediate(common.mustCall(() => p.catch(() => {})));