mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1010518 - Maintaining stack information through Task.jsm. r=paolo
This commit is contained in:
parent
e61d04813e
commit
2d13fbc16d
@ -241,6 +241,9 @@ function createAsyncFunction(aTask) {
|
||||
* that is fulfilled when the task terminates.
|
||||
*/
|
||||
function TaskImpl(iterator) {
|
||||
if (Task.Debugging.maintainStack) {
|
||||
this._stack = (new Error()).stack;
|
||||
}
|
||||
this.deferred = Promise.defer();
|
||||
this._iterator = iterator;
|
||||
this._isStarGenerator = !("send" in iterator);
|
||||
@ -346,7 +349,54 @@ TaskImpl.prototype = {
|
||||
* The uncaught exception to handle.
|
||||
*/
|
||||
_handleException: function TaskImpl_handleException(aException) {
|
||||
if (aException && typeof aException == "object" && "name" in aException &&
|
||||
if (aException && typeof aException == "object" && "stack" in aException) {
|
||||
|
||||
let stack = aException.stack;
|
||||
|
||||
if (Task.Debugging.maintainStack &&
|
||||
aException._capturedTaskStack != this._stack &&
|
||||
typeof stack == "string") {
|
||||
|
||||
// Rewrite the stack for more readability.
|
||||
|
||||
let bottomStack = this._stack;
|
||||
let topStack = aException.stack;
|
||||
|
||||
// Cut `topStack` at the first line that contains Task.jsm, keep the head.
|
||||
let reLine = /([^\r\n])+/g;
|
||||
let match;
|
||||
let lines = [];
|
||||
while ((match = reLine.exec(topStack))) {
|
||||
let line = match[0];
|
||||
if (line.indexOf("/Task.jsm:") != -1) {
|
||||
break;
|
||||
}
|
||||
lines.push(line);
|
||||
}
|
||||
|
||||
// Cut `bottomStack` at the last line of the first block that contains Task.jsm
|
||||
reLine = /([^\r\n])+/g;
|
||||
while ((match = reLine.exec(bottomStack))) {
|
||||
let line = match[0];
|
||||
if (line.indexOf("/Task.jsm:") == -1) {
|
||||
let tail = bottomStack.substring(match.index);
|
||||
lines.push(tail);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
stack = lines.join("\n");
|
||||
|
||||
aException.stack = stack;
|
||||
|
||||
// If aException is reinjected in the same task and rethrown,
|
||||
// we don't want to perform the rewrite again.
|
||||
aException._capturedTaskStack = bottomStack;
|
||||
} else if (!stack) {
|
||||
stack = "Not available";
|
||||
}
|
||||
|
||||
if ("name" in aException &&
|
||||
ERRORS_TO_REPORT.indexOf(aException.name) != -1) {
|
||||
|
||||
// We suspect that the exception is a programmer error, so we now
|
||||
@ -355,14 +405,18 @@ TaskImpl.prototype = {
|
||||
// users to see it. Also, if the programmer handles errors correctly,
|
||||
// they will either treat the error or log them somewhere.
|
||||
|
||||
let stack = ("stack" in aException) ? aException.stack : "not available";
|
||||
dump("*************************\n");
|
||||
dump("A coding exception was thrown and uncaught in a Task.\n\n");
|
||||
dump("Full message: " + aException + "\n");
|
||||
dump("Full stack: " + stack + "\n");
|
||||
dump("Full stack: " + aException.stack + "\n");
|
||||
dump("*************************\n");
|
||||
}
|
||||
}
|
||||
|
||||
this.deferred.reject(aException);
|
||||
}
|
||||
};
|
||||
|
||||
Task.Debugging = {
|
||||
maintainStack: false
|
||||
};
|
||||
|
@ -385,3 +385,216 @@ add_test(function test_async_throw_on_function_in_place_of_promise()
|
||||
do_throw("Unexpected error: " + ex);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
////////////////// Test rewriting of stack traces
|
||||
|
||||
// Backup Task.Debuggin.maintainStack.
|
||||
// Will be restored by `exit_stack_tests`.
|
||||
let maintainStack;
|
||||
add_test(function enter_stack_tests() {
|
||||
maintainStack = Task.Debugging.maintainStack;
|
||||
Task.Debugging.maintainStack = true;
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Ensure that a list of frames appear in a stack, in the right order
|
||||
*/
|
||||
function do_check_rewritten_stack(frames, ex) {
|
||||
do_print("Checking that the expected frames appear in the right order");
|
||||
do_print(frames.join(", "));
|
||||
let stack = ex.stack;
|
||||
do_print(stack);
|
||||
|
||||
let framesFound = 0;
|
||||
let lineNumber = 0;
|
||||
let reLine = /([^\r\n])+/g;
|
||||
let match;
|
||||
while (framesFound < frames.length && (match = reLine.exec(stack))) {
|
||||
let line = match[0];
|
||||
let frame = frames[framesFound];
|
||||
do_print("Searching for " + frame + " in line " + line);
|
||||
if (line.indexOf(frame) != -1) {
|
||||
do_print("Found " + frame);
|
||||
++framesFound;
|
||||
} else {
|
||||
do_print("Didn't find " + frame);
|
||||
}
|
||||
}
|
||||
|
||||
if (framesFound >= frames.length) {
|
||||
return;
|
||||
}
|
||||
do_throw("Did not find: " + frames.slice(framesFound).join(", ") +
|
||||
" in " + stack.substr(reLine.lastIndex));
|
||||
|
||||
do_print("Ensuring that we have removed Task.jsm, Promise.jsm");
|
||||
do_check_true(stack.indexOf("Task.jsm") == -1);
|
||||
do_check_true(stack.indexOf("Promise.jsm") == -1);
|
||||
do_check_true(stack.indexOf("Promise-backend.js") == -1);
|
||||
}
|
||||
|
||||
|
||||
// Test that we get an acceptable rewritten stack when we launch
|
||||
// an error in a Task.spawn.
|
||||
add_test(function test_spawn_throw_stack() {
|
||||
Task.spawn(function* task_spawn_throw_stack() {
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
yield Promise.resolve(); // Without stack rewrite, this would lose valuable information
|
||||
}
|
||||
throw new Error("BOOM");
|
||||
}).then(do_throw, function(ex) {
|
||||
do_check_rewritten_stack(["task_spawn_throw_stack",
|
||||
"test_spawn_throw_stack"],
|
||||
ex);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
// Test that we get an acceptable rewritten stack when we yield
|
||||
// a rejection in a Task.spawn.
|
||||
add_test(function test_spawn_yield_reject_stack() {
|
||||
Task.spawn(function* task_spawn_yield_reject_stack() {
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
yield Promise.resolve(); // Without stack rewrite, this would lose valuable information
|
||||
}
|
||||
yield Promise.reject(new Error("BOOM"));
|
||||
}).then(do_throw, function(ex) {
|
||||
do_check_rewritten_stack(["task_spawn_yield_reject_stack",
|
||||
"test_spawn_yield_reject_stack"],
|
||||
ex);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
// Test that we get an acceptable rewritten stack when we launch
|
||||
// an error in a Task.async function.
|
||||
add_test(function test_async_function_throw_stack() {
|
||||
let task_async_function_throw_stack = Task.async(function*() {
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
yield Promise.resolve(); // Without stack rewrite, this would lose valuable information
|
||||
}
|
||||
throw new Error("BOOM");
|
||||
})().then(do_throw, function(ex) {
|
||||
do_check_rewritten_stack(["task_async_function_throw_stack",
|
||||
"test_async_function_throw_stack"],
|
||||
ex);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
// Test that we get an acceptable rewritten stack when we launch
|
||||
// an error in a Task.async function.
|
||||
add_test(function test_async_function_yield_reject_stack() {
|
||||
let task_async_function_yield_reject_stack = Task.async(function*() {
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
yield Promise.resolve(); // Without stack rewrite, this would lose valuable information
|
||||
}
|
||||
yield Promise.reject(new Error("BOOM"));
|
||||
})().then(do_throw, function(ex) {
|
||||
do_check_rewritten_stack(["task_async_function_yield_reject_stack",
|
||||
"test_async_function_yield_reject_stack"],
|
||||
ex);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
// Test that we get an acceptable rewritten stack when we launch
|
||||
// an error in a Task.async function.
|
||||
add_test(function test_async_method_throw_stack() {
|
||||
let object = {
|
||||
task_async_method_throw_stack: Task.async(function*() {
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
yield Promise.resolve(); // Without stack rewrite, this would lose valuable information
|
||||
}
|
||||
throw new Error("BOOM");
|
||||
})
|
||||
};
|
||||
object.task_async_method_throw_stack().then(do_throw, function(ex) {
|
||||
do_check_rewritten_stack(["task_async_method_throw_stack",
|
||||
"test_async_method_throw_stack"],
|
||||
ex);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
// Test that we get an acceptable rewritten stack when we launch
|
||||
// an error in a Task.async function.
|
||||
add_test(function test_async_method_yield_reject_stack() {
|
||||
let object = {
|
||||
task_async_method_yield_reject_stack: Task.async(function*() {
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
yield Promise.resolve(); // Without stack rewrite, this would lose valuable information
|
||||
}
|
||||
yield Promise.reject(new Error("BOOM"));
|
||||
})
|
||||
};
|
||||
object.task_async_method_yield_reject_stack().then(do_throw, function(ex) {
|
||||
do_check_rewritten_stack(["task_async_method_yield_reject_stack",
|
||||
"test_async_method_yield_reject_stack"],
|
||||
ex);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
// Put things together
|
||||
add_test(function test_throw_complex_stack()
|
||||
{
|
||||
// Setup the following stack:
|
||||
// inner_method()
|
||||
// task_3()
|
||||
// task_2()
|
||||
// task_1()
|
||||
// function_3()
|
||||
// function_2()
|
||||
// function_1()
|
||||
// test_throw_complex_stack()
|
||||
(function function_1() {
|
||||
return (function function_2() {
|
||||
return (function function_3() {
|
||||
return Task.spawn(function* task_1() {
|
||||
yield Promise.resolve();
|
||||
try {
|
||||
yield Task.spawn(function* task_2() {
|
||||
yield Promise.resolve();
|
||||
yield Task.spawn(function* task_3() {
|
||||
yield Promise.resolve();
|
||||
let inner_object = {
|
||||
inner_method: Task.async(function*() {
|
||||
throw new Error("BOOM");
|
||||
})
|
||||
};
|
||||
yield Promise.resolve();
|
||||
yield inner_object.inner_method();
|
||||
});
|
||||
});
|
||||
} catch (ex) {
|
||||
yield Promise.resolve();
|
||||
throw ex;
|
||||
}
|
||||
});
|
||||
})();
|
||||
})();
|
||||
})().then(
|
||||
() => do_throw("Shouldn't have succeeded"),
|
||||
(ex) => {
|
||||
let expect = ["inner_method",
|
||||
"task_3",
|
||||
"task_2",
|
||||
"task_1",
|
||||
"function_3",
|
||||
"function_2",
|
||||
"function_1",
|
||||
"test_throw_complex_stack"];
|
||||
do_check_rewritten_stack(expect, ex);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function exit_stack_tests() {
|
||||
Task.Debugging.maintainStack = false;
|
||||
run_next_test();
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user