WebAssemblyJavascriptGenerator.js 7.3 KB

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