Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix handling of errors thrown in async modules #15266

Merged
merged 1 commit into from Jan 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 4 additions & 4 deletions lib/async-modules/AwaitDependenciesInitFragment.js
Expand Up @@ -30,8 +30,8 @@ class AwaitDependenciesInitFragment extends InitFragment {
}

merge(other) {
const promises = new Set(this.promises);
for (const p of other.promises) {
const promises = new Set(other.promises);
for (const p of this.promises) {
promises.add(p);
}
return new AwaitDependenciesInitFragment(promises);
Expand All @@ -51,7 +51,7 @@ class AwaitDependenciesInitFragment extends InitFragment {
for (const p of promises) {
return Template.asString([
`var __webpack_async_dependencies__ = __webpack_handle_async_dependencies__([${p}]);`,
`${p} = (__webpack_async_dependencies__.then ? await __webpack_async_dependencies__ : __webpack_async_dependencies__)[0];`,
`${p} = (__webpack_async_dependencies__.then ? (await __webpack_async_dependencies__)() : __webpack_async_dependencies__)[0];`,
""
]);
}
Expand All @@ -60,7 +60,7 @@ class AwaitDependenciesInitFragment extends InitFragment {
// TODO check if destructuring is supported
return Template.asString([
`var __webpack_async_dependencies__ = __webpack_handle_async_dependencies__([${sepPromises}]);`,
`([${sepPromises}] = __webpack_async_dependencies__.then ? await __webpack_async_dependencies__ : __webpack_async_dependencies__);`,
`([${sepPromises}] = __webpack_async_dependencies__.then ? (await __webpack_async_dependencies__)() : __webpack_async_dependencies__);`,
""
]);
}
Expand Down
10 changes: 5 additions & 5 deletions lib/dependencies/HarmonyCompatibilityDependency.js
Expand Up @@ -74,14 +74,14 @@ HarmonyCompatibilityDependency.Template = class HarmonyExportDependencyTemplate
initFragments.push(
new InitFragment(
runtimeTemplate.supportsArrowFunction()
? `${RuntimeGlobals.asyncModule}(${module.moduleArgument}, async (__webpack_handle_async_dependencies__) => {\n`
: `${RuntimeGlobals.asyncModule}(${module.moduleArgument}, async function (__webpack_handle_async_dependencies__) {\n`,
? `${RuntimeGlobals.asyncModule}(${module.moduleArgument}, async (__webpack_handle_async_dependencies__, __webpack_async_result__) => { try {\n`
: `${RuntimeGlobals.asyncModule}(${module.moduleArgument}, async function (__webpack_handle_async_dependencies__, __webpack_async_result__) { try {\n`,
InitFragment.STAGE_ASYNC_BOUNDARY,
0,
undefined,
module.buildMeta.async
? `\n__webpack_handle_async_dependencies__();\n}, 1);`
: "\n});"
`\n__webpack_async_result__();\n} catch(e) { __webpack_async_result__(e); } }${
module.buildMeta.async ? ", 1" : ""
});`
)
);
}
Expand Down
40 changes: 25 additions & 15 deletions lib/runtime/AsyncModuleRuntimeModule.js
Expand Up @@ -22,6 +22,7 @@ class AsyncModuleRuntimeModule extends HelperRuntimeModule {
return Template.asString([
'var webpackThen = typeof Symbol === "function" ? Symbol("webpack then") : "__webpack_then__";',
'var webpackExports = typeof Symbol === "function" ? Symbol("webpack exports") : "__webpack_exports__";',
'var webpackError = typeof Symbol === "function" ? Symbol("webpack error") : "__webpack_error__";',
`var completeQueue = ${runtimeTemplate.basicFunction("queue", [
"if(queue) {",
Template.indent([
Expand Down Expand Up @@ -56,9 +57,13 @@ class AsyncModuleRuntimeModule extends HelperRuntimeModule {
"obj[webpackExports] = r;",
"completeQueue(queue);",
"queue = 0;"
])}, ${runtimeTemplate.basicFunction("e", [
"obj[webpackError] = e;",
"completeQueue(queue);",
"queue = 0;"
])});`,
`var obj = {};
obj[webpackThen] = ${runtimeTemplate.expressionFunction(
"var obj = {};",
`obj[webpackThen] = ${runtimeTemplate.expressionFunction(
"queueFunction(queue, fn), dep['catch'](reject)",
"fn, reject"
)};`,
Expand All @@ -67,13 +72,13 @@ class AsyncModuleRuntimeModule extends HelperRuntimeModule {
"}"
]),
"}",
`var ret = {};
ret[webpackThen] = ${runtimeTemplate.expressionFunction(
"var ret = {};",
`ret[webpackThen] = ${runtimeTemplate.expressionFunction(
"completeFunction(fn)",
"fn"
)};
ret[webpackExports] = dep;
return ret;`
)};`,
"ret[webpackExports] = dep;",
"return ret;"
])})`,
"deps"
)};`,
Expand Down Expand Up @@ -119,24 +124,29 @@ class AsyncModuleRuntimeModule extends HelperRuntimeModule {
)};`,
"module.exports = promise;",
`body(${runtimeTemplate.basicFunction("deps", [
"if(!deps) return outerResolve();",
"currentDeps = wrapDeps(deps);",
"var fn, result;",
"var fn;",
`var getResult = ${runtimeTemplate.returningFunction(
`currentDeps.map(${runtimeTemplate.basicFunction("d", [
"if(d[webpackError]) throw d[webpackError];",
"return d[webpackExports];"
])})`
)}`,
`var promise = new Promise(${runtimeTemplate.basicFunction(
"resolve, reject",
[
`fn = ${runtimeTemplate.expressionFunction(
`resolve(result = currentDeps.map(${runtimeTemplate.returningFunction(
"d[webpackExports]",
"d"
)}))`
"resolve(getResult)"
)};`,
"fn.r = 0;",
"whenAll(currentDeps, fn, reject);"
]
)});`,
"return fn.r ? promise : result;"
])}).then(outerResolve, reject);`,
"return fn.r ? promise : getResult();"
])}, ${runtimeTemplate.expressionFunction(
"err && reject(promise[webpackError] = err), outerResolve()",
"err"
)});`,
"isEvaluating = false;"
])};`
]);
Expand Down
12 changes: 9 additions & 3 deletions lib/wasm-async/AsyncWebAssemblyJavascriptGenerator.js
Expand Up @@ -167,14 +167,20 @@ class AsyncWebAssemblyJavascriptGenerator extends Generator {
)}`,
`${RuntimeGlobals.asyncModule}(${
module.moduleArgument
}, ${runtimeTemplate.basicFunction(
"__webpack_handle_async_dependencies__",
}, async ${runtimeTemplate.basicFunction(
"__webpack_handle_async_dependencies__, __webpack_async_result__",
[
"try {",
importsCode,
`var __webpack_async_dependencies__ = __webpack_handle_async_dependencies__([${promises.join(
", "
)}]);`,
"return __webpack_async_dependencies__.then ? __webpack_async_dependencies__.then(__webpack_instantiate__) : __webpack_instantiate__(__webpack_async_dependencies__);"
`var [${promises.join(
", "
)}] = __webpack_async_dependencies__.then ? (await __webpack_async_dependencies__)() : __webpack_async_dependencies__;`,
`${importsCompatCode}await ${instantiateCall};`,
"__webpack_async_result__();",
"} catch(e) { __webpack_async_result__(e); }"
]
)}, 1);`
])
Expand Down
12 changes: 6 additions & 6 deletions test/__snapshots__/StatsTestCases.basictest.js.snap
Expand Up @@ -4554,9 +4554,9 @@ webpack x.x.x compiled with 1 warning in X ms"
`;

exports[`StatsTestCases should print correct stats for wasm-explorer-examples-sync 1`] = `
"assets by path *.js 21.8 KiB
asset bundle.js 16.4 KiB [emitted] (name: main)
asset 325.bundle.js 3.79 KiB [emitted]
"assets by path *.js 22.2 KiB
asset bundle.js 16.7 KiB [emitted] (name: main)
asset 325.bundle.js 3.9 KiB [emitted]
asset 795.bundle.js 557 bytes [emitted]
asset 526.bundle.js 366 bytes [emitted] (id hint: vendors)
asset 189.bundle.js 243 bytes [emitted]
Expand All @@ -4571,8 +4571,8 @@ assets by path *.wasm 1.37 KiB
asset 0301cb3f9f4151b567f5.module.wasm 120 bytes [emitted] [immutable]
chunk (runtime: main) 20.bundle.js 50 bytes (javascript) 531 bytes (webassembly) [rendered]
./duff.wasm 50 bytes (javascript) 531 bytes (webassembly) [built] [code generated]
chunk (runtime: main) bundle.js (main) 586 bytes (javascript) 9.25 KiB (runtime) [entry] [rendered]
runtime modules 9.25 KiB 11 modules
chunk (runtime: main) bundle.js (main) 586 bytes (javascript) 9.49 KiB (runtime) [entry] [rendered]
runtime modules 9.49 KiB 11 modules
./index.js 586 bytes [built] [code generated]
chunk (runtime: main) 189.bundle.js 50 bytes (javascript) 156 bytes (webassembly) [rendered]
./Q_rsqrt.wasm 50 bytes (javascript) 156 bytes (webassembly) [built] [code generated]
Expand All @@ -4586,7 +4586,7 @@ chunk (runtime: main) 526.bundle.js (id hint: vendors) 34 bytes [rendered] split
chunk (runtime: main) 795.bundle.js 110 bytes (javascript) 444 bytes (webassembly) [rendered]
./fact.wasm 50 bytes (javascript) 154 bytes (webassembly) [built] [code generated]
./fast-math.wasm 60 bytes (javascript) 290 bytes (webassembly) [built] [code generated]
runtime modules 9.25 KiB 11 modules
runtime modules 9.49 KiB 11 modules
cacheable modules 2.31 KiB (javascript) 1.37 KiB (webassembly)
webassembly modules 310 bytes (javascript) 1.37 KiB (webassembly)
./Q_rsqrt.wasm 50 bytes (javascript) 156 bytes (webassembly) [built] [code generated]
Expand Down
3 changes: 3 additions & 0 deletions test/cases/async-modules/double-import/a.js
@@ -0,0 +1,3 @@
import x from "./shared";

export default x + " world";
3 changes: 3 additions & 0 deletions test/cases/async-modules/double-import/b.js
@@ -0,0 +1,3 @@
import x from "./shared";

export default x + " world";
4 changes: 4 additions & 0 deletions test/cases/async-modules/double-import/index.js
@@ -0,0 +1,4 @@
it("should allow to import an async module twice", async () => {
const result = await require("./main");
expect(result.default).toBe("hello world, hello world");
});
4 changes: 4 additions & 0 deletions test/cases/async-modules/double-import/main.js
@@ -0,0 +1,4 @@
import a from "./a";
import b from "./b";

export default a + ", " + b;
3 changes: 3 additions & 0 deletions test/cases/async-modules/double-import/shared.js
@@ -0,0 +1,3 @@
await 1;
await 1;
export default "hello";
6 changes: 6 additions & 0 deletions test/cases/async-modules/top-level-error/counter.js
@@ -0,0 +1,6 @@
await 1;
let value = 0;
export const count = () => {
value++;
return value;
};
39 changes: 39 additions & 0 deletions test/cases/async-modules/top-level-error/index.js
@@ -0,0 +1,39 @@
it("should allow to import an rejected async module again", async () => {
await expect(require("./main")).rejects.toEqual(
expect.objectContaining({
message: expect.stringContaining("expected rejection 1")
})
);
await expect(require("./module")).rejects.toEqual(
expect.objectContaining({
message: expect.stringContaining("expected rejection 1")
})
);
await expect(require("./module?2")).rejects.toEqual(
expect.objectContaining({
message: expect.stringContaining("expected rejection 2")
})
);
await expect(require("./reexport?2")).rejects.toEqual(
expect.objectContaining({
message: expect.stringContaining("expected rejection 1")
})
);
await Promise.all([
expect(require("./module?3")).rejects.toEqual(
expect.objectContaining({
message: expect.stringContaining("expected rejection 3")
})
),
expect(require("./module?4")).rejects.toEqual(
expect.objectContaining({
message: expect.stringContaining("expected rejection 4")
})
),
expect(require("./module?5")).rejects.toEqual(
expect.objectContaining({
message: expect.stringContaining("expected rejection 5")
})
)
]);
});
2 changes: 2 additions & 0 deletions test/cases/async-modules/top-level-error/main.js
@@ -0,0 +1,2 @@
export { default as a } from "./reexport";
export { default as b } from "./module?2";
6 changes: 6 additions & 0 deletions test/cases/async-modules/top-level-error/module.js
@@ -0,0 +1,6 @@
import { count } from "./counter";

const c = count();
throw new Error("expected rejection " + c);

export default "ok";
1 change: 1 addition & 0 deletions test/cases/async-modules/top-level-error/reexport.js
@@ -0,0 +1 @@
export { default as default } from "./module";