AsyncWebAssemblyJavascriptGenerator.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  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("./AsyncWebAssemblyModulesPlugin").AsyncWasmModuleClass} AsyncWasmModule */
  15. /** @typedef {import("../Generator").GenerateContext} GenerateContext */
  16. /** @typedef {import("../Module")} Module */
  17. /** @typedef {import("../Module").SourceType} SourceType */
  18. /** @typedef {import("../Module").SourceTypes} SourceTypes */
  19. /** @typedef {import("../NormalModule")} NormalModule */
  20. /**
  21. * Represents the async web assembly javascript generator runtime component.
  22. * @typedef {{ request: string, importVar: string, dependency: WebAssemblyImportDependency }} ImportObjRequestItem
  23. */
  24. class AsyncWebAssemblyJavascriptGenerator extends Generator {
  25. /**
  26. * Returns the source types available for this module.
  27. * @param {NormalModule} module fresh module
  28. * @returns {SourceTypes} available types (do not mutate)
  29. */
  30. getTypes(module) {
  31. return WEBASSEMBLY_TYPES;
  32. }
  33. /**
  34. * Returns the estimated size for the requested source type.
  35. * @param {NormalModule} module the module
  36. * @param {SourceType=} type source type
  37. * @returns {number} estimate size of the module
  38. */
  39. getSize(module, type) {
  40. // it's only estimated so this number is probably fine
  41. // Example: m.exports=s.v(e,_.id,"6db474f11db19c35388a")
  42. if (/** @type {AsyncWasmModule} */ (module).phase === "source") {
  43. return 44;
  44. }
  45. return 40 + module.dependencies.length * 10;
  46. }
  47. /**
  48. * Generates generated code for this runtime module.
  49. * @param {NormalModule} module module for which the code should be generated
  50. * @param {GenerateContext} generateContext context for generate
  51. * @returns {Source | null} generated code
  52. */
  53. generate(module, generateContext) {
  54. const {
  55. runtimeTemplate,
  56. chunkGraph,
  57. moduleGraph,
  58. runtimeRequirements,
  59. runtime
  60. } = generateContext;
  61. // Check if this is a source phase import
  62. if (/** @type {AsyncWasmModule} */ (module).phase === "source") {
  63. return this._generateSourcePhase(module, generateContext);
  64. }
  65. runtimeRequirements.add(RuntimeGlobals.module);
  66. runtimeRequirements.add(RuntimeGlobals.moduleId);
  67. runtimeRequirements.add(RuntimeGlobals.exports);
  68. runtimeRequirements.add(RuntimeGlobals.instantiateWasm);
  69. /** @type {InitFragment<GenerateContext>[]} */
  70. const initFragments = [];
  71. /** @type {Map<Module, ImportObjRequestItem>} */
  72. const depModules = new Map();
  73. /** @type {Map<string, WebAssemblyImportDependency[]>} */
  74. const wasmDepsByRequest = new Map();
  75. for (const dep of module.dependencies) {
  76. if (dep instanceof WebAssemblyImportDependency) {
  77. const module = /** @type {Module} */ (moduleGraph.getModule(dep));
  78. if (!depModules.has(module)) {
  79. depModules.set(module, {
  80. request: dep.request,
  81. importVar: `WEBPACK_IMPORTED_MODULE_${depModules.size}`,
  82. dependency: dep
  83. });
  84. }
  85. let list = wasmDepsByRequest.get(dep.request);
  86. if (list === undefined) {
  87. list = [];
  88. wasmDepsByRequest.set(dep.request, list);
  89. }
  90. list.push(dep);
  91. }
  92. }
  93. /** @type {string[]} */
  94. const promises = [];
  95. const importStatements = Array.from(
  96. depModules,
  97. ([importedModule, { request, importVar, dependency }]) => {
  98. if (moduleGraph.isAsync(importedModule)) {
  99. promises.push(importVar);
  100. }
  101. return runtimeTemplate.importStatement({
  102. update: false,
  103. module: importedModule,
  104. moduleGraph,
  105. chunkGraph,
  106. request,
  107. originModule: module,
  108. importVar,
  109. runtimeRequirements,
  110. dependency
  111. });
  112. }
  113. );
  114. const importsCode = importStatements.map(([x]) => x).join("");
  115. const importsCompatCode = importStatements.map(([_, x]) => x).join("");
  116. const importObjRequestItems = Array.from(
  117. wasmDepsByRequest,
  118. ([request, deps]) => {
  119. const exportItems = deps.map((dep) => {
  120. const importedModule =
  121. /** @type {Module} */
  122. (moduleGraph.getModule(dep));
  123. const importVar =
  124. /** @type {ImportObjRequestItem} */
  125. (depModules.get(importedModule)).importVar;
  126. return `${JSON.stringify(
  127. dep.name
  128. )}: ${runtimeTemplate.exportFromImport({
  129. moduleGraph,
  130. module: importedModule,
  131. chunkGraph,
  132. request,
  133. exportName: dep.name,
  134. originModule: module,
  135. asiSafe: true,
  136. isCall: false,
  137. callContext: false,
  138. defaultInterop: true,
  139. importVar,
  140. initFragments,
  141. runtime,
  142. runtimeRequirements,
  143. dependency: dep
  144. })}`;
  145. });
  146. return Template.asString([
  147. `${JSON.stringify(request)}: {`,
  148. Template.indent(exportItems.join(",\n")),
  149. "}"
  150. ]);
  151. }
  152. );
  153. const importsObj =
  154. importObjRequestItems.length > 0
  155. ? Template.asString([
  156. "{",
  157. Template.indent(importObjRequestItems.join(",\n")),
  158. "}"
  159. ])
  160. : undefined;
  161. const instantiateCall = `${RuntimeGlobals.instantiateWasm}(${module.exportsArgument}, ${
  162. module.moduleArgument
  163. }.id, ${JSON.stringify(
  164. chunkGraph.getRenderedModuleHash(module, runtime)
  165. )}${importsObj ? `, ${importsObj})` : ")"}`;
  166. if (promises.length > 0) {
  167. runtimeRequirements.add(RuntimeGlobals.asyncModule);
  168. }
  169. const source = new RawSource(
  170. promises.length > 0
  171. ? Template.asString([
  172. `var __webpack_instantiate__ = ${runtimeTemplate.basicFunction(
  173. `[${promises.join(", ")}]`,
  174. `${importsCompatCode}return ${instantiateCall};`
  175. )}`,
  176. `${RuntimeGlobals.asyncModule}(${
  177. module.moduleArgument
  178. }, async ${runtimeTemplate.basicFunction(
  179. "__webpack_handle_async_dependencies__, __webpack_async_result__",
  180. [
  181. "try {",
  182. importsCode,
  183. `var __webpack_async_dependencies__ = __webpack_handle_async_dependencies__([${promises.join(
  184. ", "
  185. )}]);`,
  186. `var [${promises.join(
  187. ", "
  188. )}] = __webpack_async_dependencies__.then ? (await __webpack_async_dependencies__)() : __webpack_async_dependencies__;`,
  189. `${importsCompatCode}await ${instantiateCall};`,
  190. "__webpack_async_result__();",
  191. "} catch(e) { __webpack_async_result__(e); }"
  192. ]
  193. )}, 1);`
  194. ])
  195. : `${importsCode}${importsCompatCode}module.exports = ${instantiateCall};`
  196. );
  197. return InitFragment.addToSource(source, initFragments, generateContext);
  198. }
  199. /**
  200. * Generate code for source phase import (returns WebAssembly.Module)
  201. * @param {NormalModule} module module for which the code should be generated
  202. * @param {GenerateContext} generateContext context for generate
  203. * @returns {Source} generated code
  204. */
  205. _generateSourcePhase(module, generateContext) {
  206. const { chunkGraph, runtimeTemplate, runtimeRequirements, runtime } =
  207. generateContext;
  208. runtimeRequirements.add(RuntimeGlobals.module);
  209. runtimeRequirements.add(RuntimeGlobals.moduleId);
  210. runtimeRequirements.add(RuntimeGlobals.exports);
  211. runtimeRequirements.add(RuntimeGlobals.compileWasm);
  212. runtimeRequirements.add(RuntimeGlobals.asyncModule);
  213. runtimeRequirements.add(RuntimeGlobals.definePropertyGetters);
  214. // Source phase: export default WebAssembly.Module (via compileWasm)
  215. const compileCall = `${RuntimeGlobals.compileWasm}(${
  216. module.moduleArgument
  217. }.id, ${JSON.stringify(chunkGraph.getRenderedModuleHash(module, runtime))})`;
  218. // Use async module wrapper to handle the Promise from compileWasm
  219. return new RawSource(
  220. Template.asString([
  221. `${RuntimeGlobals.asyncModule}(${
  222. module.moduleArgument
  223. }, async ${runtimeTemplate.basicFunction(
  224. "__webpack_handle_async_dependencies__, __webpack_async_result__",
  225. [
  226. "try {",
  227. `var __webpack_wasm_module__ = await ${compileCall};`,
  228. `${RuntimeGlobals.definePropertyGetters}(${module.exportsArgument}, { "default": ${runtimeTemplate.returningFunction("__webpack_wasm_module__")} });`,
  229. "__webpack_async_result__();",
  230. "} catch(e) { __webpack_async_result__(e); }"
  231. ]
  232. )}, 1);`
  233. ])
  234. );
  235. }
  236. /**
  237. * Generates fallback output for the provided error condition.
  238. * @param {Error} error the error
  239. * @param {NormalModule} module module for which the code should be generated
  240. * @param {GenerateContext} generateContext context for generate
  241. * @returns {Source | null} generated code
  242. */
  243. generateError(error, module, generateContext) {
  244. return new RawSource(`throw new Error(${JSON.stringify(error.message)});`);
  245. }
  246. }
  247. module.exports = AsyncWebAssemblyJavascriptGenerator;