mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 23:02:20 +00:00
Bug 1771428 - [devtools] Remove last statement AwaitExpression in mapTopLevelAwait. r=bomsy.
We use to transform expression like `await obj` into ``` (async () => { return await obj; })() `` So we were basically returning the expression, wrapped in an async iife, adding a `return` statement before the last statement. But in the case the last statement is an `AwaitExpression`, we can simply return the argument, stripping the `await` keyword. This fixes an issue when awaiting for an object with a `then` getter would execute the getter twice. A test case is added in mochitest, and unit test are updated to reflect the new output. Differential Revision: https://phabricator.services.mozilla.com/D148806
This commit is contained in:
parent
1489168d8e
commit
fe472aae2e
@ -2445,7 +2445,7 @@
|
||||
"byName": {},
|
||||
"byBlocks": {},
|
||||
"usedIds": {
|
||||
"1": 1
|
||||
"0": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2466,7 +2466,7 @@
|
||||
"byName": {},
|
||||
"byBlocks": {},
|
||||
"usedIds": {
|
||||
"1": 1
|
||||
"0": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
12
devtools/client/debugger/dist/parser-worker.js
vendored
12
devtools/client/debugger/dist/parser-worker.js
vendored
@ -47826,8 +47826,16 @@ function translateDeclarationIntoAssignment(node) {
|
||||
|
||||
function addReturnNode(ast) {
|
||||
const statements = ast.program.body;
|
||||
const lastStatement = statements[statements.length - 1];
|
||||
return statements.slice(0, -1).concat(t.returnStatement(lastStatement.expression));
|
||||
const lastStatement = statements.pop(); // if the last expression is an awaitExpression, strip the `await` part and directly
|
||||
// return the argument to avoid calling the argument's `then` function twice when the
|
||||
// mapped expression gets evaluated (See Bug 1771428)
|
||||
|
||||
if (t.isAwaitExpression(lastStatement.expression)) {
|
||||
lastStatement.expression = lastStatement.expression.argument;
|
||||
}
|
||||
|
||||
statements.push(t.returnStatement(lastStatement.expression));
|
||||
return statements;
|
||||
}
|
||||
|
||||
function getDeclarations(node) {
|
||||
|
@ -39,10 +39,16 @@ function translateDeclarationIntoAssignment(node) {
|
||||
*/
|
||||
function addReturnNode(ast) {
|
||||
const statements = ast.program.body;
|
||||
const lastStatement = statements[statements.length - 1];
|
||||
return statements
|
||||
.slice(0, -1)
|
||||
.concat(t.returnStatement(lastStatement.expression));
|
||||
const lastStatement = statements.pop();
|
||||
|
||||
// if the last expression is an awaitExpression, strip the `await` part and directly
|
||||
// return the argument to avoid calling the argument's `then` function twice when the
|
||||
// mapped expression gets evaluated (See Bug 1771428)
|
||||
if (t.isAwaitExpression(lastStatement.expression)) {
|
||||
lastStatement.expression = lastStatement.expression.argument;
|
||||
}
|
||||
statements.push(t.returnStatement(lastStatement.expression));
|
||||
return statements;
|
||||
}
|
||||
|
||||
function getDeclarations(node) {
|
||||
|
@ -39,7 +39,7 @@ describe("mapExpression", () => {
|
||||
{
|
||||
name: "await",
|
||||
expression: "await a()",
|
||||
newExpression: formatAwait("return await a()"),
|
||||
newExpression: formatAwait("return a()"),
|
||||
bindings: [],
|
||||
mappings: {},
|
||||
shouldMapBindings: true,
|
||||
@ -78,7 +78,7 @@ describe("mapExpression", () => {
|
||||
{
|
||||
name: "await (multiple awaits)",
|
||||
expression: "const x = await a(); await b(x)",
|
||||
newExpression: formatAwait("self.x = await a(); return await b(x);"),
|
||||
newExpression: formatAwait("self.x = await a(); return b(x);"),
|
||||
bindings: [],
|
||||
mappings: {},
|
||||
shouldMapBindings: true,
|
||||
|
@ -64,4 +64,21 @@ add_task(async function() {
|
||||
`Promise {`
|
||||
);
|
||||
ok(message, "Promise are displayed as expected");
|
||||
|
||||
info("Check that then getters aren't called twice");
|
||||
message = await executeAndWaitForResultMessage(
|
||||
hud,
|
||||
// It's important to keep the last statement of the expression as it covers the original issue.
|
||||
// We could execute another expression to get `object.called`, but since we get a preview
|
||||
// of the object with an accurate `called` value, this is enough.
|
||||
`
|
||||
var obj = {
|
||||
called: 0,
|
||||
get then(){
|
||||
this.called++
|
||||
}
|
||||
};
|
||||
await obj`,
|
||||
`Object { called: 1, then: Getter }`
|
||||
);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user