summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoyee Cheung <joyeec9h3@gmail.com>2019-04-25 03:03:48 +0800
committerJoyee Cheung <joyeec9h3@gmail.com>2019-04-28 15:36:28 +0800
commit757f3f8b2cf9854292caaa143a1b0a7fbac5e0ea (patch)
tree09c9a3a888fc9477d91e75cd9972cd9e6d0cdfd3
parentc5817abff5033da7c09302256a331e64473422a8 (diff)
downloadandroid-node-v8-757f3f8b2cf9854292caaa143a1b0a7fbac5e0ea.tar.gz
android-node-v8-757f3f8b2cf9854292caaa143a1b0a7fbac5e0ea.tar.bz2
android-node-v8-757f3f8b2cf9854292caaa143a1b0a7fbac5e0ea.zip
process: reduce the number of internal frames in async stack trace
Previously, we call the JS land `runNextTicks` implementation immediately from JS land after evaluating the main module or the input, so these synchronous JS call frames would show up in the stack trace of the async errors, which can be confusing. This patch moves those calls into C++ so that more of these internal scheduler implementation details can be hidden and the users can see a cleaner a cleaner async JS stack trace. PR-URL: https://github.com/nodejs/node/pull/27392 Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: Minwoo Jung <minwoo@nodesource.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
-rw-r--r--lib/internal/modules/cjs/loader.js4
-rw-r--r--lib/internal/process/execution.js4
-rw-r--r--lib/internal/process/task_queues.js1
-rw-r--r--src/node.cc10
-rw-r--r--src/node_process.h10
-rw-r--r--src/node_task_queue.cc15
-rw-r--r--test/fixtures/async-error.js27
-rw-r--r--test/message/async_error_eval_cjs.js39
-rw-r--r--test/message/async_error_eval_cjs.out6
-rw-r--r--test/message/async_error_eval_esm.js42
-rw-r--r--test/message/async_error_eval_esm.out7
-rw-r--r--test/message/async_error_microtask_main.js13
-rw-r--r--test/message/async_error_microtask_main.out6
-rw-r--r--test/message/async_error_nexttick_main.js13
-rw-r--r--test/message/async_error_nexttick_main.out7
-rw-r--r--test/message/async_error_sync_esm.mjs14
-rw-r--r--test/message/async_error_sync_esm.out7
-rw-r--r--test/message/async_error_sync_main.js13
-rw-r--r--test/message/async_error_sync_main.out6
-rw-r--r--test/message/events_unhandled_error_nexttick.out3
-rw-r--r--test/message/nexttick_throw.out3
-rw-r--r--test/message/unhandled_promise_trace_warnings.out6
22 files changed, 233 insertions, 23 deletions
diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js
index a1e5046d40..96b5e8b7b8 100644
--- a/lib/internal/modules/cjs/loader.js
+++ b/lib/internal/modules/cjs/loader.js
@@ -819,13 +819,9 @@ Module.runMain = function() {
true /* fromPromise */
);
});
- // Handle any nextTicks added in the first tick of the program
- process._tickCallback();
return;
}
Module._load(process.argv[1], null, true);
- // Handle any nextTicks added in the first tick of the program
- process._tickCallback();
};
Module.createRequireFromPath = (filename) => {
diff --git a/lib/internal/process/execution.js b/lib/internal/process/execution.js
index 93855ddbfc..c95567c506 100644
--- a/lib/internal/process/execution.js
+++ b/lib/internal/process/execution.js
@@ -48,8 +48,6 @@ function evalModule(source, printResult) {
print(kStderr, e);
process.exit(1);
});
- // Handle any nextTicks added in the first tick of the program.
- process._tickCallback();
}
function evalScript(name, body, breakFirstLine, printResult) {
@@ -81,8 +79,6 @@ function evalScript(name, body, breakFirstLine, printResult) {
const { kStdout, print } = require('internal/util/print');
print(kStdout, result);
}
- // Handle any nextTicks added in the first tick of the program.
- process._tickCallback();
}
const exceptionHandlerState = { captureFn: null };
diff --git a/lib/internal/process/task_queues.js b/lib/internal/process/task_queues.js
index 12e34b7ff7..309e27e6ec 100644
--- a/lib/internal/process/task_queues.js
+++ b/lib/internal/process/task_queues.js
@@ -49,6 +49,7 @@ function setHasTickScheduled(value) {
const queue = new FixedQueue();
+// Should be in sync with RunNextTicksNative in node_task_queue.cc
function runNextTicks() {
if (!hasTickScheduled() && !hasRejectionToWarn())
runMicrotasks();
diff --git a/src/node.cc b/src/node.cc
index 10ef0d5bc7..636a92eab3 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -379,9 +379,13 @@ MaybeLocal<Value> StartExecution(Environment* env, const char* main_script_id) {
->GetFunction(env->context())
.ToLocalChecked()};
- MaybeLocal<Value> result =
- ExecuteBootstrapper(env, main_script_id, &parameters, &arguments);
- return scope.EscapeMaybe(result);
+ Local<Value> result;
+ if (!ExecuteBootstrapper(env, main_script_id, &parameters, &arguments)
+ .ToLocal(&result) ||
+ !task_queue::RunNextTicksNative(env)) {
+ return MaybeLocal<Value>();
+ }
+ return scope.Escape(result);
}
MaybeLocal<Value> StartMainThreadExecution(Environment* env) {
diff --git a/src/node_process.h b/src/node_process.h
index cb6ad85828..f01445b3c5 100644
--- a/src/node_process.h
+++ b/src/node_process.h
@@ -36,6 +36,16 @@ v8::MaybeLocal<v8::Object> CreateProcessObject(
const std::vector<std::string>& args,
const std::vector<std::string>& exec_args);
void PatchProcessObject(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+namespace task_queue {
+// Handle any nextTicks added in the first tick of the program.
+// We use the native version here for once so that any microtasks
+// created by the main module is then handled from C++, and
+// the call stack of the main script does not show up in the async error
+// stack trace.
+bool RunNextTicksNative(Environment* env);
+} // namespace task_queue
+
} // namespace node
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
#endif // SRC_NODE_PROCESS_H_
diff --git a/src/node_task_queue.cc b/src/node_task_queue.cc
index b125f5d01e..af1d96fab2 100644
--- a/src/node_task_queue.cc
+++ b/src/node_task_queue.cc
@@ -2,6 +2,7 @@
#include "node.h"
#include "node_errors.h"
#include "node_internals.h"
+#include "node_process.h"
#include "v8.h"
#include <atomic>
@@ -38,6 +39,20 @@ static void EnqueueMicrotask(const FunctionCallbackInfo<Value>& args) {
isolate->EnqueueMicrotask(args[0].As<Function>());
}
+// Should be in sync with runNextTicks in internal/process/task_queues.js
+bool RunNextTicksNative(Environment* env) {
+ TickInfo* tick_info = env->tick_info();
+ if (!tick_info->has_tick_scheduled() && !tick_info->has_rejection_to_warn())
+ env->isolate()->RunMicrotasks();
+ if (!tick_info->has_tick_scheduled() && !tick_info->has_rejection_to_warn())
+ return true;
+
+ Local<Function> callback = env->tick_callback_function();
+ CHECK(!callback.IsEmpty());
+ return !callback->Call(env->context(), env->process_object(), 0, nullptr)
+ .IsEmpty();
+}
+
static void RunMicrotasks(const FunctionCallbackInfo<Value>& args) {
args.GetIsolate()->RunMicrotasks();
}
diff --git a/test/fixtures/async-error.js b/test/fixtures/async-error.js
new file mode 100644
index 0000000000..48b19b40d8
--- /dev/null
+++ b/test/fixtures/async-error.js
@@ -0,0 +1,27 @@
+'use strict';
+
+async function one() {
+ throw new Error('test');
+}
+
+async function breaker() {
+ return true;
+}
+
+async function stack() {
+ await breaker();
+}
+
+async function two() {
+ await stack();
+ await one();
+}
+async function three() {
+ await two();
+}
+
+async function four() {
+ await three();
+}
+
+module.exports = four;
diff --git a/test/message/async_error_eval_cjs.js b/test/message/async_error_eval_cjs.js
new file mode 100644
index 0000000000..a2caaa0937
--- /dev/null
+++ b/test/message/async_error_eval_cjs.js
@@ -0,0 +1,39 @@
+'use strict';
+
+require('../common');
+const { spawnSync } = require('child_process');
+
+const four = require('../common/fixtures')
+ .readSync('async-error.js')
+ .toString()
+ .split('\n')
+ .slice(2, -2)
+ .join('\n');
+
+const main = `${four}
+
+async function main() {
+ try {
+ await four();
+ } catch (e) {
+ console.log(e);
+ }
+}
+
+main();
+`;
+
+// --eval CJS
+{
+ const child = spawnSync(process.execPath, [
+ '-e',
+ main
+ ], {
+ env: { ...process.env }
+ });
+
+ if (child.status !== 0) {
+ console.error(child.stderr.toString());
+ }
+ console.error(child.stdout.toString());
+}
diff --git a/test/message/async_error_eval_cjs.out b/test/message/async_error_eval_cjs.out
new file mode 100644
index 0000000000..a8d89eebcb
--- /dev/null
+++ b/test/message/async_error_eval_cjs.out
@@ -0,0 +1,6 @@
+Error: test
+ at one ([eval]:2:9)
+ at two ([eval]:15:9)
+ at async three ([eval]:18:3)
+ at async four ([eval]:22:3)
+ at async main ([eval]:28:5) \ No newline at end of file
diff --git a/test/message/async_error_eval_esm.js b/test/message/async_error_eval_esm.js
new file mode 100644
index 0000000000..33675b5a9d
--- /dev/null
+++ b/test/message/async_error_eval_esm.js
@@ -0,0 +1,42 @@
+'use strict';
+
+require('../common');
+const { spawnSync } = require('child_process');
+
+const four = require('../common/fixtures')
+ .readSync('async-error.js')
+ .toString()
+ .split('\n')
+ .slice(2, -2)
+ .join('\n');
+
+const main = `${four}
+
+async function main() {
+ try {
+ await four();
+ } catch (e) {
+ console.log(e);
+ }
+}
+
+main();
+`;
+
+// --eval ESM
+{
+ const child = spawnSync(process.execPath, [
+ '--experimental-modules',
+ '--input-type',
+ 'module',
+ '-e',
+ main
+ ], {
+ env: { ...process.env }
+ });
+
+ if (child.status !== 0) {
+ console.error(child.stderr.toString());
+ }
+ console.error(child.stdout.toString());
+}
diff --git a/test/message/async_error_eval_esm.out b/test/message/async_error_eval_esm.out
new file mode 100644
index 0000000000..91ce0ce21c
--- /dev/null
+++ b/test/message/async_error_eval_esm.out
@@ -0,0 +1,7 @@
+Error: test
+ at one (eval:1:2:9)
+ at two (eval:1:15:9)
+ at processTicksAndRejections (internal/process/task_queues.js:*:*)
+ at async three (eval:1:18:3)
+ at async four (eval:1:22:3)
+ at async main (eval:1:28:5)
diff --git a/test/message/async_error_microtask_main.js b/test/message/async_error_microtask_main.js
new file mode 100644
index 0000000000..5520c650ad
--- /dev/null
+++ b/test/message/async_error_microtask_main.js
@@ -0,0 +1,13 @@
+'use strict';
+require('../common');
+const four = require('../fixtures/async-error');
+
+async function main() {
+ try {
+ await four();
+ } catch (e) {
+ console.error(e);
+ }
+}
+
+queueMicrotask(main);
diff --git a/test/message/async_error_microtask_main.out b/test/message/async_error_microtask_main.out
new file mode 100644
index 0000000000..9512270730
--- /dev/null
+++ b/test/message/async_error_microtask_main.out
@@ -0,0 +1,6 @@
+Error: test
+ at one (*fixtures*async-error.js:4:9)
+ at two (*fixtures*async-error.js:17:9)
+ at async three (*fixtures*async-error.js:20:3)
+ at async four (*fixtures*async-error.js:24:3)
+ at async main (*message*async_error_microtask_main.js:7:5)
diff --git a/test/message/async_error_nexttick_main.js b/test/message/async_error_nexttick_main.js
new file mode 100644
index 0000000000..ecd0531852
--- /dev/null
+++ b/test/message/async_error_nexttick_main.js
@@ -0,0 +1,13 @@
+'use strict';
+require('../common');
+const four = require('../fixtures/async-error');
+
+async function main() {
+ try {
+ await four();
+ } catch (e) {
+ console.error(e);
+ }
+}
+
+process.nextTick(main);
diff --git a/test/message/async_error_nexttick_main.out b/test/message/async_error_nexttick_main.out
new file mode 100644
index 0000000000..c6ac1e4180
--- /dev/null
+++ b/test/message/async_error_nexttick_main.out
@@ -0,0 +1,7 @@
+Error: test
+ at one (*fixtures*async-error.js:4:9)
+ at two (*fixtures*async-error.js:17:9)
+ at processTicksAndRejections (internal/process/task_queues.js:*:*)
+ at async three (*fixtures*async-error.js:20:3)
+ at async four (*fixtures*async-error.js:24:3)
+ at async main (*message*async_error_nexttick_main.js:7:5)
diff --git a/test/message/async_error_sync_esm.mjs b/test/message/async_error_sync_esm.mjs
new file mode 100644
index 0000000000..86a901a122
--- /dev/null
+++ b/test/message/async_error_sync_esm.mjs
@@ -0,0 +1,14 @@
+// Flags: --experimental-modules
+/* eslint-disable node-core/required-modules */
+import '../common/index.mjs';
+import four from '../fixtures/async-error.js';
+
+async function main() {
+ try {
+ await four();
+ } catch (e) {
+ console.error(e);
+ }
+}
+
+main();
diff --git a/test/message/async_error_sync_esm.out b/test/message/async_error_sync_esm.out
new file mode 100644
index 0000000000..99ce9ed0b2
--- /dev/null
+++ b/test/message/async_error_sync_esm.out
@@ -0,0 +1,7 @@
+(node:*) ExperimentalWarning: The ESM module loader is experimental.
+Error: test
+ at one (*fixtures*async-error.js:4:9)
+ at two (*fixtures*async-error.js:17:9)
+ at async three (*fixtures*async-error.js:20:3)
+ at async four (*fixtures*async-error.js:24:3)
+ at async main (*message*async_error_sync_esm.mjs:8:5)
diff --git a/test/message/async_error_sync_main.js b/test/message/async_error_sync_main.js
new file mode 100644
index 0000000000..fa33c86d2c
--- /dev/null
+++ b/test/message/async_error_sync_main.js
@@ -0,0 +1,13 @@
+'use strict';
+require('../common');
+const four = require('../fixtures/async-error');
+
+async function main() {
+ try {
+ await four();
+ } catch (e) {
+ console.error(e);
+ }
+}
+
+main();
diff --git a/test/message/async_error_sync_main.out b/test/message/async_error_sync_main.out
new file mode 100644
index 0000000000..565affbaf7
--- /dev/null
+++ b/test/message/async_error_sync_main.out
@@ -0,0 +1,6 @@
+Error: test
+ at one (*fixtures*async-error.js:4:9)
+ at two (*fixtures*async-error.js:17:9)
+ at async three (*fixtures*async-error.js:20:3)
+ at async four (*fixtures*async-error.js:24:3)
+ at async main (*message*async_error_sync_main.js:7:5)
diff --git a/test/message/events_unhandled_error_nexttick.out b/test/message/events_unhandled_error_nexttick.out
index ddb13807ee..f592d91b52 100644
--- a/test/message/events_unhandled_error_nexttick.out
+++ b/test/message/events_unhandled_error_nexttick.out
@@ -13,6 +13,3 @@ Error
Emitted 'error' event at:
at *events_unhandled_error_nexttick.js:*:*
at processTicksAndRejections (internal/process/task_queues.js:*:*)
- at process.runNextTicks [as _tickCallback] (internal/process/task_queues.js:*:*)
- at Function.Module.runMain (internal/modules/cjs/loader.js:*:*)
- at internal/main/run_main_module.js:*:*
diff --git a/test/message/nexttick_throw.out b/test/message/nexttick_throw.out
index f180c4ab55..d0a45b3cec 100644
--- a/test/message/nexttick_throw.out
+++ b/test/message/nexttick_throw.out
@@ -5,6 +5,3 @@
ReferenceError: undefined_reference_error_maker is not defined
at *test*message*nexttick_throw.js:*:*
at processTicksAndRejections (internal/process/task_queues.js:*:*)
- at process.runNextTicks [as _tickCallback] (internal/process/task_queues.js:*:*)
- at Function.Module.runMain (internal/modules/cjs/loader.js:*:*)
- at internal/main/run_main_module.js:*:*
diff --git a/test/message/unhandled_promise_trace_warnings.out b/test/message/unhandled_promise_trace_warnings.out
index 13778fb798..c9fff40efb 100644
--- a/test/message/unhandled_promise_trace_warnings.out
+++ b/test/message/unhandled_promise_trace_warnings.out
@@ -9,9 +9,6 @@
at *
at *
at *
- at *
- at *
- at *
(node:*) Error: This was rejected
at * (*test*message*unhandled_promise_trace_warnings.js:*)
at *
@@ -25,9 +22,6 @@
at *
at *
at *
- at *
- at *
- at *
(node:*) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
at handledRejection (internal/process/promises.js:*)
at promiseRejectHandler (internal/process/promises.js:*)