WebAssemblyJavascriptGenerator.js 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  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 { UsageState } = require("../ExportsInfo");
  8. const Generator = require("../Generator");
  9. const InitFragment = require("../InitFragment");
  10. const { WEBASSEMBLY_TYPES } = require("../ModuleSourceTypeConstants");
  11. const RuntimeGlobals = require("../RuntimeGlobals");
  12. const Template = require("../Template");
  13. const ModuleDependency = require("../dependencies/ModuleDependency");
  14. const WebAssemblyExportImportedDependency = require("../dependencies/WebAssemblyExportImportedDependency");
  15. const WebAssemblyImportDependency = require("../dependencies/WebAssemblyImportDependency");
  16. /** @import { Source } from "webpack-sources" */
  17. /** @import { GenerateContext } from "../Generator" */
  18. /** @import Module, { SourceType, SourceTypes } from "../Module" */
  19. /** @import NormalModule from "../NormalModule" */
  20. class WebAssemblyJavascriptGenerator extends Generator {
  21. /**
  22. * Returns the source types available for this module.
  23. * @param {NormalModule} module fresh module
  24. * @returns {SourceTypes} available types (do not mutate)
  25. */
  26. getTypes(module) {
  27. return WEBASSEMBLY_TYPES;
  28. }
  29. /**
  30. * Returns the estimated size for the requested source type.
  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 95 + module.dependencies.length * 5;
  37. }
  38. /**
  39. * Generates generated code for this runtime module.
  40. * @param {NormalModule} module module for which the code should be generated
  41. * @param {GenerateContext} generateContext context for generate
  42. * @returns {Source | null} generated code
  43. */
  44. generate(module, generateContext) {
  45. const {
  46. runtimeTemplate,
  47. moduleGraph,
  48. chunkGraph,
  49. runtimeRequirements,
  50. runtime
  51. } = generateContext;
  52. /** @type {InitFragment<GenerateContext>[]} */
  53. const initFragments = [];
  54. const exportsInfo = moduleGraph.getExportsInfo(module);
  55. let needExportsCopy = false;
  56. /** @typedef {{ dependency: ModuleDependency | undefined, importVar: string, index: number, request: string | undefined, names: Set<string>, reexports: string[] }} ImportData */
  57. /** @type {Map<Module, ImportData>} */
  58. const importedModules = new Map();
  59. /** @type {string[]} */
  60. const initParams = [];
  61. let index = 0;
  62. for (const dep of module.dependencies) {
  63. const moduleDep =
  64. dep && dep instanceof ModuleDependency ? dep : undefined;
  65. const mod = moduleGraph.getModule(dep);
  66. if (mod) {
  67. let importData = importedModules.get(mod);
  68. if (importData === undefined) {
  69. importedModules.set(
  70. mod,
  71. (importData = {
  72. dependency: moduleDep,
  73. importVar: `m${index}`,
  74. index,
  75. request: (moduleDep && moduleDep.userRequest) || undefined,
  76. names: new Set(),
  77. reexports: []
  78. })
  79. );
  80. index++;
  81. }
  82. if (dep instanceof WebAssemblyImportDependency) {
  83. importData.names.add(dep.name);
  84. if (dep.description.type === "GlobalType") {
  85. const exportName = dep.name;
  86. const importedModule = moduleGraph.getModule(dep);
  87. if (importedModule) {
  88. const usedName = moduleGraph
  89. .getExportsInfo(importedModule)
  90. .getUsedName(exportName, runtime);
  91. if (usedName) {
  92. initParams.push(
  93. runtimeTemplate.exportFromImport({
  94. moduleGraph,
  95. chunkGraph,
  96. module: importedModule,
  97. request: dep.request,
  98. importVar: importData.importVar,
  99. originModule: module,
  100. exportName: dep.name,
  101. asiSafe: true,
  102. isCall: false,
  103. callContext: null,
  104. defaultInterop: true,
  105. initFragments,
  106. runtime,
  107. runtimeRequirements,
  108. dependency: dep
  109. })
  110. );
  111. }
  112. }
  113. }
  114. }
  115. if (dep instanceof WebAssemblyExportImportedDependency) {
  116. importData.names.add(dep.name);
  117. const usedName = moduleGraph
  118. .getExportsInfo(module)
  119. .getUsedName(dep.exportName, runtime);
  120. if (usedName) {
  121. runtimeRequirements.add(RuntimeGlobals.exports);
  122. const exportProp = `${module.exportsArgument}[${JSON.stringify(
  123. usedName
  124. )}]`;
  125. const defineStatement = Template.asString([
  126. `${exportProp} = ${runtimeTemplate.exportFromImport({
  127. moduleGraph,
  128. module: /** @type {Module} */ (moduleGraph.getModule(dep)),
  129. chunkGraph,
  130. request: dep.request,
  131. importVar: importData.importVar,
  132. originModule: module,
  133. exportName: dep.name,
  134. asiSafe: true,
  135. isCall: false,
  136. callContext: null,
  137. defaultInterop: true,
  138. initFragments,
  139. runtime,
  140. runtimeRequirements,
  141. dependency: dep
  142. })};`,
  143. `if(WebAssembly.Global) ${exportProp} = ` +
  144. `new WebAssembly.Global({ value: ${JSON.stringify(
  145. dep.valueType
  146. )} }, ${exportProp});`
  147. ]);
  148. importData.reexports.push(defineStatement);
  149. needExportsCopy = true;
  150. }
  151. }
  152. }
  153. }
  154. const importsCode = Template.asString(
  155. Array.from(
  156. importedModules,
  157. ([module, { importVar, request, reexports, dependency }]) => {
  158. const importStatement = runtimeTemplate.importStatement({
  159. module,
  160. moduleGraph,
  161. chunkGraph,
  162. request,
  163. importVar,
  164. originModule: module,
  165. runtimeRequirements,
  166. dependency
  167. });
  168. return importStatement[0] + importStatement[1] + reexports.join("\n");
  169. }
  170. )
  171. );
  172. const copyAllExports =
  173. exportsInfo.otherExportsInfo.getUsed(runtime) === UsageState.Unused &&
  174. !needExportsCopy;
  175. // need these globals
  176. runtimeRequirements.add(RuntimeGlobals.module);
  177. runtimeRequirements.add(RuntimeGlobals.moduleId);
  178. runtimeRequirements.add(RuntimeGlobals.wasmInstances);
  179. if (exportsInfo.otherExportsInfo.getUsed(runtime) !== UsageState.Unused) {
  180. runtimeRequirements.add(RuntimeGlobals.makeNamespaceObject);
  181. runtimeRequirements.add(RuntimeGlobals.exports);
  182. }
  183. if (!copyAllExports) {
  184. runtimeRequirements.add(RuntimeGlobals.exports);
  185. }
  186. // create source
  187. const source = new RawSource(
  188. [
  189. '"use strict";',
  190. "// Instantiate WebAssembly module",
  191. `var wasmExports = ${RuntimeGlobals.wasmInstances}[${module.moduleArgument}.id];`,
  192. exportsInfo.otherExportsInfo.getUsed(runtime) !== UsageState.Unused
  193. ? `${RuntimeGlobals.makeNamespaceObject}(${module.exportsArgument});`
  194. : "",
  195. // this must be before import for circular dependencies
  196. "// export exports from WebAssembly module",
  197. copyAllExports
  198. ? `${module.moduleArgument}.exports = wasmExports;`
  199. : "for(var name in wasmExports) " +
  200. "if(name) " +
  201. `${module.exportsArgument}[name] = wasmExports[name];`,
  202. "// exec imports from WebAssembly module (for esm order)",
  203. importsCode,
  204. "",
  205. "// exec wasm module",
  206. `wasmExports[""](${initParams.join(", ")})`
  207. ].join("\n")
  208. );
  209. return InitFragment.addToSource(source, initFragments, generateContext);
  210. }
  211. /**
  212. * Generates fallback output for the provided error condition.
  213. * @param {Error} error the error
  214. * @param {NormalModule} module module for which the code should be generated
  215. * @param {GenerateContext} generateContext context for generate
  216. * @returns {Source | null} generated code
  217. */
  218. generateError(error, module, generateContext) {
  219. return new RawSource(
  220. Generator.throwBuildErrorCode(error, "WebAssembly.CompileError")
  221. );
  222. }
  223. }
  224. module.exports = WebAssemblyJavascriptGenerator;