AsyncWebAssemblyJavascriptGenerator.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { RawSource } = require("webpack-sources");
  7. const Generator = require("../Generator");
  8. const InitFragment = require("../InitFragment");
  9. const { WEBASSEMBLY_TYPES } = require("../ModuleSourceTypeConstants");
  10. const RuntimeGlobals = require("../RuntimeGlobals");
  11. const Template = require("../Template");
  12. const WebAssemblyImportDependency = require("../dependencies/WebAssemblyImportDependency");
  13. /** @typedef {import("webpack-sources").Source} Source */
  14. /** @typedef {import("../Generator").GenerateContext} GenerateContext */
  15. /** @typedef {import("../Module")} Module */
  16. /** @typedef {import("../Module").SourceType} SourceType */
  17. /** @typedef {import("../Module").SourceTypes} SourceTypes */
  18. /** @typedef {import("../NormalModule")} NormalModule */
  19. /**
  20. * @typedef {{ request: string, importVar: string, dependency: WebAssemblyImportDependency }} ImportObjRequestItem
  21. */
  22. class AsyncWebAssemblyJavascriptGenerator extends Generator {
  23. /**
  24. * @param {NormalModule} module fresh module
  25. * @returns {SourceTypes} available types (do not mutate)
  26. */
  27. getTypes(module) {
  28. return WEBASSEMBLY_TYPES;
  29. }
  30. /**
  31. * @param {NormalModule} module the module
  32. * @param {SourceType=} type source type
  33. * @returns {number} estimate size of the module
  34. */
  35. getSize(module, type) {
  36. return 40 + module.dependencies.length * 10;
  37. }
  38. /**
  39. * @param {NormalModule} module module for which the code should be generated
  40. * @param {GenerateContext} generateContext context for generate
  41. * @returns {Source | null} generated code
  42. */
  43. generate(module, generateContext) {
  44. const {
  45. runtimeTemplate,
  46. chunkGraph,
  47. moduleGraph,
  48. runtimeRequirements,
  49. runtime
  50. } = generateContext;
  51. runtimeRequirements.add(RuntimeGlobals.module);
  52. runtimeRequirements.add(RuntimeGlobals.moduleId);
  53. runtimeRequirements.add(RuntimeGlobals.exports);
  54. runtimeRequirements.add(RuntimeGlobals.instantiateWasm);
  55. /** @type {InitFragment<GenerateContext>[]} */
  56. const initFragments = [];
  57. /** @type {Map<Module, ImportObjRequestItem>} */
  58. const depModules = new Map();
  59. /** @type {Map<string, WebAssemblyImportDependency[]>} */
  60. const wasmDepsByRequest = new Map();
  61. for (const dep of module.dependencies) {
  62. if (dep instanceof WebAssemblyImportDependency) {
  63. const module = /** @type {Module} */ (moduleGraph.getModule(dep));
  64. if (!depModules.has(module)) {
  65. depModules.set(module, {
  66. request: dep.request,
  67. importVar: `WEBPACK_IMPORTED_MODULE_${depModules.size}`,
  68. dependency: dep
  69. });
  70. }
  71. let list = wasmDepsByRequest.get(dep.request);
  72. if (list === undefined) {
  73. list = [];
  74. wasmDepsByRequest.set(dep.request, list);
  75. }
  76. list.push(dep);
  77. }
  78. }
  79. /** @type {string[]} */
  80. const promises = [];
  81. const importStatements = Array.from(
  82. depModules,
  83. ([importedModule, { request, importVar, dependency }]) => {
  84. if (moduleGraph.isAsync(importedModule)) {
  85. promises.push(importVar);
  86. }
  87. return runtimeTemplate.importStatement({
  88. update: false,
  89. module: importedModule,
  90. moduleGraph,
  91. chunkGraph,
  92. request,
  93. originModule: module,
  94. importVar,
  95. runtimeRequirements,
  96. dependency
  97. });
  98. }
  99. );
  100. const importsCode = importStatements.map(([x]) => x).join("");
  101. const importsCompatCode = importStatements.map(([_, x]) => x).join("");
  102. const importObjRequestItems = Array.from(
  103. wasmDepsByRequest,
  104. ([request, deps]) => {
  105. const exportItems = deps.map((dep) => {
  106. const importedModule =
  107. /** @type {Module} */
  108. (moduleGraph.getModule(dep));
  109. const importVar =
  110. /** @type {ImportObjRequestItem} */
  111. (depModules.get(importedModule)).importVar;
  112. return `${JSON.stringify(
  113. dep.name
  114. )}: ${runtimeTemplate.exportFromImport({
  115. moduleGraph,
  116. module: importedModule,
  117. chunkGraph,
  118. request,
  119. exportName: dep.name,
  120. originModule: module,
  121. asiSafe: true,
  122. isCall: false,
  123. callContext: false,
  124. defaultInterop: true,
  125. importVar,
  126. initFragments,
  127. runtime,
  128. runtimeRequirements,
  129. dependency: dep
  130. })}`;
  131. });
  132. return Template.asString([
  133. `${JSON.stringify(request)}: {`,
  134. Template.indent(exportItems.join(",\n")),
  135. "}"
  136. ]);
  137. }
  138. );
  139. const importsObj =
  140. importObjRequestItems.length > 0
  141. ? Template.asString([
  142. "{",
  143. Template.indent(importObjRequestItems.join(",\n")),
  144. "}"
  145. ])
  146. : undefined;
  147. const instantiateCall = `${RuntimeGlobals.instantiateWasm}(${module.exportsArgument}, ${
  148. module.moduleArgument
  149. }.id, ${JSON.stringify(
  150. chunkGraph.getRenderedModuleHash(module, runtime)
  151. )}${importsObj ? `, ${importsObj})` : ")"}`;
  152. if (promises.length > 0) {
  153. runtimeRequirements.add(RuntimeGlobals.asyncModule);
  154. }
  155. const source = new RawSource(
  156. promises.length > 0
  157. ? Template.asString([
  158. `var __webpack_instantiate__ = ${runtimeTemplate.basicFunction(
  159. `[${promises.join(", ")}]`,
  160. `${importsCompatCode}return ${instantiateCall};`
  161. )}`,
  162. `${RuntimeGlobals.asyncModule}(${
  163. module.moduleArgument
  164. }, async ${runtimeTemplate.basicFunction(
  165. "__webpack_handle_async_dependencies__, __webpack_async_result__",
  166. [
  167. "try {",
  168. importsCode,
  169. `var __webpack_async_dependencies__ = __webpack_handle_async_dependencies__([${promises.join(
  170. ", "
  171. )}]);`,
  172. `var [${promises.join(
  173. ", "
  174. )}] = __webpack_async_dependencies__.then ? (await __webpack_async_dependencies__)() : __webpack_async_dependencies__;`,
  175. `${importsCompatCode}await ${instantiateCall};`,
  176. "__webpack_async_result__();",
  177. "} catch(e) { __webpack_async_result__(e); }"
  178. ]
  179. )}, 1);`
  180. ])
  181. : `${importsCode}${importsCompatCode}module.exports = ${instantiateCall};`
  182. );
  183. return InitFragment.addToSource(source, initFragments, generateContext);
  184. }
  185. /**
  186. * @param {Error} error the error
  187. * @param {NormalModule} module module for which the code should be generated
  188. * @param {GenerateContext} generateContext context for generate
  189. * @returns {Source | null} generated code
  190. */
  191. generateError(error, module, generateContext) {
  192. return new RawSource(`throw new Error(${JSON.stringify(error.message)});`);
  193. }
  194. }
  195. module.exports = AsyncWebAssemblyJavascriptGenerator;